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

时间:2016-09-29 12:29:22

标签: c++ opencv opencv-contour

假设我们正在处理图像,有没有办法访问轮廓内的像素?

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

任何建议都欢迎!!

谢谢!

3 个答案:

答案 0 :(得分: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);
                }
            }

        }
    }   
}

修改

由于您使用的是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;
}

其次,你可以用洪水填充你自己的工作。因此,您遍历图像并开始每个白色像素的填充,迭代边界矩形并获得具有相同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++;
        }
    }
}

我用过这张图片:

enter image description here

以下是边界框和轮廓内的点可视化:

enter image description here

答案 1 :(得分:0)

答案 2 :(得分:0)

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

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

然后使用findNonZero找到该图像中的非零像素。

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

&#34;指数&#34;结果是指轮廓内的像素