I am creating an application to find the most used color of an image, i am up to getting the RGB value of the color, but how to get the color name, help plz.
I am creating an application to find the most used color of an image, i am up to getting the RGB value of the color, but how to get the color name, help plz.
I'm using .NETCF 3.5, and "System.Drawing.KnowColor enumeration" not exists. For debug only, I'm using this function to return an known color;
public static string ColorName(System.Drawing.Color c)
{
var colorName = string.Format("0x{0:X4}", c.ToArgb());
var colorList = new List<System.Reflection.PropertyInfo>();
var props = typeof(System.Drawing.Color).GetProperties();
colorList.AddRange(props);
props = typeof(System.Drawing.SystemColors).GetProperties();
var prop = colorList.Where(p1 => (System.Drawing.Color)p1.GetValue(null, null) == c).FirstOrDefault();
if (prop != null) System.Diagnostics.Debug.WriteLine(prop.Name);
else System.Diagnostics.Debug.WriteLine("unkown name Color");
foreach (var item in colorList)
{
var argb = (System.Drawing.Color)item.GetValue(null, null);
System.Diagnostics.Debug.WriteLine(string.Format("ColorName {0} ARGB {1:X4}", item.Name, argb.ToArgb()));
if (c == argb) return item.Name;
}
return colorName;
}
Because I recently was working on solving this same problem, I went and wrote a static lookup cache which can be pre-populated with well-known .NET Framework color values derived from the system enum System.Drawing.KnownColor indirectly. I decompiled .NET, and saw that the color properties call the constructor with the associated enum states. I am using reflection, but it's a one-time deal, that takes place in the static initializer of my class. This is NOT a thread-safe solution, but it can can be trivially made to be thread-safe, and also maintains a permanent cache in memory.
What can be useful about my approach, is while it first pre-populates with the list of .NET Named colors, you can also add application runtime-defined well-known colors - read from JSON, downloaded from the web, parsed from a database, etc... The major potential downside here, is that this is a singleton, due to being static, and there may be unwanted side-effects as a result of this design choice. Make this class instanced where you see fit.
I made the public-facing interface as simple as possible - LookupName(Color) returns string.Empty if there is a cache miss, or else, returns the string name of the color. AddToCache(Color, string) returns true on success, false on fail (say, a known value is already there). ToNearestNamedColor(Color) is a moderately expensive numerical method, derived from color quantization techniques, using the Pythagorean Distance to select the closest "named" color in the static cache, and it returns the string of the color name, the System.Drawing.Color structure for that name, and as a final bonus, a double representing the score, or fitness, of the nearest known / named Color input.
Using the Pythagorean Distance to select the closest named color is suboptimal, but provides a decent starting point, before using a different baseline heuristic to maximize approximate similarity.
Finally, ToRGB(Color) is a helper-function I wrote to convert a System.Drawing.Color to an integer representing the RGB values of the color, trimming (zeroing) the leading Alpha-component, stuffing a 24 bitstring into a 32-bit int structure.
ToRGB(Color) Helper (static extension) function:
/// <summary>
/// Converts the specified <see cref="Color"/> to a 24-bitstring on an int, of 00000000RRRRRRRRGGGGGGGGBBBBBBBB.
/// </summary>
/// <param name="toRGB">The color to convert to rgb as a 24 bitstring. Ignores Alpha.</param>
/// <returns>an integer representation of the specified <see cref="Color"/>.</returns>
public static int ToRGB(this Color toRGB)
{
return
(toRGB.R << 16) |
(toRGB.G << 8) |
toRGB.B;
}
With this helper-function defined (could be public, could be internal, up to you), I now present my NamedColorStaticCache.cs (place in whatever namespace you want / need).
// Using-statements:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
/// <summary>
/// Static class to assist with looking up known, named colors, by name.
/// </summary>
public static class NamedColorStaticCache
{
/// <summary>
/// Stores the lookup cache of RGB colors to known names.
/// </summary>
private static Dictionary<int, string> rgbLookupCache;
/// <summary>
/// Initializes static members of the <see cref="NamedColorStaticCache"/> class.
/// </summary>
static NamedColorStaticCache()
{
rgbLookupCache = new Dictionary<int, string>();
Type colorType = typeof(Color);
PropertyInfo[] knownColorProperties = colorType
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
.Where(t => t.PropertyType == typeof(Color))
.ToArray();
// Avoid treating "transparent" as "white".
AddToCache(Color.White, "White");
foreach (PropertyInfo pi in knownColorProperties)
{
Color asColor = (Color)pi.GetValue(null);
AddToCache(asColor, pi.Name);
}
}
/// <summary>
/// Looks up the name of the specified <see cref="Color"/>.
/// </summary>
/// <param name="toLookup">The <see cref="Color"/> to lookup a name for.</param>
/// <returns>A string of the associated name of the specified <see cref="Color"/>.</returns>
public static string LookupName(this Color toLookup)
{
int rgb = toLookup.ToRGB();
if (rgbLookupCache.ContainsKey(rgb))
{
return rgbLookupCache[rgb];
}
return string.Empty;
}
/// <summary>
/// Adds the specified <see cref="Color"/> to a lookup cache of named colors.
/// </summary>
/// <param name="toAdd">The <see cref="Color"/> to add to the lookup cache.</param>
/// <param name="name">The name of the <see cref="Color"/> to add to the lookup cache.</param>
/// <returns>True if adding successful, else, false (the color was already in the cache).</returns>
public static bool AddToCache(this Color toAdd, string name)
{
int rgb = toAdd.ToRGB();
if (rgbLookupCache.ContainsKey(rgb))
{
return false;
}
rgbLookupCache.Add(rgb, name);
return true;
}
/// <summary>
/// Takes the specified input <see cref="Color"/>, and translates it to its nearest counterpart, using root square sum.
/// </summary>
/// <param name="toNearest">The <see cref="Color"/> to look up to the nearest named color.</param>
/// <returns>A tuple structure of name, color, error.</returns>
public static Tuple<string, Color, double> ToNearestNamedColor(this Color toNearest)
{
string foundName = string.Empty;
Color foundColor = Color.Black;
double error = double.MaxValue;
int toNearestRGB = toNearest.ToRGB();
if (rgbLookupCache.ContainsKey(toNearestRGB))
{
foundName = rgbLookupCache[toNearestRGB];
foundColor = toNearest;
error = 0;
}
else
{
foreach (KeyValuePair<int, string> pair in rgbLookupCache)
{
int rgb = pair.Key;
byte r = (byte)(rgb >> 16);
byte g = (byte)(rgb << 16 >> 24);
byte b = (byte)(rgb << 24 >> 24);
int dr = r - toNearest.R;
int dg = g - toNearest.G;
int db = b - toNearest.B;
double newError =
Math.Sqrt(
(dr * dr) +
(dg * dg) +
(db * db));
if (newError < error)
{
foundName = pair.Value;
foundColor = Color.FromArgb(r, g, b);
error = newError;
}
if (newError <= .0005)
{
break;
}
}
}
return Tuple.Create(foundName, foundColor, error);
}
}
Note: Edited an hour later to provide the lookup function to grab the nearest named color. This is not the best way to do it (converting to CIEL*a*b* would be "better"), but this is a solid foundation using a ToNearestColor-approach. This way the original poster can give the closest approximate color name, in addition to looking up solely discrete color names.