简体   繁体   English

如何在OpenCV中计算颜色段中的像素

[英]How to count pixels in color segment in OpenCV

I have a OpenCV C++ application. 我有一个OpenCV C ++应用程序。 I have segmented an image with pyrMeanShiftFiltering function. 我已经用pyrMeanShiftFiltering函数对图像进行了分割。 Now I need to count the pixel in a segment and the number of pixel having the most frequent value in the same segment in order to compute a ratio between them. 现在,我需要计算一个段中的像素和同一段中具有最频繁值的像素数,以便计算它们之间的比率。 How could I do that? 我该怎么办?

I am using Tsukuba image and the code is. 我正在使用筑波图像,代码是。
筑波

Mat image, segmented;
image = imread("TsukubaL.jpg", 1 );
pyrMeanShiftFiltering(image, segmented, 16, 32);

The segmented image is: 分割后的图像为:
分段筑波
If I consider a pixel in a single segment, the part where I count the pixel in that segment is: 如果考虑单个段中的像素,则我对该段中的像素进行计数的部分是:

int cont=0;
Vec3b x = segmented.at<Vec3b>(160, 136);
for(int i = 160; i < segmented.rows; ++i) { //check right-down
    for(int j = 136; j < segmented.cols; ++j) {
        if(segmented.at<Vec3b>(i, j) == x)
            cont++;
        else
            continue;
    }
}
for(int i = 160; i > 0; --i) {  //check right-up
    for(int j = 136; j < segmented.cols; ++j) {
        if(segmented.at<Vec3b>(i, j) == x)
            cont++;
        else
            continue;
    }
}
for(int i = 160; i < segmented.rows; ++i) { //check down-left
    for(int j = 136; j > 0; --j) {
        if(segmented.at<Vec3b>(i, j) == x)
            cont++;
        else
            continue;
    }
}
for(int i = 160; i > 0; --i) {  //check up-left
    for(int j = 136; j > 0; --j) {
        if(segmented.at<Vec3b>(i, j) == x)
            cont++;
        else
            continue;
    }
}
cout<<"Pixel "<<x<<"cont = "<<cont<<endl;  

In this example, I consider a white pixel in position (160, 136) and count the same pixel to the central one in the four direction starting from it, and the output is: 在此示例中,我考虑位置(160,136)处的白色像素,并从该像素开始在四个方向上将相同像素计数到中心像素,输出为:

Pixel [206, 222, 240]cont = 127 像素[206,222,240] cont = 127

Could it be a possible good way to do it? 这可能是一个好的方法吗?

First you need to define a mask with pixels having the same color of your initial point (called seed here). 首先,您需要定义一个具有与初始点相同颜色(此处称为种子 )的像素的蒙版。 You can use inRange with a given tolerance. 您可以使用给定公差的inRange Assuming a seed on the head , you'll get something like: 假设种子在头上 ,您将得到类似以下内容的东西:

在此处输入图片说明

Now you need to find the connected component that contains your seed. 现在,您需要找到包含种子的已连接组件。 You can do this in many ways. 您可以通过多种方式执行此操作。 Here I modified a generative labeling algorithm (the can be found here ). 在这里,我修改了生成标记算法(可以在此处找到)。 You get the list of points of the blob that contains the seed. 您将获得包含种子的斑点的点列表。 You can then make a mask with these points: 然后可以使用以下几点制作蒙版:

在此处输入图片说明

Now that you have all points it's trivial to find the number of points in the segment. 现在您已经拥有了所有点,现在可以轻松地找到线段中的点数。 To find the most frequent color you can make an histogram with the BGR values contained in the segment. 要查找最常见的颜色,可以使用该段中包含的BGR值制作直方图。 Since an histogram with all RGB values will have 256*256*256 bins, it's more practical to use a map. 由于具有所有RGB值的直方图将具有256 * 256 * 256个bin,因此使用地图更为实用。 I modified the code found here to make an histogram with a given mask. 我修改了此处找到的代码以使用给定的蒙版制作直方图。

Now you just need to find the color value with higher frequency. 现在,您只需要查找频率较高的颜色值即可。 For this example, I got: 对于此示例,我得到了:

# points in segment: 2860
Most frequent color: [209, 226, 244]    #: 168

Take a look at the code: 看一下代码:

#include <opencv2/opencv.hpp>
#include <vector>
#include <stack>
#include <map>
using namespace cv;
using namespace std;

vector<Point> connected_components(const Mat1b& img, Point seed)
{
    Mat1b src = img > 0;

    int label = 0;
    int w = src.cols;
    int h = src.rows;
    int i;

    cv::Point point;

    // Start from seed
    std::stack<int, std::vector<int>> stack2;
    i = seed.x + seed.y*w;
    stack2.push(i);

    // Current component
    std::vector<cv::Point> comp;

    while (!stack2.empty())
    {
        i = stack2.top();
        stack2.pop();

        int x2 = i%w;
        int y2 = i / w;

        src(y2, x2) = 0;

        point.x = x2;
        point.y = y2;
        comp.push_back(point);

        // 4 connected
        if (x2 > 0 && (src(y2, x2 - 1) != 0))
        {
            stack2.push(i - 1);
            src(y2, x2 - 1) = 0;
        }
        if (y2 > 0 && (src(y2 - 1, x2) != 0))
        {
            stack2.push(i - w);
            src(y2 - 1, x2) = 0;
        }
        if (y2 < h - 1 && (src(y2 + 1, x2) != 0))
        {
            stack2.push(i + w);
            src(y2 + 1, x2) = 0;
        }
        if (x2 < w - 1 && (src(y2, x2 + 1) != 0))
        {
            stack2.push(i + 1);
            src(y2, x2 + 1) = 0;
        }

        // 8 connected
        if (x2 > 0 && y2 > 0 && (src(y2 - 1, x2 - 1) != 0))
        {
            stack2.push(i - w - 1);
            src(y2 - 1, x2 - 1) = 0;
        }
        if (x2 > 0 && y2 < h - 1 && (src(y2 + 1, x2 - 1) != 0))
        {
            stack2.push(i + w - 1);
            src(y2 + 1, x2 - 1) = 0;
        }
        if (x2 < w - 1 && y2>0 && (src(y2 - 1, x2 + 1) != 0))
        {
            stack2.push(i - w + 1);
            src(y2 - 1, x2 + 1) = 0;
        }
        if (x2 < w - 1 && y2 < h - 1 && (src(y2 + 1, x2 + 1) != 0))
        {
            stack2.push(i + w + 1);
            src(y2 + 1, x2 + 1) = 0;
        }
    }

    return comp;
}


struct lessVec3b
{
    bool operator()(const Vec3b& lhs, const Vec3b& rhs) {
        return (lhs[0] != rhs[0]) ? (lhs[0] < rhs[0]) : ((lhs[1] != rhs[1]) ? (lhs[1] < rhs[1]) : (lhs[2] < rhs[2]));
    }
};

map<Vec3b, int, lessVec3b> getPalette(const Mat3b& src, const Mat1b& mask)
{
    map<Vec3b, int, lessVec3b> palette;
    for (int r = 0; r < src.rows; ++r)
    {
        for (int c = 0; c < src.cols; ++c)
        {
            if (mask(r, c))
            {
                Vec3b color = src(r, c);
                if (palette.count(color) == 0)
                {
                    palette[color] = 1;
                }
                else
                {
                    palette[color] = palette[color] + 1;
                }
            }
        }
    }
    return palette;
}


int main()
{
    // Read the image
    Mat3b image = imread("tsukuba.jpg");

    // Segment
    Mat3b segmented;
    pyrMeanShiftFiltering(image, segmented, 16, 32);

    // Seed
    Point seed(140, 160);

    // Define a tolerance
    Vec3b tol(10,10,10);

    // Extract mask of pixels with same value as seed
    Mat1b mask;
    inRange(segmented, segmented(seed) - tol, segmented(seed) + tol, mask);

    // Find the connected component containing the seed
    vector<Point> pts = connected_components(mask, seed);

    // Number of pixels in the segment
    int n_of_pixels_in_segment = pts.size();

    Mat1b mask_segment(image.rows, image.cols, uchar(0));
    for (const auto& pt : pts)
    {
        mask_segment(pt) = uchar(255);
    }

    // Get palette
    map<Vec3b, int, lessVec3b> palette = getPalette(segmented, mask_segment);

    // Get most frequent color 
    Vec3b most_frequent_color;
    int freq = 0;

    for (const auto& pal : palette)
    {
        if (pal.second > freq)
        {
            most_frequent_color = pal.first;
            freq = pal.second;
        }
    }

    cout << "# points in segment: " << n_of_pixels_in_segment << endl;
    cout << "Most frequent color: " << most_frequent_color <<  " \t#: " << freq << endl;


    return 0;
}

After creating the required mask as shown in previous answer or by any other means, you can create a contour around the mask image. 在按照上一个答案所示或通过任何其他方式创建所需的蒙版之后,可以在蒙版图像周围创建轮廓。 This will give allow you to directly count the number of pixels within segment by using contourArea function . 这将使您可以使用轮廓轮廓函数直接计算段内的像素数。

You can segment out the selected area into a new submat and calculate histogram on it get most frequent values. 您可以将所选区域划分为一个新的子垫,并在该子垫上计算直方图以获得最频繁的值。 If you are concerned with color values only and not the intensity values, you should also convert your image into HSV, LAB, or YCbCr color space as per requirement. 如果仅关注颜色值而不关注亮度值,则还应根据要求将图像转换为HSV,LAB或YCbCr颜色空间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM