Search code examples
c++objective-copencvdrawingcontour

Filter OpenCV Contours by inside colors


I have a problem with filtering some contours by colors in it. I want to remove all contours, which has black pixels inside and keep only contours with white pixels (see pictures below).

Code to create a contours list. I've used a RETR_TREE contour retrieval mode with CHAIN_APPROX_SIMPLE points selection to avoid a lot of points inside contours.

cv::cvtColor(src_img, gray_img, cv::COLOR_BGR2GRAY);
cv::threshold(gray_img, bin_img, minRGB, maxRGB, cv::THRESH_BINARY_INV);

std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;

cv::findContours(bin_img, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);

Then, using these contours, I've built closed paths and display them on the screen.

An input image:

Input image

Current my results:

current result

What I need. Fill only contours, which has white content.

enter image description here

I've tried to scale all contours to 1 pixel inside and check if all the pixels equal to dark, but it doesn't work as I've expected. See the code below.

double scaleX = (double(src_img.cols) - 2) / double(src_img.cols);
double scaleY = (double(src_img.rows) - 2) / double(src_img.rows);

for (int i = 0; i < contours.size(); i++) {
    std::vector<cv::Point> contour = contours[i];

    cv::Moments M = cv::moments(contour);

    int cx = int(M.m10 / M.m00);
    int cy = int(M.m01 / M.m00);

    std::vector<cv::Point> scaledContour(contour.size());

    for (int j = 0; j < contour.size(); j++) {
        cv::Point point = contour[j];
        point = cv::Point(point.x - cx, point.y - cy);
        point = cv::Point(point.x * scaleX, point.y * scaleY);
        point = cv::Point(point.x + cx, point.y + cy);
        scaledContour[j] = point;
    }

    contours[i] = scaledContour;
}

I will be very grateful if you help with any ideas or solutions, thank you very much!


Solution

  • Hopefully, one thing is clear that the objects in the image should be white and the background black when finding contours that you have done by using THRESH_BINARY_INV.

    So we are essentially trying to find white lines and not black. I am not providing the code as I work in python but I'll list it out how it can be done.

    1. Create a black array of the size of the input image. Let's call it mask.
    2. After finding the contours, draw them on mask with white i.e. 255, while providing thickness=-1. This means we are essentially filling the contour.
    3. Now we need to remove the boundary of the contour so the only portion left is the part inside the contour. This can be achieved by again drawing the contour on mask, this time with black with a thickness of 1.
    4. Perform bitwise_and between the image and mask. Only areas having white inside the contour will be left.

    Now you just need to see whether the output is completely black or not. If it is not that means you don't need to fill that contour as it contains something inside it.

    EDIT

    Ohh I didn't realize that your images would be having 600 contours, yes it will take a lot of time for that and I don't know why I didn't think of using hierarchy before.

    You can use RETR_TREE itself and the hierarchy values are [next, previous, first_child, parent]. So we just need to check if the value of first_child=-1, that would mean there are no contours inside and you can fill it.