Skip to content

Instantly share code, notes, and snippets.

@bakercp
Last active June 6, 2019 09:26
Show Gist options
  • Save bakercp/b443755a55b2cc1cbedaaeb75a93ddb6 to your computer and use it in GitHub Desktop.
Save bakercp/b443755a55b2cc1cbedaaeb75a93ddb6 to your computer and use it in GitHub Desktop.
An openFrameworks convex hull / defect index finder using opencv (because there is a bug in the opencv version). bug https://github.com/opencv/opencv/issues/4954
//
// Copyright (c) 2017 Christopher Baker <https://christopherbaker.net>
//
// SPDX-License-Identifier: MIT
//
#include "ConvexHull.h"
ConvexHull::ConvexHull()
{
}
ConvexHull::ConvexHull(const ofPolyline& contour)
{
setContour(contour);
}
void ConvexHull::setContour(const ofPolyline& contour)
{
_contour = contour;
std::vector<cv::Point2i> contour2i;
cv::Mat(ofxCv::toCv(_contour)).copyTo(contour2i);
cv::Mat contourMat(contour2i);
// By passing std::vector<int> we get indices.
std::vector<int> hullIndices;
cv::convexHull(contourMat, hullIndices);
if (hullIndices.size() > 0 && contour.size() > 0)
{
std::vector<cv::Vec4i> convexityDefects;
// TODO: has a bug
// cv::convexityDefects(contourMat, hullIndices, convexityDefects);
// Our local, drop-in, inefficient (?) hack.
ConvexHull::convexityDefects(contourMat, hullIndices, convexityDefects);
_convexHullDefects.clear();
for (auto defect: convexityDefects)
{
Defect _defect;
_defect.startIndex = defect[0];
_defect.endIndex = defect[1];
_defect.index = defect[2];
_defect.depth = defect[3] / 256.0;
_convexHullDefects.push_back(_defect);
}
}
_convexHullIndices.clear();
for (auto index: hullIndices)
{
if (index > -1)
{
_convexHullIndices.push_back(index);
}
else
{
ofLogWarning("ConvexHull::setContour") << "Found negative convex hull index, skipping.";
}
}
}
ofPolyline ConvexHull::contour() const
{
return _contour;
}
ofPolyline ConvexHull::convexHull() const
{
if (_convexHullIndices.size() != 0 && _convexHull.size() != _convexHullIndices.size())
{
_convexHull.clear();
_convexHull.setClosed(true);
for (auto index: _convexHullIndices)
{
_convexHull.addVertex(_contour[index]);
}
}
return _convexHull;
}
std::vector<std::size_t> ConvexHull::convexHullIndices() const
{
return _convexHullIndices;
}
std::vector<ConvexHull::Defect> ConvexHull::convexHullDefects() const
{
return _convexHullDefects;
}
void ConvexHull::convexityDefects(const cv::Mat& _contourMat,
const std::vector<int>& _hullIndices,
std::vector<cv::Vec4i>& convexityDefects)
{
// Make a sorted version of the hull indices.
auto hullIndices = _hullIndices;
std::sort(hullIndices.begin(), hullIndices.end());
CvMat contourMat = _contourMat;
CvMat hullMat = cvMat(1, hullIndices.size(), CV_32SC1, reinterpret_cast<void*>(&hullIndices[0]));
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* defects = cvConvexityDefects(&contourMat, &hullMat, storage);
for (auto i = 0; i < defects->total; ++i)
{
CvConvexityDefect* cur = reinterpret_cast<CvConvexityDefect*>(cvGetSeqElem(defects, i));
// Find the two convex hull indices.
int startIndex = -1;
int endIndex = -1;
for (int hullIndex: hullIndices)
{
auto point = _contourMat.at<cv::Point2i>(hullIndex);
if (startIndex < 0 || endIndex < 0)
{
if (startIndex < 0 &&
cur->start->x == point.x &&
cur->start->y == point.y)
{
startIndex = hullIndex;
}
if (endIndex < 0 &&
cur->end->x == point.x &&
cur->end->y == point.y)
{
endIndex = hullIndex;
}
}
else
{
break;
}
}
// Find the defec index by searching between the start and end hull indices.
int defectIndex = -1;
for (int index = startIndex; index <= std::max(_contourMat.total(), std::size_t(endIndex)); ++index)
{
auto point = _contourMat.at<cv::Point2i>(index);
if (cur->depth_point->x == point.x &&
cur->depth_point->y == point.y)
{
defectIndex = index;
break;
}
}
if (startIndex >= 0 && endIndex >= 0 && defectIndex >= 0)
{
cv::Vec4i defect;
defect[0] = startIndex;
defect[1] = endIndex;
defect[2] = defectIndex;
defect[3] = cur->depth * 256.0; // Convert it, to match C++ api.
convexityDefects.push_back(defect);
}
else
{
ofLogError("ConvexHull::convexityDefects") << "Invalid index.";
}
}
cvReleaseMemStorage(&storage);
}
//
// Copyright (c) 2017 Christopher Baker <https://christopherbaker.net>
//
// SPDX-License-Identifier: MIT
//
#pragma once
#include "ofxCv.h"
class ConvexHull
{
public:
struct Defect
{
/// \brief The index of the point in the original contour.
std::size_t index = 0;
/// \brief The index of the defect start point in the original contour.
///
/// This point corresponds to a point in the convex hull.
std::size_t startIndex = 0;
/// \brief The index of the defect end point in the original contour.
///
/// This point corresponds to a point in the convex hull.
std::size_t endIndex = 0;
/// \brief The perpendicular intercept between.
///
/// The vector describing the perpendicular
glm::vec2 defectHullChordNormal;
/// \brief The depth of the defect
///
/// The normal distance from the hull to the defect.
float depth = 0;
};
/// \brief Create a convex hull.
ConvexHull();
/// \brief Create a cotext hull.
/// \param contour The contour to analyze.
ConvexHull(const ofPolyline& contour);
void setContour(const ofPolyline& contour);
/// \brief Get the original contour.
ofPolyline contour() const;
/// \brief Create the convex hull from the contour.
ofPolyline convexHull() const;
/// \returns the indices of the contour that represent the convex hull.
std::vector<std::size_t> convexHullIndices() const;
/// \returns the indices of the contour that represent the convex hull defects.
std::vector<Defect> convexHullDefects() const;
private:
/// \brief The original contour.
ofPolyline _contour;
/// \brief The indices of the contour that represent the convex hull.
std::vector<std::size_t> _convexHullIndices;
/// \brief The convex hull.
mutable ofPolyline _convexHull;
/// \brief the indices of the contour that represent the convex hull defects.
std::vector<Defect> _convexHullDefects;
/// \brief Custom C++ implementation that finds indices.
static void convexityDefects(const cv::Mat& contourMat,
const std::vector<int>& hullIndices,
std::vector<cv::Vec4i>& convexityDefects);
};
@thisissebastian
Copy link

Thanks, lightning speed!
So most likely an issue with Visual Studio? You developed/tested this on OSX I'm afraid?

@bakercp
Copy link
Author

bakercp commented Jun 5, 2019

Yes, I developed and tested on macos and Linux. So it definitely sounds like a VS bug.

@thisissebastian
Copy link

sweet! was a VS bug, thanks a lot for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment