/// <summary> /// Convert from HSL to RGB color space. /// </summary> /// /// <param name="hsl">Source color in <b>HSL</b> color space.</param> /// <param name="rgb">Destination color in <b>RGB</b> color space.</param> /// public static void ToRGB(HSL hsl, RGB rgb) { if (hsl.Saturation == 0) { // gray values rgb.Red = rgb.Green = rgb.Blue = (byte)(hsl.Luminance * 255); } else { float v1, v2; float hue = (float)hsl.Hue / 360; v2 = (hsl.Luminance < 0.5) ? (hsl.Luminance * (1 + hsl.Saturation)) : ((hsl.Luminance + hsl.Saturation) - (hsl.Luminance * hsl.Saturation)); v1 = 2 * hsl.Luminance - v2; rgb.Red = (byte)(255 * Hue_2_RGB(v1, v2, hue + (1.0f / 3))); rgb.Green = (byte)(255 * Hue_2_RGB(v1, v2, hue)); rgb.Blue = (byte)(255 * Hue_2_RGB(v1, v2, hue - (1.0f / 3))); } rgb.Alpha = 255; }
/// <summary> /// Paint the controls. /// </summary> /// /// <param name="e">Paint event arguments.</param> /// protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; Rectangle rc = this.ClientRectangle; Rectangle rcPie; Brush brush; RGB rgb = new RGB(); HSL hsl = new HSL(); // get pie rectangle rcPie = new Rectangle(4, 4, System.Math.Min(rc.Right, rc.Bottom) - 8, System.Math.Min(rc.Right, rc.Bottom) - 8); // init HSL value hsl.Luminance = 0.5f; hsl.Saturation = 1.0f; if (type == HuePickerType.Value) { // draw HSL pie for (int i = 0; i < 360; i++) { hsl.Hue = i; // convert from HSL to RGB Accord.Imaging.HSL.ToRGB(hsl, rgb); // create brush brush = new SolidBrush(rgb.Color); // draw one hue value g.FillPie(brush, rcPie, -i, -1); brush.Dispose(); } } else { // draw HSL pie for (int i = 0; i < 360; i++) { if ( ((min < max) && (i >= min) && (i <= max)) || ((min > max) && ((i >= min) || (i <= max)))) { hsl.Hue = i; // convert from HSL to RGB Accord.Imaging.HSL.ToRGB(hsl, rgb); // create brush brush = new SolidBrush(rgb.Color); } else { brush = new SolidBrush(Color.FromArgb(128, 128, 128)); } // draw one hue value g.FillPie(brush, rcPie, -i, -1); brush.Dispose(); } } // double halfWidth = (double)rcPie.Width / 2; double angleRad = -min * System.Math.PI / 180; double angleCos = System.Math.Cos(angleRad); double angleSin = System.Math.Sin(angleRad); double x = halfWidth * angleCos; double y = halfWidth * angleSin; ptCenter.X = rcPie.Left + (int)(halfWidth); ptCenter.Y = rcPie.Top + (int)(halfWidth); ptMin.X = rcPie.Left + (int)(halfWidth + x); ptMin.Y = rcPie.Top + (int)(halfWidth + y); // draw MIN pointer g.FillEllipse(blackBrush, rcPie.Left + (int)(halfWidth + x) - 4, rcPie.Top + (int)(halfWidth + y) - 4, 8, 8); g.DrawLine(blackPen, ptCenter, ptMin); // check picker type if (type == HuePickerType.Range) { angleRad = -max * System.Math.PI / 180; angleCos = System.Math.Cos(angleRad); angleSin = System.Math.Sin(angleRad); x = halfWidth * angleCos; y = halfWidth * angleSin; ptMax.X = rcPie.Left + (int)(halfWidth + x); ptMax.Y = rcPie.Top + (int)(halfWidth + y); // draw MAX pointer g.FillEllipse(whiteBrush, rcPie.Left + (int)(halfWidth + x) - 4, rcPie.Top + (int)(halfWidth + y) - 4, 8, 8); g.DrawLine(whitePen, ptCenter, ptMax); } base.OnPaint(e); }
// Gather statistics for the specified image private unsafe void ProcessImage(UnmanagedImage image, byte *mask, int maskLineSize) { // get image dimension int width = image.Width; int height = image.Height; pixels = pixelsWithoutBlack = 0; int[] s = new int[256]; int[] l = new int[256]; int[] swb = new int[256]; int[] lwb = new int[256]; RGB rgb = new RGB(); HSL hsl = new HSL(); int pixelSize = (image.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4; int offset = image.Stride - width * pixelSize; int maskOffset = maskLineSize - width; // do the job byte *p = (byte *)image.ImageData.ToPointer(); if (mask == null) { // for each line for (int y = 0; y < height; y++) { // for each pixel for (int x = 0; x < width; x++, p += pixelSize) { rgb.Red = p[RGB.R]; rgb.Green = p[RGB.G]; rgb.Blue = p[RGB.B]; // convert to HSL color space Accord.Imaging.HSL.FromRGB(rgb, ref hsl); s[(int)(hsl.Saturation * 255)]++; l[(int)(hsl.Luminance * 255)]++; pixels++; if (hsl.Luminance != 0.0) { swb[(int)(hsl.Saturation * 255)]++; lwb[(int)(hsl.Luminance * 255)]++; pixelsWithoutBlack++; } } p += offset; } } else { // for each line for (int y = 0; y < height; y++) { // for each pixel for (int x = 0; x < width; x++, p += pixelSize, mask++) { if (*mask == 0) { continue; } rgb.Red = p[RGB.R]; rgb.Green = p[RGB.G]; rgb.Blue = p[RGB.B]; // convert to HSL color space Accord.Imaging.HSL.FromRGB(rgb, ref hsl); s[(int)(hsl.Saturation * 255)]++; l[(int)(hsl.Luminance * 255)]++; pixels++; if (hsl.Luminance != 0.0) { swb[(int)(hsl.Saturation * 255)]++; lwb[(int)(hsl.Luminance * 255)]++; pixelsWithoutBlack++; } } p += offset; mask += maskOffset; } } // create histograms saturation = new ContinuousHistogram(s, new Range(0, 1)); luminance = new ContinuousHistogram(l, new Range(0, 1)); saturationWithoutBlack = new ContinuousHistogram(swb, new Range(0, 1)); luminanceWithoutBlack = new ContinuousHistogram(lwb, new Range(0, 1)); }
/// <summary> /// Creates a color histogram discarding low intensity colors /// </summary> /// private unsafe float[] createHistogram(UnmanagedImage frame, Rectangle area) { int width = frame.Width; int height = frame.Height; int stride = frame.Stride; int pixelSize = Bitmap.GetPixelFormatSize(frame.PixelFormat) / 8; int offset = stride - area.Width * pixelSize; float[] histogram = new float[histogramSize]; // stay inside the image int areaX = Math.Max(area.X, 0); int areaY = Math.Max(area.Y, 0); int areaWidth = Math.Min(area.Width, width - areaX); int areaHeight = Math.Min(area.Height, height - areaY); if (mode == CamshiftMode.HSL) { // Process as HSL HSL hsl = new HSL(); RGB rgb = new RGB(); byte* src = (byte*)frame.ImageData.ToPointer() + areaX * pixelSize + areaY * stride; for (int y = 0; y < areaHeight; y++) { for (int x = 0; x < areaWidth; x++, src += 3) { rgb.Red = (*(src + RGB.R)); rgb.Green = (*(src + RGB.G)); rgb.Blue = (*(src + RGB.B)); Accord.Imaging.HSL.FromRGB(rgb, hsl); if ((hsl.Saturation >= hslSaturation.Min) && (hsl.Saturation <= hslSaturation.Max) && (hsl.Luminance >= hslLuminance.Min) && (hsl.Luminance <= hslLuminance.Max)) histogram[hsl.Hue] += 1; } src += offset; } } else if (mode == CamshiftMode.Mixed) { // Process in mixed mode HSL hsl = new HSL(); RGB rgb = new RGB(); byte* src = (byte*)frame.ImageData.ToPointer() + areaX * pixelSize + areaY * stride; for (int y = 0; y < areaHeight; y++) { for (int x = 0; x < areaWidth; x++, src += 3) { rgb.Red = (*(src + RGB.R)); rgb.Green = (*(src + RGB.G)); rgb.Blue = (*(src + RGB.B)); Accord.Imaging.HSL.FromRGB(rgb, hsl); if ((hsl.Saturation >= hslSaturation.Min) && (hsl.Saturation <= hslSaturation.Max) && (hsl.Luminance >= hslLuminance.Min) && (hsl.Luminance <= hslLuminance.Max)) histogram[(int)(hsl.Hue * 10 + hsl.Saturation * 100 + hsl.Luminance * 10)] += 1; } src += offset; } } else { // Process as RGB byte* src = (byte*)frame.ImageData.ToPointer() + areaX * pixelSize + areaY * stride; for (int y = 0; y < areaHeight; y++) { for (int x = 0; x < areaWidth; x++, src += 3) { // (small values are discarded) int r = (int)(*(src + RGB.R)) >> 4; int g = (int)(*(src + RGB.G)) >> 4; int b = (int)(*(src + RGB.B)) >> 4; histogram[256 * r + 16 * g + b] += 1; } src += offset; } } return histogram; }
/// <summary> /// Image histogram back-projection. /// </summary> /// private unsafe void generateBackprojectionMap(UnmanagedImage frame, float[] ratioHistogram) { int width = frame.Width; int height = frame.Height; int stride = frame.Stride; int pixelSize = Bitmap.GetPixelFormatSize(frame.PixelFormat) / 8; int offset = stride - width * pixelSize; fixed (float* map_ptr = map) { byte* src = (byte*)frame.ImageData.ToPointer(); float* dst = map_ptr; if (mode == CamshiftMode.HSL) { // Process as HSL HSL hsl = new HSL(); RGB rgb = new RGB(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++, src += pixelSize, dst++) { // RGB rgb.Red = (*(src + RGB.R)); rgb.Green = (*(src + RGB.G)); rgb.Blue = (*(src + RGB.B)); // Transform into HSL Accord.Imaging.HSL.FromRGB(rgb, hsl); if ((hsl.Saturation >= hslSaturation.Min) && (hsl.Saturation <= hslSaturation.Max) && (hsl.Luminance >= hslLuminance.Min) && (hsl.Luminance <= hslLuminance.Max)) *dst = ratioHistogram[hsl.Hue]; else *dst = 0; } src += offset; } } else if (mode == CamshiftMode.Mixed) { // Process in mixed mode HSL hsl = new HSL(); RGB rgb = new RGB(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++, src += pixelSize, dst++) { // RGB rgb.Red = (*(src + RGB.R)); rgb.Green = (*(src + RGB.G)); rgb.Blue = (*(src + RGB.B)); // Transform into HSL Accord.Imaging.HSL.FromRGB(rgb, hsl); if ((hsl.Saturation >= hslSaturation.Min) && (hsl.Saturation <= hslSaturation.Max) && (hsl.Luminance >= hslLuminance.Min) && (hsl.Luminance <= hslLuminance.Max)) *dst = ratioHistogram[(int)(hsl.Hue * 10 + hsl.Saturation * 100 + hsl.Luminance * 10)]; else *dst = 0; } src += offset; } } else { // Process as RGB for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++, src += pixelSize, dst++) { // RGB int r = (int)(*(src + RGB.R)) >> 4; int g = (int)(*(src + RGB.G)) >> 4; int b = (int)(*(src + RGB.B)) >> 4; *dst = ratioHistogram[256 * r + 16 * g + b]; } src += offset; } } } }
/// <summary> /// Convert from RGB to HSL color space. /// </summary> /// /// <param name="rgb">Source color in <b>RGB</b> color space.</param> /// /// <returns>Returns <see cref="HSL"/> instance, which represents converted color value.</returns> /// public static HSL FromRGB(RGB rgb) { HSL hsl = new HSL(); FromRGB(rgb, hsl); return hsl; }
/// <summary> /// Convert from RGB to HSL color space. /// </summary> /// /// <param name="rgb">Source color in <b>RGB</b> color space.</param> /// <param name="hsl">Destination color in <b>HSL</b> color space.</param> /// /// <remarks><para>See <a href="http://en.wikipedia.org/wiki/HSI_color_space#Conversion_from_RGB_to_HSL_or_HSV">HSL and HSV Wiki</a> /// for information about the algorithm to convert from RGB to HSL.</para></remarks> /// public static void FromRGB(RGB rgb, HSL hsl) { float r = (rgb.Red / 255.0f); float g = (rgb.Green / 255.0f); float b = (rgb.Blue / 255.0f); float min = Math.Min(Math.Min(r, g), b); float max = Math.Max(Math.Max(r, g), b); float delta = max - min; // get luminance value hsl.Luminance = (max + min) / 2; if (delta == 0) { // gray color hsl.Hue = 0; hsl.Saturation = 0.0f; } else { // get saturation value hsl.Saturation = (hsl.Luminance <= 0.5) ? (delta / (max + min)) : (delta / (2 - max - min)); // get hue value float hue; if (r == max) { hue = ((g - b) / 6) / delta; } else if (g == max) { hue = (1.0f / 3) + ((b - r) / 6) / delta; } else { hue = (2.0f / 3) + ((r - g) / 6) / delta; } // correct hue if needed if (hue < 0) hue += 1; if (hue > 1) hue -= 1; hsl.Hue = (int)(hue * 360); } }
// Gather statistics for the specified image private unsafe void ProcessImage(UnmanagedImage image, byte* mask, int maskLineSize) { // get image dimension int width = image.Width; int height = image.Height; pixels = pixelsWithoutBlack = 0; int[] s = new int[256]; int[] l = new int[256]; int[] swb = new int[256]; int[] lwb = new int[256]; RGB rgb = new RGB(); HSL hsl = new HSL(); int pixelSize = (image.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4; int offset = image.Stride - width * pixelSize; int maskOffset = maskLineSize - width; // do the job byte* p = (byte*)image.ImageData.ToPointer(); if (mask == null) { // for each line for (int y = 0; y < height; y++) { // for each pixel for (int x = 0; x < width; x++, p += pixelSize) { rgb.Red = p[RGB.R]; rgb.Green = p[RGB.G]; rgb.Blue = p[RGB.B]; // convert to HSL color space Accord.Imaging.HSL.FromRGB(rgb, hsl); s[(int)(hsl.Saturation * 255)]++; l[(int)(hsl.Luminance * 255)]++; pixels++; if (hsl.Luminance != 0.0) { swb[(int)(hsl.Saturation * 255)]++; lwb[(int)(hsl.Luminance * 255)]++; pixelsWithoutBlack++; } } p += offset; } } else { // for each line for (int y = 0; y < height; y++) { // for each pixel for (int x = 0; x < width; x++, p += pixelSize, mask++) { if (*mask == 0) continue; rgb.Red = p[RGB.R]; rgb.Green = p[RGB.G]; rgb.Blue = p[RGB.B]; // convert to HSL color space Accord.Imaging.HSL.FromRGB(rgb, hsl); s[(int)(hsl.Saturation * 255)]++; l[(int)(hsl.Luminance * 255)]++; pixels++; if (hsl.Luminance != 0.0) { swb[(int)(hsl.Saturation * 255)]++; lwb[(int)(hsl.Luminance * 255)]++; pixelsWithoutBlack++; } } p += offset; mask += maskOffset; } } // create histograms saturation = new ContinuousHistogram(s, new Range(0, 1)); luminance = new ContinuousHistogram(l, new Range(0, 1)); saturationWithoutBlack = new ContinuousHistogram(swb, new Range(0, 1)); luminanceWithoutBlack = new ContinuousHistogram(lwb, new Range(0, 1)); }