Browse Source

Refactor GlobalHistogramEqualizationProcessor<TPixel>

af/octree-no-pixelmap
Sergio Pedri 6 years ago
parent
commit
36dd4a3d0f
  1. 163
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs

163
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<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels
ParallelRowIterator.IterateRows(
workingRect,
this.Configuration,
new GrayscaleLevelsRowIntervalAction(workingRect, histogramBuffer, source, this.LuminanceLevels));
Span<int> histogram = histogramBuffer.GetSpan();
if (this.ClipHistogramEnabled)
{
this.ClipHistogram(histogram, this.ClipLimit);
}
using IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(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));
}
/// <summary>
/// A <see langword="struct"/> implementing the grayscale levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>.
/// </summary>
private readonly struct GrayscaleLevelsRowIntervalAction : IRowIntervalAction
{
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> histogramBuffer;
private readonly ImageFrame<TPixel> source;
private readonly int luminanceLevels;
[MethodImpl(InliningOptions.ShortMethod)]
public GrayscaleLevelsRowIntervalAction(
in Rectangle bounds,
IMemoryOwner<int> histogramBuffer,
ImageFrame<TPixel> 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<int> histogram = histogramBuffer.GetSpan();
if (this.ClipHistogramEnabled)
this.bounds = bounds;
this.histogramBuffer = histogramBuffer;
this.source = source;
this.luminanceLevels = luminanceLevels;
}
/// <inheritdoc/>
[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)++;
}
}
}
}
/// <summary>
/// A <see langword="struct"/> implementing the cdf application levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>.
/// </summary>
private readonly struct CdfApplicationRowIntervalAction : IRowIntervalAction
{
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer;
private readonly ImageFrame<TPixel> 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<int> cdfBuffer,
ImageFrame<TPixel> source,
int luminanceLevels,
float numberOfPixelsMinusCdfMin)
{
this.bounds = bounds;
this.cdfBuffer = cdfBuffer;
this.source = source;
this.luminanceLevels = luminanceLevels;
this.numberOfPixelsMinusCdfMin = numberOfPixelsMinusCdfMin;
}
/// <inheritdoc/>
[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));
}
}
}
}
}

Loading…
Cancel
Save