OpenCV last convexity defect not right

434 views Asked by At

I am trying to write code to track hands. I am using the convexity defects function to find fingers, but for some reason, there seems to always be a problem with the last defect.

Here is a picture of what I'm talking about (sorry, i''m new to the forum, so cannot post images)

The cyan line is the contours, the yellow line is the hull points, and the red lines are the defect points. As you can see the last defect point detects the defect from the wrong side of the contour.

Here is my code:

#include "opencv2\opencv.hpp"

using namespace cv;
using namespace std;

int main() {
    VideoCapture cap(0);
    Mat src, gray, background, binary, diff;
    cap >> background;
    cvtColor(background, background, CV_BGR2GRAY);
    vector<vector<Point>> contours;
    vector < vector<int>> hullI = vector<vector<int>>(1);
    vector < vector<Point>> hullP = vector<vector<Point>>(1);
    vector<Vec4i> defects;
    while (waitKey(30)!='q') {
        cap >> src;
        cvtColor(src, gray, CV_BGR2GRAY);
        blur(gray, gray, Size(3, 3));
        absdiff(gray, background, diff);
        threshold(diff, binary, 15, 255, THRESH_BINARY);
        erode(binary, binary, Mat(Size(5, 5), CV_8U));

        imshow("binary", binary);

        findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
        if (!contours.empty()) {
            sort(contours.begin(), contours.end(), [](vector<Point> a, vector<Point> b) { return a.size() > b.size(); });
            drawContours(src, contours, 0, Scalar(255, 255, 0));

            convexHull(contours[0], hullI[0]);
            convexHull(contours[0], hullP[0]);
            drawContours(src, hullP, 0, Scalar(0, 255, 255));

            if (hullI[0].size() > 2) {
                convexityDefects(contours[0], hullI[0], defects);

                for (Vec4i defect : defects) {
                    line(src, contours[0][defect[0]], contours[0][defect[2]], Scalar(0, 0, 255));
                    line(src, contours[0][defect[1]], contours[0][defect[2]], Scalar(0, 0, 255));
                }
            }
        }
        imshow("src", src);
        char key = waitKey(30);
        if (key == 'q')break;
        else if (key == 'p') waitKey();
        else if (key == 'b') {
            cap >> background;
            cvtColor(background, background, CV_BGR2GRAY);
        }
    }
}

I have confirmed through experiments that it is always the last defect in the defect vector that this happens too. Is this a bug in opencv or am I doing something wrong?

2

There are 2 answers

1
sturkmen On BEST ANSWER

i tested your code (with a small modification) with the image below (OpenCV version is 3.2).

as you can see on the result image it works as expected. probably you are using an old version of OpenCV and getting a buggy result. (i think it was a bug recently fixed)

enter image description here

enter image description here

#include "opencv2\opencv.hpp"

using namespace cv;
using namespace std;

int main() {
    //VideoCapture cap(0);
    Mat src, gray, background, binary, diff;
    //cap >> background;
    //cvtColor(background, background, CV_BGR2GRAY);
    vector<vector<Point> > contours;
    vector < vector<int> > hullI = vector<vector<int> >(1);
    vector < vector<Point> > hullP = vector<vector<Point> >(1);
    vector<Vec4i> defects;
        src = imread("hand.png");
        cvtColor(src, gray, CV_BGR2GRAY);
        blur(gray, gray, Size(3, 3));
        threshold(gray, binary, 150, 255, THRESH_BINARY_INV);
        //erode(binary, binary, Mat(Size(5, 5), CV_8U));
        imshow("binary", binary);
        findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
        if (!contours.empty()) {
            sort(contours.begin(), contours.end(), [](vector<Point> a, vector<Point> b) { return a.size() > b.size(); });
            drawContours(src, contours, 0, Scalar(255, 255, 0));

            convexHull(contours[0], hullI[0]);
            convexHull(contours[0], hullP[0]);
            drawContours(src, hullP, 0, Scalar(0, 255, 255));

            if (hullI[0].size() > 2) {
                convexityDefects(contours[0], hullI[0], defects);

                for (Vec4i defect : defects) {
                    line(src, contours[0][defect[0]], contours[0][defect[2]], Scalar(0, 0, 255));
                    line(src, contours[0][defect[1]], contours[0][defect[2]], Scalar(0, 0, 255));
                }
            }
        }
        imshow("result", src);
        char key = waitKey(0);
        return 0;
    }
0
Jeru Luke On

I have a solution which involves detecting the skin using OpenCV. I implemented it using python, which you can convert easily to C++.

I obtained the HSV values of the image you uploaded using:

hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

This the range for the HSV values of human skin:

l = np.array([0, 48, 80], dtype = "uint8")
u = np.array([20, 255, 255], dtype = "uint8")

skin_img = cv2.inRange(hsv_img, l, u)
cv2.imshow("Hand", skin_img)

I then performed morphological dilation and obtained the following:

enter image description here

You can now apply contour hull and find convexity defects.