diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
index 4e1498ac76..eb6da5d043 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
@@ -12,7 +12,8 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{
///
- /// Applies an adaptive histogram equalization to the image.
+ /// Applies an adaptive histogram equalization to the image. The image is split up in tiles. For each tile a cumulative distribution function (cdf) is calculated.
+ /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated.
///
/// The pixel format.
internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor
@@ -55,52 +56,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int halfTileWidth = tileWidth / 2;
int halfTileHeight = tileHeight / 2;
- var cdfData = new CdfData[numTilesX, numTilesY];
using (System.Buffers.IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean))
using (System.Buffers.IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean))
{
Span histogram = histogramBuffer.GetSpan();
Span cdf = cdfBuffer.GetSpan();
- // The image is split up into square tiles of the size of the parameter GridSize.
- // For each tile the cumulative distribution function will be calculated.
+ // The image is split up into tiles. For each tile the cumulative distribution function will be calculated.
+ CdfData[,] cdfData = this.CalculateLookupTables(source, histogram, cdf, numTilesX, numTilesY, tileWidth, tileHeight);
+
int tileX = 0;
int tileY = 0;
- for (int y = 0; y < source.Height; y += tileHeight)
- {
- tileX = 0;
- for (int x = 0; x < source.Width; x += tileWidth)
- {
- histogram.Clear();
- cdf.Clear();
- int ylimit = Math.Min(y + tileHeight, source.Height);
- int xlimit = Math.Min(x + tileWidth, source.Width);
- for (int dy = y; dy < ylimit; dy++)
- {
- for (int dx = x; dx < xlimit; dx++)
- {
- int luminace = this.GetLuminance(source[dx, dy], this.LuminanceLevels);
- histogram[luminace]++;
- }
- }
-
- if (this.ClipHistogramEnabled)
- {
- this.ClipHistogram(histogram, this.ClipLimitPercentage, pixelsInTile);
- }
-
- int cdfMin = this.CalculateCdf(cdf, histogram, histogram.Length - 1);
- var currentCdf = new CdfData(cdf.ToArray(), cdfMin);
- cdfData[tileX, tileY] = currentCdf;
-
- tileX++;
- }
-
- tileY++;
- }
-
- tileX = 0;
- tileY = 0;
for (int y = halfTileHeight; y < source.Height - tileHeight; y += tileHeight)
{
tileX = 0;
@@ -128,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
float cdfRightTopLuminance = cdfData[xRight, yTop].RemapGreyValue(luminace, pixelsInTile);
float cdfLeftBottomLuminance = cdfData[xLeft, yBottom].RemapGreyValue(luminace, pixelsInTile);
float cdfRightBottomLuminance = cdfData[xRight, yBottom].RemapGreyValue(luminace, pixelsInTile);
- float luminanceEqualized = this.BilinearInterpolation(tilePosX, tilePosY, tilePosX / (float)(tileWidth - 1), ty, cdfLeftTopLuminance, cdfRightTopLuminance, cdfLeftBottomLuminance, cdfRightBottomLuminance);
+ float luminanceEqualized = this.BilinearInterpolation(tilePosX / (float)(tileWidth - 1), ty, cdfLeftTopLuminance, cdfRightTopLuminance, cdfLeftBottomLuminance, cdfRightBottomLuminance);
pixels[(dy * source.Width) + dx].PackFromVector4(new Vector4(luminanceEqualized));
tilePosX++;
@@ -145,11 +111,62 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
}
}
+ ///
+ /// Calculates the lookup tables for each tile of the image.
+ ///
+ /// The input image for which the tiles will be calculated.
+ /// Histogram buffer.
+ /// Buffer for calculating the cumulative distribution function.
+ /// Number of tiles in the X Direction.
+ /// Number of tiles in Y Direction
+ /// Width in pixels of one tile.
+ /// Height in pixels of one tile.
+ /// All lookup tables for each tile in the image.
+ private CdfData[,] CalculateLookupTables(ImageFrame source, Span histogram, Span cdf, int numTilesX, int numTilesY, int tileWidth, int tileHeight)
+ {
+ var cdfData = new CdfData[numTilesX, numTilesY];
+ int pixelsInTile = tileWidth * tileHeight;
+ int tileX = 0;
+ int tileY = 0;
+ for (int y = 0; y < source.Height; y += tileHeight)
+ {
+ tileX = 0;
+ for (int x = 0; x < source.Width; x += tileWidth)
+ {
+ histogram.Clear();
+ cdf.Clear();
+ int ylimit = Math.Min(y + tileHeight, source.Height);
+ int xlimit = Math.Min(x + tileWidth, source.Width);
+ for (int dy = y; dy < ylimit; dy++)
+ {
+ for (int dx = x; dx < xlimit; dx++)
+ {
+ int luminace = this.GetLuminance(source[dx, dy], this.LuminanceLevels);
+ histogram[luminace]++;
+ }
+ }
+
+ if (this.ClipHistogramEnabled)
+ {
+ this.ClipHistogram(histogram, this.ClipLimitPercentage, pixelsInTile);
+ }
+
+ int cdfMin = this.CalculateCdf(cdf, histogram, histogram.Length - 1);
+ var currentCdf = new CdfData(cdf.ToArray(), cdfMin);
+ cdfData[tileX, tileY] = currentCdf;
+
+ tileX++;
+ }
+
+ tileY++;
+ }
+
+ return cdfData;
+ }
+
///
/// Bilinear interpolation between four tiles.
///
- /// X position.
- /// Y position.
/// The interpolation value in x direction in the range of [0, 1].
/// The interpolation value in y direction in the range of [0, 1].
/// Luminance from top left tile.
@@ -157,7 +174,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// Luminance from left bottom tile.
/// Luminance from right bottom tile.
/// Interpolated Luminance.
- private float BilinearInterpolation(int x, int y, float tx, float ty, float lt, float rt, float lb, float rb)
+ private float BilinearInterpolation(float tx, float ty, float lt, float rt, float lb, float rb)
{
return this.LinearInterpolation(this.LinearInterpolation(lt, rt, tx), this.LinearInterpolation(lb, rb, tx), ty);
}
diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
index 63b8bc29cf..03db1d00db 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
@@ -12,7 +12,7 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{
///
- /// Applies an adaptive histogram equalization to the image.
+ /// Applies an adaptive histogram equalization to the image using an sliding window approach.
///
/// The pixel format.
internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor