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

Blo*_*klo 5 c++ opencv opencv-contour

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

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

任何建议都欢迎!!

谢谢!

PSc*_*chn 5

正如@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);
                }
            }

        }
    }   
}
Run Code Online (Sandbox Code Playgroud)

编辑:

由于您使用的是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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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++;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用了这张图片:

在此处输入图片说明

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

在此处输入图片说明