mirror of https://github.com/SixLabors/ImageSharp
36 changed files with 941 additions and 56 deletions
@ -0,0 +1,229 @@ |
|||||
|
// <copyright file="CieLuv.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The CIE 1976 (L*, u*, v*) color space, commonly known by its abbreviation CIELUV, is a color space adopted by the International
|
||||
|
/// Commission on Illumination (CIE) in 1976, as a simple-to-compute transformation of the 1931 CIE XYZ color space, but which
|
||||
|
/// attempted perceptual uniformity
|
||||
|
/// <see href="https://en.wikipedia.org/wiki/CIELUV"/>
|
||||
|
/// </summary>
|
||||
|
public struct CieLuv : IColorVector, IEquatable<CieLuv>, IAlmostEquatable<CieLuv, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// D65 standard illuminant.
|
||||
|
/// Used when reference white is not specified explicitly.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="CieLuv"/> that has L, U, and V values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieLuv Empty = default(CieLuv); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector3 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param>
|
||||
|
/// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv(float l, float u, float v) |
||||
|
: this(new Vector3(l, u, v), DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param>
|
||||
|
/// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv(float l, float u, float v, CieXyz whitePoint) |
||||
|
: this(new Vector3(l, u, v), whitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, u, v components.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv(Vector3 vector) |
||||
|
: this(vector, DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, u, v components.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv(Vector3 vector, CieXyz whitePoint) |
||||
|
: this() |
||||
|
{ |
||||
|
this.backingVector = vector; |
||||
|
this.WhitePoint = whitePoint; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the reference white point of this color
|
||||
|
/// </summary>
|
||||
|
public CieXyz WhitePoint |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the lightness dimension
|
||||
|
/// <remarks>A value usually ranging between 0 and 100.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float L |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue-yellow chromaticity coordinate of the given whitepoint.
|
||||
|
/// <remarks>A value usually ranging between -100 and 100.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float U |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red-green chromaticity coordinate of the given whitepoint.
|
||||
|
/// <remarks>A value usually ranging between -100 and 100.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float V |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Z; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="CieLuv"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Vector3 Vector |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLuv"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLuv"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLuv"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(CieLuv left, CieLuv right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLuv"/> objects for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLuv"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLuv"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(CieLuv left, CieLuv right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
unchecked |
||||
|
{ |
||||
|
int hashCode = this.WhitePoint.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.backingVector.GetHashCode(); |
||||
|
return hashCode; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "CieLuv [ Empty ]"; |
||||
|
} |
||||
|
|
||||
|
return $"CieLuv [ L={this.L:#0.##}, U={this.U:#0.##}, V={this.V:#0.##} ]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is CieLuv) |
||||
|
{ |
||||
|
return this.Equals((CieLuv)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(CieLuv other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector) |
||||
|
&& this.WhitePoint.Equals(other.WhitePoint); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(CieLuv other, float precision) |
||||
|
{ |
||||
|
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return this.WhitePoint.Equals(other.WhitePoint) |
||||
|
&& result.X <= precision |
||||
|
&& result.Y <= precision |
||||
|
&& result.Z <= precision; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,178 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
||||
|
/// </summary>
|
||||
|
public partial class ColorSpaceConverter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed |
||||
|
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) |
||||
|
: color; |
||||
|
|
||||
|
// Conversion
|
||||
|
CieXyzToCieLuvConverter converter = new CieXyzToCieLuvConverter(this.TargetLuvWhitePoint); |
||||
|
return converter.Convert(adapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
// <copyright file="CieLuvToCieXyzConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieLuv"/> to <see cref="CieXyz"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieLuvToCieXyzConverter : IColorConversion<CieLuv, CieXyz> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyz Convert(CieLuv input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html
|
||||
|
float l = input.L, u = input.U, v = input.V; |
||||
|
|
||||
|
float u0 = ComputeU0(input.WhitePoint); |
||||
|
float v0 = ComputeV0(input.WhitePoint); |
||||
|
|
||||
|
float y = l > CieConstants.Kappa * CieConstants.Epsilon |
||||
|
? MathF.Pow((l + 16) / 116, 3) |
||||
|
: l / CieConstants.Kappa; |
||||
|
|
||||
|
float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3; |
||||
|
float b = -5 * y; |
||||
|
float c = -0.3333333F; |
||||
|
float d = y * ((39 * l / (v + (13 * l * v0))) - 5); |
||||
|
|
||||
|
float x = (d - b) / (a - c); |
||||
|
float z = (x * a) + b; |
||||
|
|
||||
|
if (float.IsNaN(x) || x < 0) |
||||
|
{ |
||||
|
x = 0; |
||||
|
} |
||||
|
|
||||
|
if (float.IsNaN(y) || y < 0) |
||||
|
{ |
||||
|
y = 0; |
||||
|
} |
||||
|
|
||||
|
if (float.IsNaN(z) || z < 0) |
||||
|
{ |
||||
|
z = 0; |
||||
|
} |
||||
|
|
||||
|
return new CieXyz(x, y, z); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the blue-yellow chromacity based on the given whitepoint.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static float ComputeU0(CieXyz input) |
||||
|
{ |
||||
|
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the red-green chromacity based on the given whitepoint.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static float ComputeV0(CieXyz input) |
||||
|
{ |
||||
|
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,102 @@ |
|||||
|
// <copyright file="CieXyzToCieLuvConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieXyz"/> to <see cref="CieLuv"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieXyzToCieLuvConverter : IColorConversion<CieXyz, CieLuv> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzToCieLuvConverter() |
||||
|
: this(CieLuv.DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="luvWhitePoint">The target reference luv white point</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzToCieLuvConverter(CieXyz luvWhitePoint) |
||||
|
{ |
||||
|
this.LuvWhitePoint = luvWhitePoint; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the target reference whitepoint. When not set, <see cref="CieLuv.DefaultWhitePoint"/> is used.
|
||||
|
/// </summary>
|
||||
|
public CieXyz LuvWhitePoint |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv Convert(CieXyz input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html
|
||||
|
float yr = input.Y / this.LuvWhitePoint.Y; |
||||
|
float up = ComputeUp(input); |
||||
|
float vp = ComputeVp(input); |
||||
|
float upr = ComputeUp(this.LuvWhitePoint); |
||||
|
float vpr = ComputeVp(this.LuvWhitePoint); |
||||
|
|
||||
|
float l = yr > CieConstants.Epsilon ? ((116 * MathF.Pow(yr, 0.3333333F)) - 16F) : (CieConstants.Kappa * yr); |
||||
|
|
||||
|
if (float.IsNaN(l) || l < 0) |
||||
|
{ |
||||
|
l = 0; |
||||
|
} |
||||
|
|
||||
|
float u = 13 * l * (up - upr); |
||||
|
float v = 13 * l * (vp - vpr); |
||||
|
|
||||
|
if (float.IsNaN(u)) |
||||
|
{ |
||||
|
u = 0; |
||||
|
} |
||||
|
|
||||
|
if (float.IsNaN(v)) |
||||
|
{ |
||||
|
v = 0; |
||||
|
} |
||||
|
|
||||
|
return new CieLuv(l, u, v, this.LuvWhitePoint); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the blue-yellow chromacity based on the given whitepoint.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static float ComputeUp(CieXyz input) |
||||
|
{ |
||||
|
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the red-green chromacity based on the given whitepoint.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
private static float ComputeVp(CieXyz input) |
||||
|
{ |
||||
|
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,4 +1,4 @@ |
|||||
namespace ImageSharp.Tests.Colors.Colorspaces |
namespace ImageSharp.Tests.Colorspaces |
||||
{ |
{ |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
using ImageSharp.ColorSpaces; |
using ImageSharp.ColorSpaces; |
||||
@ -0,0 +1,71 @@ |
|||||
|
namespace ImageSharp.Tests |
||||
|
{ |
||||
|
using System.Collections.Generic; |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion; |
||||
|
|
||||
|
using Xunit; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Tests <see cref="CieXyz"/>-<see cref="CieLuv"/> conversions.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Test data generated using:
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?ColorCalculator.html"/>
|
||||
|
/// </remarks>
|
||||
|
public class CieXyzAndCieLuvConversionTest |
||||
|
{ |
||||
|
private static readonly IEqualityComparer<float> FloatRoundingComparer = new FloatRoundingComparer(4); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Tests conversion from <see cref="CieLuv"/> to <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>).
|
||||
|
/// </summary>
|
||||
|
[Theory] |
||||
|
[InlineData(0, 0, 0, 0, 0, 0)] |
||||
|
[InlineData(0, 100, 50, 0, 0, 0)] |
||||
|
[InlineData(0.1, 100, 50, 0.000493, 0.000111, 0)] |
||||
|
[InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)] |
||||
|
[InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)] |
||||
|
[InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)] |
||||
|
[InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)] |
||||
|
public void Convert_Luv_to_Xyz(float l, float u, float v, float x, float y, float z) |
||||
|
{ |
||||
|
// Arrange
|
||||
|
CieLuv input = new CieLuv(l, u, v, Illuminants.D65); |
||||
|
ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; |
||||
|
|
||||
|
// Act
|
||||
|
CieXyz output = converter.ToCieXyz(input); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(x, output.X, FloatRoundingComparer); |
||||
|
Assert.Equal(y, output.Y, FloatRoundingComparer); |
||||
|
Assert.Equal(z, output.Z, FloatRoundingComparer); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Tests conversion from <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>) to <see cref="CieLuv"/>.
|
||||
|
/// </summary>
|
||||
|
[Theory] |
||||
|
[InlineData(0, 0, 0, 0, 0, 0)] |
||||
|
[InlineData(0.000493, 0.000111, 0, 0.1003, 0.9332, -0.0070)] |
||||
|
[InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)] |
||||
|
[InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)] |
||||
|
[InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)] |
||||
|
[InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)] |
||||
|
public void Convert_Xyz_to_Luv(float x, float y, float z, float l, float u, float v) |
||||
|
{ |
||||
|
// Arrange
|
||||
|
CieXyz input = new CieXyz(x, y, z); |
||||
|
ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; |
||||
|
|
||||
|
// Act
|
||||
|
CieLuv output = converter.ToCieLuv(input); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(l, output.L, FloatRoundingComparer); |
||||
|
Assert.Equal(u, output.U, FloatRoundingComparer); |
||||
|
Assert.Equal(v, output.V, FloatRoundingComparer); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,4 +1,4 @@ |
|||||
namespace ImageSharp.Tests.Colors.Colorspaces |
namespace ImageSharp.Tests.Colorspaces |
||||
{ |
{ |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
|
||||
@ -1,4 +1,4 @@ |
|||||
namespace ImageSharp.Tests.Colors.Colorspaces |
namespace ImageSharp.Tests.Colorspaces |
||||
{ |
{ |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
|
||||
@ -1,4 +1,4 @@ |
|||||
namespace ImageSharp.Tests.Colors.Colorspaces |
namespace ImageSharp.Tests.Colorspaces |
||||
{ |
{ |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
|
||||
@ -1,4 +1,4 @@ |
|||||
namespace ImageSharp.Tests.Colors.Colorspaces |
namespace ImageSharp.Tests.Colorspaces |
||||
{ |
{ |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
|
||||
@ -1,4 +1,4 @@ |
|||||
namespace ImageSharp.Tests.Colors.Colorspaces |
namespace ImageSharp.Tests.Colorspaces |
||||
{ |
{ |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
|
||||
Loading…
Reference in new issue