From 609cb673cce9639ac6dcae621488c7b0a5598f52 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 23 Jan 2019 23:23:35 +1100 Subject: [PATCH] Begin optimizing the global histogram --- .../AdaptiveHistEqualizationProcessor.cs | 5 ++-- .../AdaptiveHistEqualizationSWProcessor.cs | 27 ++++++++++++------- .../GlobalHistogramEqualizationProcessor.cs | 11 +++++--- .../HistogramEqualizationProcessor.cs | 8 +++--- .../HistogramEqualizationTests.cs | 8 +++--- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs index f03c3ff9b2..5092b85ff4 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs @@ -484,11 +484,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization using (IMemoryOwner histogramBuffer = this.memoryAllocator.Allocate(luminanceLevels)) { Span histogram = histogramBuffer.GetSpan(); + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); for (int x = 0; x < sourceWidth; x += tileWidth) { histogram.Clear(); - Span cdf = this.GetCdfLutSpan(cdfX, index); + ref int cdfBase = ref MemoryMarshal.GetReference(this.GetCdfLutSpan(cdfX, index)); int xlimit = Math.Min(x + tileWidth, sourceWidth); for (int dy = y; dy < endY; dy++) @@ -506,7 +507,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); } - Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(cdf, histogram, histogram.Length - 1); + Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); cdfX++; } diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs index 2c24d79129..8c86e58c73 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs @@ -2,7 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -59,21 +62,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization parallelOptions, x => { - using (System.Buffers.IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (System.Buffers.IMemoryOwner histogramBufferCopy = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (System.Buffers.IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (System.Buffers.IMemoryOwner pixelRowBuffer = memoryAllocator.Allocate(tileWidth, AllocationOptions.Clean)) + using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner histogramBufferCopy = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner pixelRowBuffer = memoryAllocator.Allocate(tileWidth, AllocationOptions.Clean)) { Span histogram = histogramBuffer.GetSpan(); + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); Span histogramCopy = histogramBufferCopy.GetSpan(); - Span cdf = cdfBuffer.GetSpan(); + ref int histogramCopyBase = ref MemoryMarshal.GetReference(histogramCopy); + ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); + Span pixelRow = pixelRowBuffer.GetSpan(); int maxHistIdx = 0; // Build the histogram of grayscale values for the current tile. for (int dy = -halfTileWith; dy < halfTileWith; dy++) { - Span rowSpan = this.GetPixelRow(source, pixelRow, (int)x - halfTileWith, dy, tileWidth); + Span rowSpan = this.GetPixelRow(source, pixelRow, x - halfTileWith, dy, tileWidth); int maxIdx = this.AddPixelsToHistogram(rowSpan, histogram, this.LuminanceLevels); if (maxIdx > maxHistIdx) { @@ -91,12 +97,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } // Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value. - int cdfMin = this.ClipHistogramEnabled ? this.CalculateCdf(cdf, histogramCopy, maxHistIdx) : this.CalculateCdf(cdf, histogram, maxHistIdx); + int cdfMin = this.ClipHistogramEnabled + ? this.CalculateCdf(ref cdfBase, ref histogramCopyBase, maxHistIdx) + : this.CalculateCdf(ref cdfBase, ref histogramBase, maxHistIdx); + float numberOfPixelsMinusCdfMin = pixeInTile - cdfMin; // Map the current pixel to the new equalized value int luminance = GetLuminance(source[x, y], this.LuminanceLevels); - float luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin; + float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; targetPixels[x, y].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, source[x, y].ToVector4().W)); // Remove top most row from the histogram, mirroring rows which exceeds the borders. @@ -218,7 +227,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization // If the histogram at the maximum index has changed to 0, search for the next smaller value. if (luminance == maxHistIdx && histogram[luminance] == 0) { - for (int j = luminance; j >= 0; j--) + for (int j = luminance; j >= 0; j--) { maxHistIdx = j; if (histogram[j] != 0) diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs index daa045afb3..cbd3c6174c 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -37,11 +39,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int numberOfPixels = source.Width * source.Height; Span pixels = source.GetPixelSpan(); - using (System.Buffers.IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (System.Buffers.IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) { // Build the histogram of the grayscale levels. Span histogram = histogramBuffer.GetSpan(); + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + for (int i = 0; i < pixels.Length; i++) { TPixel sourcePixel = pixels[i]; @@ -56,7 +60,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization // Calculate the cumulative distribution function, which will map each input pixel to a new value. Span cdf = cdfBuffer.GetSpan(); - int cdfMin = this.CalculateCdf(cdf, histogram, histogram.Length - 1); + ref int cdfBase = ref MemoryMarshal.GetReference(cdf); + int cdfMin = this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); // Apply the cdf to each pixel of the image float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index 9290d941cc..23bdbf2a36 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -53,17 +53,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Calculates the cumulative distribution function. /// - /// The array holding the cdf. - /// The histogram of the input image. + /// The reference to the array holding the cdf. + /// The reference to the histogram of the input image. /// Index of the maximum of the histogram. /// The first none zero value of the cdf. - public int CalculateCdf(Span cdf, Span histogram, int maxIdx) + public int CalculateCdf(ref int cdfBase, ref int histogramBase, int maxIdx) { int histSum = 0; int cdfMin = 0; bool cdfMinFound = false; - ref int cdfBase = ref MemoryMarshal.GetReference(cdf); - ref int histogramBase = ref MemoryMarshal.GetReference(histogram); for (int i = 0; i <= maxIdx; i++) { diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 41c399bd5b..84d592bd96 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization { for (int x = 0; x < 8; x++) { - byte luminance = pixels[y * 8 + x]; + byte luminance = pixels[(y * 8) + x]; image[x, y] = new Rgba32(luminance, luminance, luminance); } } @@ -66,9 +66,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization for (int x = 0; x < 8; x++) { Rgba32 actual = image[x, y]; - Assert.Equal(expected[y * 8 + x], actual.R); - Assert.Equal(expected[y * 8 + x], actual.G); - Assert.Equal(expected[y * 8 + x], actual.B); + Assert.Equal(expected[(y * 8) + x], actual.R); + Assert.Equal(expected[(y * 8) + x], actual.G); + Assert.Equal(expected[(y * 8) + x], actual.B); } } }