//***************************************************************************************** // File: ColorLib.cs // Project: TouchlessLib // Author(s): Michael Wasserman (Michael.Wasserman@microsoft.com) // // These helper classes represent colors. // // TODO: Improve HSV colorspace binning/partitioning? Replace with grouping/clustering? // TODO: Replace RGB->HSV with a lookup table? //***************************************************************************************** using System; using System.Collections.Generic; using System.Text; namespace TouchlessLib { /// /// Represents a color in RGB (Red, Green, Blue) /// byte Red: [0-255] /// byte Grn: [0-255] /// byte Blu: [0-255] /// internal struct RGB { public static byte HSV_MAX_RED = 255; public static byte HSV_MAX_GRN = 255; public static byte HSV_MAX_BLU = 255; /// /// Create a new RGB (Red, Green, Blue) struct /// /// RGB Red [0-255] /// RGB Green [0-255] /// RGB Blue [0-255] public RGB(byte r, byte g, byte b) { this.r = r; this.g = g; this.b = b; } /// /// Create a new RGB (Red, Green, Blue) struct from an ARGB int /// /// ARGB integer in the format 0xAARRGGBB public RGB(int argb) { this.b = (byte)(argb & 0xff); this.g = (byte)((argb >>= 8) & 0xff); this.r = (byte)((argb >> 8) & 0xff); } #region Accessors /// /// RGB Red [0-255] /// public byte Red { get { return r; } set { r = value; } } /// /// RGB Green [0-255] /// public byte Grn { get { return g; } set { g = value; } } /// /// RGB Blue [0-255] /// public byte Blu { get { return b; } set { b = value; } } #endregion Accessors #region Static Conversion Methods /// /// Convert a System.Drawing.Color to its corresponding RGB /// /// A System.Drawing.Color /// A corresponding RGB structure public static RGB ConvertFromColor(System.Drawing.Color c) { return new RGB(c.R, c.G, c.B); } /// /// Convert an RGB structure to its corresponding System.Drawing.Color /// /// An RGB structure /// A corresponding System.Drawing.Color public static System.Drawing.Color ConvertToColor(RGB rgb) { return System.Drawing.Color.FromArgb(rgb.r, rgb.g, rgb.b); } /// /// Convert an RGB structure to its corresponding HSV structure /// /// An RGB structure /// A corresponding HSV structure public static HSV ConvertToHSV( RGB rgb ) { HSV hsv = new HSV(); byte min, max; // Find the min / max for r, g, b if (rgb.r <= rgb.g) { if (rgb.r >= rgb.b) { min = rgb.b; max = rgb.g; } else { min = rgb.r; max = (rgb.g >= rgb.b)? rgb.g : rgb.b; } } else if (rgb.r <= rgb.b) { min = rgb.g; max = rgb.b; } else { max = rgb.r; min = (rgb.g <= rgb.b) ? rgb.g : rgb.b; } // HSV value and saturation hsv.Val = max; hsv.Sat = (byte)((max == 0) ? 0 : 255* (max - min) / max); // HSV hue if (max == min) hsv.Hue = 0; else { if (max == rgb.r) hsv.Hue = (short)((60 * (rgb.g - rgb.b) / (max - min) + 360) % 360); else if (max == rgb.g) hsv.Hue = (short)(60 * (rgb.b - rgb.r) / (max - min) + 120); else if (max == rgb.b) hsv.Hue = (short)(60 * (rgb.r - rgb.g) / (max - min) + 240); } return hsv; } #endregion private byte r; private byte g; private byte b; } /// /// Represents a color in HSV (Hue, Saturation, Value) /// byte Hue: [0-360] /// byte Sat: [0-255] /// byte Val: [0-255] /// internal struct HSV { public static short HSV_MAX_HUE = 360; public static byte HSV_MAX_SAT = 255; public static byte HSV_MAX_VAL = 255; /// /// Create a new HSV (Hue, Saturation, Value) struct /// /// HSV Hue [0-360] /// HSV Saturation [0-255] /// HSV Value [0-255] public HSV(short h, byte s, byte v) { this.h = h; this.s = s; this.v = v; } #region Accessors /// /// HSV Hue [0-360] /// public short Hue { get { return h; } set { h = value; } } /// /// HSV Saturation [0-255] /// public byte Sat { get { return s; } set { s = value; } } /// /// HSV Value [0-255] /// public byte Val { get { return v; } set { v = value; } } #endregion Accessors #region Static Conversion Methods /// /// Convert a System.Drawing.Color to its corresponding HSV /// /// A System.Drawing.Color /// A corresponding HSV structure public static HSV ConvertFromColor(System.Drawing.Color c) { return RGB.ConvertToHSV(new RGB(c.R, c.G, c.B)); } /// /// Convert an HSV structure to its corresponding System.Drawing.Color /// /// An HSV structure /// A corresponding System.Drawing.Color public static System.Drawing.Color ConvertToColor(HSV hsv) { RGB rgb = HSV.ConvertToRGB(hsv); return System.Drawing.Color.FromArgb(rgb.Red, rgb.Grn, rgb.Blu); } /// /// Convert an HSV structure to its corresponding RGB structure /// /// An HSV structure /// A corresponding RGB structure public static RGB ConvertToRGB(HSV hsv) { int h = (hsv.h / 60) % 6; float f = hsv.h / 60.0F - hsv.h / 60; byte p = (byte)((hsv.v * (HSV.HSV_MAX_SAT - hsv.s)) / HSV.HSV_MAX_SAT); byte q = (byte)((hsv.v * (HSV.HSV_MAX_SAT - f * hsv.s)) / HSV.HSV_MAX_SAT); byte t = (byte)((hsv.v * (HSV.HSV_MAX_SAT - (1 - f) * hsv.s)) / HSV.HSV_MAX_SAT); RGB rgb = new RGB(); if (h == 0) rgb = new RGB(hsv.v, t, p); else if (h == 1) rgb = new RGB(q, hsv.v, p); else if (h == 2) rgb = new RGB(p, hsv.v, t); else if (h == 3) rgb = new RGB(p, q, hsv.v); else if (h == 4) rgb = new RGB(t, p, hsv.v); else if (h == 5) rgb = new RGB(hsv.v, p, q); return rgb; } /// /// Returns an HSV representing the binned version of the argument HSV color /// /// The HSV color to be binned and then used to generate the hash key /// The count of bins per Hue, Sat, Val space public static HSV GetBinnedHSV(HSV hsv, HSV binCounts) { HSV binnedHSV = new HSV(); binnedHSV.Hue = (short)(hsv.Hue * (binCounts.Hue - 1) / HSV.HSV_MAX_HUE); binnedHSV.Sat = (byte)(hsv.Sat * (binCounts.Sat - 1) / HSV.HSV_MAX_SAT); binnedHSV.Val = (byte)(hsv.Val * (binCounts.Val - 1) / HSV.HSV_MAX_VAL); return binnedHSV; } #endregion private short h; private byte s; private byte v; } /// /// A hash key that represents an HSV color /// internal struct ColorKey { /// /// Constructor that takes an HSV color /// /// The HSV color used to generate the hash key public ColorKey(HSV h) { hsv = h; key = h.Hue + h.Sat * HSV.HSV_MAX_HUE + h.Val * HSV.HSV_MAX_SAT * HSV.HSV_MAX_HUE; } /// /// The HSV Value used to generate the hash key /// public HSV Hsv { get { return hsv; } set { hsv = value; key = value.Hue + value.Sat * HSV.HSV_MAX_HUE + value.Val * HSV.HSV_MAX_SAT * HSV.HSV_MAX_HUE; } } /// /// Provides a hash key from the HSV value /// /// A unique int used as a hash key for this color (depends on HSV colorspace partitioning model) public override int GetHashCode() { return key; } /// /// Hashtable equality tester /// /// /// Returns true if the color components (h,s,v) are the same public override bool Equals(object obj) { if (obj.GetType() != typeof(ColorKey)) return false; ColorKey other = (ColorKey)obj; return other.hsv.Hue == hsv.Hue && other.hsv.Sat == hsv.Sat && other.hsv.Val == hsv.Val; } private HSV hsv; private int key; } }