Browse Source

Begin optimizing the global histogram

pull/673/head
James Jackson-South 8 years ago
parent
commit
609cb673cc
  1. 5
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
  2. 27
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
  3. 11
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs
  4. 8
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
  5. 8
      tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs

5
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs

@ -484,11 +484,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
using (IMemoryOwner<int> histogramBuffer = this.memoryAllocator.Allocate<int>(luminanceLevels))
{
Span<int> histogram = histogramBuffer.GetSpan();
ref int histogramBase = ref MemoryMarshal.GetReference(histogram);
for (int x = 0; x < sourceWidth; x += tileWidth)
{
histogram.Clear();
Span<int> cdf = this.GetCdfLutSpan(cdfX, index);
ref int cdfBase = ref MemoryMarshal.GetReference(this.GetCdfLutSpan(cdfX, index));
int xlimit = Math.Min(x + tileWidth, sourceWidth);
for (int dy = y; dy < endY; dy++)
@ -506,7 +507,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile);
}
Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(cdf, histogram, histogram.Length - 1);
Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1);
cdfX++;
}

27
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs

@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@ -59,21 +62,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
parallelOptions,
x =>
{
using (System.Buffers.IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (System.Buffers.IMemoryOwner<int> histogramBufferCopy = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (System.Buffers.IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (System.Buffers.IMemoryOwner<TPixel> pixelRowBuffer = memoryAllocator.Allocate<TPixel>(tileWidth, AllocationOptions.Clean))
using (IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (IMemoryOwner<int> histogramBufferCopy = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (IMemoryOwner<TPixel> pixelRowBuffer = memoryAllocator.Allocate<TPixel>(tileWidth, AllocationOptions.Clean))
{
Span<int> histogram = histogramBuffer.GetSpan();
ref int histogramBase = ref MemoryMarshal.GetReference(histogram);
Span<int> histogramCopy = histogramBufferCopy.GetSpan();
Span<int> cdf = cdfBuffer.GetSpan();
ref int histogramCopyBase = ref MemoryMarshal.GetReference(histogramCopy);
ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan());
Span<TPixel> pixelRow = pixelRowBuffer.GetSpan();
int maxHistIdx = 0;
// Build the histogram of grayscale values for the current tile.
for (int dy = -halfTileWith; dy < halfTileWith; dy++)
{
Span<TPixel> rowSpan = this.GetPixelRow(source, pixelRow, (int)x - halfTileWith, dy, tileWidth);
Span<TPixel> rowSpan = this.GetPixelRow(source, pixelRow, x - halfTileWith, dy, tileWidth);
int maxIdx = this.AddPixelsToHistogram(rowSpan, histogram, this.LuminanceLevels);
if (maxIdx > maxHistIdx)
{
@ -91,12 +97,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
}
// Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value.
int cdfMin = this.ClipHistogramEnabled ? this.CalculateCdf(cdf, histogramCopy, maxHistIdx) : this.CalculateCdf(cdf, histogram, maxHistIdx);
int cdfMin = this.ClipHistogramEnabled
? this.CalculateCdf(ref cdfBase, ref histogramCopyBase, maxHistIdx)
: this.CalculateCdf(ref cdfBase, ref histogramBase, maxHistIdx);
float numberOfPixelsMinusCdfMin = pixeInTile - cdfMin;
// Map the current pixel to the new equalized value
int luminance = GetLuminance(source[x, y], this.LuminanceLevels);
float luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin;
float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin;
targetPixels[x, y].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, source[x, y].ToVector4().W));
// Remove top most row from the histogram, mirroring rows which exceeds the borders.
@ -218,7 +227,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// If the histogram at the maximum index has changed to 0, search for the next smaller value.
if (luminance == maxHistIdx && histogram[luminance] == 0)
{
for (int j = luminance; j >= 0; j--)
for (int j = luminance; j >= 0; j--)
{
maxHistIdx = j;
if (histogram[j] != 0)

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

@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -37,11 +39,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int numberOfPixels = source.Width * source.Height;
Span<TPixel> pixels = source.GetPixelSpan();
using (System.Buffers.IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (System.Buffers.IMemoryOwner<int> cdfBuffer = 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))
{
// Build the histogram of the grayscale levels.
Span<int> histogram = histogramBuffer.GetSpan();
ref int histogramBase = ref MemoryMarshal.GetReference(histogram);
for (int i = 0; i < pixels.Length; i++)
{
TPixel sourcePixel = pixels[i];
@ -56,7 +60,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// Calculate the cumulative distribution function, which will map each input pixel to a new value.
Span<int> cdf = cdfBuffer.GetSpan();
int cdfMin = this.CalculateCdf(cdf, histogram, histogram.Length - 1);
ref int cdfBase = ref MemoryMarshal.GetReference(cdf);
int cdfMin = this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1);
// Apply the cdf to each pixel of the image
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;

8
src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs

@ -53,17 +53,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary>
/// Calculates the cumulative distribution function.
/// </summary>
/// <param name="cdf">The array holding the cdf.</param>
/// <param name="histogram">The histogram of the input image.</param>
/// <param name="cdfBase">The reference to the array holding the cdf.</param>
/// <param name="histogramBase">The reference to the histogram of the input image.</param>
/// <param name="maxIdx">Index of the maximum of the histogram.</param>
/// <returns>The first none zero value of the cdf.</returns>
public int CalculateCdf(Span<int> cdf, Span<int> histogram, int maxIdx)
public int CalculateCdf(ref int cdfBase, ref int histogramBase, int maxIdx)
{
int histSum = 0;
int cdfMin = 0;
bool cdfMinFound = false;
ref int cdfBase = ref MemoryMarshal.GetReference(cdf);
ref int histogramBase = ref MemoryMarshal.GetReference(histogram);
for (int i = 0; i <= maxIdx; i++)
{

8
tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs

@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization
{
for (int x = 0; x < 8; x++)
{
byte luminance = pixels[y * 8 + x];
byte luminance = pixels[(y * 8) + x];
image[x, y] = new Rgba32(luminance, luminance, luminance);
}
}
@ -66,9 +66,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization
for (int x = 0; x < 8; x++)
{
Rgba32 actual = image[x, y];
Assert.Equal(expected[y * 8 + x], actual.R);
Assert.Equal(expected[y * 8 + x], actual.G);
Assert.Equal(expected[y * 8 + x], actual.B);
Assert.Equal(expected[(y * 8) + x], actual.R);
Assert.Equal(expected[(y * 8) + x], actual.G);
Assert.Equal(expected[(y * 8) + x], actual.B);
}
}
}

Loading…
Cancel
Save