mirror of https://github.com/SixLabors/ImageSharp
2 changed files with 148 additions and 2 deletions
@ -0,0 +1,146 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Numerics; |
||||
|
using SixLabors.ImageSharp.Advanced; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.Memory; |
||||
|
using SixLabors.Primitives; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
||||
|
{ |
||||
|
internal class AdaptiveHistEqualizationProcessor<TPixel> : HistogramEqualizationProcessor<TPixel> |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
public AdaptiveHistEqualizationProcessor(int luminanceLevels) |
||||
|
: base(luminanceLevels) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) |
||||
|
{ |
||||
|
MemoryAllocator memoryAllocator = configuration.MemoryAllocator; |
||||
|
int numberOfPixels = source.Width * source.Height; |
||||
|
Span<TPixel> pixels = source.GetPixelSpan(); |
||||
|
|
||||
|
int gridSize = 64; |
||||
|
int halfGridSize = gridSize / 2; |
||||
|
int[] histogram = new int[this.LuminanceLevels]; |
||||
|
int[] histogramCopy = new int[this.LuminanceLevels]; |
||||
|
int[] cdf = new int[this.LuminanceLevels]; |
||||
|
using (Buffer2D<TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D<TPixel>(source.Width, source.Height)) |
||||
|
{ |
||||
|
for (int y = halfGridSize; y < source.Height - halfGridSize; y++) |
||||
|
{ |
||||
|
var pixelsGrid = new List<TPixel>(); |
||||
|
int x = halfGridSize; |
||||
|
for (int dy = y - halfGridSize; dy < (y + halfGridSize); dy++) |
||||
|
{ |
||||
|
Span<TPixel> rowSpan = source.GetPixelRowSpan(dy); |
||||
|
for (int dx = x - halfGridSize; dx < (x + halfGridSize); dx++) |
||||
|
{ |
||||
|
pixelsGrid.Add(rowSpan[dx]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.CleanArray(histogram); |
||||
|
this.CleanArray(cdf); |
||||
|
this.CalcHistogram(pixelsGrid, histogram, this.LuminanceLevels); |
||||
|
|
||||
|
for (x = halfGridSize + 1; x < source.Width - halfGridSize; x++) |
||||
|
{ |
||||
|
// remove left most column from the histogram
|
||||
|
var leftMostColumnPixels = new List<TPixel>(); |
||||
|
int dx = x - halfGridSize - 1; |
||||
|
for (int dy = y - halfGridSize; dy < (y + halfGridSize); dy++) |
||||
|
{ |
||||
|
leftMostColumnPixels.Add(pixels[(dy * source.Width) + dx]); |
||||
|
} |
||||
|
|
||||
|
this.RemovePixelsFromHistogram(leftMostColumnPixels, histogram, this.LuminanceLevels); |
||||
|
|
||||
|
// add right column from the histogram
|
||||
|
dx = x + halfGridSize - 1; |
||||
|
var rightMostColumnPixels = new List<TPixel>(); |
||||
|
for (int dy = y - halfGridSize; dy < (y + halfGridSize); dy++) |
||||
|
{ |
||||
|
rightMostColumnPixels.Add(pixels[(dy * source.Width) + dx]); |
||||
|
} |
||||
|
|
||||
|
this.AddPixelsTooHistogram(rightMostColumnPixels, histogram, this.LuminanceLevels); |
||||
|
|
||||
|
histogram.AsSpan().CopyTo(histogramCopy); |
||||
|
this.ClipHistogram(histogramCopy, 5, gridSize); |
||||
|
int cdfMin = this.CalculateCdf(cdf, histogramCopy); |
||||
|
double numberOfPixelsMinusCdfMin = (double)(pixelsGrid.Count - cdfMin); |
||||
|
|
||||
|
int luminance = this.GetLuminance(pixels[(y * source.Width) + x], this.LuminanceLevels); |
||||
|
double luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin; |
||||
|
|
||||
|
targetPixels[x, y].PackFromVector4(new Vector4((float)luminanceEqualized)); |
||||
|
|
||||
|
this.CleanArray(cdf); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Buffer2D<TPixel>.SwapOrCopyContent(source.PixelBuffer, targetPixels); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ClipHistogram(Span<int> histogram, int contrastLimit, int gridSize) |
||||
|
{ |
||||
|
int clipLimit = (contrastLimit * (gridSize * gridSize)) / this.LuminanceLevels; |
||||
|
|
||||
|
int sumOverClip = 0; |
||||
|
for (int i = 0; i < histogram.Length; i++) |
||||
|
{ |
||||
|
if (histogram[i] > clipLimit) |
||||
|
{ |
||||
|
sumOverClip += histogram[i] - clipLimit; |
||||
|
histogram[i] = clipLimit; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int addToEachBin = (int)Math.Floor(sumOverClip / (double)this.LuminanceLevels); |
||||
|
for (int i = 0; i < histogram.Length; i++) |
||||
|
{ |
||||
|
histogram[i] += addToEachBin; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void AddPixelsTooHistogram(List<TPixel> greyValues, Span<int> histogram, int luminanceLevels) |
||||
|
{ |
||||
|
for (int i = 0; i < greyValues.Count; i++) |
||||
|
{ |
||||
|
int luminance = this.GetLuminance(greyValues[i], luminanceLevels); |
||||
|
histogram[luminance]++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void RemovePixelsFromHistogram(List<TPixel> greyValues, Span<int> histogram, int luminanceLevels) |
||||
|
{ |
||||
|
for (int i = 0; i < greyValues.Count; i++) |
||||
|
{ |
||||
|
int luminance = this.GetLuminance(greyValues[i], luminanceLevels); |
||||
|
histogram[luminance]--; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void CalcHistogram(List<TPixel> greyValues, Span<int> histogram, int luminanceLevels) |
||||
|
{ |
||||
|
for (int i = 0; i < greyValues.Count; i++) |
||||
|
{ |
||||
|
int luminance = this.GetLuminance(greyValues[i], luminanceLevels); |
||||
|
histogram[luminance]++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void CleanArray(Span<int> array) |
||||
|
{ |
||||
|
for (int i = 0; i < array.Length; i++) |
||||
|
{ |
||||
|
array[i] = 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue