判断一个图像(OpenCV的&C++)

问题描述:

中最频繁出现的颜色比方说,我有如下形象:判断一个图像(OpenCV的&C++)

enter image description here

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

到目前为止,我已经尝试了一些方法,以各种不良的结果来实现这一点。我目前的做法是首先减少图像中的颜色。

enter image description here

这是用下面的代码完成:

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 

的问题是,对于这个图像I得到以下值:

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

然而,我我期待的HSV值更接近这个:

  • 色调:356
  • 饱和度:94
  • 值:58

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

+0

懒得分析你的代码,但只是一个提示测试你正确处理你的图像像素格式?我敢打赌,你得到RGB并将其作为BGR处理,反之亦然,因为色调240是蓝色而不是红色(我习惯于GDI/Canvas通常对某些像素格式的原始图像数据的反转顺序)但S ,V设置为零确实很奇怪。也请看看这个:[HSV直方图](https://*.com/a/29286584/2521214) – Spektre

+0

只是好奇,如何计算颜色直方图使用[calcHist](https://docs.opencv.org /2.4/modules/imgproc/doc/histograms.html#calchist)并找到高峰?当然,不是HSV值问题的答案。 calcHist将比kmeans快得多。 – dhanushka

+0

@dhanushka速度取决于颜色直方图的实现,数量等等......所以它可能也不一定比具有相同精度的k-means更快。但是,我也会使用直方图,这就是为什么我建议在以前的评论中的链接,因为有我的C++实现,即RGB - > HSV转换,计算和渲染HSV直方图(无openCV) – Spektre

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

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

如果您需要实施最小二乘法的帮助,请在评论中写下。

更新:

你的情况最小二乘的解决方案只是平均值水平与直方图给出的权重。

result = sum(n * hist [n])/ sum(hist [n])。

请注意,该公式适用于每种颜色成分,即它们是独立的。