diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
index 167a10579c..232bb1b0a7 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
@@ -45,89 +45,80 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
///
protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
{
- MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
int numberOfPixels = source.Width * source.Height;
-
int tileWidth = Convert.ToInt32(Math.Ceiling(source.Width / (double)this.Tiles));
int tileHeight = Convert.ToInt32(Math.Ceiling(source.Height / (double)this.Tiles));
int pixelsInTile = tileWidth * tileHeight;
int halfTileWidth = tileWidth / 2;
int halfTileHeight = tileHeight / 2;
- 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 tiles. For each tile the cumulative distribution function will be calculated.
+ CdfData[,] cdfData = this.CalculateLookupTables(source, configuration, this.Tiles, this.Tiles, tileWidth, tileHeight);
- // The image is split up into tiles. For each tile the cumulative distribution function will be calculated.
- CdfData[,] cdfData = this.CalculateLookupTables(source, histogram, cdf, this.Tiles, this.Tiles, tileWidth, tileHeight);
+ var tileYStartPositions = new List<(int y, int cdfY)>();
+ int cdfY = 0;
+ for (int y = halfTileHeight; y < source.Height - halfTileHeight; y += tileHeight)
+ {
+ tileYStartPositions.Add((y, cdfY));
+ cdfY++;
+ }
- var tileYStartPositions = new List<(int y, int cdfY)>();
- int cdfY = 0;
- for (int y = halfTileHeight; y < source.Height - halfTileHeight; y += tileHeight)
- {
- tileYStartPositions.Add((y, cdfY));
- cdfY++;
- }
+ Parallel.ForEach(tileYStartPositions, new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, (tileYStartPosition) =>
+ {
+ int cdfX = 0;
+ int tileX = 0;
+ int tileY = 0;
+ int y = tileYStartPosition.y;
- Parallel.ForEach(tileYStartPositions, new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, (tileYStartPosition) =>
+ cdfX = 0;
+ for (int x = halfTileWidth; x < source.Width - halfTileWidth; x += tileWidth)
{
- int cdfX = 0;
- int tileX = 0;
- int tileY = 0;
- int y = tileYStartPosition.y;
-
- cdfX = 0;
- for (int x = halfTileWidth; x < source.Width - halfTileWidth; x += tileWidth)
+ tileY = 0;
+ int yEnd = Math.Min(y + tileHeight, source.Height);
+ int xEnd = Math.Min(x + tileWidth, source.Width);
+ for (int dy = y; dy < yEnd; dy++)
{
- tileY = 0;
- int yEnd = Math.Min(y + tileHeight, source.Height);
- int xEnd = Math.Min(x + tileWidth, source.Width);
- for (int dy = y; dy < yEnd; dy++)
+ Span pixelRow = source.GetPixelRowSpan(dy);
+ tileX = 0;
+ for (int dx = x; dx < xEnd; dx++)
{
- Span pixelRow = source.GetPixelRowSpan(dy);
- tileX = 0;
- for (int dx = x; dx < xEnd; dx++)
- {
- float luminanceEqualized = this.InterpolateBetweenFourTiles(source[dx, dy], cdfData, tileX, tileY, cdfX, tileYStartPosition.cdfY, tileWidth, tileHeight, pixelsInTile);
- pixelRow[dx].FromVector4(new Vector4(luminanceEqualized));
- tileX++;
- }
-
- tileY++;
+ float luminanceEqualized = this.InterpolateBetweenFourTiles(source[dx, dy], cdfData, tileX, tileY, cdfX, tileYStartPosition.cdfY, tileWidth, tileHeight, pixelsInTile);
+ pixelRow[dx].FromVector4(new Vector4(luminanceEqualized));
+ tileX++;
}
- cdfX++;
+ tileY++;
}
- });
- Span pixels = source.GetPixelSpan();
+ cdfX++;
+ }
+ });
+
+ Span pixels = source.GetPixelSpan();
- // fix left column
- this.ProcessBorderColumn(source, pixels, cdfData, 0, tileWidth, tileHeight, xStart: 0, xEnd: halfTileWidth);
+ // fix left column
+ this.ProcessBorderColumn(source, pixels, cdfData, 0, tileWidth, tileHeight, xStart: 0, xEnd: halfTileWidth);
- // fix right column
- this.ProcessBorderColumn(source, pixels, cdfData, this.Tiles - 1, tileWidth, tileHeight, xStart: source.Width - halfTileWidth, xEnd: source.Width);
+ // fix right column
+ this.ProcessBorderColumn(source, pixels, cdfData, this.Tiles - 1, tileWidth, tileHeight, xStart: source.Width - halfTileWidth, xEnd: source.Width);
- // fix top row
- this.ProcessBorderRow(source, pixels, cdfData, 0, tileWidth, tileHeight, yStart: 0, yEnd: halfTileHeight);
+ // fix top row
+ this.ProcessBorderRow(source, pixels, cdfData, 0, tileWidth, tileHeight, yStart: 0, yEnd: halfTileHeight);
- // fix bottom row
- this.ProcessBorderRow(source, pixels, cdfData, this.Tiles - 1, tileWidth, tileHeight, yStart: source.Height - halfTileHeight, yEnd: source.Height);
+ // fix bottom row
+ this.ProcessBorderRow(source, pixels, cdfData, this.Tiles - 1, tileWidth, tileHeight, yStart: source.Height - halfTileHeight, yEnd: source.Height);
- // left top corner
- this.ProcessCornerTile(source, pixels, cdfData[0, 0], xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, pixelsInTile: pixelsInTile);
+ // left top corner
+ this.ProcessCornerTile(source, pixels, cdfData[0, 0], xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, pixelsInTile: pixelsInTile);
- // left bottom corner
- this.ProcessCornerTile(source, pixels, cdfData[0, this.Tiles - 1], xStart: 0, xEnd: halfTileWidth, yStart: source.Height - halfTileHeight, yEnd: source.Height, pixelsInTile: pixelsInTile);
+ // left bottom corner
+ this.ProcessCornerTile(source, pixels, cdfData[0, this.Tiles - 1], xStart: 0, xEnd: halfTileWidth, yStart: source.Height - halfTileHeight, yEnd: source.Height, pixelsInTile: pixelsInTile);
- // right top corner
- this.ProcessCornerTile(source, pixels, cdfData[this.Tiles - 1, 0], xStart: source.Width - halfTileWidth, xEnd: source.Width, yStart: 0, yEnd: halfTileHeight, pixelsInTile: pixelsInTile);
+ // right top corner
+ this.ProcessCornerTile(source, pixels, cdfData[this.Tiles - 1, 0], xStart: source.Width - halfTileWidth, xEnd: source.Width, yStart: 0, yEnd: halfTileHeight, pixelsInTile: pixelsInTile);
- // right bottom corner
- this.ProcessCornerTile(source, pixels, cdfData[this.Tiles - 1, this.Tiles - 1], xStart: source.Width - halfTileWidth, xEnd: source.Width, yStart: source.Height - halfTileHeight, yEnd: source.Height, pixelsInTile: pixelsInTile);
- }
+ // right bottom corner
+ this.ProcessCornerTile(source, pixels, cdfData[this.Tiles - 1, this.Tiles - 1], xStart: source.Width - halfTileWidth, xEnd: source.Width, yStart: source.Height - halfTileHeight, yEnd: source.Height, pixelsInTile: pixelsInTile);
}
///
@@ -286,59 +277,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return luminanceEqualized;
}
- ///
- /// 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.
///
@@ -366,6 +304,73 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return left + ((right - left) * t);
}
+ ///
+ /// Calculates the lookup tables for each tile of the image.
+ ///
+ /// The input image for which the tiles will be calculated.
+ /// The configuration.
+ /// 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, Configuration configuration, int numTilesX, int numTilesY, int tileWidth, int tileHeight)
+ {
+ MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
+ var cdfData = new CdfData[numTilesX, numTilesY];
+ int pixelsInTile = tileWidth * tileHeight;
+
+ var tileYStartPositions = new List<(int y, int cdfY)>();
+ int cdfY = 0;
+ for (int y = 0; y < source.Height; y += tileHeight)
+ {
+ tileYStartPositions.Add((y, cdfY));
+ cdfY++;
+ }
+
+ Parallel.ForEach(tileYStartPositions, new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, (tileYStartPosition) =>
+ {
+ using (System.Buffers.IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean))
+ using (System.Buffers.IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean))
+ {
+ int cdfX = 0;
+ int y = tileYStartPosition.y;
+ for (int x = 0; x < source.Width; x += tileWidth)
+ {
+ Span histogram = histogramBuffer.GetSpan();
+ Span cdf = cdfBuffer.GetSpan();
+ 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[cdfX, tileYStartPosition.cdfY] = currentCdf;
+
+ cdfX++;
+ }
+
+ cdfY++;
+ }
+ });
+
+ return cdfData;
+ }
+
///
/// Lookup table for remapping the grey values of one tile.
///
diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
index c96b590248..66775520f2 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
@@ -178,8 +178,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
///
/// Adds a row of grey values to the histogram.
///
- /// The grey values to add
- /// The histogram
+ /// The grey values to add.
+ /// The histogram.
/// The number of different luminance levels.
/// The maximum index where a value was changed.
private int AddPixelsToHistogram(Span greyValues, Span histogram, int luminanceLevels)
@@ -201,8 +201,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
///
/// Removes a row of grey values from the histogram.
///
- /// The grey values to remove
- /// The histogram
+ /// The grey values to remove.
+ /// The histogram.
/// The number of different luminance levels.
/// The current maximum index of the histogram.
/// The (maybe changed) maximum index of the histogram.