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