From 9fdc7c768dca163fdd35bf2a1fc8119d6f9fa700 Mon Sep 17 00:00:00 2001 From: James South Date: Wed, 17 Sep 2014 18:22:52 +0100 Subject: [PATCH] Adding YCbCr (not correct yet) Former-commit-id: 3e72f3c0fd48b9f990701ad65001b8f71849673c --- .../Imaging/ColorUnitTests.cs | 76 +++++++- src/ImageProcessor/ImageProcessor.csproj | 1 + .../Imaging/Colors/HSLAColor.cs | 86 ++++----- .../Imaging/Colors/YCbCrColor.cs | 163 ++++++++++++++++++ src/ImageProcessor/Processors/Hue.cs | 2 +- 5 files changed, 277 insertions(+), 51 deletions(-) create mode 100644 src/ImageProcessor/Imaging/Colors/YCbCrColor.cs diff --git a/src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs b/src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs index c522c7f65..70fbfcddb 100644 --- a/src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs +++ b/src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs @@ -10,6 +10,7 @@ namespace ImageProcessor.UnitTests.Imaging { + using System.Diagnostics.CodeAnalysis; using System.Drawing; using ImageProcessor.Imaging.Colors; using NUnit.Framework; @@ -45,17 +46,78 @@ namespace ImageProcessor.UnitTests.Imaging } /// - /// Test conversion to and from a HSLA color. + /// Test conversion to and from a . /// + /// + /// The expected output. + /// [Test] - public void TestHSLAConversion() + [TestCase("#FEFFFE")] + [TestCase("#000000")] + [TestCase("#CCFF33")] + [TestCase("#990000")] + [TestCase("#5C955C")] + [TestCase("#5C5C95")] + [TestCase("#3F3F66")] + [TestCase("#FFFFBB")] + [TestCase("#FF002B")] + [TestCase("#00ABFF")] + public void TestHslaConversion(string expected) { - const string Hex = "#FEFFFE"; - - Color color = ColorTranslator.FromHtml(Hex); + Color color = ColorTranslator.FromHtml(expected); HslaColor hslaColor = HslaColor.FromColor(color); - string outPut = ColorTranslator.ToHtml(hslaColor); - Assert.AreEqual(Hex, outPut); + string result = ColorTranslator.ToHtml(hslaColor); + Assert.AreEqual(expected, result); + } + + /// + /// Test conversion to and from a . + /// + /// + /// The expected output. + /// + [Test] + [TestCase("#FEFFFE")] + [TestCase("#000000")] + [TestCase("#CCFF33")] + [TestCase("#990000")] + [TestCase("#5C955C")] + [TestCase("#5C5C95")] + [TestCase("#3F3F66")] + [TestCase("#FFFFBB")] + [TestCase("#FF002B")] + [TestCase("#00ABFF")] + public void TestRgbaConversion(string expected) + { + Color color = ColorTranslator.FromHtml(expected); + RgbaColor rgbaColor = RgbaColor.FromColor(color); + string result = ColorTranslator.ToHtml(rgbaColor); + Assert.AreEqual(expected, result); } + + ///// + ///// Test conversion to and from a . + ///// + ///// + ///// The expected output. + ///// + //[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here."),Test] + //[TestCase("#FEFFFE")] + //[TestCase("#000000")] + //[TestCase("#CCFF33")] + //[TestCase("#990000")] + //[TestCase("#5C955C")] + //[TestCase("#5C5C95")] + //[TestCase("#3F3F66")] + //[TestCase("#FFFFBB")] + //[TestCase("#FF002B")] + //[TestCase("#00ABFF")] + //public void TestYCbCrConversion(string expected) + //{ + // Color color = ColorTranslator.FromHtml(expected); + // YCbCrColor yCbCrColor = YCbCrColor.FromColor(color); + // string result = ColorTranslator.ToHtml(yCbCrColor); + // Assert.AreEqual(expected, result); + //} } } diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 50a87b97a..1243ce923 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -72,6 +72,7 @@ + diff --git a/src/ImageProcessor/Imaging/Colors/HSLAColor.cs b/src/ImageProcessor/Imaging/Colors/HSLAColor.cs index 706608f2a..d42ee3a87 100644 --- a/src/ImageProcessor/Imaging/Colors/HSLAColor.cs +++ b/src/ImageProcessor/Imaging/Colors/HSLAColor.cs @@ -27,22 +27,22 @@ namespace ImageProcessor.Imaging.Colors /// /// The hue component. /// - private readonly double h; + private readonly float h; /// /// The luminosity component. /// - private readonly double l; + private readonly float l; /// /// The saturation component. /// - private readonly double s; + private readonly float s; /// /// The alpha component. /// - private readonly double a; + private readonly float a; /// /// Initializes a new instance of the struct. @@ -59,7 +59,7 @@ namespace ImageProcessor.Imaging.Colors /// /// The alpha component. /// - private HslaColor(double hue, double saturation, double luminosity, double alpha) + private HslaColor(float hue, float saturation, float luminosity, float alpha) { this.h = Clamp(hue); this.s = Clamp(saturation); @@ -85,7 +85,7 @@ namespace ImageProcessor.Imaging.Colors /// /// Gets the hue component. /// - public double H + public float H { get { @@ -96,7 +96,7 @@ namespace ImageProcessor.Imaging.Colors /// /// Gets the luminosity component. /// - public double L + public float L { get { @@ -107,7 +107,7 @@ namespace ImageProcessor.Imaging.Colors /// /// Gets the saturation component. /// - public double S + public float S { get { @@ -118,7 +118,7 @@ namespace ImageProcessor.Imaging.Colors /// /// Gets the alpha component. /// - public double A + public float A { get { @@ -127,7 +127,7 @@ namespace ImageProcessor.Imaging.Colors } /// - /// Creates a structure from the three 64-bit HSLA + /// Creates a structure from the three 32-bit HSLA /// components (hue, saturation, and luminosity) values. /// /// @@ -142,13 +142,13 @@ namespace ImageProcessor.Imaging.Colors /// /// The . /// - public static HslaColor FromHslaColor(double hue, double saturation, double luminosity) + public static HslaColor FromHslaColor(float hue, float saturation, float luminosity) { - return new HslaColor(hue, saturation, luminosity, 1.0); + return new HslaColor(hue, saturation, luminosity, 1.0f); } /// - /// Creates a structure from the four 64-bit HSLA + /// Creates a structure from the four 32-bit HSLA /// components (hue, saturation, luminosity, and alpha) values. /// /// @@ -166,7 +166,7 @@ namespace ImageProcessor.Imaging.Colors /// /// The . /// - public static HslaColor FromHslaColor(double hue, double saturation, double luminosity, double alpha) + public static HslaColor FromHslaColor(float hue, float saturation, float luminosity, float alpha) { return new HslaColor(hue, saturation, luminosity, alpha); } @@ -198,7 +198,7 @@ namespace ImageProcessor.Imaging.Colors public static implicit operator HslaColor(Color color) { HslaColor hslColor = new HslaColor( - color.GetHue() / 360.0, + color.GetHue() / 360.0f, color.GetSaturation(), color.GetBrightness(), color.A / 255f); @@ -220,7 +220,7 @@ namespace ImageProcessor.Imaging.Colors { Color color = rgbaColor; HslaColor hslColor = new HslaColor( - color.GetHue() / 360.0, + color.GetHue() / 360.0f, color.GetSaturation(), color.GetBrightness(), color.A / 255f); @@ -240,7 +240,7 @@ namespace ImageProcessor.Imaging.Colors /// public static implicit operator Color(HslaColor hslaColor) { - double r = 0, g = 0, b = 0; + float r = 0, g = 0, b = 0; if (Math.Abs(hslaColor.l - 0) > .0001) { if (Math.Abs(hslaColor.s - 0) <= .0001) @@ -249,12 +249,12 @@ namespace ImageProcessor.Imaging.Colors } else { - double temp2 = GetTemp2(hslaColor); - double temp1 = (2.0 * hslaColor.l) - temp2; + float temp2 = GetTemp2(hslaColor); + float temp1 = (2.0f * hslaColor.l) - temp2; - r = GetColorComponent(temp1, temp2, hslaColor.h + (1.0 / 3.0)); + r = GetColorComponent(temp1, temp2, hslaColor.h + (1.0f / 3.0f)); g = GetColorComponent(temp1, temp2, hslaColor.h); - b = GetColorComponent(temp1, temp2, hslaColor.h - (1.0 / 3.0)); + b = GetColorComponent(temp1, temp2, hslaColor.h - (1.0f / 3.0f)); } } @@ -277,7 +277,7 @@ namespace ImageProcessor.Imaging.Colors /// public static implicit operator RgbaColor(HslaColor hslaColor) { - double r = 0, g = 0, b = 0; + float r = 0, g = 0, b = 0; if (Math.Abs(hslaColor.l - 0) > .0001) { if (Math.Abs(hslaColor.s - 0) <= .0001) @@ -286,12 +286,12 @@ namespace ImageProcessor.Imaging.Colors } else { - double temp2 = GetTemp2(hslaColor); - double temp1 = (2.0 * hslaColor.l) - temp2; + float temp2 = GetTemp2(hslaColor); + float temp1 = (2.0f * hslaColor.l) - temp2; - r = GetColorComponent(temp1, temp2, hslaColor.h + (1.0 / 3.0)); + r = GetColorComponent(temp1, temp2, hslaColor.h + (1.0f / 3.0f)); g = GetColorComponent(temp1, temp2, hslaColor.h); - b = GetColorComponent(temp1, temp2, hslaColor.h - (1.0 / 3.0)); + b = GetColorComponent(temp1, temp2, hslaColor.h - (1.0f / 3.0f)); } } @@ -363,14 +363,14 @@ namespace ImageProcessor.Imaging.Colors /// The temp 3. /// /// - /// The . + /// The . /// - private static double GetColorComponent(double temp1, double temp2, double temp3) + private static float GetColorComponent(float temp1, float temp2, float temp3) { temp3 = MoveIntoRange(temp3); if (temp3 < 1.0 / 6.0) { - return temp1 + ((temp2 - temp1) * 6.0 * temp3); + return temp1 + ((temp2 - temp1) * 6.0f * temp3); } if (temp3 < 0.5) @@ -380,7 +380,7 @@ namespace ImageProcessor.Imaging.Colors if (temp3 < 2.0 / 3.0) { - return temp1 + ((temp2 - temp1) * ((2.0 / 3.0) - temp3) * 6.0); + return temp1 + ((temp2 - temp1) * ((2.0f / 3.0f) - temp3) * 6.0f); } return temp1; @@ -393,14 +393,14 @@ namespace ImageProcessor.Imaging.Colors /// The color. /// /// - /// The . + /// The . /// - private static double GetTemp2(HslaColor hslColor) + private static float GetTemp2(HslaColor hslColor) { - double temp2; + float temp2; if (hslColor.l <= 0.5) { - temp2 = hslColor.l * (1.0 + hslColor.s); + temp2 = hslColor.l * (1.0f + hslColor.s); } else { @@ -417,17 +417,17 @@ namespace ImageProcessor.Imaging.Colors /// The temp 3. /// /// - /// The . + /// The . /// - private static double MoveIntoRange(double temp3) + private static float MoveIntoRange(float temp3) { if (temp3 < 0.0) { - temp3 += 1.0; + temp3 += 1.0f; } else if (temp3 > 1.0) { - temp3 -= 1.0; + temp3 -= 1.0f; } return temp3; @@ -440,17 +440,17 @@ namespace ImageProcessor.Imaging.Colors /// The value to check. /// /// - /// The sanitized . + /// The sanitized . /// - private static double Clamp(double value) + private static float Clamp(float value) { if (value < 0.0) { - value = 0.0; + value = 0.0f; } else if (value > 1.0) { - value = 1.0; + value = 1.0f; } return value; @@ -464,7 +464,7 @@ namespace ImageProcessor.Imaging.Colors /// private bool IsEmpty() { - const double Epsilon = .0001; + const float Epsilon = .0001f; return Math.Abs(this.h - 0) <= Epsilon && Math.Abs(this.s - 0) <= Epsilon && Math.Abs(this.l - 0) <= Epsilon && Math.Abs(this.a - 0) <= Epsilon; } diff --git a/src/ImageProcessor/Imaging/Colors/YCbCrColor.cs b/src/ImageProcessor/Imaging/Colors/YCbCrColor.cs new file mode 100644 index 000000000..d1a5497c5 --- /dev/null +++ b/src/ImageProcessor/Imaging/Colors/YCbCrColor.cs @@ -0,0 +1,163 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Represents an YCbCr (luminance, chroma, chroma) color used in digital imaging systems. +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Imaging.Colors +{ + using System; + using System.Drawing; + + using ImageProcessor.Common.Extensions; + + /// + /// Represents an YCbCr (luminance, chroma, chroma) color used in digital imaging systems. + /// + /// + public struct YCbCrColor + { + /// + /// Represents a that is null. + /// + public static readonly YCbCrColor Empty = new YCbCrColor(); + + /// + /// The y luminance component. + /// + private readonly float y; + + /// + /// The u chroma component. + /// + private readonly float cb; + + /// + /// The v chroma component. + /// + private readonly float cr; + + /// + /// Initializes a new instance of the struct. + /// + /// The y luminance component. + /// The u chroma component. + /// The v chroma component. + private YCbCrColor(float y, float cb, float cr) + { + this.y = y; //Math.Max(0.0f, Math.Min(1.0f, y)); + this.cb = cb; //Math.Max(-0.5f, Math.Min(0.5f, cb)); + this.cr = cr; //Math.Max(-0.5f, Math.Min(0.5f, cr)); + } + + /// + /// Gets the Y luminance component. + /// + public float Y + { + get + { + return this.y; + } + } + + /// + /// Gets the U chroma component. + /// + public float Cb + { + get + { + return this.y; + } + } + + /// + /// Gets the V chroma component. + /// + public float Cr + { + get + { + return this.y; + } + } + + /// + /// Creates a structure from the three 32-bit YCbCr + /// components (luminance, chroma, and chroma) values. + /// + /// The y luminance component. + /// The u chroma component. + /// The v chroma component. + /// + /// The . + /// + public static YCbCrColor FromYCbCr(float y, float cb, float cr) + { + return new YCbCrColor(y, cb, cr); + } + + /// + /// Creates a structure from the specified structure + /// + /// + /// The from which to create the new . + /// + /// + /// The . + /// + public static YCbCrColor FromColor(Color color) + { + float r = color.R; + float g = color.G; + float b = color.B; + + float y = (float)((0.299 * r) + (0.587 * g) + (0.114 * b)); + float cb = 128 - (float)((-0.168736 * r) - (0.331264 * g) + (0.5 * b)); + float cr = 128 + (float)((0.5 * r) - (0.418688 * g) - (0.081312 * b)); + + return new YCbCrColor(y, cb, cr); + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator YCbCrColor(Color color) + { + return FromColor(color); + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator Color(YCbCrColor ycbcr) + { + byte r = Convert.ToInt32(ycbcr.Y + (1.402 * (ycbcr.Cr - 128))).ToByte(); + byte g = Convert.ToInt32(ycbcr.Y - (0.34414 * (ycbcr.Cb - 128)) - (0.71414 * (ycbcr.Cr - 128))).ToByte(); + byte b = Convert.ToInt32(ycbcr.Y + (1.772 * (ycbcr.Cb - 128))).ToByte(); + + + return Color.FromArgb(255, r, g, b); + } + } +} diff --git a/src/ImageProcessor/Processors/Hue.cs b/src/ImageProcessor/Processors/Hue.cs index 0ddc7b98d..2c59dcc54 100644 --- a/src/ImageProcessor/Processors/Hue.cs +++ b/src/ImageProcessor/Processors/Hue.cs @@ -70,7 +70,7 @@ namespace ImageProcessor.Processors for (int j = 0; j < height; j++) { HslaColor original = HslaColor.FromColor(fastBitmap.GetPixel(i, j)); - HslaColor altered = HslaColor.FromHslaColor(degrees / 360D, original.S, original.L, original.A); + HslaColor altered = HslaColor.FromHslaColor(degrees / 360f, original.S, original.L, original.A); fastBitmap.SetPixel(i, j, altered); } }