From 36dd4a3d0f08c977d935d814c976b1cda2e12474 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 7 Feb 2020 16:00:51 +0100 Subject: [PATCH] Refactor GlobalHistogramEqualizationProcessor --- ...lHistogramEqualizationProcessor{TPixel}.cs | 163 ++++++++++++------ 1 file changed, 111 insertions(+), 52 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index ff68d00497..5d25bae821 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -49,62 +49,121 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int numberOfPixels = source.Width * source.Height; var workingRect = new Rectangle(0, 0, source.Width, source.Height); - using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); + + // Build the histogram of the grayscale levels + ParallelRowIterator.IterateRows( + workingRect, + this.Configuration, + new GrayscaleLevelsRowIntervalAction(workingRect, histogramBuffer, source, this.LuminanceLevels)); + + Span histogram = histogramBuffer.GetSpan(); + if (this.ClipHistogramEnabled) + { + this.ClipHistogram(histogram, this.ClipLimit); + } + + using IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); + + // Calculate the cumulative distribution function, which will map each input pixel to a new value. + int cdfMin = this.CalculateCdf( + ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()), + ref MemoryMarshal.GetReference(histogram), + histogram.Length - 1); + + float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; + + // Apply the cdf to each pixel of the image + ParallelRowIterator.IterateRows( + workingRect, + this.Configuration, + new CdfApplicationRowIntervalAction(workingRect, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin)); + } + + /// + /// A implementing the grayscale levels logic for . + /// + private readonly struct GrayscaleLevelsRowIntervalAction : IRowIntervalAction + { + private readonly Rectangle bounds; + private readonly IMemoryOwner histogramBuffer; + private readonly ImageFrame source; + private readonly int luminanceLevels; + + [MethodImpl(InliningOptions.ShortMethod)] + public GrayscaleLevelsRowIntervalAction( + in Rectangle bounds, + IMemoryOwner histogramBuffer, + ImageFrame source, + int luminanceLevels) { - // Build the histogram of the grayscale levels. - ParallelRowIterator.IterateRows( - workingRect, - this.Configuration, - rows => - { - ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan()); - for (int y = rows.Min; y < rows.Max; y++) - { - ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); - - for (int x = 0; x < workingRect.Width; x++) - { - int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels); - Unsafe.Add(ref histogramBase, luminance)++; - } - } - }); - - Span histogram = histogramBuffer.GetSpan(); - if (this.ClipHistogramEnabled) + this.bounds = bounds; + this.histogramBuffer = histogramBuffer; + this.source = source; + this.luminanceLevels = luminanceLevels; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(in RowInterval rows) + { + ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); + for (int y = rows.Min; y < rows.Max; y++) { - this.ClipHistogram(histogram, this.ClipLimit); + ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); + + for (int x = 0; x < this.bounds.Width; x++) + { + int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels); + Unsafe.Add(ref histogramBase, luminance)++; + } } + } + } + + /// + /// A implementing the cdf application levels logic for . + /// + private readonly struct CdfApplicationRowIntervalAction : IRowIntervalAction + { + private readonly Rectangle bounds; + private readonly IMemoryOwner cdfBuffer; + private readonly ImageFrame source; + private readonly int luminanceLevels; + private readonly float numberOfPixelsMinusCdfMin; - // Calculate the cumulative distribution function, which will map each input pixel to a new value. - int cdfMin = this.CalculateCdf( - ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()), - ref MemoryMarshal.GetReference(histogram), - histogram.Length - 1); - - float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; - - // Apply the cdf to each pixel of the image - ParallelRowIterator.IterateRows( - workingRect, - this.Configuration, - rows => - { - ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); - for (int y = rows.Min; y < rows.Max; y++) - { - ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); - - for (int x = 0; x < workingRect.Width; x++) - { - ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x); - int luminance = GetLuminance(pixel, this.LuminanceLevels); - float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - } - } - }); + [MethodImpl(InliningOptions.ShortMethod)] + public CdfApplicationRowIntervalAction( + in Rectangle bounds, + IMemoryOwner cdfBuffer, + ImageFrame source, + int luminanceLevels, + float numberOfPixelsMinusCdfMin) + { + this.bounds = bounds; + this.cdfBuffer = cdfBuffer; + this.source = source; + this.luminanceLevels = luminanceLevels; + this.numberOfPixelsMinusCdfMin = numberOfPixelsMinusCdfMin; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(in RowInterval rows) + { + ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan()); + for (int y = rows.Min; y < rows.Max; y++) + { + ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); + + for (int x = 0; x < this.bounds.Width; x++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x); + int luminance = GetLuminance(pixel, this.luminanceLevels); + float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / this.numberOfPixelsMinusCdfMin; + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + } + } } } }