diff --git a/.gitattributes b/.gitattributes
index 7c648c0774..6f0a3048cf 100644
--- a/.gitattributes
+++ b/.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
diff --git a/shared-infrastructure b/shared-infrastructure
index 9f515595ee..649bdbe605 160000
--- a/shared-infrastructure
+++ b/shared-infrastructure
@@ -1 +1 @@
-Subproject commit 9f515595ee8a4c3282fc88113f7f46c676fa3e9d
+Subproject commit 649bdbe6050722b030d9292d3fc083cd6ffe87ba
diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs
index eaaf7f58f4..b39fe30252 100644
--- a/src/ImageSharp/ColorSpaces/YCbCr.cs
+++ b/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 @@ public bool Equals(YCbCr other)
&& this.Cr.Equals(other.Cr);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/BinaryThresholdColorComponent.cs b/src/ImageSharp/Processing/BinaryThresholdColorComponent.cs
new file mode 100644
index 0000000000..e46070dcb6
--- /dev/null
+++ b/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
+{
+ ///
+ /// The color component to be compared to threshold.
+ ///
+ public enum BinaryThresholdColorComponent : int
+ {
+ ///
+ /// Luminance color component according to ITU-R Recommendation BT.709.
+ ///
+ Luminance = 0,
+
+ ///
+ /// HSL saturation color component.
+ ///
+ Saturation = 1,
+
+ ///
+ /// Maximum of YCbCr chroma value, i.e. Cb and Cr distance from achromatic value.
+ ///
+ MaxChroma = 2,
+ }
+}
diff --git a/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs b/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs
index d21429589a..11c6433f22 100644
--- a/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs
+++ b/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
///
public static class BinaryThresholdExtensions
{
+ ///
+ /// Applies binarization to the image splitting the pixels at the given threshold with
+ /// Luminance as the color component to be compared to threshold.
+ ///
+ /// The image this method extends.
+ /// The threshold to apply binarization of the image. Must be between 0 and 1.
+ /// The to allow chaining of operations.
+ public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold)
+ => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, BinaryThresholdColorComponent.Luminance));
+
///
/// Applies binarization to the image splitting the pixels at the given threshold.
///
/// The image this method extends.
/// The threshold to apply binarization of the image. Must be between 0 and 1.
+ /// The color component to be compared to threshold.
+ /// The to allow chaining of operations.
+ public static IImageProcessingContext BinaryThreshold(
+ this IImageProcessingContext source,
+ float threshold,
+ BinaryThresholdColorComponent colorComponent)
+ => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, colorComponent));
+
+ ///
+ /// Applies binarization to the image splitting the pixels at the given threshold with
+ /// Luminance as the color component to be compared to threshold.
+ ///
+ /// The image this method extends.
+ /// The threshold to apply binarization of the image. Must be between 0 and 1.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
/// The to allow chaining of operations.
- 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);
///
/// Applies binarization to the image splitting the pixels at the given threshold.
///
/// The image this method extends.
/// The threshold to apply binarization of the image. Must be between 0 and 1.
+ /// The color component to be compared to threshold.
///
/// The structure that specifies the portion of the image object to alter.
///
@@ -32,8 +63,25 @@ public static IImageProcessingContext BinaryThreshold(this IImageProcessingConte
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);
+
+ ///
+ /// Applies binarization to the image splitting the pixels at the given threshold with
+ /// Luminance as the color component to be compared to threshold.
+ ///
+ /// The image this method extends.
+ /// The threshold to apply binarization of the image. Must be between 0 and 1.
+ /// The color to use for pixels that are above the threshold.
+ /// The color to use for pixels that are below the threshold
+ /// The to allow chaining of operations.
+ public static IImageProcessingContext BinaryThreshold(
+ this IImageProcessingContext source,
+ float threshold,
+ Color upperColor,
+ Color lowerColor)
+ => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor, BinaryThresholdColorComponent.Luminance));
///
/// Applies binarization to the image splitting the pixels at the given threshold.
@@ -42,13 +90,35 @@ public static IImageProcessingContext BinaryThreshold(
/// The threshold to apply binarization of the image. Must be between 0 and 1.
/// The color to use for pixels that are above the threshold.
/// The color to use for pixels that are below the threshold
+ /// The color component to be compared to threshold.
/// The to allow chaining of operations.
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));
+
+ ///
+ /// Applies binarization to the image splitting the pixels at the given threshold with
+ /// Luminance as the color component to be compared to threshold.
+ ///
+ /// The image this method extends.
+ /// The threshold to apply binarization of the image. Must be between 0 and 1.
+ /// The color to use for pixels that are above the threshold.
+ /// The color to use for pixels that are below the threshold
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The to allow chaining of operations.
+ 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);
///
/// Applies binarization to the image splitting the pixels at the given threshold.
@@ -57,6 +127,7 @@ public static IImageProcessingContext BinaryThreshold(
/// The threshold to apply binarization of the image. Must be between 0 and 1.
/// The color to use for pixels that are above the threshold.
/// The color to use for pixels that are below the threshold
+ /// The color component to be compared to threshold.
///
/// The structure that specifies the portion of the image object to alter.
///
@@ -66,7 +137,8 @@ public static IImageProcessingContext BinaryThreshold(
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);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
index 460a82f0a0..24a3e6c1da 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
@@ -14,8 +14,19 @@ public class BinaryThresholdProcessor : IImageProcessor
/// Initializes a new instance of the class.
///
/// The threshold to split the image. Must be between 0 and 1.
+ /// The color component to be compared to threshold.
+ public BinaryThresholdProcessor(float threshold, BinaryThresholdColorComponent colorComponent)
+ : this(threshold, Color.White, Color.Black, colorComponent)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// Luminance as color component to be compared to threshold.
+ ///
+ /// The threshold to split the image. Must be between 0 and 1.
public BinaryThresholdProcessor(float threshold)
- : this(threshold, Color.White, Color.Black)
+ : this(threshold, Color.White, Color.Black, BinaryThresholdColorComponent.Luminance)
{
}
@@ -25,12 +36,26 @@ public BinaryThresholdProcessor(float threshold)
/// The threshold to split the image. Must be between 0 and 1.
/// The color to use for pixels that are above the threshold.
/// The color to use for pixels that are below the threshold.
- public BinaryThresholdProcessor(float threshold, Color upperColor, Color lowerColor)
+ /// The color component to be compared to threshold.
+ 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;
+ }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// Luminance as color component to be compared to threshold.
+ ///
+ /// The threshold to split the image. Must be between 0 and 1.
+ /// The color to use for pixels that are above the threshold.
+ /// The color to use for pixels that are below the threshold.
+ public BinaryThresholdProcessor(float threshold, Color upperColor, Color lowerColor)
+ : this(threshold, upperColor, lowerColor, BinaryThresholdColorComponent.Luminance)
+ {
}
///
@@ -48,7 +73,12 @@ public BinaryThresholdProcessor(float threshold, Color upperColor, Color lowerCo
///
public Color LowerColor { get; }
- ///
+ ///
+ /// Gets a value indicating whether to use saturation value instead of luminance.
+ ///
+ public BinaryThresholdColorComponent ColorComponent { get; }
+
+ ///
public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixel : unmanaged, IPixel
=> new BinaryThresholdProcessor(configuration, this, source, sourceRectangle);
diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
index df95b6f1b6..aa03cc27d2 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
+++ b/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 @@ internal class BinaryThresholdProcessor : ImageProcessor
/// The source area to process for the current processor instance.
public BinaryThresholdProcessor(Configuration configuration, BinaryThresholdProcessor definition, Image source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
- {
- this.definition = definition;
- }
+ => this.definition = definition;
///
protected override void OnFrameApply(ImageFrame source)
@@ -42,10 +39,16 @@ protected override void OnFrameApply(ImageFrame source)
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(
configuration,
interest,
in operation);
@@ -54,51 +57,131 @@ protected override void OnFrameApply(ImageFrame source)
///
/// A implementing the clone logic for .
///
- private readonly struct RowOperation : IRowOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly ImageFrame 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 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;
}
///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(int y)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Invoke(int y, Span span)
+ {
+ TPixel upper = this.upper;
+ TPixel lower = this.lower;
+
+ Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
+ PixelOperations.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 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));
}
}
}
diff --git a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs
index 5bdfda02eb..a02ca36eeb 100644
--- a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs
+++ b/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 @@ public void BinaryThreshold_CorrectProcessor()
this.operations.BinaryThreshold(.23f);
BinaryThresholdProcessor p = this.Verify();
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 @@ public void BinaryThreshold_rect_CorrectProcessor()
this.operations.BinaryThreshold(.93f, this.rect);
BinaryThresholdProcessor p = this.Verify(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 @@ public void BinaryThreshold_CorrectProcessorWithUpperLower()
this.operations.BinaryThreshold(.23f, Color.HotPink, Color.Yellow);
BinaryThresholdProcessor p = this.Verify();
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 @@ public void BinaryThreshold_rect_CorrectProcessorWithUpperLower()
{
this.operations.BinaryThreshold(.93f, Color.HotPink, Color.Yellow, this.rect);
BinaryThresholdProcessor p = this.Verify(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();
+ 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(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();
+ 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(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();
+ 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(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();
+ 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(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);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs
index c5b7808cc2..79ed4c7cdb 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs
+++ b/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 BinaryThresholdValues
@@ -19,9 +20,10 @@ public static readonly TheoryData BinaryThresholdValues
};
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 @@ public void ImageShouldApplyBinaryThresholdInBox(TestImageProvider
{
using (Image source = provider.GetImage())
- using (var image = source.Clone())
+ using (Image 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 @@ public void ImageShouldApplyBinaryThresholdInBox(TestImageProvider(TestImageProvider provider, float value)
+ where TPixel : unmanaged, IPixel
+ {
+ using (Image 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(TestImageProvider provider, float value)
+ where TPixel : unmanaged, IPixel
+ {
+ using (Image source = provider.GetImage())
+ using (Image 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(TestImageProvider provider, float value)
+ where TPixel : unmanaged, IPixel
+ {
+ using (Image 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(TestImageProvider provider, float value)
+ where TPixel : unmanaged, IPixel
+ {
+ using (Image source = provider.GetImage())
+ using (Image 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));
+ }
+ }
+ }
}
}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index ac9d6422aa..c15327e0de 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -40,6 +40,7 @@ public static class Png
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";
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
index 8d1b0f7938..cb8a0df42f 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
@@ -7,7 +7,6 @@
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
-using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Tests
{
@@ -25,19 +24,11 @@ public static partial class TestEnvironment
private static readonly Lazy SolutionDirectoryFullPathLazy = new Lazy(GetSolutionDirectoryFullPathImpl);
- private static readonly Lazy RunsOnCiLazy = new Lazy(
- () =>
- {
- bool isCi;
- return bool.TryParse(Environment.GetEnvironmentVariable("CI"), out isCi) && isCi;
- });
+ private static readonly Lazy RunsOnCiLazy = new Lazy(() => bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCi) && isCi);
private static readonly Lazy NetCoreVersionLazy = new Lazy(GetNetCoreVersion);
- static TestEnvironment()
- {
- PrepareRemoteExecutor();
- }
+ static TestEnvironment() => PrepareRemoteExecutor();
///
/// Gets the .NET Core version, if running on .NET Core, otherwise returns an empty string.
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png
new file mode 100644
index 0000000000..2493321a4c
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png
new file mode 100644
index 0000000000..5d8e11424e
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_rgb-48bpp_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_rgb-48bpp_0.25.png
new file mode 100644
index 0000000000..58f41d1a6e
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_rgb-48bpp_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdFilter_Rgba32_rgb-48bpp_0.75.png
new file mode 100644
index 0000000000..47e82d92e1
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png
new file mode 100644
index 0000000000..be28e7de22
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png
new file mode 100644
index 0000000000..c24432571b
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_rgb-48bpp_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_rgb-48bpp_0.25.png
new file mode 100644
index 0000000000..2f8536385b
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_rgb-48bpp_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryMaxChromaThresholdInBox_Rgba32_rgb-48bpp_0.75.png
new file mode 100644
index 0000000000..4d82cc2d97
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png
new file mode 100644
index 0000000000..96658289aa
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png
new file mode 100644
index 0000000000..e437183049
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_rgb-48bpp_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_rgb-48bpp_0.25.png
new file mode 100644
index 0000000000..3b7ba96029
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_rgb-48bpp_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdFilter_Rgba32_rgb-48bpp_0.75.png
new file mode 100644
index 0000000000..64d4620f88
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png
new file mode 100644
index 0000000000..11fedec168
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png
new file mode 100644
index 0000000000..4c1009b8fd
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_rgb-48bpp_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_rgb-48bpp_0.25.png
new file mode 100644
index 0000000000..2ccba1fdaf
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_rgb-48bpp_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinarySaturationThresholdInBox_Rgba32_rgb-48bpp_0.75.png
new file mode 100644
index 0000000000..4d42e08c3c
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_colors-saturation-lightness_0.25.png
new file mode 100644
index 0000000000..5cd2ac4066
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_colors-saturation-lightness_0.75.png
new file mode 100644
index 0000000000..bd7ad3e9a2
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_rgb-48bpp_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_rgb-48bpp_0.25.png
new file mode 100644
index 0000000000..a94e4a7559
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_rgb-48bpp_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdFilter_Rgba32_rgb-48bpp_0.75.png
new file mode 100644
index 0000000000..15a94b2bee
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_colors-saturation-lightness_0.25.png
new file mode 100644
index 0000000000..6969b25924
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_colors-saturation-lightness_0.75.png
new file mode 100644
index 0000000000..84895fa885
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_rgb-48bpp_0.25.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_rgb-48bpp_0.25.png
new file mode 100644
index 0000000000..bc9ca43a47
--- /dev/null
+++ b/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
diff --git a/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_rgb-48bpp_0.75.png b/tests/Images/External/ReferenceOutput/BinaryThresholdTest/ImageShouldApplyBinaryThresholdInBox_Rgba32_rgb-48bpp_0.75.png
new file mode 100644
index 0000000000..fe1af1539b
--- /dev/null
+++ b/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
diff --git a/tests/Images/Input/Png/colors-saturation-lightness.png b/tests/Images/Input/Png/colors-saturation-lightness.png
new file mode 100644
index 0000000000..7af32025c7
--- /dev/null
+++ b/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