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

How to count pixels in color segment in OpenCV

我有一个 OpenCV C++ 应用程序。 我用 pyrMeanShiftFiltering 函数对图像进行了分割。 现在我需要计算一个段中的像素和同一段中具有最频繁值的像素的数量,以便计算它们之间的比率。我该怎么做?

我用的是 Tsukuba 图片,代码是。

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

分割后的图像为:

如果我考虑单个段中的像素,我计算该段中像素的部分是:

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;  

在这个例子中,我考虑在位置 (160, 136) 的一个白色像素,并从它开始在四个方向上将相同的像素计数到中心一个,输出是:

Pixel [206, 222, 240]cont = 127

这可能是一个好的方法吗?

首先,您需要定义一个掩码,其像素具有与初始点相同的颜色(此处称为 seed)。您可以使用具有给定公差的 inRange。假设在 head 上有一个种子,你会得到类似的东西:

现在您需要找到包含您的种子的连通分量。您可以通过多种方式做到这一点。这里我修改了一个生成式标注算法(可以参考)。您将获得包含种子的 blob 的点列表。然后你可以用这些点制作面具:

现在您已经有了所有的点,很容易找到线段中的点数。要找到最常见的颜色,您可以使用包含在该段中的 BGR 值制作直方图。由于具有所有 RGB 值的直方图将具有 256*256*256 个 bin,因此使用地图更为实用。我修改了找到的代码 here 以制作具有给定掩码的直方图。

现在你只需要找到频率较高的颜色值。 对于这个例子,我得到:

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

看看代码:

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

如上一个答案所示或通过任何其他方式创建所需的蒙版后,您可以在蒙版图像周围创建轮廓。这将允许您使用 contourArea function 直接计算段内的像素数。

您可以将所选区域分割成一个新的子垫,并在其上计算直方图以获得最频繁的值。如果您只关心颜色值而不关心强度值,您还应该根据要求将图像转换为 HSV、LAB 或 YCbCr 颜色 space。