Browse Source

Merge pull request #1555 from Franz1960/max-chroma-binarization

New overloads for binary threshold operations. The new argument Color…
pull/1558/head
James Jackson-South 5 years ago
committed by GitHub
parent
commit
6038be96ad
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 35
      .gitattributes
  2. 2
      shared-infrastructure
  3. 4
      src/ImageSharp/ColorSpaces/YCbCr.cs
  4. 26
      src/ImageSharp/Processing/BinaryThresholdColorComponent.cs
  5. 90
      src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs
  6. 36
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
  7. 137
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
  8. 96
      tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs
  9. 92
      tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs
  10. 1
      tests/ImageSharp.Tests/TestImages.cs
  11. 13
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
  12. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png
  13. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png
  14. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_rgb-48bpp_0.25.png
  15. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_rgb-48bpp_0.75.png
  16. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png
  17. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png
  18. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_rgb-48bpp_0.25.png
  19. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_rgb-48bpp_0.75.png
  20. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png
  21. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png
  22. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_rgb-48bpp_0.25.png
  23. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_rgb-48bpp_0.75.png
  24. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png
  25. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png
  26. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_rgb-48bpp_0.25.png
  27. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_rgb-48bpp_0.75.png
  28. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png
  29. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png
  30. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_rgb-48bpp_0.25.png
  31. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_rgb-48bpp_0.75.png
  32. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png
  33. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png
  34. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_rgb-48bpp_0.25.png
  35. 3
      tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_rgb-48bpp_0.75.png
  36. 3
      tests/Images/Input/Png/colors-saturation-lightness.png

35
.gitattributes

@ -2,9 +2,13 @@
# Set default behavior to:
# treat as text and
# normalize to Unix-style line endings
###############################################################################
* text eol=lf
###############################################################################
# Set explicit file behavior to:
# treat as text and
# normalize to Unix-style line endings
###############################################################################
*.asm text eol=lf
*.c text eol=lf
*.clj text eol=lf
@ -49,19 +53,35 @@
*.txt text eol=lf
*.vb text eol=lf
*.yml text eol=lf
###############################################################################
# Set explicit file behavior to:
# treat as text
# normalize to Unix-style line endings and
# diff as csharp
###############################################################################
*.cs text eol=lf diff=csharp
###############################################################################
# Set explicit file behavior to:
# treat as text
# normalize to Unix-style line endings and
# use a union merge when resoling conflicts
###############################################################################
*.csproj text eol=lf merge=union
*.dbproj text eol=lf merge=union
*.fsproj text eol=lf merge=union
*.ncrunchproject text eol=lf merge=union
*.vbproj text eol=lf merge=union
###############################################################################
# Set explicit file behavior to:
# treat as text
# normalize to Windows-style line endings and
# use a union merge when resoling conflicts
###############################################################################
*.sln text eol=crlf merge=union
###############################################################################
# Set explicit file behavior to:
# treat as binary
###############################################################################
*.basis binary
*.bmp binary
*.dds binary
@ -90,7 +110,10 @@
*.woff2 binary
*.xls binary
*.xlsx binary
###############################################################################
# Set explicit file behavior to:
# diff as plain text
###############################################################################
*.doc diff=astextplain
*.docx diff=astextplain
*.dot diff=astextplain
@ -98,12 +121,4 @@
*.pptx diff=astextplain
*.rtf diff=astextplain
*.svg diff=astextplain
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.bmp filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.tif filter=lfs diff=lfs merge=lfs -text
*.tiff filter=lfs diff=lfs merge=lfs -text
*.tga filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
*.jpg,*.jpeg,*.bmp,*.gif,*.png,*.tif,*.tiff,*.tga,*.webp filter=lfs diff=lfs merge=lfs -text

2
shared-infrastructure

@ -1 +1 @@
Subproject commit 9f515595ee8a4c3282fc88113f7f46c676fa3e9d
Subproject commit 649bdbe6050722b030d9292d3fc083cd6ffe87ba

4
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -100,4 +100,4 @@ namespace SixLabors.ImageSharp.ColorSpaces
&& this.Cr.Equals(other.Cr);
}
}
}
}

26
src/ImageSharp/Processing/BinaryThresholdColorComponent.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// The color component to be compared to threshold.
/// </summary>
public enum BinaryThresholdColorComponent : int
{
/// <summary>
/// Luminance color component according to ITU-R Recommendation BT.709.
/// </summary>
Luminance = 0,
/// <summary>
/// HSL saturation color component.
/// </summary>
Saturation = 1,
/// <summary>
/// Maximum of YCbCr chroma value, i.e. Cb and Cr distance from achromatic value.
/// </summary>
MaxChroma = 2,
}
}

90
src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors.Binarization;
@ -11,20 +11,51 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
public static class BinaryThresholdExtensions
{
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold)
=> source.ApplyProcessor(new BinaryThresholdProcessor(threshold, BinaryThresholdColorComponent.Luminance));
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="colorComponent">The color component to be compared to threshold.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
BinaryThresholdColorComponent colorComponent)
=> source.ApplyProcessor(new BinaryThresholdProcessor(threshold, colorComponent));
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold) =>
source.ApplyProcessor(new BinaryThresholdProcessor(threshold));
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
Rectangle rectangle)
=> source.ApplyProcessor(new BinaryThresholdProcessor(threshold, BinaryThresholdColorComponent.Luminance), rectangle);
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="colorComponent">The color component to be compared to threshold.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
@ -32,8 +63,25 @@ namespace SixLabors.ImageSharp.Processing
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
Rectangle rectangle) =>
source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle);
BinaryThresholdColorComponent colorComponent,
Rectangle rectangle)
=> source.ApplyProcessor(new BinaryThresholdProcessor(threshold, colorComponent), rectangle);
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
Color upperColor,
Color lowerColor)
=> source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor, BinaryThresholdColorComponent.Luminance));
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold.
@ -42,13 +90,35 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
/// <param name="colorComponent">The color component to be compared to threshold.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
Color upperColor,
Color lowerColor) =>
source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor));
Color lowerColor,
BinaryThresholdColorComponent colorComponent)
=> source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor, colorComponent));
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
Color upperColor,
Color lowerColor,
Rectangle rectangle)
=> source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor, BinaryThresholdColorComponent.Luminance), rectangle);
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold.
@ -57,6 +127,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
/// <param name="colorComponent">The color component to be compared to threshold.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
@ -66,7 +137,8 @@ namespace SixLabors.ImageSharp.Processing
float threshold,
Color upperColor,
Color lowerColor,
BinaryThresholdColorComponent colorComponent,
Rectangle rectangle) =>
source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor), rectangle);
source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor, colorComponent), rectangle);
}
}
}

36
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs

@ -14,8 +14,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// Initializes a new instance of the <see cref="BinaryThresholdProcessor"/> class.
/// </summary>
/// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
/// <param name="colorComponent">The color component to be compared to threshold.</param>
public BinaryThresholdProcessor(float threshold, BinaryThresholdColorComponent colorComponent)
: this(threshold, Color.White, Color.Black, colorComponent)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BinaryThresholdProcessor"/> class with
/// Luminance as color component to be compared to threshold.
/// </summary>
/// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
public BinaryThresholdProcessor(float threshold)
: this(threshold, Color.White, Color.Black)
: this(threshold, Color.White, Color.Black, BinaryThresholdColorComponent.Luminance)
{
}
@ -25,12 +36,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold.</param>
public BinaryThresholdProcessor(float threshold, Color upperColor, Color lowerColor)
/// <param name="colorComponent">The color component to be compared to threshold.</param>
public BinaryThresholdProcessor(float threshold, Color upperColor, Color lowerColor, BinaryThresholdColorComponent colorComponent)
{
Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold));
this.Threshold = threshold;
this.UpperColor = upperColor;
this.LowerColor = lowerColor;
this.ColorComponent = colorComponent;
}
/// <summary>
/// Initializes a new instance of the <see cref="BinaryThresholdProcessor"/> class with
/// Luminance as color component to be compared to threshold.
/// </summary>
/// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold.</param>
public BinaryThresholdProcessor(float threshold, Color upperColor, Color lowerColor)
: this(threshold, upperColor, lowerColor, BinaryThresholdColorComponent.Luminance)
{
}
/// <summary>
@ -48,7 +73,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// </summary>
public Color LowerColor { get; }
/// <inheritdoc />
/// <summary>
/// Gets a value indicating whether to use saturation value instead of luminance.
/// </summary>
public BinaryThresholdColorComponent ColorComponent { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : unmanaged, IPixel<TPixel>
=> new BinaryThresholdProcessor<TPixel>(configuration, this, source, sourceRectangle);

137
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@ -27,9 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public BinaryThresholdProcessor(Configuration configuration, BinaryThresholdProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
this.definition = definition;
}
=> this.definition = definition;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source)
@ -42,10 +39,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
Configuration configuration = this.Configuration;
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
bool isAlphaOnly = typeof(TPixel) == typeof(A8);
var operation = new RowOperation(
interest.X,
source,
upper,
lower,
threshold,
this.definition.ColorComponent,
configuration);
var operation = new RowOperation(interest, source, upper, lower, threshold, isAlphaOnly);
ParallelRowIterator.IterateRows(
ParallelRowIterator.IterateRows<RowOperation, Rgb24>(
configuration,
interest,
in operation);
@ -54,51 +57,131 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// <summary>
/// A <see langword="struct"/> implementing the clone logic for <see cref="BinaryThresholdProcessor{TPixel}"/>.
/// </summary>
private readonly struct RowOperation : IRowOperation
private readonly struct RowOperation : IRowOperation<Rgb24>
{
private readonly ImageFrame<TPixel> source;
private readonly TPixel upper;
private readonly TPixel lower;
private readonly byte threshold;
private readonly int minX;
private readonly int maxX;
private readonly bool isAlphaOnly;
private readonly BinaryThresholdColorComponent colorComponent;
private readonly int startX;
private readonly Configuration configuration;
[MethodImpl(InliningOptions.ShortMethod)]
public RowOperation(
Rectangle bounds,
int startX,
ImageFrame<TPixel> source,
TPixel upper,
TPixel lower,
byte threshold,
bool isAlphaOnly)
BinaryThresholdColorComponent colorComponent,
Configuration configuration)
{
this.startX = startX;
this.source = source;
this.upper = upper;
this.lower = lower;
this.threshold = threshold;
this.minX = bounds.X;
this.maxX = bounds.Right;
this.isAlphaOnly = isAlphaOnly;
this.colorComponent = colorComponent;
this.configuration = configuration;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(int y, Span<Rgb24> span)
{
TPixel upper = this.upper;
TPixel lower = this.lower;
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToRgb24(this.configuration, rowSpan, span);
switch (this.colorComponent)
{
case BinaryThresholdColorComponent.Luminance:
{
byte threshold = this.threshold;
for (int x = 0; x < rowSpan.Length; x++)
{
Rgb24 rgb = span[x];
byte luminance = ColorNumerics.Get8BitBT709Luminance(rgb.R, rgb.G, rgb.B);
ref TPixel color = ref rowSpan[x];
color = luminance >= threshold ? upper : lower;
}
break;
}
case BinaryThresholdColorComponent.Saturation:
{
float threshold = this.threshold / 255F;
for (int x = 0; x < rowSpan.Length; x++)
{
float saturation = GetSaturation(span[x]);
ref TPixel color = ref rowSpan[x];
color = saturation >= threshold ? upper : lower;
}
break;
}
case BinaryThresholdColorComponent.MaxChroma:
{
float threshold = this.threshold / 2F;
for (int x = 0; x < rowSpan.Length; x++)
{
float chroma = GetMaxChroma(span[x]);
ref TPixel color = ref rowSpan[x];
color = chroma >= threshold ? upper : lower;
}
break;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float GetSaturation(Rgb24 rgb)
{
Rgba32 rgba = default;
Span<TPixel> row = this.source.GetPixelRowSpan(y);
ref TPixel rowRef = ref MemoryMarshal.GetReference(row);
// Slimmed down RGB => HSL formula. See HslAndRgbConverter.
float r = rgb.R / 255F;
float g = rgb.G / 255F;
float b = rgb.B / 255F;
for (int x = this.minX; x < this.maxX; x++)
float max = MathF.Max(r, MathF.Max(g, b));
float min = MathF.Min(r, MathF.Min(g, b));
float chroma = max - min;
if (MathF.Abs(chroma) < Constants.Epsilon)
{
ref TPixel color = ref Unsafe.Add(ref rowRef, x);
color.ToRgba32(ref rgba);
return 0F;
}
// Convert to grayscale using ITU-R Recommendation BT.709 if required
byte luminance = this.isAlphaOnly ? rgba.A : ColorNumerics.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
color = luminance >= this.threshold ? this.upper : this.lower;
float l = (max + min) / 2F;
if (l <= .5F)
{
return chroma / (max + min);
}
else
{
return chroma / (2F - max - min);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float GetMaxChroma(Rgb24 rgb)
{
// Slimmed down RGB => YCbCr formula. See YCbCrAndRgbConverter.
float r = rgb.R;
float g = rgb.G;
float b = rgb.B;
const float achromatic = 127.5F;
float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b));
float cr = 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b));
return MathF.Max(MathF.Abs(cb - achromatic), MathF.Abs(cr - achromatic));
}
}
}

96
tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing;
@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization
this.operations.BinaryThreshold(.23f);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>();
Assert.Equal(.23f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.Luminance, p.ColorComponent);
Assert.Equal(Color.White, p.UpperColor);
Assert.Equal(Color.Black, p.LowerColor);
}
@ -26,6 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization
this.operations.BinaryThreshold(.93f, this.rect);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>(this.rect);
Assert.Equal(.93f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.Luminance, p.ColorComponent);
Assert.Equal(Color.White, p.UpperColor);
Assert.Equal(Color.Black, p.LowerColor);
}
@ -36,6 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization
this.operations.BinaryThreshold(.23f, Color.HotPink, Color.Yellow);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>();
Assert.Equal(.23f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.Luminance, p.ColorComponent);
Assert.Equal(Color.HotPink, p.UpperColor);
Assert.Equal(Color.Yellow, p.LowerColor);
}
@ -45,9 +48,98 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization
{
this.operations.BinaryThreshold(.93f, Color.HotPink, Color.Yellow, this.rect);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>(this.rect);
Assert.Equal(BinaryThresholdColorComponent.Luminance, p.ColorComponent);
Assert.Equal(.93f, p.Threshold);
Assert.Equal(Color.HotPink, p.UpperColor);
Assert.Equal(Color.Yellow, p.LowerColor);
}
[Fact]
public void BinarySaturationThreshold_CorrectProcessor()
{
this.operations.BinaryThreshold(.23f, BinaryThresholdColorComponent.Saturation);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>();
Assert.Equal(.23f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.Saturation, p.ColorComponent);
Assert.Equal(Color.White, p.UpperColor);
Assert.Equal(Color.Black, p.LowerColor);
}
[Fact]
public void BinarySaturationThreshold_rect_CorrectProcessor()
{
this.operations.BinaryThreshold(.93f, BinaryThresholdColorComponent.Saturation, this.rect);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>(this.rect);
Assert.Equal(.93f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.Saturation, p.ColorComponent);
Assert.Equal(Color.White, p.UpperColor);
Assert.Equal(Color.Black, p.LowerColor);
}
[Fact]
public void BinarySaturationThreshold_CorrectProcessorWithUpperLower()
{
this.operations.BinaryThreshold(.23f, Color.HotPink, Color.Yellow, BinaryThresholdColorComponent.Saturation);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>();
Assert.Equal(.23f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.Saturation, p.ColorComponent);
Assert.Equal(Color.HotPink, p.UpperColor);
Assert.Equal(Color.Yellow, p.LowerColor);
}
[Fact]
public void BinarySaturationThreshold_rect_CorrectProcessorWithUpperLower()
{
this.operations.BinaryThreshold(.93f, Color.HotPink, Color.Yellow, BinaryThresholdColorComponent.Saturation, this.rect);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>(this.rect);
Assert.Equal(.93f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.Saturation, p.ColorComponent);
Assert.Equal(Color.HotPink, p.UpperColor);
Assert.Equal(Color.Yellow, p.LowerColor);
}
[Fact]
public void BinaryMaxChromaThreshold_CorrectProcessor()
{
this.operations.BinaryThreshold(.23f, BinaryThresholdColorComponent.MaxChroma);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>();
Assert.Equal(.23f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.MaxChroma, p.ColorComponent);
Assert.Equal(Color.White, p.UpperColor);
Assert.Equal(Color.Black, p.LowerColor);
}
[Fact]
public void BinaryMaxChromaThreshold_rect_CorrectProcessor()
{
this.operations.BinaryThreshold(.93f, BinaryThresholdColorComponent.MaxChroma, this.rect);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>(this.rect);
Assert.Equal(.93f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.MaxChroma, p.ColorComponent);
Assert.Equal(Color.White, p.UpperColor);
Assert.Equal(Color.Black, p.LowerColor);
}
[Fact]
public void BinaryMaxChromaThreshold_CorrectProcessorWithUpperLower()
{
this.operations.BinaryThreshold(.23f, Color.HotPink, Color.Yellow, BinaryThresholdColorComponent.MaxChroma);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>();
Assert.Equal(.23f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.MaxChroma, p.ColorComponent);
Assert.Equal(Color.HotPink, p.UpperColor);
Assert.Equal(Color.Yellow, p.LowerColor);
}
[Fact]
public void BinaryMaxChromaThreshold_rect_CorrectProcessorWithUpperLower()
{
this.operations.BinaryThreshold(.93f, Color.HotPink, Color.Yellow, BinaryThresholdColorComponent.MaxChroma, this.rect);
BinaryThresholdProcessor p = this.Verify<BinaryThresholdProcessor>(this.rect);
Assert.Equal(.93f, p.Threshold);
Assert.Equal(BinaryThresholdColorComponent.MaxChroma, p.ColorComponent);
Assert.Equal(Color.HotPink, p.UpperColor);
Assert.Equal(Color.Yellow, p.LowerColor);
}
}
}
}

92
tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs

@ -1,14 +1,15 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Globalization;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Binarization;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
{
using SixLabors.ImageSharp.Processing;
public class BinaryThresholdTest
{
public static readonly TheoryData<float> BinaryThresholdValues
@ -19,9 +20,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
};
public static readonly string[] CommonTestImages =
{
TestImages.Png.CalliphoraPartial, TestImages.Png.Bike
};
{
TestImages.Png.Rgb48Bpp,
TestImages.Png.ColorsSaturationLightness,
};
public const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24;
@ -43,9 +45,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (var image = source.Clone())
using (Image<TPixel> image = source.Clone())
{
var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2);
var bounds = new Rectangle(image.Width / 8, image.Height / 8, 6 * image.Width / 8, 6 * image.Width / 8);
image.Mutate(x => x.BinaryThreshold(value, bounds));
image.DebugSave(provider, value);
@ -53,5 +55,81 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds);
}
}
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(BinaryThresholdValues), PixelTypes.Rgba32)]
public void ImageShouldApplyBinarySaturationThresholdFilter<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.BinaryThreshold(value, BinaryThresholdColorComponent.Saturation));
image.DebugSave(provider, value);
image.CompareToReferenceOutput(ImageComparer.Exact, provider, value.ToString("0.00", NumberFormatInfo.InvariantInfo));
}
}
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(BinaryThresholdValues), PixelTypes.Rgba32)]
public void ImageShouldApplyBinarySaturationThresholdInBox<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (Image<TPixel> image = source.Clone())
{
var bounds = new Rectangle(image.Width / 8, image.Height / 8, 6 * image.Width / 8, 6 * image.Width / 8);
image.Mutate(x => x.BinaryThreshold(value, BinaryThresholdColorComponent.Saturation, bounds));
image.DebugSave(provider, value);
image.CompareToReferenceOutput(ImageComparer.Exact, provider, value.ToString("0.00", NumberFormatInfo.InvariantInfo));
}
}
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(BinaryThresholdValues), PixelTypes.Rgba32)]
public void ImageShouldApplyBinaryMaxChromaThresholdFilter<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.BinaryThreshold(value, BinaryThresholdColorComponent.MaxChroma));
image.DebugSave(provider, value);
if (!TestEnvironment.Is64BitProcess && TestEnvironment.IsFramework)
{
var comparer = ImageComparer.TolerantPercentage(0.0004F);
image.CompareToReferenceOutput(comparer, provider, value.ToString("0.00", NumberFormatInfo.InvariantInfo));
}
else
{
image.CompareToReferenceOutput(ImageComparer.Exact, provider, value.ToString("0.00", NumberFormatInfo.InvariantInfo));
}
}
}
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(BinaryThresholdValues), PixelTypes.Rgba32)]
public void ImageShouldApplyBinaryMaxChromaThresholdInBox<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (Image<TPixel> image = source.Clone())
{
var bounds = new Rectangle(image.Width / 8, image.Height / 8, 6 * image.Width / 8, 6 * image.Width / 8);
image.Mutate(x => x.BinaryThreshold(value, BinaryThresholdColorComponent.MaxChroma, bounds));
image.DebugSave(provider, value);
if (!TestEnvironment.Is64BitProcess && TestEnvironment.IsFramework)
{
var comparer = ImageComparer.TolerantPercentage(0.0004F);
image.CompareToReferenceOutput(comparer, provider, value.ToString("0.00", NumberFormatInfo.InvariantInfo));
}
else
{
image.CompareToReferenceOutput(ImageComparer.Exact, provider, value.ToString("0.00", NumberFormatInfo.InvariantInfo));
}
}
}
}
}

1
tests/ImageSharp.Tests/TestImages.cs

@ -40,6 +40,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png";
public const string Rgb48BppTrans = "Png/rgb-16-tRNS.png";
public const string Rgba64Bpp = "Png/rgb-16-alpha.png";
public const string ColorsSaturationLightness = "Png/colors-saturation-lightness.png";
public const string CalliphoraPartial = "Png/CalliphoraPartial.png";
public const string CalliphoraPartialGrayscale = "Png/CalliphoraPartialGrayscale.png";
public const string Bike = "Png/Bike.png";

13
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs

@ -7,7 +7,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Tests
{
@ -25,19 +24,11 @@ namespace SixLabors.ImageSharp.Tests
private static readonly Lazy<string> SolutionDirectoryFullPathLazy = new Lazy<string>(GetSolutionDirectoryFullPathImpl);
private static readonly Lazy<bool> RunsOnCiLazy = new Lazy<bool>(
() =>
{
bool isCi;
return bool.TryParse(Environment.GetEnvironmentVariable("CI"), out isCi) && isCi;
});
private static readonly Lazy<bool> RunsOnCiLazy = new Lazy<bool>(() => bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCi) && isCi);
private static readonly Lazy<string> NetCoreVersionLazy = new Lazy<string>(GetNetCoreVersion);
static TestEnvironment()
{
PrepareRemoteExecutor();
}
static TestEnvironment() => PrepareRemoteExecutor();
/// <summary>
/// Gets the .NET Core version, if running on .NET Core, otherwise returns an empty string.

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:51cb254212641ec3d81a7d412cc31c2e0e6b006581796d7507af15127e1d08a9
size 7913

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cc004ff9b47bcd9300b7dfbd14f2351f17ed4bff205f90845aac823713531dfc
size 4388

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_rgb-48bpp_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c0110c27732a1986430589bd9caf849f92525b5dae7be820879883cec7dbd14f
size 10367

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_rgb-48bpp_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:428c04136ce785178f498d2496d395fd173c35593a290da3224ac798041bf7da
size 9843

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11d9396bf4d48bd2dccdc2e4d9d0fe3bd2d1b1b774453151e520db7fb1c0bfac
size 325147

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6c350a217923d4812599bf7d53d0dc1d69384bf44ce08b348c5c0d7d41cf63c2
size 324065

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_rgb-48bpp_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e936af882acaa38d82aef579b976c3d6183482e7b9611a0dd9868056f0190647
size 316095

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_rgb-48bpp_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b43edc1dea9209588dd81fc21cec9ae9dd067e74df7c6d157519371e21c54292
size 316286

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0565725dc6b81dab6e4553b5d1956f7fdce5d442c99af9d3bd4eca5faafb2a4c
size 9120

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e2fac1d23e6638ff2479ea6010a1466651b6a9b2e4e8ab1c5309361682377175
size 8501

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_rgb-48bpp_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ade68e760e7aa28499ba7fa9324027b66d377438565901b4bf3db40ebb95c3f
size 9795

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_rgb-48bpp_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:351b945c4f1ae794d166a141d745c509003147e54fc735c3a97987b7912bf1f0
size 10813

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7cb7b90177c8f136accea0e5649fcf91c5467cac8310c39818001bd67ade1931
size 325514

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8058aba0e2f553d9badd6bc12b152465fd8bd3dfb0933d303a565c3472b8cf91
size 325033

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_rgb-48bpp_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:260a432d56e9972a4b0617c47c81090dd768363eac3cae27c91a4176eac30fde
size 315799

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_rgb-48bpp_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a0abfdf31816948bb3ea72b2d4b1ea0d6e648c87e8f2283e38cf133505f1d39c
size 316284

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ffe0d135aa932ec246edca7124a550121acd090d7d56c8cb3f1d80cd6139831f
size 6906

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b76320eec406b546e6d9e7c00f01faf4528d1e5573b52665df4664392b406c6a
size 6708

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_rgb-48bpp_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ade047ec10bfd20a7ac0205c9aff3bda33607883291d4fe23704ac8864ac239
size 8066

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_rgb-48bpp_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bbe9c25d68423cba508713f85c4753c302dbf95afec45a0ed5bc21299438e222
size 6817

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7427a84c0ea1cb41543604eeb8aba6bfad15799c9814e7a9e8346bea2c01a354
size 324724

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c7408fa9679d211fc161b89850e6e68cb90719ddc4d1d7ff4d029debbac24e32
size 325185

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_rgb-48bpp_0.25.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e0f34b0685e327e986186775288fb034d9154073c2986c714eb59eae7f4112bf
size 315109

3
tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_rgb-48bpp_0.75.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:51a988d37b88327b244b7ca991c225550b5e5ba1e5322c0756a8108e7cf6cb98
size 314609

3
tests/Images/Input/Png/colors-saturation-lightness.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bccb0284da98a4edd0a6262ac6a608e252f88c7c63321098b24e9cc11d7a201c
size 146402
Loading…
Cancel
Save