Browse Source

Fixing YcbCr

Former-commit-id: 940697fd8a7d4889a263209975a1043d5e24f125
pull/17/head
James South 12 years ago
parent
commit
41d049a178
  1. 78
      src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs
  2. 4
      src/ImageProcessor/Common/Extensions/DoubleExtensions.cs
  3. 75
      src/ImageProcessor/Imaging/Colors/HSLAColor.cs
  4. 42
      src/ImageProcessor/Imaging/Colors/RGBAColor.cs
  5. 163
      src/ImageProcessor/Imaging/Colors/YCbCrColor.cs

78
src/ImageProcessor.UnitTests/Imaging/ColorUnitTests.cs

@ -25,7 +25,7 @@ namespace ImageProcessor.UnitTests.Imaging
/// Tests the <see cref="RgbaColor"/> struct equality operators. /// Tests the <see cref="RgbaColor"/> struct equality operators.
/// </summary> /// </summary>
[Test] [Test]
public void TestRGBAEquality() public void TestRgbaEquality()
{ {
RgbaColor first = RgbaColor.FromColor(Color.White); RgbaColor first = RgbaColor.FromColor(Color.White);
RgbaColor second = RgbaColor.FromColor(Color.White); RgbaColor second = RgbaColor.FromColor(Color.White);
@ -34,10 +34,10 @@ namespace ImageProcessor.UnitTests.Imaging
} }
/// <summary> /// <summary>
/// Tests the <see cref="RgbaColor"/> struct equality operators. /// Tests the <see cref="HslaColor"/> struct equality operators.
/// </summary> /// </summary>
[Test] [Test]
public void TestHSLAEquality() public void TestHslaEquality()
{ {
HslaColor first = HslaColor.FromColor(Color.White); HslaColor first = HslaColor.FromColor(Color.White);
HslaColor second = HslaColor.FromColor(Color.White); HslaColor second = HslaColor.FromColor(Color.White);
@ -45,6 +45,18 @@ namespace ImageProcessor.UnitTests.Imaging
Assert.AreEqual(first, second); Assert.AreEqual(first, second);
} }
/// <summary>
/// Tests the <see cref="YCbCrColor"/> struct equality operators.
/// </summary>
[Test]
public void TestYCbCrEquality()
{
YCbCrColor first = YCbCrColor.FromColor(Color.White);
YCbCrColor second = YCbCrColor.FromColor(Color.White);
Assert.AreEqual(first, second);
}
/// <summary> /// <summary>
/// Test conversion to and from a <see cref="HslaColor"/>. /// Test conversion to and from a <see cref="HslaColor"/>.
/// </summary> /// </summary>
@ -52,9 +64,13 @@ namespace ImageProcessor.UnitTests.Imaging
/// The expected output. /// The expected output.
/// </param> /// </param>
[Test] [Test]
[TestCase("#FFFFFF")]
[TestCase("#FEFFFE")] [TestCase("#FEFFFE")]
[TestCase("#F0F8FF")]
[TestCase("#000000")] [TestCase("#000000")]
[TestCase("#CCFF33")] [TestCase("#CCFF33")]
[TestCase("#00FF00")]
[TestCase("#FF00FF")]
[TestCase("#990000")] [TestCase("#990000")]
[TestCase("#5C955C")] [TestCase("#5C955C")]
[TestCase("#5C5C95")] [TestCase("#5C5C95")]
@ -77,9 +93,13 @@ namespace ImageProcessor.UnitTests.Imaging
/// The expected output. /// The expected output.
/// </param> /// </param>
[Test] [Test]
[TestCase("#FFFFFF")]
[TestCase("#FEFFFE")] [TestCase("#FEFFFE")]
[TestCase("#F0F8FF")]
[TestCase("#000000")] [TestCase("#000000")]
[TestCase("#CCFF33")] [TestCase("#CCFF33")]
[TestCase("#00FF00")]
[TestCase("#FF00FF")]
[TestCase("#990000")] [TestCase("#990000")]
[TestCase("#5C955C")] [TestCase("#5C955C")]
[TestCase("#5C5C95")] [TestCase("#5C5C95")]
@ -95,29 +115,33 @@ namespace ImageProcessor.UnitTests.Imaging
Assert.AreEqual(expected, result); Assert.AreEqual(expected, result);
} }
///// <summary> /// <summary>
///// Test conversion to and from a <see cref="RgbaColor"/>. /// Test conversion to and from a <see cref="RgbaColor"/>.
///// </summary> /// </summary>
///// <param name="expected"> /// <param name="expected">
///// The expected output. /// The expected output.
///// </param> /// </param>
//[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here."),Test] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here."), Test]
//[TestCase("#FEFFFE")] [TestCase("#FFFFFF")]
//[TestCase("#000000")] [TestCase("#FEFFFE")]
//[TestCase("#CCFF33")] [TestCase("#F0F8FF")]
//[TestCase("#990000")] [TestCase("#000000")]
//[TestCase("#5C955C")] [TestCase("#CCFF33")]
//[TestCase("#5C5C95")] [TestCase("#00FF00")]
//[TestCase("#3F3F66")] [TestCase("#FF00FF")]
//[TestCase("#FFFFBB")] [TestCase("#990000")]
//[TestCase("#FF002B")] [TestCase("#5C955C")]
//[TestCase("#00ABFF")] [TestCase("#5C5C95")]
//public void TestYCbCrConversion(string expected) [TestCase("#3F3F66")]
//{ [TestCase("#FFFFBB")]
// Color color = ColorTranslator.FromHtml(expected); [TestCase("#FF002B")]
// YCbCrColor yCbCrColor = YCbCrColor.FromColor(color); [TestCase("#00ABFF")]
// string result = ColorTranslator.ToHtml(yCbCrColor); public void TestYCbCrConversion(string expected)
// Assert.AreEqual(expected, result); {
//} Color color = ColorTranslator.FromHtml(expected);
YCbCrColor yCbCrColor = YCbCrColor.FromColor(color);
string result = ColorTranslator.ToHtml(yCbCrColor);
Assert.AreEqual(expected, result);
}
} }
} }

4
src/ImageProcessor/Common/Extensions/DoubleExtensions.cs

@ -10,6 +10,8 @@
namespace ImageProcessor.Common.Extensions namespace ImageProcessor.Common.Extensions
{ {
using System;
/// <summary> /// <summary>
/// Encapsulates a series of time saving extension methods to the <see cref="T:System.Double"/> class. /// Encapsulates a series of time saving extension methods to the <see cref="T:System.Double"/> class.
/// </summary> /// </summary>
@ -30,7 +32,7 @@ namespace ImageProcessor.Common.Extensions
/// </returns> /// </returns>
public static byte ToByte(this double d) public static byte ToByte(this double d)
{ {
return (byte)((d > byte.MaxValue) ? byte.MaxValue : ((d < byte.MinValue) ? byte.MinValue : d)); return Convert.ToByte(Math.Max(0.0d, Math.Min(255d, d))); // ((d > byte.MaxValue) ? byte.MaxValue : ((d < byte.MinValue) ? byte.MinValue : d));
} }
} }
} }

75
src/ImageProcessor/Imaging/Colors/HSLAColor.cs

@ -84,6 +84,7 @@ namespace ImageProcessor.Imaging.Colors
/// <summary> /// <summary>
/// Gets the hue component. /// Gets the hue component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
public float H public float H
{ {
@ -95,6 +96,7 @@ namespace ImageProcessor.Imaging.Colors
/// <summary> /// <summary>
/// Gets the luminosity component. /// Gets the luminosity component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
public float L public float L
{ {
@ -106,6 +108,7 @@ namespace ImageProcessor.Imaging.Colors
/// <summary> /// <summary>
/// Gets the saturation component. /// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
public float S public float S
{ {
@ -117,6 +120,7 @@ namespace ImageProcessor.Imaging.Colors
/// <summary> /// <summary>
/// Gets the alpha component. /// Gets the alpha component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary> /// </summary>
public float A public float A
{ {
@ -197,17 +201,11 @@ namespace ImageProcessor.Imaging.Colors
/// </returns> /// </returns>
public static implicit operator HslaColor(Color color) public static implicit operator HslaColor(Color color)
{ {
HslaColor hslColor = new HslaColor( return FromColor(color);
color.GetHue() / 360.0f,
color.GetSaturation(),
color.GetBrightness(),
color.A / 255f);
return hslColor;
} }
/// <summary> /// <summary>
/// Allows the implicit conversion of an instance of <see cref="System.Drawing.Color"/> to a /// Allows the implicit conversion of an instance of <see cref="RgbaColor"/> to a
/// <see cref="HslaColor"/>. /// <see cref="HslaColor"/>.
/// </summary> /// </summary>
/// <param name="rgbaColor"> /// <param name="rgbaColor">
@ -218,14 +216,22 @@ namespace ImageProcessor.Imaging.Colors
/// </returns> /// </returns>
public static implicit operator HslaColor(RgbaColor rgbaColor) public static implicit operator HslaColor(RgbaColor rgbaColor)
{ {
Color color = rgbaColor; return FromColor(rgbaColor);
HslaColor hslColor = new HslaColor( }
color.GetHue() / 360.0f,
color.GetSaturation(), /// <summary>
color.GetBrightness(), /// Allows the implicit conversion of an instance of <see cref="YCbCrColor"/> to a
color.A / 255f); /// <see cref="HslaColor"/>.
/// </summary>
return hslColor; /// <param name="ycbcrColor">
/// The instance of <see cref="YCbCrColor"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="YCbCrColor"/>.
/// </returns>
public static implicit operator HslaColor(YCbCrColor ycbcrColor)
{
return FromColor(ycbcrColor);
} }
/// <summary> /// <summary>
@ -277,29 +283,22 @@ namespace ImageProcessor.Imaging.Colors
/// </returns> /// </returns>
public static implicit operator RgbaColor(HslaColor hslaColor) public static implicit operator RgbaColor(HslaColor hslaColor)
{ {
float r = 0, g = 0, b = 0; return RgbaColor.FromColor(hslaColor);
if (Math.Abs(hslaColor.l - 0) > .0001) }
{
if (Math.Abs(hslaColor.s - 0) <= .0001)
{
r = g = b = hslaColor.l;
}
else
{
float temp2 = GetTemp2(hslaColor);
float temp1 = (2.0f * hslaColor.l) - temp2;
r = GetColorComponent(temp1, temp2, hslaColor.h + (1.0f / 3.0f));
g = GetColorComponent(temp1, temp2, hslaColor.h);
b = GetColorComponent(temp1, temp2, hslaColor.h - (1.0f / 3.0f));
}
}
return RgbaColor.FromRgba( /// <summary>
Convert.ToByte(255 * r), /// Allows the implicit conversion of an instance of <see cref="HslaColor"/> to a
Convert.ToByte(255 * g), /// <see cref="YCbCrColor"/>.
Convert.ToByte(255 * b), /// </summary>
Convert.ToByte(255 * hslaColor.a)); /// <param name="hslaColor">
/// The instance of <see cref="HslaColor"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="YCbCrColor"/>.
/// </returns>
public static implicit operator YCbCrColor(HslaColor hslaColor)
{
return YCbCrColor.FromColor(hslaColor);
} }
/// <summary> /// <summary>

42
src/ImageProcessor/Imaging/Colors/RGBAColor.cs

@ -191,22 +191,37 @@ namespace ImageProcessor.Imaging.Colors
/// </returns> /// </returns>
public static implicit operator RgbaColor(Color color) public static implicit operator RgbaColor(Color color)
{ {
return new RgbaColor(color); return FromColor(color);
} }
/// <summary> /// <summary>
/// Allows the implicit conversion of an instance of <see cref="System.Drawing.Color"/> to a /// Allows the implicit conversion of an instance of <see cref="HslaColor"/> to a
/// <see cref="RgbaColor"/>. /// <see cref="RgbaColor"/>.
/// </summary> /// </summary>
/// <param name="color"> /// <param name="hslaColor">
/// The instance of <see cref="System.Drawing.Color"/> to convert. /// The instance of <see cref="HslaColor"/> to convert.
/// </param> /// </param>
/// <returns> /// <returns>
/// An instance of <see cref="RgbaColor"/>. /// An instance of <see cref="RgbaColor"/>.
/// </returns> /// </returns>
public static implicit operator RgbaColor(HslaColor color) public static implicit operator RgbaColor(HslaColor hslaColor)
{ {
return new RgbaColor(color); return FromColor(hslaColor);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCrColor"/> to a
/// <see cref="RgbaColor"/>.
/// </summary>
/// <param name="ycbcrColor">
/// The instance of <see cref="YCbCrColor"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="RgbaColor"/>.
/// </returns>
public static implicit operator RgbaColor(YCbCrColor ycbcrColor)
{
return FromColor(ycbcrColor);
} }
/// <summary> /// <summary>
@ -239,6 +254,21 @@ namespace ImageProcessor.Imaging.Colors
return HslaColor.FromColor(rgba); return HslaColor.FromColor(rgba);
} }
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="RgbaColor"/> to a
/// <see cref="YCbCrColor"/>.
/// </summary>
/// <param name="rgba">
/// The instance of <see cref="RgbaColor"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="YCbCrColor"/>.
/// </returns>
public static implicit operator YCbCrColor(RgbaColor rgba)
{
return YCbCrColor.FromColor(rgba);
}
/// <summary> /// <summary>
/// Returns a <see cref="System.String" /> that represents this instance. /// Returns a <see cref="System.String" /> that represents this instance.
/// </summary> /// </summary>

163
src/ImageProcessor/Imaging/Colors/YCbCrColor.cs

@ -4,7 +4,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// <summary> // <summary>
// Represents an YCbCr (luminance, chroma, chroma) color used in digital imaging systems. // Represents an YCbCr (luminance, chroma, chroma) color conforming to the ITU-R BT.601 standard used in digital imaging systems.
// <see href="http://en.wikipedia.org/wiki/YCbCr" /> // <see href="http://en.wikipedia.org/wiki/YCbCr" />
// </summary> // </summary>
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
@ -14,10 +14,8 @@ namespace ImageProcessor.Imaging.Colors
using System; using System;
using System.Drawing; using System.Drawing;
using ImageProcessor.Common.Extensions;
/// <summary> /// <summary>
/// Represents an YCbCr (luminance, chroma, chroma) color used in digital imaging systems. /// Represents an YCbCr (luminance, chroma, chroma) color conforming to the ITU-R BT.601 standard used in digital imaging systems.
/// <see href="http://en.wikipedia.org/wiki/YCbCr"/> /// <see href="http://en.wikipedia.org/wiki/YCbCr"/>
/// </summary> /// </summary>
public struct YCbCrColor public struct YCbCrColor
@ -50,13 +48,14 @@ namespace ImageProcessor.Imaging.Colors
/// <param name="cr">The v chroma component.</param> /// <param name="cr">The v chroma component.</param>
private YCbCrColor(float y, float cb, float cr) private YCbCrColor(float y, float cb, float cr)
{ {
this.y = y; //Math.Max(0.0f, Math.Min(1.0f, y)); this.y = Math.Max(0f, Math.Min(255f, y));
this.cb = cb; //Math.Max(-0.5f, Math.Min(0.5f, cb)); this.cb = Math.Max(-255f, Math.Min(255f, cb));
this.cr = cr; //Math.Max(-0.5f, Math.Min(0.5f, cr)); this.cr = Math.Max(-255f, Math.Min(255f, cr));
} }
/// <summary> /// <summary>
/// Gets the Y luminance component. /// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary> /// </summary>
public float Y public float Y
{ {
@ -68,23 +67,25 @@ namespace ImageProcessor.Imaging.Colors
/// <summary> /// <summary>
/// Gets the U chroma component. /// Gets the U chroma component.
/// <remarks>A value ranging between -255 and 255.</remarks>
/// </summary> /// </summary>
public float Cb public float Cb
{ {
get get
{ {
return this.y; return this.cb;
} }
} }
/// <summary> /// <summary>
/// Gets the V chroma component. /// Gets the V chroma component.
/// <remarks>A value ranging between -255 and 255.</remarks>
/// </summary> /// </summary>
public float Cr public float Cr
{ {
get get
{ {
return this.y; return this.cr;
} }
} }
@ -114,12 +115,12 @@ namespace ImageProcessor.Imaging.Colors
/// </returns> /// </returns>
public static YCbCrColor FromColor(Color color) public static YCbCrColor FromColor(Color color)
{ {
float r = color.R; byte r = color.R;
float g = color.G; byte g = color.G;
float b = color.B; byte b = color.B;
float y = (float)((0.299 * r) + (0.587 * g) + (0.114 * 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 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)); float cr = 128 + (float)((0.5 * r) - (0.418688 * g) - (0.081312 * b));
return new YCbCrColor(y, cb, cr); return new YCbCrColor(y, cb, cr);
@ -140,24 +141,148 @@ namespace ImageProcessor.Imaging.Colors
return FromColor(color); return FromColor(color);
} }
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="RgbaColor"/> to a
/// <see cref="YCbCrColor"/>.
/// </summary>
/// <param name="rgbaColor">
/// The instance of <see cref="RgbaColor"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="YCbCrColor"/>.
/// </returns>
public static implicit operator YCbCrColor(RgbaColor rgbaColor)
{
return FromColor(rgbaColor);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="HslaColor"/> to a
/// <see cref="YCbCrColor"/>.
/// </summary>
/// <param name="hslaColor">
/// The instance of <see cref="HslaColor"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="YCbCrColor"/>.
/// </returns>
public static implicit operator YCbCrColor(HslaColor hslaColor)
{
return FromColor(hslaColor);
}
/// <summary> /// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCrColor"/> to a /// Allows the implicit conversion of an instance of <see cref="YCbCrColor"/> to a
/// <see cref="System.Drawing.Color"/>. /// <see cref="System.Drawing.Color"/>.
/// </summary> /// </summary>
/// <param name="ycbcr"> /// <param name="ycbcrColor">
/// The instance of <see cref="YCbCrColor"/> to convert. /// The instance of <see cref="YCbCrColor"/> to convert.
/// </param> /// </param>
/// <returns> /// <returns>
/// An instance of <see cref="System.Drawing.Color"/>. /// An instance of <see cref="System.Drawing.Color"/>.
/// </returns> /// </returns>
public static implicit operator Color(YCbCrColor ycbcr) public static implicit operator Color(YCbCrColor ycbcrColor)
{ {
byte r = Convert.ToInt32(ycbcr.Y + (1.402 * (ycbcr.Cr - 128))).ToByte(); float y = ycbcrColor.Y;
byte g = Convert.ToInt32(ycbcr.Y - (0.34414 * (ycbcr.Cb - 128)) - (0.71414 * (ycbcr.Cr - 128))).ToByte(); float cb = ycbcrColor.Cb - 128;
byte b = Convert.ToInt32(ycbcr.Y + (1.772 * (ycbcr.Cb - 128))).ToByte(); float cr = ycbcrColor.Cr - 128;
byte r = Convert.ToByte(Math.Max(0.0f, Math.Min(255f, y + (1.402 * cr))));
byte g = Convert.ToByte(Math.Max(0.0f, Math.Min(255f, y - (0.34414 * cb) - (0.71414 * cr))));
byte b = Convert.ToByte(Math.Max(0.0f, Math.Min(255f, y + (1.772 * cb))));
return Color.FromArgb(255, r, g, b); return Color.FromArgb(255, r, g, b);
} }
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCrColor"/> to a
/// <see cref="RgbaColor"/>.
/// </summary>
/// <param name="ycbcrColor">
/// The instance of <see cref="YCbCrColor"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="RgbaColor"/>.
/// </returns>
public static implicit operator RgbaColor(YCbCrColor ycbcrColor)
{
return RgbaColor.FromColor(ycbcrColor);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCrColor"/> to a
/// <see cref="HslaColor"/>.
/// </summary>
/// <param name="ycbcrColor">
/// The instance of <see cref="YCbCrColor"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="HslaColor"/>.
/// </returns>
public static implicit operator HslaColor(YCbCrColor ycbcrColor)
{
return HslaColor.FromColor(ycbcrColor);
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
if (this.IsEmpty())
{
return "YCbCrColor [Empty]";
}
return string.Format("YCbCrColor [ Y={0:#0.##}, Cb={1:#0.##}, Cr={2:#0.##}]", this.Y, this.Cb, this.Cr);
}
/// <summary>
/// Indicates whether this instance and a specified object are equal.
/// </summary>
/// <returns>
/// true if <paramref name="obj"/> and this instance are the same type and represent the same value; otherwise, false.
/// </returns>
/// <param name="obj">Another object to compare to. </param>
public override bool Equals(object obj)
{
if (obj is YCbCrColor)
{
Color thisColor = this;
Color otherColor = (YCbCrColor)obj;
return thisColor.Equals(otherColor);
}
return false;
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
public override int GetHashCode()
{
Color thisColor = this;
return thisColor.GetHashCode();
}
/// <summary>
/// Returns a value indicating whether the current instance is empty.
/// </summary>
/// <returns>
/// The true if this instance is empty; otherwise, false.
/// </returns>
private bool IsEmpty()
{
const float Epsilon = .0001f;
return Math.Abs(this.y - 0) <= Epsilon && Math.Abs(this.cb - 0) <= Epsilon &&
Math.Abs(this.cr - 0) <= Epsilon;
}
} }
} }

Loading…
Cancel
Save