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