【问题标题】:Determine the most frequently occurring color within an image (OpenCV & C++)确定图像中最常出现的颜色(OpenCV 和 C++)
【发布时间】:2018-03-21 05:20:00
【问题描述】:

假设我有以下图片:

我正在寻找一种以编程方式确定红色是图片中最常见颜色的方法。

到目前为止,我已经尝试了几种方法,但结果很差,很糟糕。我目前的做法是先减少图像中的颜色。

这是通过以下代码完成的:

Mat samples(src.rows * src.cols, 3, CV_32F);
for( int y = 0; y < src.rows; y++ )
for( int x = 0; x < src.cols; x++ )
for( int z = 0; z < 3; z++)
samples.at<float>(y + x * src.rows, z) = src.at<Vec3b>(y,x)[z];

int clusterCount = 16;
Mat labels;
int attempts = 2;
Mat centers;
kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10000, 0.0001), attempts, KMEANS_PP_CENTERS, centers );

Mat reduced( src.size(), src.type() );
for( int y = 0; y < src.rows; y++ )
for( int x = 0; x < src.cols; x++ )
{
    int cluster_idx = labels.at<int>(y + x * src.rows,0);
    reduced.at<Vec3b>(y,x)[0] = centers.at<float>(cluster_idx, 0);
    reduced.at<Vec3b>(y,x)[1] = centers.at<float>(cluster_idx, 1);
    reduced.at<Vec3b>(y,x)[2] = centers.at<float>(cluster_idx, 2);
}

它有一个令人讨厌的问题,它的缩放问题会遗漏一部分权利,但我现在可以忍受。

接下来,我尝试了一些方法来绘制颜色,例如使用直方图。

Mat image_hsv;

cvtColor(src, image_hsv, CV_BGR2HSV);

// Quanta Ratio
int scale = 10;

int hbins = 36, sbins = 25, vbins = 25;
int histSize[] = {hbins, sbins, vbins};

float hranges[] = { 0, 360 };
float sranges[] = { 0, 256 };
float vranges[] = { 0, 256 };

const float* ranges[] = { hranges, sranges, vranges };
MatND hist;

int channels[] = {0, 1, 2};

calcHist( &image_hsv, 1, channels, Mat(), // do not use mask
         hist, 3, histSize, ranges,
         true, // the histogram is uniform
         false );

int maxVal = 0;

int hue = 0;
int saturation = 0;
int value = 0;

for( int h = 0; h < hbins; h++ )
for( int s = 0; s < sbins; s++ )
for( int v = 0; v < vbins; v++ )
{
    int binVal = hist.at<int>(h, s, v);
    if(binVal > maxVal)
    {
        maxVal = binVal;

        hue = h;
        saturation = s;
        value = v;
    }
}

hue = hue * scale * scale; // angle 0 - 360
saturation = saturation * scale; // 0 - 255
value = value * scale; // 0 - 255

问题是,对于这张图片,我得到以下值:

  • 色相:240
  • 饱和度:0
  • 值:0

但是,我希望 HSV 值更接近这个:

  • 色相:356
  • 饱和度:94
  • 价值:58

希望有人能指出我哪里出错了。

【问题讨论】:

  • 懒得分析您的代码,但只是提示您测试您是否正确处理图像像素格式?我敢打赌你得到 RGB 并将其作为 BGR 处理,反之亦然,因为色调 240 是蓝色而不是红色(我习惯于 GDI/Canvas 通常具有与某些像素格式的原始图像数据相反的通道顺序)但是 S ,V 设置为零确实很奇怪。也看看这个:HSV histogram
  • 只是好奇,如何使用calcHist 计算颜色直方图并找到峰值?当然,不是用 HSV 值来回答你的问题。 calcHist 将比 kmeans 快得多。
  • @dhanushka 速度取决于颜色直方图的实现、箱数等......因此它可能不一定比具有相同精度的 k-means 快。但是是的,我也会使用直方图,这就是为什么我建议在之前的评论中使用链接,因为我的 C++ 实现可以进行 RGB -> HSV 转换、计算和渲染 HSV 直方图(没有 openCV)

标签: c++ opencv image-processing colors


【解决方案1】:

这确实是计算机图形学中的经典问题。仅仅从字面上找到最常见的颜色 wrt 直方图并不是一个好主意,令人惊讶的是,您可能会发现它是黄色/绿色而不是红色的阴影,因为“红色”的值包含变化并且几乎不会落在同一个直方图箱中.

适当的算法基于最小二乘。您需要找到一种“距离”平方和最小的颜色。您可以将平方距离定义为 dr^2+dg^2+db^2(r/b/g 代表红色/绿色/蓝色分量),或者您可以使用权重来反映对这些分量的不同敏感性。你也可以用不同的基础来表示它(即 yuv 等)

如果您在实现最小二乘方面需要帮助,请写在 cmets 中。

更新:

在您的情况下,最小二乘的解只是直方图给出权重的平均值水平。

结果 = sum(n * hist[n]) / sum(hist[n])。

请注意,此公式适用于每个颜色分量,即它们是独立的。

【讨论】:

    猜你喜欢
    • 2016-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 1970-01-01
    • 2011-05-24
    • 1970-01-01
    相关资源
    最近更新 更多