Search code examples
c++opencvcontour

OpenCV : How to find the pixels inside a contour in c++


Suppose if we are working on an image, is there any way to access the pixels inside the contour?

I have already found the contour using the function findContours() and even found the moments but I couldn't find the pixels inside the contour.

Any suggestions are Welcome!!

Thank you!


Solution

  • As @Miki already mentioned you can use connectedComponents to perform a labeling. Then you iterate through the bounding box of your object like @Amitay Nachmani suggested. But instead of using pointPolygonTest you can check if the value at your current positions matches your current label Here is a small example:

    #include "opencv2/imgproc.hpp"
    #include "opencv2/highgui.hpp"
    #include <vector>
    
    using namespace cv;
    using namespace std;
    
    Mat binary, labels, stats, centroids;
    int main()
    {   
        Mat src = imread("C:\\Users\\phili\\Pictures\\t06-4.png",0);    
        threshold(src, binary, 0, 255, CV_THRESH_OTSU);
        int nLabels = connectedComponentsWithStats(binary, labels, stats, centroids);
        vector<vector<Point>> blobs(nLabels-1); 
        for (int i = 1; i < nLabels; i++) //0 is background
        {       
            //get bounding rect
            int left =  stats.at<int>(i, CC_STAT_LEFT) ;
            int top = stats.at<int>(i, CC_STAT_TOP);
            int width = stats.at<int>(i, CC_STAT_WIDTH);
            int height = stats.at<int>(i, CC_STAT_HEIGHT);
    
            blobs[i - 1].reserve(width*height);     
            int x_end = left + width;
            int y_end = top + height;
            for (int x = left; x < x_end; x++)
            {
                for (int y = top; y < y_end; y++)
                {
                    Point p(x, y);              
                    if (i == labels.at<int>(p))
                    {                   
                        blobs[i-1].push_back(p);
                    }
                }
    
            }
        }   
    }
    

    EDIT:

    Since youre using OpenCV 2.4 there are two ways to achieve the same results. First you could use findContours to detect the blobs, then draw them (filled) into a new image with a specific color as label (be aware that your blobs could contain holes) Then iterate through the image inside the bounding rectangle of each contour and get all points with the label of your current contour. If you just iterate through the bounding rectangle inside your binary image, you have problems with objects overlapping the bounding rectangle. Here is the code:

    int getBlobs(Mat binary, vector<vector<Point>> & blobs)
    {   
        Mat labels(src.size(), CV_32S);     
        vector<vector<Point>> contours;
        vector<Vec4i> hierarchy;            
        findContours(binary, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
        blobs.clear();
        blobs.reserve(contours.size());
        int count = 1; //0 is background
        for (int i = 0; i < contours.size(); i++) // iterate through each contour.
        {
            //if contour[i] is not a hole
            if (hierarchy[i][3] == -1)
            {                       
                //draw contour without holes    
                drawContours(labels, contours, i, Scalar(count),CV_FILLED, 0, hierarchy, 2, Point());
                Rect rect = boundingRect(contours[i]);          
                int left = rect.x;
                int top = rect.y;
                int width = rect.width;
                int height = rect.height;           
                int x_end = left + width;
                int y_end = top + height;
                vector<Point> blob;                 
                blob.reserve(width*height);
                for (size_t x = left; x < x_end; x++)
                {
                    for (size_t y = top; y < y_end; y++)
                    {
                        Point p(x, y);
                        if (count == labels.at<int>(p))
                        {
                            blob.push_back(p);                      
                        }
                    }
                }
                blobs.push_back(blob);
                count++;
            }
    
        }
        count--;    
        return count;
    }
    

    Second you can perform your own labling with floodfill. Therefore you iterate through your image and start floodfill for every white pixel, iterate through the bounding rectangle and get all points that have the same seedColor. Here is the code:

    int labeling(Mat binary, vector<vector<Point>> &blobs)
    {   
        FindBlobs(binary, blobs);   
        return blobs.size();
    }
    

    with

    void FindBlobs(const Mat &binary, vector<vector<Point>> &blobs)
    {
        blobs.clear();
        // Fill the label_image with the blobs
        // 0  - background
        // 1  - unlabelled foreground
        // 2+ - labelled foreground
        cv::Mat label_image;
        binary.convertTo(label_image, CV_32FC1);    
        float label_count = 2; // starts at 2 because 0,1 are used already
        for (int y = 0; y < label_image.rows; y++) {
            float *row = (float*)label_image.ptr(y);
            for (int x = 0; x < label_image.cols; x++) {            
                if (row[x] != 255) {
                    continue;
                }
                cv::Rect rect;
                cv::floodFill(label_image, Point(x, y), Scalar(label_count), &rect, Scalar(0), Scalar(0), 4 );                  
                vector<Point> blob;
                blob.reserve(rect.width*rect.height);
    
                for (int i = rect.y; i < (rect.y + rect.height); i++) {
                    float *row2 = (float*)label_image.ptr(i);
                    for (int j = rect.x; j < (rect.x + rect.width); j++) {
                        if (row2[j] != label_count) 
                        {
                            continue;
                        }
                        blob.push_back(Point(j, i));
                    }
                }
    
                blobs.push_back(blob);
                label_count++;
            }
        }
    }
    

    I used this image:

    enter image description here

    And here are the bounding boxes and the points inside the contour for visualization:

    enter image description here