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)) using (IMemoryOwner<int> histogramBuffer = this.memoryAllocator.Allocate<int>(luminanceLevels))
{ {
Span<int> histogram = histogramBuffer.GetSpan(); Span<int> histogram = histogramBuffer.GetSpan();
ref int histogramBase = ref MemoryMarshal.GetReference(histogram);
for (int x = 0; x < sourceWidth; x += tileWidth) for (int x = 0; x < sourceWidth; x += tileWidth)
{ {
histogram.Clear(); 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); int xlimit = Math.Min(x + tileWidth, sourceWidth);
for (int dy = y; dy < endY; dy++) for (int dy = y; dy < endY; dy++)
@ -506,7 +507,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); 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++; cdfX++;
} }

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

@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -59,21 +62,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
parallelOptions, parallelOptions,
x => x =>
{ {
using (System.Buffers.IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (System.Buffers.IMemoryOwner<int> histogramBufferCopy = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner<int> histogramBufferCopy = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (System.Buffers.IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (System.Buffers.IMemoryOwner<TPixel> pixelRowBuffer = memoryAllocator.Allocate<TPixel>(tileWidth, AllocationOptions.Clean)) using (IMemoryOwner<TPixel> pixelRowBuffer = memoryAllocator.Allocate<TPixel>(tileWidth, AllocationOptions.Clean))
{ {
Span<int> histogram = histogramBuffer.GetSpan(); Span<int> histogram = histogramBuffer.GetSpan();
ref int histogramBase = ref MemoryMarshal.GetReference(histogram);
Span<int> histogramCopy = histogramBufferCopy.GetSpan(); 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(); Span<TPixel> pixelRow = pixelRowBuffer.GetSpan();
int maxHistIdx = 0; int maxHistIdx = 0;
// Build the histogram of grayscale values for the current tile. // Build the histogram of grayscale values for the current tile.
for (int dy = -halfTileWith; dy < halfTileWith; dy++) 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); int maxIdx = this.AddPixelsToHistogram(rowSpan, histogram, this.LuminanceLevels);
if (maxIdx > maxHistIdx) 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. // 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; float numberOfPixelsMinusCdfMin = pixeInTile - cdfMin;
// Map the current pixel to the new equalized value // Map the current pixel to the new equalized value
int luminance = GetLuminance(source[x, y], this.LuminanceLevels); 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)); 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. // 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 the histogram at the maximum index has changed to 0, search for the next smaller value.
if (luminance == maxHistIdx && histogram[luminance] == 0) if (luminance == maxHistIdx && histogram[luminance] == 0)
{ {
for (int j = luminance; j >= 0; j--) for (int j = luminance; j >= 0; j--)
{ {
maxHistIdx = j; maxHistIdx = j;
if (histogram[j] != 0) if (histogram[j] != 0)

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

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

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

@ -53,17 +53,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary> /// <summary>
/// Calculates the cumulative distribution function. /// Calculates the cumulative distribution function.
/// </summary> /// </summary>
/// <param name="cdf">The array holding the cdf.</param> /// <param name="cdfBase">The reference to the array holding the cdf.</param>
/// <param name="histogram">The histogram of the input image.</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> /// <param name="maxIdx">Index of the maximum of the histogram.</param>
/// <returns>The first none zero value of the cdf.</returns> /// <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 histSum = 0;
int cdfMin = 0; int cdfMin = 0;
bool cdfMinFound = false; bool cdfMinFound = false;
ref int cdfBase = ref MemoryMarshal.GetReference(cdf);
ref int histogramBase = ref MemoryMarshal.GetReference(histogram);
for (int i = 0; i <= maxIdx; i++) 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++) 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); 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++) for (int x = 0; x < 8; x++)
{ {
Rgba32 actual = image[x, y]; Rgba32 actual = image[x, y];
Assert.Equal(expected[y * 8 + x], actual.R); Assert.Equal(expected[(y * 8) + x], actual.R);
Assert.Equal(expected[y * 8 + x], actual.G); Assert.Equal(expected[(y * 8) + x], actual.G);
Assert.Equal(expected[y * 8 + x], actual.B); Assert.Equal(expected[(y * 8) + x], actual.B);
} }
} }
} }

Loading…
Cancel
Save