Browse Source

Parallelize GlobalHistogramEqualizationProcessor

pull/673/head
James Jackson-South 7 years ago
parent
commit
f2930c75d3
  1. 68
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs

68
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs

@ -4,9 +4,11 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -23,8 +25,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="luminanceLevels">The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// <param name="luminanceLevels">
/// or 65536 for 16-bit grayscale images.</param> /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images
/// or 65536 for 16-bit grayscale images.
/// </param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param> /// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value.</param>
public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage)
@ -38,42 +42,64 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
MemoryAllocator memoryAllocator = configuration.MemoryAllocator; MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
int numberOfPixels = source.Width * source.Height; int numberOfPixels = source.Width * source.Height;
Span<TPixel> pixels = source.GetPixelSpan(); Span<TPixel> pixels = source.GetPixelSpan();
var workingRect = new Rectangle(0, 0, source.Width, source.Height);
using (IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
{ {
// Build the histogram of the grayscale levels. // Build the histogram of the grayscale levels.
Span<int> histogram = histogramBuffer.GetSpan(); ParallelHelper.IterateRows(
ref int histogramBase = ref MemoryMarshal.GetReference(histogram); 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++) for (int x = 0; x < workingRect.Width; x++)
{ {
TPixel sourcePixel = pixels[i]; int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels);
int luminance = GetLuminance(sourcePixel, this.LuminanceLevels); Unsafe.Add(ref histogramBase, luminance)++;
histogram[luminance]++; }
} }
});
Span<int> histogram = histogramBuffer.GetSpan();
if (this.ClipHistogramEnabled) if (this.ClipHistogramEnabled)
{ {
this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels);
} }
// Calculate the cumulative distribution function, which will map each input pixel to a new value. // Calculate the cumulative distribution function, which will map each input pixel to a new value.
Span<int> cdf = cdfBuffer.GetSpan(); int cdfMin = this.CalculateCdf(
ref int cdfBase = ref MemoryMarshal.GetReference(cdf); ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()),
int cdfMin = this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); ref MemoryMarshal.GetReference(histogram),
histogram.Length - 1);
// Apply the cdf to each pixel of the image
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
for (int i = 0; i < pixels.Length; i++)
{
TPixel sourcePixel = pixels[i];
int luminance = GetLuminance(sourcePixel, this.LuminanceLevels); // Apply the cdf to each pixel of the image
float luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin; 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));
}
}
});
} }
} }
} }

Loading…
Cancel
Save