Browse Source

gridsize and cliplimit are now parameters of the constructor

pull/673/head
popow 8 years ago
parent
commit
024873c8f0
  1. 59
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs

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

@ -21,13 +21,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary>
/// Initializes a new instance of the <see cref="AdaptiveHistEqualizationProcessor{TPixel}"/> class.
/// </summary>
/// <param name="gridSize">The grid size of the adaptive histogram equalization.</param>
/// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="luminanceLevels">The number of different luminance levels. Typical values are 256 for 8-bit grayscale images
/// or 65536 for 16-bit grayscale images.</param>
public AdaptiveHistEqualizationProcessor(int luminanceLevels)
public AdaptiveHistEqualizationProcessor(int gridSize, int clipLimit, int luminanceLevels)
: base(luminanceLevels)
{
this.GridSize = gridSize;
this.ClipLimit = clipLimit;
}
/// <summary>
/// Gets the size of the grid for the adaptive histogram equalization.
/// </summary>
public int GridSize { get; }
/// <summary>
/// Gets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.
/// </summary>
public int ClipLimit { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
@ -35,10 +49,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int numberOfPixels = source.Width * source.Height;
Span<TPixel> pixels = source.GetPixelSpan();
int gridSize = 64;
int pixelsInGrid = gridSize * gridSize;
int contrastLimit = 5;
int halfGridSize = gridSize / 2;
int pixelsInGrid = this.GridSize * this.GridSize;
int halfGridSize = this.GridSize / 2;
using (IBuffer<int> histogramBuffer = memoryAllocator.AllocateClean<int>(this.LuminanceLevels))
using (IBuffer<int> histogramBufferCopy = memoryAllocator.AllocateClean<int>(this.LuminanceLevels))
using (IBuffer<int> cdfBuffer = memoryAllocator.AllocateClean<int>(this.LuminanceLevels))
@ -53,25 +65,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
histogram.Clear();
// Build the histogram of grayscale values for the current grid.
for (int dy = 0; dy < gridSize; dy++)
for (int dy = -halfGridSize; dy < halfGridSize; dy++)
{
Span<TPixel> rowSpan = this.GetPixelRow(source, x - halfGridSize, dy, gridSize);
Span<TPixel> rowSpan = this.GetPixelRow(source, x - halfGridSize, dy, this.GridSize);
this.AddPixelsTooHistogram(rowSpan, histogram, this.LuminanceLevels);
}
for (int y = 1; y < source.Height; y++)
for (int y = 0; y < source.Height; y++)
{
// Remove top most row from the histogram, mirroring rows which exceeds the borders
Span<TPixel> rowSpan = this.GetPixelRow(source, x - halfGridSize, y - halfGridSize - 1, gridSize);
this.RemovePixelsFromHistogram(rowSpan, histogram, this.LuminanceLevels);
// Add new bottom row to the histogram, mirroring rows which exceeds the borders
rowSpan = this.GetPixelRow(source, x - halfGridSize, y + halfGridSize - 1, gridSize);
this.AddPixelsTooHistogram(rowSpan, histogram, this.LuminanceLevels);
// Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration
histogram.CopyTo(histogramCopy);
this.ClipHistogram(histogramCopy, gridSize, contrastLimit);
this.ClipHistogram(histogramCopy, this.ClipLimit);
cdf.Clear();
int cdfMin = this.CalculateCdf(cdf, histogramCopy);
double numberOfPixelsMinusCdfMin = (double)(pixelsInGrid - cdfMin);
@ -80,6 +84,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int luminance = this.GetLuminance(pixels[(y * source.Width) + x], this.LuminanceLevels);
double luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin;
targetPixels[x, y].PackFromVector4(new Vector4((float)luminanceEqualized));
// Remove top most row from the histogram, mirroring rows which exceeds the borders
Span<TPixel> rowSpan = this.GetPixelRow(source, x - halfGridSize, y - halfGridSize, this.GridSize);
this.RemovePixelsFromHistogram(rowSpan, histogram, this.LuminanceLevels);
// Add new bottom row to the histogram, mirroring rows which exceeds the borders
rowSpan = this.GetPixelRow(source, x - halfGridSize, y + halfGridSize, this.GridSize);
this.AddPixelsTooHistogram(rowSpan, histogram, this.LuminanceLevels);
}
}
@ -145,14 +157,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// Clipping the histogram is meant to reduce this effect, by cutting of histogram bin's which exceed a certain amount and redistribute
/// the values over the clip limit to all other bins equally.
/// </summary>
/// <param name="histogram">The histogram to apply the clipping</param>
/// <param name="gridSize">The grid size of the AHE</param>
/// <param name="contrastLimit">The contrast limit. Defaults to 5.</param>
/// <returns>The number of pixels redistributed to all other bins</returns>
private int ClipHistogram(Span<int> histogram, int gridSize, int contrastLimit = 5)
/// <param name="histogram">The histogram to apply the clipping.</param>
/// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <returns>The number of pixels redistributed to all other bins.</returns>
private int ClipHistogram(Span<int> histogram, int clipLimit)
{
int clipLimit = (contrastLimit * (gridSize * gridSize)) / this.LuminanceLevels;
int sumOverClip = 0;
for (int i = 0; i < histogram.Length; i++)
{
@ -173,7 +182,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// Re-Applying the clipping until this effect no longer occurs.
while (addToEachBin > 1)
{
addToEachBin = this.ClipHistogram(histogram, gridSize, contrastLimit);
addToEachBin = this.ClipHistogram(histogram, clipLimit);
}
return addToEachBin;

Loading…
Cancel
Save