Cómo comparar el objeto Color y obtener el color más cercano en un color ?

Digamos que tengo una matriz con colores (con todo el espectro de colores, de rojo a rojo). Una versión más corta se vería así:

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

Ahora si tengo una separada

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

¿Cómo puedo obtener el valor en la matriz más cercana al color seleccionado? ¿Y esto es posible?

La distancia de color no es una cosa definida con precisión. Entonces aquí hay tres métodos para medirlo:

  • Un método que solo verifica los matices de los colores, ignorando tanto la saturación como el brillo
  • Uno que solo mide la distancia directa en el espacio RGB
  • Y uno que pesa el tono, la saturación y el brillo de alguna manera.

Obviamente, es posible que desee cambiar los números mágicos en la tercera medición: el tono está en 0-360, el brillo y la saturación están en 0-1, por lo que con estos números el tono pesa alrededor de 3.6 veces más fuerte que la saturación y el brillo.

Actualización : la solución original que publiqué contenía varios errores:

  • El Linq que utilicé no encontró el más cercano pero más cercanoFromBelow; esto significaba un 50% de posibilidades de estar fuera por uno.
  • En algunos lugares usé el método de color.GetBrightness() . Esto es, para decirlo suavemente, totalmente inútil. A saber: ¡ Blue y Yellow tienen el mismo valor de 0.5 !
  • Los valores para hue van de 0-360, pero por supuesto se envuelven. Lo extrañé por completo …

He reemplazado la mayor parte de la respuesta original con el código corregido:

Estas son ahora la nueva versión de los métodos, cada uno devuelve el índice de la coincidencia más cercana encontrada:

 // 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); } 

Algunas funciones de ayuda:

  // 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)); } 

Aquí está el práctico pequeño ayudante que utilicé para los textos de captura de pantalla:

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

He actualizado la captura de pantalla para mostrar no solo 13 colores, sino también varios colores principalmente rojizos para probar; todos los colores se muestran con sus valores de tono, saturación y brillo. Los últimos tres números son los resultados de los tres métodos.

Como puede ver, el método de distancia simple es bastante engañoso para los colores shinys y no saturados: el último color (marfil) es de hecho un amarillo shiny y pálido.

El tercer método que mide todas las propiedades de color es lo mejor. Sin embargo, ¡debes jugar con los números de medición!

Al final, realmente depende de lo que quieres lograr; si, como parece, solo te importan los tonos de los colores, ¡simplemente utiliza el primer método! Puede llamarlo, usando su matriz de esta manera:

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

Para obtener más información sobre las distancias de color, consulte Wikipedia .

distancias de color

 // 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); 

Prueba esto:

  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; } 

aquí interpreto closest como distancia euclidiana en el espacio ARGB