Opencv with Python: pointPolygonTest gives obvious

2019-08-03 03:22发布

问题:

I have a contour below. As I wanted to extract all the pixels inside this contour and black out everything else to remove noise, I used cv2.pointPolygonTest for the purpose.

Below are the code I used to attempt to create the mask.

inside_or_not = np.zeros(img.shape[:2],dtype = np.int32)
for i in range(0,img.shape[0]):
    for j in range(0,img.shape[1]):
        inside_or_not[i,j] = cv2.pointPolygonTest(body_line,(i,j),False)

Only 2 points were found to be inside the point. On top of that, I expect the number of points lying on the contour, hence returning 0 from the cv2.pointPolygonTest should match the number of pixels defining the contour. However when I run sum(sum(inside_or_not == 0)), it does not match the No. of pixels on the contour. I also used mouse click to click a point obviously inside the contour and put that point into the test; but -1 is returned indicating the test failed.

I also used a approxPolyDP function to attempt to approximate the contour with less vertices. This time a bit more points were returned. However I have no idea why!

Any help is appreciated, thanks.

回答1:

Since you have a well defined contour, you can simply use findContour with CV_RETR_EXTERNAL to detect the external contour, and draw the region inside it with drawContour(..., CV_FILLED). Once you have the mask, you can use copyTo to copy only the parts of your original image according to the mask.

Here an example, it's C++, but the porting to python is straightforward.

#include <opencv2\opencv.hpp>
#include <vector>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    // Load image
    Mat3b img = imread("path_to_image_shape");

    // Just to have a background image the same size of your image.
    Mat3b bkg = imread("path_to_image_background");
    resize(bkg, bkg, img.size());

    // Convert to gray
    Mat1b gray;
    cvtColor(img, gray, COLOR_BGR2GRAY);

    // Get external contour
    vector<vector<Point>> contours;
    findContours(gray.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    // contours has size 1 here, you need to check this in a real application

    // Create a mask with the inner part of the contour
    Mat1b mask(img.size(), uchar(0));
    drawContours(mask, contours, 0, Scalar(255), CV_FILLED);

    // Create a black RGB image
    Mat3b res(img.size(), Vec3b(0,0,0));

    // Copy the image according to the mask
    bkg.copyTo(res, mask);

    imshow("result", res);
    waitKey();

    return 0;
}