Как сравнить объект Color и получить ближайший цвет в цвете ?

Предположим, у меня есть массив с цветами (со всем спектром цветов, от красного до красного). Более короткая версия будет выглядеть так:

public Color[] ColorArray = new Color[360] { Color.FromArgb(255, 245, 244, 242), Color.FromArgb(255, 245, 244, 240), Color.FromArgb(255, 245, 244, 238) } 

Теперь, если у меня есть отдельный

 Color object (Color c = Color.FromArgb(255, 14, 4, 5)) 

Как я могу получить значение в массиве, которое ближе всего к выбранному цвету? И возможно ли это?

Расстояние цвета не является точно определенной вещью. Итак, вот три метода его измерения:

  • Один метод, который проверяет только оттенки цветов, игнорируя как насыщенность, так и яркость
  • Тот, который измеряет только прямое расстояние в пространстве RGB
  • И тот, который каким-то образом взвешивает оттенок, насыщенность и яркость.

Очевидно, что вы можете изменить магические числа в третьем измерении: оттенок находится в 0-360, яркость и насыщенность находятся в 0-1, поэтому с этими цифрами оттенок весит примерно в 3,6 раза сильнее насыщенности и яркости.

Обновление . Исходное решение, которое я опубликовал, содержит несколько ошибок:

  • Используемый Linq не нашел ближайшего, но ближайшего FromBelow; это означало 50% -ный шанс быть одним.
  • В некоторых местах я использовал метод color.GetBrightness() . Это, мягко говоря, совершенно бесполезно. Для остроты: Blue и Yellow имеют то же значение 0.5 !
  • Значения для оттенка идут от 0-360, но, конечно, они обертываются! Я пропустил это полностью ..

Я заменил большую часть исходного ответа исправленным кодом:

Это теперь новая версия методов, каждая из которых возвращает индекс ближайшего совпадения:

 // closed match for hues only: int closestColor1(List colors, Color target) { var hue1 = target.GetHue(); var diffs = colors.Select(n => getHueDistance(n.GetHue(), hue1)); var diffMin = diffs.Min(n => n); return diffs.ToList().FindIndex(n => n == diffMin); } // closed match in RGB space int closestColor2(List colors, Color target) { var colorDiffs = colors.Select(n => ColorDiff(n, target)).Min(n =>n); return colors.FindIndex(n => ColorDiff(n, target) == colorDiffs); } // weighed distance using hue, saturation and brightness int closestColor3(List colors, Color target) { float hue1 = target.GetHue(); var num1 = ColorNum(target); var diffs = colors.Select(n => Math.Abs(ColorNum(n) - num1) + getHueDistance(n.GetHue(), hue1) ); var diffMin = diffs.Min(x => x); return diffs.ToList().FindIndex(n => n == diffMin); } 

Несколько вспомогательных функций:

  // color brightness as perceived: float getBrightness(Color c) { return (cR * 0.299f + cG * 0.587f + cB *0.114f) / 256f;} // distance between two hues: float getHueDistance(float hue1, float hue2) { float d = Math.Abs(hue1 - hue2); return d > 180 ? 360 - d : d; } // weighed only by saturation and brightness (from my trackbars) float ColorNum(Color c) { return c.GetSaturation() * factorSat + getBrightness(c) * factorBri; } // distance in RGB space int ColorDiff(Color c1, Color c2) { return (int ) Math.Sqrt((c1.R - c2.R) * (c1.R - c2.R) + (c1.G - c2.G) * (c1.G - c2.G) + (c1.B - c2.B)*(c1.B - c2.B)); } 

Вот удобный помощник, который я использовал для скриншотов:

 Brush tBrush(Color c) { return getBrightness(c) < 0.5 ? Brushes.White : Brushes.Black; } 

Я обновил скриншот, чтобы отображать не только 13 цветов, но и ряд наиболее красноватых цветов для тестирования; все цвета отображаются с их значениями для оттенка, насыщенности и яркости. Последние три числа - это результаты трех методов.

Как вы можете видеть, простой метод расстояния довольно заблуждение для ярких и ненасыщенных цветов: последний цвет (Ivory) на самом деле яркий и бледно-желтый!

Третий метод, который измеряет все свойства цвета, является лучшим imo. Однако вы должны поиграть с калибровочными номерами!

В конце концов, это действительно зависит от того, чего вы хотите достичь; если, по-видимому, вы только заботитесь о цветах, просто идите для первого метода! Вы можете назвать это, используя свой массив следующим образом:

 int indexInArray = closestColor1(clist.ToList(), someColor); 

Подробнее о цветовых расстояниях см. В Википедии !

цветные расстояния

 // the colors I used: // your array Color[] clist = new Color[13]; clist[0] = Color.Blue; clist[1] = Color.BlueViolet; clist[2] = Color.Magenta; clist[3] = Color.Purple; clist[4] = Color.Red; clist[5] = Color.Tomato; clist[6] = Color.Orange; clist[7] = Color.Yellow; clist[8] = Color.YellowGreen; clist[9] = Color.Green; clist[10] = Color.SpringGreen; clist[11] = Color.Cyan; clist[12] = Color.Ivory; // and a list of color to test: List targets = new List(); targets.Add(Color.Pink); targets.Add(Color.OrangeRed); targets.Add(Color.LightPink); targets.Add(Color.DarkSalmon); targets.Add(Color.LightCoral); targets.Add(Color.DarkRed); targets.Add(Color.IndianRed); targets.Add(Color.LavenderBlush); targets.Add(Color.Lavender); 

Попробуй это:

  static void Main() { Color[] ColorArray = { Color.FromArgb(255, 245, 244, 242), Color.FromArgb(255, 245, 244, 240), Color.FromArgb(255, 245, 244, 238) }; var closest = GetClosestColor(ColorArray, Color.FromArgb(255, 245, 244, 241)); Console.WriteLine(closest); } private static Color GetClosestColor(Color[] colorArray, Color baseColor) { var colors = colorArray.Select(x => new {Value = x, Diff = GetDiff(x, baseColor)}).ToList(); var min = colors.Min(x => x.Diff); return colors.Find(x => x.Diff == min).Value; } private static int GetDiff(Color color, Color baseColor) { int a = color.A - baseColor.A, r = color.R - baseColor.R, g = color.G - baseColor.G, b = color.B - baseColor.B; return a*a + r*r + g*g + b*b; } 

здесь я интерпретирую closest как евклидово расстояние в пространстве ARGB

  • Данная System.Uri не может быть преобразована в Windows.Foundation.Uri
  • Разрешение не доверенных SSL-сертификатов с помощью HttpClient
  • Добавление жестов салфетки для открытия панели SplitView
  • GridView с двумя столбцами, шириной заливки
  • Как resize изображения в C # WinRT / winmd?
  • Как написать файл с помощью StreamWriter в Windows 8?
  • Событие приостановки, не использующее WinRT
  • Как приложение Metro в Windows 8 взаимодействует с настольным настольным окном на одном компьютере?
  • Давайте будем гением компьютера.