判断一个图像(OpenCV的&C++)
问题描述:
中最频繁出现的颜色比方说,我有如下形象:判断一个图像(OpenCV的&C++)
我正在寻找一种方法以编程方式确定红色是最图片中常见的颜色。
到目前为止,我已经尝试了一些方法,以各种不良的结果来实现这一点。我目前的做法是首先减少图像中的颜色。
这是用下面的代码完成:
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
希望有人能指出我哪里错了。
答
这确实是计算机图形学中的一个经典问题。只是从字面上直接找到最常见的颜色和直方图并不是一个好主意,令人惊讶的是,您可能会发现它是黄色/绿色的阴影而不是红色,因为“红色”的值包含变化并且几乎不会落入相同的直方图箱。
适当的算法是基于最小二乘法。你需要找到颜色的平方“距离”是最小的。您可以将您的平方距离定义为dr^2 + dg^2 + db^2(r/b/g表示红色/绿色/蓝色分量),或者您可以使用权重来反映对这些分量的不同敏感度。您也可以用不同的基数表示(如yuv等)
如果您需要实施最小二乘法的帮助,请在评论中写下。
更新:
你的情况最小二乘的解决方案只是平均值水平与直方图给出的权重。
result = sum(n * hist [n])/ sum(hist [n])。
请注意,该公式适用于每种颜色成分,即它们是独立的。
懒得分析你的代码,但只是一个提示测试你正确处理你的图像像素格式?我敢打赌,你得到RGB并将其作为BGR处理,反之亦然,因为色调240是蓝色而不是红色(我习惯于GDI/Canvas通常对某些像素格式的原始图像数据的反转顺序)但S ,V设置为零确实很奇怪。也请看看这个:[HSV直方图](https://*.com/a/29286584/2521214) – Spektre
只是好奇,如何计算颜色直方图使用[calcHist](https://docs.opencv.org /2.4/modules/imgproc/doc/histograms.html#calchist)并找到高峰?当然,不是HSV值问题的答案。 calcHist将比kmeans快得多。 – dhanushka
@dhanushka速度取决于颜色直方图的实现,数量等等......所以它可能也不一定比具有相同精度的k-means更快。但是,我也会使用直方图,这就是为什么我建议在以前的评论中的链接,因为有我的C++实现,即RGB - > HSV转换,计算和渲染HSV直方图(无openCV) – Spektre