OpenCV:如何在 C++ 中找到轮廓内的像素

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

假设我们正在处理图像,是否有任何方法可以访问轮廓内的像素?

我已经使用函数 findContours() 找到了轮廓,甚至找到了时刻,但我找不到轮廓内的像素。

欢迎提出任何建议!!

谢谢!

在 contour 轮廓边界框内的所有像素上使用 pointPolygonTest http://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=pointpolygontest#pointpolygontest

正如@Miki 已经提到的,您可以使用 connectedComponents 来执行标记。然后像@Amitay Nachmani 建议的那样遍历对象的边界框。但是,您可以不使用 pointPolygonTest 检查当前位置的值是否与当前标签匹配这是一个小示例:

#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);
                }
            }

        }
    }   
}

编辑:

由于您使用的是 OpenCV 2.4,因此有两种方法可以达到相同的结果。 首先,您可以使用 findContours 来检测斑点,然后将它们绘制(填充)到具有特定颜色作为标签的新图像中(请注意,您的斑点可能包含孔洞)然后遍历每个轮廓的边界矩形内的图像并获得所有带有当前轮廓标签的点。如果您只是遍历二值图像内的边界矩形,则会遇到对象与边界矩形重叠的问题。 这是代码:

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;
}

其次,您可以使用 floodfill 执行自己的标记。因此,您遍历图像并为每个白色像素开始填充,遍历边界矩形并获取具有相同 seedColor 的所有点。 这是代码:

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

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++;
        }
    }
}

我使用了这张图片:

这里是用于可视化的边界框和轮廓内的点:

使用 fillPoly 创建具有填充轮廓的新图像。

     fillPoly(filledImage, contours, Scalar(255, 255, 255));

然后使用 findNonZero 在该图像中找到 non-zero 像素。

    vector<Point> indices;
    findNonZero(filledImage, indices);

"indices"结果指的是轮廓内的像素