Calculating convexityDefects using OpenCV 2.4 in c++
From openCV wiki :
Finds the convexity defects of a contour.
So you should include it in your loop.
std::vector<Vec4i> defects;
vector<cv::vector<int> >hull( contours.size() );
for (int i = 0; i < contours.size(); i++)
{
convexHull( contours[i], hull[i], false );
convexityDefects(contours[i], hull[i], defects[i]);
}
Also, as you mentioned, in wiki is said:
hull – Output convex hull. It is either an integer vector of indices
or vector of points. In the first case, the hull elements are 0-based
indices of the convex hull points in the original array (since the set
of convex hull points is a subset of the original point set). In the
second case, hull elements are the convex hull points themselves.
OpenCv Convexity Defects
std::vector<Vec4i> defects;
should be
std::vector< std::vector<Vec4i> > defects( contours.size() );
(one for each contour / hull)
C++ OpenCv error in function ConvexityDefects
Okay, the problem was mainly because due to some odd reason i had contours so small that the hull was merely a straight line (meaning consisting only of 2 points).
So the error was referencing to the size of the hull vector, other than in the other posts where it seemed to have something to do with the type of vector.
So, just replacing
if(contours[i].size() > 3 )
with
if(hulls[i].size() > 2 )
works fine.
OpenCV C++ cv::convexityDefects error
The second argument of convexityDefects
has to be the type of vector<vector<int>
, while yours is vector<Point>
.
OpenCV convexity defects drawing
convexityDefects needs a
Convex hull obtained using convexHull() that should contain indices of the contour points that make the hull.
that contains more than 3 indices. So you need this:
vector<vector<Point> >hull(Contours.size());
vector<vector<int> > hullsI(Contours.size()); // Indices to contour points
vector<vector<Vec4i>> defects(Contours.size());
for (int i = 0; i < Contours.size(); i++)
{
convexHull(Contours[i], hull[i], false);
convexHull(Contours[i], hullsI[i], false);
if(hullsI[i].size() > 3 ) // You need more than 3 indices
{
convexityDefects(Contours[i], hullsI[i], defects[i]);
}
}
Then your drawing part is (adapted from here):
/// Draw convexityDefects
for (int i = 0; i < Contours.size(); ++i)
{
for(const Vec4i& v : defects[i])
{
float depth = v[3] / 256;
if (depth > 10) // filter defects by depth, e.g more than 10
{
int startidx = v[0]; Point ptStart(Contours[i][startidx]);
int endidx = v[1]; Point ptEnd(Contours[i][endidx]);
int faridx = v[2]; Point ptFar(Contours[i][faridx]);
line(frame, ptStart, ptEnd, Scalar(0, 255, 0), 1);
line(frame, ptStart, ptFar, Scalar(0, 255, 0), 1);
line(frame, ptEnd, ptFar, Scalar(0, 255, 0), 1);
circle(frame, ptFar, 4, Scalar(0, 255, 0), 2);
}
}
}
Complete code
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img, frame, img2, img3;
VideoCapture cam(0);
while (true){
cam.read(frame);
cvtColor(frame, img, CV_BGR2HSV);
//thresholding
inRange(img, Scalar(0, 143, 86), Scalar(39, 255, 241), img2);
imshow("hi", img2);
//finding contours
vector<vector<Point>> Contours;
vector<Vec4i> hier;
//morphological transformations
erode(img2, img2, getStructuringElement(MORPH_RECT, Size(3, 3)));
erode(img2, img2, getStructuringElement(MORPH_RECT, Size(3, 3)));
dilate(img2, img2, getStructuringElement(MORPH_RECT, Size(8, 8)));
dilate(img2, img2, getStructuringElement(MORPH_RECT, Size(8, 8)));
//finding the contours required
findContours(img2, Contours, hier, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, Point(0, 0));
//finding the contour of largest area and storing its index
int lrgctridx = 0;
int maxarea = 0;
for (int i = 0; i < Contours.size(); i++)
{
double a = contourArea(Contours[i]);
if (a> maxarea)
{
maxarea = a;
lrgctridx = i;
}
}
//convex hulls
vector<vector<Point> >hull(Contours.size());
vector<vector<int> > hullsI(Contours.size());
vector<vector<Vec4i>> defects(Contours.size());
for (int i = 0; i < Contours.size(); i++)
{
convexHull(Contours[i], hull[i], false);
convexHull(Contours[i], hullsI[i], false);
if(hullsI[i].size() > 3 )
{
convexityDefects(Contours[i], hullsI[i], defects[i]);
}
}
//REQUIRED contour is detected,then convex hell is found and also convexity defects are found and stored in defects
if (maxarea>100){
drawContours(frame, hull, lrgctridx, Scalar(2555, 0, 255), 3, 8, vector<Vec4i>(), 0, Point());
/// Draw convexityDefects
for(int j=0; j<defects[lrgctridx].size(); ++j)
{
const Vec4i& v = defects[lrgctridx][j];
float depth = v[3] / 256;
if (depth > 10) // filter defects by depth
{
int startidx = v[0]; Point ptStart(Contours[lrgctridx][startidx]);
int endidx = v[1]; Point ptEnd(Contours[lrgctridx][endidx]);
int faridx = v[2]; Point ptFar(Contours[lrgctridx][faridx]);
line(frame, ptStart, ptEnd, Scalar(0, 255, 0), 1);
line(frame, ptStart, ptFar, Scalar(0, 255, 0), 1);
line(frame, ptEnd, ptFar, Scalar(0, 255, 0), 1);
circle(frame, ptFar, 4, Scalar(0, 255, 0), 2);
}
}
}
imshow("output", frame);
char key = waitKey(33);
if (key == 27) break;
}
}
Convexity defects C++ OpenCv
I raised this question because I wasn't able to figure out a solution (it is not only today that I was dealing with the matter hehe), but after all I was able to manage the problem!
I had to change the way I calculated the convex hull, using the index array form. So now we have a vector< int > instead a vector< Point >.
This is the code I used (it works I painted the points over an image):
void HandDetection::findConvexityDefects(vector<Point>& contour, vector<int>& hull, vector<Point>& convexDefects){
if(hull.size() > 0 && contour.size() > 0){
CvSeq* contourPoints;
CvSeq* defects;
CvMemStorage* storage;
CvMemStorage* strDefects;
CvMemStorage* contourStr;
CvConvexityDefect *defectArray = 0;
strDefects = cvCreateMemStorage();
defects = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq),sizeof(CvPoint), strDefects );
//We transform our vector<Point> into a CvSeq* object of CvPoint.
contourStr = cvCreateMemStorage();
contourPoints = cvCreateSeq(CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), contourStr);
for(int i=0; i<(int)contour.size(); i++) {
CvPoint cp = {contour[i].x, contour[i].y};
cvSeqPush(contourPoints, &cp);
}
//Now, we do the same thing with the hull index
int count = (int)hull.size();
//int hullK[count];
int* hullK = (int*)malloc(count*sizeof(int));
for(int i=0; i<count; i++){hullK[i] = hull.at(i);}
CvMat hullMat = cvMat(1, count, CV_32SC1, hullK);
//We calculate convexity defects
storage = cvCreateMemStorage(0);
defects = cvConvexityDefects(contourPoints, &hullMat, storage);
defectArray = (CvConvexityDefect*)malloc(sizeof(CvConvexityDefect)*defects->total);
cvCvtSeqToArray(defects, defectArray, CV_WHOLE_SEQ);
//printf("DefectArray %i %i\n",defectArray->end->x, defectArray->end->y);
//We store defects points in the convexDefects parameter.
for(int i = 0; i<defects->total; i++){
CvPoint ptf;
ptf.x = defectArray[i].depth_point->x;
ptf.y = defectArray[i].depth_point->y;
convexDefects.push_back(ptf);
}
//We release memory
cvReleaseMemStorage(contourStr);
cvReleaseMemStorage(strDefects);
cvReleaseMemStorage(storage);
}
}
This worked for me. If you see something wrong or another way to manage it, please tell me!
finding convexity defects in opencv? [crashes depending on the given input image..]
cvConvexityDefects
expects the convexHull
sequence (second argument) to contain indices into the contour
sequence (first argument):
Convex hull obtained using ConvexHull2 that should contain pointers or indices to the contour points, not the hull points themselves
In the most trivial case, where
cvFindContours
returns a single simple contour (your second image) you got lucky and your code would supply the correct sequence as the first parameter.In case
cvFindContours
finds holes in the contour (your third image), or if there are several simple contours or contours with holes (your first image) your code:finds a convex hull of each contour in turn, but only remembers the last one (since each iteration of the loop overwrites
retHulls
variable)passes the whole hierarchy of the contours, which doesn't correspond to indices in
retHulls
, tocvConvexityDefects
as the first argument.
Instead, you should have:
passed
CV_RETR_EXTERNAL
to thecvFindContour
to only get the outer contours (you don't care for defects of holes)moved the
cvConvexityDefects
inside the last loop.
Something like:
/* ... */
if (argc < 2) {
std::cerr << "Usage: convexity IMAGE\n";
exit(1);
}
cvNamedWindow( "original", 1 );
cvNamedWindow( "contours", 1 );
cvNamedWindow( "hull", 1 );
IplImage* original_img = NULL;
original_img = cvLoadImage(argv[1], CV_LOAD_IMAGE_GRAYSCALE );
IplImage* img_edge = cvCreateImage( cvGetSize(original_img), 8, 1 );
IplImage* contour_img = cvCreateImage( cvGetSize(original_img), 8, 3 );
IplImage* hull_img = cvCreateImage( cvGetSize(original_img), 8, 3 );
cvThreshold( original_img, img_edge, 128, 255, CV_THRESH_BINARY );
CvMemStorage* storage = cvCreateMemStorage();
CvSeq* first_contour = NULL;
int Nc = cvFindContours(
img_edge,
storage,
&first_contour,
sizeof(CvContour),
CV_RETR_EXTERNAL // Try all four values and see what happens
);
cvCvtColor( original_img, contour_img, CV_GRAY2BGR );
for( CvSeq* c=first_contour; c!=NULL; c=c->h_next ) {
cvDrawContours(
contour_img,
c,
CVX_RED,
CVX_BLUE,
0,
2,
8
);
}
cvShowImage( "contours", contour_img );
//----------------------------------------------------------------------Convex Hull
//-------------------------------------------------------------------Convex Defects
CvMemStorage* hull_storage = cvCreateMemStorage();
CvSeq* retHulls = NULL;
cvCvtColor( original_img, hull_img, CV_GRAY2BGR );
for(CvSeq* i = first_contour; i != NULL; i = i->h_next){
retHulls = cvConvexHull2(i,hull_storage,CV_CLOCKWISE,0);
printf(" %d elements:\n", retHulls->total );
CvSeq* defect = NULL;
defect = cvConvexityDefects(i,retHulls, NULL); // reuse storage of the contour
printf(" %d defect:\n", defect->total );
// drawing hull.... you can't use the one returned above since it only
// contains indices
retHulls = cvConvexHull2(i,hull_storage,CV_CLOCKWISE,1);
cvDrawContours(
hull_img,
retHulls,
CVX_RED,
CVX_BLUE,
0,
2,
8
);
}
cvShowImage( "hull", hull_img );
/* ... */
Related Topics
On How to Recognize Rvalue or Lvalue Reference and If-It-Has-A-Name Rule
Serializing Opencv Mat_<Vec3F>
Is the Behavior of Subtracting Two Null Pointers Defined
Why Is Std::Iterator Deprecated
How to Make Template Rvalue Reference Parameter Only Bind to Rvalue Reference
Createprocess Doesn't Pass Command Line Arguments
Program Can't Find Libgcc_S_Dw2-1.Dll
Why, Really, Deleting an Incomplete Type Is Undefined Behaviour
Warning: Returning Reference to Temporary
How to Implement Timeout for Function in C++
How to Generate Random Numbers in C++
Multiple Implicit Conversions on Custom Types Not Allowed
Critique My Non-Intrusive Heap Debugger
Pass by Reference and Value with Pointers
C++ Visual Studio "Non-Standard Syntax; Use '&' to Create a Pointer to Member"