Last active
June 6, 2019 09:26
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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); | |
}; |
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
Yes, I developed and tested on macos and Linux. So it definitely sounds like a VS bug.