从图像生成网站配色方案






3.88/5 (9投票s)
如何从图像中提取配色方案? 确定配色方案的问题在于图像中颜色太多,但转换图像的算法必须找到一组最准确地代表原始图像的颜色。 Atalasoft 在这里提供了代码。
引言
下次您尝试为网站组合配色方案时,请查看您的数码照片。 如果您找到喜欢的照片,使用 DotImage 和一些代码可以很容易地找到图像中最常见的颜色。
查找照片中最常见颜色的算法很简单,我将逐步介绍并向您展示代码。
步骤 1:将图像转换为使用 8 位调色板
您的相机拍摄的数码照片以 24 位颜色存储。 这意味着每个像素使用 24 位来表示颜色:红色、绿色和蓝色各 8 位。 尝试查找配色方案的问题在于图像中颜色太多。 将其转换为 8 位调色板的算法必须找到一组 256 种颜色,这些颜色最能代表原始颜色。 这意味着颜色将根据彼此的相似程度进行聚类。 幸运的是,您不需要知道此算法,因为这是使用 DotImage 的代码
private AtalaImage ConvertTo8BitPaletteImage(AtalaImage image)
{
AtalaImage img8bpp = image;
if (image.PixelFormat != PixelFormat.Pixel8bppIndexed)
{
img8bpp = image.GetChangedPixelFormat(PixelFormat.Pixel8bppIndexed);
}
return img8bpp;
}
步骤 2:计算每种颜色的像素数
现在我们已经将其缩小到只有 256 种最常见的颜色,我们现在可以看到每种颜色有多少像素。 这种频率计数称为直方图,并且创建起来也很简单
private int[] GetColorFrequency(AtalaImage img)
{
Histogram h = new Histogram(img);
return h.GetChannelHistogram(0);
}
在 24 位彩色图像中,通道表示每种组成颜色(红色、绿色和蓝色)。 但是,在 8 位调色板图像中,只有一个通道,并且存储在其中的数字是调色板的索引。 此函数返回的 int[]
是一个 256 元素数组,其中索引处的每个元素表示具有与调色板中该索引关联的颜色的像素数。 例如,如果调色板的第 0 个元素是白色,则通道直方图的第 0 个元素是白色像素的数量。
步骤 3:按频率对调色板中的颜色进行排序
这主要由 .NET 的 Array.Sort()
处理。 我们只需要一个包含颜色的 Color
数组和一个表示排序顺序的整数数组。 我们在步骤二中创建的频率数组是我们想要的排序顺序,所以我们只需要从调色板中创建一个颜色数组。 这是代码
private Color[] SortColorsByFrequency(AtalaImage image, int[] colorFrequency)
{
// make an array of Color object from the entries
// in the palette
Palette p = image.Palette;
Color[] colors = new Color[p.Colors];
for (int i = 0; i < p.Colors; ++i)
{
colors[i] = p.GetEntry(i);
}
// sort the array of colors based on frequency
Array.Sort(colorFrequency, colors);
return colors;
}
步骤 4:定义颜色的距离函数
即使是这个颜色数组也会有许多颜色彼此接近。 为了获得足够不同的颜色列表,我们需要函数来告诉我们两种颜色是否不同(基于容差),以及颜色是否不在颜色列表的容差范围内。 这是代码
private bool WithinTolerance(Color c1, Color c2, double tolerance)
{
double maxDistance = 255 * 255 * 3;
int toleranceDistance = (int)(tolerance * maxDistance);
int distance = (int)Math.Pow((double)(c1.R - c2.R), 2);
distance += (int)Math.Pow((double)(c1.G - c2.G), 2);
distance += (int)Math.Pow((double)(c1.B - c2.B), 2);
return (bool)(distance <= toleranceDistance);
}
函数 WithinTolerance
接受两种颜色和一个容差。 容差是一个介于 0(表示必须完全匹配)到 1.0 之间的实数,这意味着 100% 的容差并且所有颜色都相互匹配。 通过实验,我看到 .01(或 1%)是一个很好的使用值。 我将两种颜色之间的差异定义为红色、绿色和蓝色差异的平方和。
这是一个函数,如果颜色不在列表中任何颜色的容差范围内,则返回 true
。
private bool ColorIsDifferent(Color color, List<Color> colorList)
{
foreach (Color c in colorList)
{
if (WithinTolerance(c, color, .01)) {
return false;
}
}
return true;
}

步骤 5:循环遍历我们所有的颜色并挑选出最上面的颜色
private List<Color> GetTopUniqueColors(Color[] colors, int maxColors)
{
List<Color> uniqueColors = new List<Color>();
for (int i = 0; i < colors.Length && uniqueColors.Count < maxColors; ++i)
{
// read the colors from the end of the array
// since they are sorted in increasing order of frequency
Color color = colors[colors.Length - 1 - i];
if (ColorIsDifferent(color, uniqueColors))
{
uniqueColors.Add(color);
}
}
return uniqueColors;
}
此函数的返回值是图像中最上面的颜色列表。
我整理了一个网站,该网站使用此算法从您可以上传的图像生成配色方案。 您可以在这里访问它
该网站的完整代码也可在那里获得。