Skip to content

Commit f2930c7

Browse files
Parallelize GlobalHistogramEqualizationProcessor
1 parent bf6a750 commit f2930c7

File tree

1 file changed

+47
-21
lines changed

1 file changed

+47
-21
lines changed

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

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
using System;
55
using System.Buffers;
66
using System.Numerics;
7+
using System.Runtime.CompilerServices;
78
using System.Runtime.InteropServices;
89
using SixLabors.ImageSharp.Advanced;
910
using SixLabors.ImageSharp.Memory;
11+
using SixLabors.ImageSharp.ParallelUtils;
1012
using SixLabors.ImageSharp.PixelFormats;
1113
using SixLabors.Memory;
1214
using SixLabors.Primitives;
@@ -23,8 +25,10 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
2325
/// <summary>
2426
/// Initializes a new instance of the <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/> class.
2527
/// </summary>
26-
/// <param name="luminanceLevels">The number of different luminance levels. Typical values are 256 for 8-bit grayscale images
27-
/// or 65536 for 16-bit grayscale images.</param>
28+
/// <param name="luminanceLevels">
29+
/// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images
30+
/// or 65536 for 16-bit grayscale images.
31+
/// </param>
2832
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
2933
/// <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>
3034
public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage)
@@ -38,42 +42,64 @@ protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle source
3842
MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
3943
int numberOfPixels = source.Width * source.Height;
4044
Span<TPixel> pixels = source.GetPixelSpan();
45+
var workingRect = new Rectangle(0, 0, source.Width, source.Height);
4146

4247
using (IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
4348
using (IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
4449
{
4550
// Build the histogram of the grayscale levels.
46-
Span<int> histogram = histogramBuffer.GetSpan();
47-
ref int histogramBase = ref MemoryMarshal.GetReference(histogram);
51+
ParallelHelper.IterateRows(
52+
workingRect,
53+
configuration,
54+
rows =>
55+
{
56+
ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan());
57+
for (int y = rows.Min; y < rows.Max; y++)
58+
{
59+
ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y));
4860

49-
for (int i = 0; i < pixels.Length; i++)
50-
{
51-
TPixel sourcePixel = pixels[i];
52-
int luminance = GetLuminance(sourcePixel, this.LuminanceLevels);
53-
histogram[luminance]++;
54-
}
61+
for (int x = 0; x < workingRect.Width; x++)
62+
{
63+
int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels);
64+
Unsafe.Add(ref histogramBase, luminance)++;
65+
}
66+
}
67+
});
5568

69+
Span<int> histogram = histogramBuffer.GetSpan();
5670
if (this.ClipHistogramEnabled)
5771
{
5872
this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels);
5973
}
6074

6175
// Calculate the cumulative distribution function, which will map each input pixel to a new value.
62-
Span<int> cdf = cdfBuffer.GetSpan();
63-
ref int cdfBase = ref MemoryMarshal.GetReference(cdf);
64-
int cdfMin = this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1);
76+
int cdfMin = this.CalculateCdf(
77+
ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()),
78+
ref MemoryMarshal.GetReference(histogram),
79+
histogram.Length - 1);
6580

66-
// Apply the cdf to each pixel of the image
6781
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
68-
for (int i = 0; i < pixels.Length; i++)
69-
{
70-
TPixel sourcePixel = pixels[i];
7182

72-
int luminance = GetLuminance(sourcePixel, this.LuminanceLevels);
73-
float luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin;
83+
// Apply the cdf to each pixel of the image
84+
ParallelHelper.IterateRows(
85+
workingRect,
86+
configuration,
87+
rows =>
88+
{
89+
ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan());
90+
for (int y = rows.Min; y < rows.Max; y++)
91+
{
92+
ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y));
7493

75-
pixels[i].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, sourcePixel.ToVector4().W));
76-
}
94+
for (int x = 0; x < workingRect.Width; x++)
95+
{
96+
ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x);
97+
int luminance = GetLuminance(pixel, this.LuminanceLevels);
98+
float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin;
99+
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W));
100+
}
101+
}
102+
});
77103
}
78104
}
79105
}

0 commit comments

Comments
 (0)