From bc7ea4f8bffeb21e4f39e9cdebc42b8c67aa39ae Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 8 Nov 2020 12:41:38 +0100 Subject: [PATCH 1/3] Use Interlocked.Increment during histogram calculation, fixes issue #1416 --- .../GlobalHistogramEqualizationProcessor{TPixel}.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 274376671..0c5a109a6 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); - // Build the histogram of the grayscale levels + // Build the histogram of the grayscale levels. var grayscaleOperation = new GrayscaleLevelsRowOperation(interest, histogramBuffer, source, this.LuminanceLevels); ParallelRowIterator.IterateRows( this.Configuration, @@ -114,7 +115,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization #endif public void Invoke(int y) { - ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); int levels = this.luminanceLevels; @@ -123,7 +123,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization // TODO: We should bulk convert here. var vector = Unsafe.Add(ref pixelBase, x).ToVector4(); int luminance = ImageMaths.GetBT709Luminance(ref vector, levels); - Unsafe.Add(ref histogramBase, luminance)++; + ref int histogramAtLuminance = ref MemoryMarshal.GetReference(this.histogramBuffer.Slice(luminance)); + Interlocked.Increment(ref histogramAtLuminance); } } } From 82a5a61f3d1ba11fdc6fae5cbcfebe379bc4686c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 8 Nov 2020 12:55:53 +0100 Subject: [PATCH 2/3] Add global histogram equalization test which compares result to reference output --- .../HistogramEqualizationTests.cs | 39 ++++++++++++++----- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/External | 2 +- .../640px-Unequalized_Hawkes_Bay_NZ.jpg | 3 ++ 4 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 tests/Images/Input/Jpg/baseline/640px-Unequalized_Hawkes_Bay_NZ.jpg diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 1c1da6f19..4460f04fb 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization [Theory] [InlineData(256)] [InlineData(65536)] - public void HistogramEqualizationTest(int luminanceLevels) + public void GlobalHistogramEqualization_WithDifferentLumanceLevels(int luminanceLevels) { // Arrange var pixels = new byte[] @@ -45,20 +45,21 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization var expected = new byte[] { - 0, 12, 53, 32, 146, 53, 174, 53, - 57, 32, 12, 227, 219, 202, 32, 154, - 65, 85, 93, 239, 251, 227, 65, 158, - 73, 146, 146, 247, 255, 235, 154, 130, - 97, 166, 117, 231, 243, 210, 117, 117, - 117, 190, 36, 190, 178, 93, 20, 170, - 130, 202, 73, 20, 12, 53, 85, 194, - 146, 206, 130, 117, 85, 166, 182, 215 + 0, 12, 53, 32, 146, 53, 174, 53, + 57, 32, 12, 227, 219, 202, 32, 154, + 65, 85, 93, 239, 251, 227, 65, 158, + 73, 146, 146, 247, 255, 235, 154, 130, + 97, 166, 117, 231, 243, 210, 117, 117, + 117, 190, 36, 190, 178, 93, 20, 170, + 130, 202, 73, 20, 12, 53, 85, 194, + 146, 206, 130, 117, 85, 166, 182, 215 }; // Act image.Mutate(x => x.HistogramEqualization(new HistogramEqualizationOptions { - LuminanceLevels = luminanceLevels + LuminanceLevels = luminanceLevels, + Method = HistogramEqualizationMethod.Global })); // Assert @@ -75,6 +76,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization } } + [Theory] + [WithFile(TestImages.Jpeg.Baseline.HistogramEqImage, PixelTypes.Rgba32)] + public void GlobalHistogramEqualization_CompareToReferenceOutput(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new HistogramEqualizationOptions + { + Method = HistogramEqualizationMethod.Global, + LuminanceLevels = 256, + }; + image.Mutate(x => x.HistogramEqualization(options)); + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider, extension: "png"); + } + } + [Theory] [WithFile(TestImages.Jpeg.Baseline.LowContrast, PixelTypes.Rgba32)] public void Adaptive_SlidingWindow_15Tiles_WithClipping(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index dce36bb0f..e4c73928d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -196,6 +196,7 @@ namespace SixLabors.ImageSharp.Tests public const string YcckSubsample1222 = "Jpg/baseline/ycck-subsample-1222.jpg"; public const string Iptc = "Jpg/baseline/iptc.jpg"; public const string App13WithEmptyIptc = "Jpg/baseline/iptc-psAPP13-wIPTCempty.jpg"; + public const string HistogramEqImage = "Jpg/baseline/640px-Unequalized_Hawkes_Bay_NZ.jpg"; public static readonly string[] All = { diff --git a/tests/Images/External b/tests/Images/External index cc6465910..8b43d14d2 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit cc6465910d092319ef9bf4e99698a0649996d3c5 +Subproject commit 8b43d14d21ce9b436af3d12a70d38402cdba176b diff --git a/tests/Images/Input/Jpg/baseline/640px-Unequalized_Hawkes_Bay_NZ.jpg b/tests/Images/Input/Jpg/baseline/640px-Unequalized_Hawkes_Bay_NZ.jpg new file mode 100644 index 000000000..bb89de589 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/640px-Unequalized_Hawkes_Bay_NZ.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1fafc61231325c42d94fe163486a6c5144fb6211ccdceb902d5cb4ddebda9e1 +size 32428 From 2222db8e0baf52f46623b0668b356b8e42c37bb9 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 8 Nov 2020 17:45:29 +0100 Subject: [PATCH 3/3] Use Interlocked.Increment(ref Unsafe.Add(ref histogramBase, luminance)); --- .../GlobalHistogramEqualizationProcessor{TPixel}.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 0c5a109a6..488426f93 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -115,6 +115,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization #endif public void Invoke(int y) { + ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); int levels = this.luminanceLevels; @@ -123,8 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization // TODO: We should bulk convert here. var vector = Unsafe.Add(ref pixelBase, x).ToVector4(); int luminance = ImageMaths.GetBT709Luminance(ref vector, levels); - ref int histogramAtLuminance = ref MemoryMarshal.GetReference(this.histogramBuffer.Slice(luminance)); - Interlocked.Increment(ref histogramAtLuminance); + Interlocked.Increment(ref Unsafe.Add(ref histogramBase, luminance)); } } }