Browse Source

Add rgb adaptation

af/merge-core
James Jackson-South 9 years ago
parent
commit
fbcc4a9b45
  1. 71
      src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Adapt.cs
  2. 12
      src/ImageSharp/Colors/Spaces/LinearRgb.cs
  3. 12
      src/ImageSharp/Colors/Spaces/Rgb.cs
  4. 34
      tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs
  5. 6
      tests/ImageSharp.Tests/App.config
  6. 106
      tests/ImageSharp.Tests/Colors/Colorspaces/ColorConverterAdaptTest.cs

71
src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Adapt.cs

@ -8,6 +8,7 @@ namespace ImageSharp.Colors.Spaces.Conversion
using System;
using ImageSharp.Colors.Spaces;
using ImageSharp.Colors.Spaces.Conversion.Implementation.Rgb;
/// <summary>
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
@ -15,7 +16,7 @@ namespace ImageSharp.Colors.Spaces.Conversion
public partial class ColorSpaceConverter
{
/// <summary>
/// Performs chromatic adaptation of given XYZ color.
/// Performs chromatic adaptation of given <see cref="CieXyz"/> color.
/// Target white point is <see cref="WhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
@ -33,5 +34,73 @@ namespace ImageSharp.Colors.Spaces.Conversion
return this.ChromaticAdaptation.Transform(color, sourceWhitePoint, this.WhitePoint);
}
/// <summary>
/// Adapts a <see cref="LinearRgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public LinearRgb Adapt(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WorkingSpace.Equals(this.TargetRgbWorkingSpace))
{
return color;
}
// Conversion to XYZ
LinearRgbToCieXyzConverter converterToXYZ = this.GetLinearRgbToCieXyzConverter(color.WorkingSpace);
CieXyz unadapted = converterToXYZ.Convert(color);
// Adaptation
CieXyz adapted = this.ChromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint);
// Conversion back to RGB
CieXyzToLinearRgbConverter converterToRGB = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace);
return converterToRGB.Convert(adapted);
}
/// <summary>
/// Adapts an <see cref="Rgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public Rgb Adapt(Rgb color)
{
Guard.NotNull(color, nameof(color));
LinearRgb linearInput = this.ToLinearRgb(color);
LinearRgb linearOutput = this.Adapt(linearInput);
return this.ToRgb(linearOutput);
}
/// <summary>
/// Adapts <see cref="CieLab"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public CieLab Adapt(CieLab color)
{
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
{
return color;
}
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
}
}

12
src/ImageSharp/Colors/Spaces/LinearRgb.cs

@ -40,6 +40,18 @@ namespace ImageSharp.Colors.Spaces
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
/// </summary>
/// <param name="r">The red component ranging between 0 and 1.</param>
/// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param>
/// <param name="workingSpace">The rgb working space.</param>
public LinearRgb(float r, float g, float b, IRgbWorkingSpace workingSpace)
: this(new Vector3(r, g, b), workingSpace)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
/// </summary>

12
src/ImageSharp/Colors/Spaces/Rgb.cs

@ -40,6 +40,18 @@ namespace ImageSharp.Colors.Spaces
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgb"/> struct.
/// </summary>
/// <param name="r">The red component ranging between 0 and 1.</param>
/// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param>
/// <param name="workingSpace">The rgb working space.</param>
public Rgb(float r, float g, float b, IRgbWorkingSpace workingSpace)
: this(new Vector3(r, g, b), workingSpace)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgb"/> struct.
/// </summary>

34
tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs

@ -0,0 +1,34 @@
namespace ImageSharp.Benchmarks.Color
{
using BenchmarkDotNet.Attributes;
using Colourful;
using Colourful.Conversion;
using ImageSharp.Colors.Spaces;
using ImageSharp.Colors.Spaces.Conversion;
public class RgbWorkingSpaceAdapt
{
private static readonly Rgb Rgb = new Rgb(0.206162F, 0.260277F, 0.746717F, RgbWorkingSpaces.WideGamutRgb);
private static readonly RGBColor RGBColor = new RGBColor(0.206162, 0.260277, 0.746717, RGBWorkingSpaces.WideGamutRGB);
private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb };
private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter { TargetRGBWorkingSpace = RGBWorkingSpaces.sRGB };
[Benchmark(Baseline = true, Description = "Colourful Adapt")]
public RGBColor ColourfulConvert()
{
return ColourfulConverter.Adapt(RGBColor);
}
[Benchmark(Description = "ImageSharp Adapt")]
public Rgb ColorSpaceConvert()
{
return ColorSpaceConverter.Adapt(Rgb);
}
}
}

6
tests/ImageSharp.Tests/App.config

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="xunit.methodDisplay" value="method"/>
</appSettings>
</configuration>

106
tests/ImageSharp.Tests/Colors/Colorspaces/ColorConverterAdaptTest.cs

@ -12,6 +12,89 @@ namespace ImageSharp.Tests
{
private static readonly IEqualityComparer<float> FloatComparer = new ApproximateFloatComparer(4);
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(1, 1, 1, 1, 1, 1)]
[InlineData(0.206162, 0.260277, 0.746717, 0.220000, 0.130000, 0.780000)]
public void Adapt_RGB_WideGamutRGB_To_sRGB(float r1, float g1, float b1, float r2, float g2, float b2)
{
// Arrange
Rgb input = new Rgb(r1, g1, b1, RgbWorkingSpaces.WideGamutRgb);
Rgb expectedOutput = new Rgb(r2, g2, b2, RgbWorkingSpaces.SRgb);
ColorSpaceConverter converter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb };
// Action
Rgb output = converter.Adapt(input);
// Assert
Assert.Equal(expectedOutput.WorkingSpace, output.WorkingSpace);
Assert.Equal(output.R, expectedOutput.R, FloatComparer);
Assert.Equal(output.G, expectedOutput.G, FloatComparer);
Assert.Equal(output.B, expectedOutput.B, FloatComparer);
}
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(1, 1, 1, 1, 1, 1)]
[InlineData(0.220000, 0.130000, 0.780000, 0.206162, 0.260277, 0.746717)]
public void Adapt_RGB_SRGB_To_WideGamutRGB(float r1, float g1, float b1, float r2, float g2, float b2)
{
// Arrange
Rgb input = new Rgb(r1, g1, b1, RgbWorkingSpaces.SRgb);
Rgb expectedOutput = new Rgb(r2, g2, b2, RgbWorkingSpaces.WideGamutRgb);
ColorSpaceConverter converter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.WideGamutRgb };
// Action
Rgb output = converter.Adapt(input);
// Assert
Assert.Equal(expectedOutput.WorkingSpace, output.WorkingSpace);
Assert.Equal(output.R, expectedOutput.R, FloatComparer);
Assert.Equal(output.G, expectedOutput.G, FloatComparer);
Assert.Equal(output.B, expectedOutput.B, FloatComparer);
}
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(22, 33, 1, 22.269869, 32.841164, 1.633926)]
public void Adapt_Lab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2)
{
// Arrange
CieLab input = new CieLab(l1, a1, b1, Illuminants.D65);
CieLab expectedOutput = new CieLab(l2, a2, b2);
ColorSpaceConverter converter = new ColorSpaceConverter { TargetLabWhitePoint = Illuminants.D50 };
// Action
CieLab output = converter.Adapt(input);
// Assert
Assert.Equal(output.L, expectedOutput.L, FloatComparer);
Assert.Equal(output.A, expectedOutput.A, FloatComparer);
Assert.Equal(output.B, expectedOutput.B, FloatComparer);
}
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(0.5, 0.5, 0.5, 0.510286, 0.501489, 0.378970)]
public void Adapt_XYZ_D65_To_D50_Bradford(float x1, float y1, float z1, float x2, float y2, float z2)
{
// Arrange
CieXyz input = new CieXyz(x1, y1, z1);
CieXyz expectedOutput = new CieXyz(x2, y2, z2);
ColorSpaceConverter converter = new ColorSpaceConverter
{
WhitePoint = Illuminants.D50
};
// Action
CieXyz output = converter.Adapt(input, Illuminants.D65);
// Assert
Assert.Equal(output.X, expectedOutput.X, FloatComparer);
Assert.Equal(output.Y, expectedOutput.Y, FloatComparer);
Assert.Equal(output.Z, expectedOutput.Z, FloatComparer);
}
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)]
@ -34,5 +117,28 @@ namespace ImageSharp.Tests
Assert.Equal(output.Y, expectedOutput.Y, FloatComparer);
Assert.Equal(output.Z, expectedOutput.Z, FloatComparer);
}
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)]
public void Adapt_XYZ_D65_To_D50_XYZScaling(float x1, float y1, float z1, float x2, float y2, float z2)
{
// Arrange
CieXyz input = new CieXyz(x1, y1, z1);
CieXyz expectedOutput = new CieXyz(x2, y2, z2);
ColorSpaceConverter converter = new ColorSpaceConverter
{
ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XYZScaling),
WhitePoint = Illuminants.D50
};
// Action
CieXyz output = converter.Adapt(input, Illuminants.D65);
// Assert
Assert.Equal(output.X, expectedOutput.X, FloatComparer);
Assert.Equal(output.Y, expectedOutput.Y, FloatComparer);
Assert.Equal(output.Z, expectedOutput.Z, FloatComparer);
}
}
}
Loading…
Cancel
Save