有趣... :)
不仅需要识别最近的颜色,还需要减少使用的颜色数量。您不希望最终得到一个使用数百种不同颜色的针织图案...
我编写了一些基础代码来实现这一点。(很抱歉它是用C#编写的,但我希望它可以在某种程度上有所帮助。)
当然,在方法有效之前还需要进行一些进一步的调整。GetDistance方法会权衡色相、饱和度和亮度之间的重要性,找到最佳平衡当然是很重要的,以便找到最接近的颜色。
在减少调色板的方法方面,还有很多可以做的。在这个例子中,我只选择了最常用的颜色,但您可能想考虑调色板中颜色的相似度。这可以通过选择最常用的颜色,根据到选定颜色的距离降低其余颜色的计数,然后重新排序列表来完成。
包含DMC颜色的Hsl类可以计算与另一种颜色的距离,并在颜色列表中找到最近的颜色:
public class Hsl {
public string DmcNumber { get; private set; }
public Color Color { get; private set; }
public float Hue { get; private set; }
public float Saturation { get; private set; }
public float Brightness { get; private set; }
public int Count { get; set; }
public Hsl(Color c) {
DmcNumber = "unknown";
Color = c;
Hue = c.GetHue();
Saturation = c.GetSaturation();
Brightness = c.GetBrightness();
Count = 0;
}
public Hsl(string dmc, int r, int g, int b)
: this(Color.FromArgb(r, g, b))
{
DmcNumber = dmc;
}
private static float AngleDifference(float a1, float a2) {
float a = Math.Abs(a1 - a2);
if (a > 180f) {
a = 360f - a;
}
return a / 180f;
}
public float GetDistance(Hsl other) {
return
AngleDifference(Hue, other.Hue) * 3.0f +
Math.Abs(Saturation - other.Saturation) +
Math.Abs(Brightness - other.Brightness) * 4.0f;
}
public Hsl GetNearest(IEnumerable<Hsl> dmcColors) {
Hsl nearest = null;
float nearestDistance = float.MaxValue;
foreach (Hsl dmc in dmcColors) {
float distance = GetDistance(dmc);
if (distance < nearestDistance) {
nearestDistance = distance;
nearest = dmc;
}
}
return nearest;
}
}
这段代码设置了一个(大大减少的)DMC颜色列表,加载了一张图片,计算了颜色数量,缩小了调色板并转换了图像。当然,您还需要将缩小的调色板信息保存在某个地方。
Hsl[] dmcColors = {
new Hsl("blanc", 255, 255, 255),
new Hsl("310", 0, 0, 0),
new Hsl("317", 167, 139, 136),
new Hsl("318", 197, 198, 190),
new Hsl("322", 81, 109, 135),
new Hsl("336", 36, 73, 103),
new Hsl("413", 109, 95, 95),
new Hsl("414", 167, 139, 136),
new Hsl("415", 221, 221, 218),
new Hsl("451", 179, 151, 143),
new Hsl("452", 210, 185, 175),
new Hsl("453", 235, 207, 185),
new Hsl("503", 195, 206, 183),
new Hsl("504", 206, 221, 193),
new Hsl("535", 85, 85, 89)
};
Bitmap image = (Bitmap)Image.FromFile(@"d:\temp\pattern.jpg");
List<Hsl> usage = new List<Hsl>();
for (int y = 0; y < image.Height; y++) {
for (int x = 0; x < image.Width; x++) {
Hsl color = new Hsl(image.GetPixel(x, y));
Hsl nearest = color.GetNearest(dmcColors);
int index = usage.FindIndex(h => h.Color.Equals(nearest.Color));
if (index != -1) {
usage[index].Count++;
} else {
nearest.Count = 1;
usage.Add(nearest);
}
}
}
Hsl[] reduced = usage.OrderBy(c => -c.Count).Take(5).ToArray();
for (int y = 0; y < image.Height; y++) {
for (int x = 0; x < image.Width; x++) {
Hsl color = new Hsl(image.GetPixel(x, y));
Hsl nearest = color.GetNearest(reduced);
image.SetPixel(x, y, nearest.Color);
}
}
image.Save(@"d:\temp\pattern.png", System.Drawing.Imaging.ImageFormat.Png);