有時候我們需要找出一張圖片中的主色調,這里的主色調並不是指顏色最多的,而是指最“醒目”的。
例如 win8 在更換桌面壁紙后,會將任務欄的背景色改為該壁紙的主色調。
看下面幾張圖可以更直觀的明白“醒目”的意思。

(這張圖里面最醒目的無疑是紫紅色的小花,而不是顏色占大多數的淡青色背景)

(這張圖也一樣,最醒目的顏色應該是深紅色,而不是深藍色)
我想了一個簡單的辦法,就是根據圖片中每個像素的色調值去判斷哪些像素符合“醒目”這個特性。
分三步進行
1.計算整個圖片的色調的平均值 (avg_hue)
2.遍歷每個像素,計算該像素的色調值與 avg_hue 的色差(即將二者相減后取絕對值),如果該色差大於一個閾值(本文中取 30),
則將該像素加入到“醒目像素”的列表
3.計算整個“醒目像素列表”的顏色均值,得到的結果即為該圖片的主色調。
下面看看該算法的效果如何
我將計算出的主色調畫在原圖的左上角,以方便對比。結果如下


可以看到基本上達到了我想要的效果。
下面給出具體代碼,代碼未經任何優化,速度比較慢。
Color get_major_color(Bitmap bitmap)
{
//色調的總和
var sum_hue = 0d;
//色差的閾值
var threshold = 30;
//計算色調總和
for (int h = 0; h < bitmap.Height; h++)
{
for (int w = 0; w < bitmap.Width; w++)
{
var hue = bitmap.GetPixel(w, h).GetHue();
sum_hue += hue;
}
}
var avg_hue = sum_hue / (bitmap.Width * bitmap.Height);
//色差大於閾值的顏色值
var rgbs = new List<Color>();
for (int h = 0; h < bitmap.Height; h++)
{
for (int w = 0; w < bitmap.Width; w++)
{
var color = bitmap.GetPixel(w, h);
var hue = color.GetHue();
//如果色差大於閾值,則加入列表
if (Math.Abs(hue - avg_hue) > threshold)
{
rgbs.Add(color);
}
}
}
if (rgbs.Count == 0)
return Color.Black;
//計算列表中的顏色均值,結果即為該圖片的主色調
int sum_r = 0, sum_g = 0, sum_b = 0;
foreach (var rgb in rgbs)
{
sum_r += rgb.R;
sum_g += rgb.G;
sum_b += rgb.B;
}
return Color.FromArgb(sum_r / rgbs.Count,
sum_g / rgbs.Count,
sum_b / rgbs.Count);
}
