Browse Source

Merge pull request #275 from dampee/feature/colorspace-ciexyz

Colorspace CieXyz

Former-commit-id: e54c42bffb75eb5334a862f62c2b396438581a3a
Former-commit-id: f7737fb6e0dab94d9c3c29035a5d87fc1c0396f1
Former-commit-id: 44d27846ca204067a189aca1d7bf42edc2884e7d
af/merge-core
James Jackson-South 10 years ago
parent
commit
774e8e23e2
  1. 25
      src/ImageProcessor/Colors/ColorTransforms.cs
  2. 176
      src/ImageProcessor/Colors/Colorspaces/CieXyz.cs
  3. 28
      tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs

25
src/ImageProcessor/Colors/ColorTransforms.cs

@ -67,6 +67,31 @@ namespace ImageProcessor
return new Color(r, g, b);
}
public static implicit operator Color(CieXyz color)
{
var x = color.X;
var y = color.Y;
var z = color.Z;
// assume sRGB
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
float r = (x * 3.240969941904521F) + (y * -1.537383177570093F) + (z * -0.498610760293F);
float g = (x * -0.96924363628087F) + (y * 1.87596750150772F) + (z * 0.041555057407175F);
float b = (x * 0.055630079696993F) + (y * -0.20397695888897F) + (z * 1.056971514242878F);
r = r > 0.0031308 ? (float)((1.055 * Math.Pow(r, 1.0 / 2.4)) - 0.055) : r = (r * 12.92F);
g = g > 0.0031308 ? (float)((1.055 * Math.Pow(g, 1.0 / 2.4)) - 0.055) : g = (g * 12.92F);
b = b > 0.0031308 ? (float)((1.055 * Math.Pow(b, 1.0 / 2.4)) - 0.055) : b = (b * 12.92F);
r = Math.Min(Math.Max(0, r), 1);
g = Math.Min(Math.Max(0, g), 1);
b = Math.Min(Math.Max(0, b), 1);
return new Color(r, g, b);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Hsv"/> to a
/// <see cref="Color"/>.

176
src/ImageProcessor/Colors/Colorspaces/CieXyz.cs

@ -0,0 +1,176 @@
// <copyright file="CieXyz.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// Represents an CIE 1931 color
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space"/>
/// </summary>
public struct CieXyz : IEquatable<CieXyz>
{
/// <summary>
/// Represents a <see cref="CieXyz"/> that has Y, Cb, and Cr values set to zero.
/// </summary>
public static readonly CieXyz Empty = default(CieXyz);
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector3 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyz"/> struct.
/// </summary>
/// <param name="y">The y luminance component.</param>
/// <param name="x">X is a mix (a linear combination) of cone response curves chosen to be nonnegative</param>
/// <param name="z">Z is quasi-equal to blue stimulation, or the S cone of the human eye.</param>
public CieXyz(float x, float y, float z)
: this()
{
// ToDo: check clamp values
this.backingVector.X = x.Clamp(0, 2);
this.backingVector.Y = y.Clamp(0, 2);
this.backingVector.Z = z.Clamp(0, 2);
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value ranging between 380 and 780.</remarks>
/// </summary>
public float X => this.backingVector.X;
/// <summary>
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 380 and 780.</remarks>
/// </summary>
public float Y => this.backingVector.Y;
/// <summary>
/// Gets the Cr chroma component.
/// <remarks>A value ranging between 380 and 780.</remarks>
/// </summary>
public float Z => this.backingVector.Z;
/// <summary>
/// Gets a value indicating whether this <see cref="CieXyz"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.backingVector.Equals(default(Vector3));
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="CieXyz"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Color"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="CieXyz"/>.
/// </returns>
public static implicit operator CieXyz(Color color)
{
var r = color.R;
var g = color.G;
var b = color.B;
// assume sRGB
r = r > 0.04045 ? (float)Math.Pow(((r + 0.055) / 1.055), 2.4) : (float)(r / 12.92);
g = g > 0.04045 ? (float)Math.Pow(((g + 0.055) / 1.055), 2.4) : (float)(g / 12.92);
b = b > 0.04045 ? (float)Math.Pow(((b + 0.055) / 1.055), 2.4) : (float)(b / 12.92);
var x = (r * 0.41239079926595F) + (g * 0.35758433938387F) + (b * 0.18048078840183F);
var y = (r * 0.21263900587151F) + (g * 0.71516867876775F) + (b * 0.072192315360733F);
var z = (r * 0.019330818715591F) + (g * 0.11919477979462F) + (b * 0.95053215224966F);
return new CieXyz(x, y, z);
}
/// <summary>
/// Compares two <see cref="CieXyz"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyz"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(CieXyz left, CieXyz right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="CieXyz"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyz"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(CieXyz left, CieXyz right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is CieXyz)
{
CieXyz color = (CieXyz)obj;
return this.backingVector == color.backingVector;
}
return false;
}
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "CieXyz [ Empty ]";
}
return $"CieXyz [ X={this.X:#0.##}, Y={this.Y:#0.##}, Z={this.Z:#0.##} ]";
}
/// <inheritdoc/>
public bool Equals(CieXyz other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Hsv"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private static int GetHashCode(CieXyz color) => color.backingVector.GetHashCode();
}
}

28
tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs

@ -91,6 +91,34 @@ namespace ImageProcessor.Tests
Assert.Equal(1f, color3.A, 1);
}
[Fact]
public void ColorToCieXyz()
{
Color color = new Color(1, 1, 1);
CieXyz cieXyz = color;
Assert.Equal(0.9505F, cieXyz.X, 4);
Assert.Equal(1.0000F, cieXyz.Y, 4);
Assert.Equal(1.089F, cieXyz.Z, 3);
}
[Fact]
public void CieXyzToColor()
{
CieXyz cieXyz = new CieXyz(0.25F, 0.40F, 0.10F);
Color color = cieXyz;
Assert.Equal(0.4174F, color.R, 3);
Assert.Equal(0.7434F, color.G, 3);
Assert.Equal(0.2162F, color.B, 2);
//xyz2rgb([0.25 0.40 0.10])
//ans = 0.4174 0.7434 0.2152
}
/// <summary>
/// Tests the implicit conversion from <see cref="Color"/> to <see cref="Hsv"/>.
/// </summary>

Loading…
Cancel
Save