From f2930c75d3f662dd10ad12e00b4b1ae95d43a105 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 24 Jan 2019 10:27:24 +1100 Subject: [PATCH] Parallelize GlobalHistogramEqualizationProcessor --- .../GlobalHistogramEqualizationProcessor.cs | 68 +++++++++++++------ 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs index cbd3c6174c..aadde2424b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs @@ -4,9 +4,11 @@ using System; using System.Buffers; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -23,8 +25,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Initializes a new instance of the class. /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value. public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) @@ -38,42 +42,64 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization MemoryAllocator memoryAllocator = configuration.MemoryAllocator; int numberOfPixels = source.Width * source.Height; Span pixels = source.GetPixelSpan(); + 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)) { // Build the histogram of the grayscale levels. - Span histogram = histogramBuffer.GetSpan(); - ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + ParallelHelper.IterateRows( + workingRect, + 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 i = 0; i < pixels.Length; i++) - { - TPixel sourcePixel = pixels[i]; - int luminance = GetLuminance(sourcePixel, this.LuminanceLevels); - histogram[luminance]++; - } + 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.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); } // Calculate the cumulative distribution function, which will map each input pixel to a new value. - Span cdf = cdfBuffer.GetSpan(); - ref int cdfBase = ref MemoryMarshal.GetReference(cdf); - int cdfMin = this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); + int cdfMin = this.CalculateCdf( + ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()), + ref MemoryMarshal.GetReference(histogram), + histogram.Length - 1); - // Apply the cdf to each pixel of the image float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; - for (int i = 0; i < pixels.Length; i++) - { - TPixel sourcePixel = pixels[i]; - int luminance = GetLuminance(sourcePixel, this.LuminanceLevels); - float luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin; + // Apply the cdf to each pixel of the image + ParallelHelper.IterateRows( + workingRect, + 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)); - pixels[i].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, sourcePixel.ToVector4().W)); - } + 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)); + } + } + }); } } }