Let's say I have the following image:
What I am looking for is a way to programatically determine that red is the most common color in the picture.
So far, I have tried a few approaches, with various results of poor to abysmal to accomplish this. My current approach is to first reduce the colors in the image.
This is done with the following code:
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);
}
There is one annoying issue with it where it has an issue with scaling that leaves out a portion of the right, but I can live with that for now.
Next, I tried a few approaches where I want to get the colors mapped out, such as with histograms.
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
The problem is, for this image I get the following values:
- Hue: 240
- Saturation: 0
- Value: 0
However, I am expecting HSV values closer to this:
- Hue: 356
- Saturation: 94
- Value: 58
Hopefully, someone can point out where I went wrong.