Интересно ...:)
Вы бы не только идентифицировали ближайшие цвета, но и хотели бы уменьшить количество используемых цветов. Вы не хотите, чтобы получился узор сшивания, который использует сотни разных цветов ...
Я собрал некоторый код, который делает это на базовом уровне. (Извините, что это в C #, я надеюсь, что это все равно может быть полезно.)
Конечно, перед тем, как метод заработает, нужно еще кое-что изменить. Метод GetDistance взвешивает важность оттенка, насыщенности и яркости по отношению друг к другу, и, конечно, важно найти наилучший баланс между ними, чтобы найти цвет, который выглядит ближе всего.
Существует также многое, что можно сделать с помощью метода уменьшения палитры. В примере я только что выбрал наиболее используемые цвета, но вы, вероятно, хотите оценить, насколько похожи цвета в палитре. Это можно сделать, выбрав наиболее используемый цвет, уменьшив количество оставшихся цветов в списке в зависимости от расстояния до выбранного цвета, а затем прибавив список.
Класс Hsl, который содержит цвет DMC, может рассчитать расстояние до другого цвета и найти ближайший цвет в списке цветов:
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");
// count colors used
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);
}
}
}
// reduce number of colors by picking the most used
Hsl[] reduced = usage.OrderBy(c => -c.Count).Take(5).ToArray();
// convert image
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);