diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
index ab73845f57..167a10579c 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
@@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Collections.Generic;
using System.Numerics;
+using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -26,11 +28,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// or 65536 for 16-bit grayscale images.
/// Indicating whether to clip the histogram bins at a specific value.
/// Histogram clip limit in percent of the total pixels in the grid. Histogram bins which exceed this limit, will be capped at this value.
- /// The number of tiles the image is split into (horizontal and vertically).
+ /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2.
public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
{
- Guard.MustBeGreaterThanOrEqualTo(tiles, 0, nameof(tiles));
+ Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles));
this.Tiles = tiles;
}
@@ -45,7 +47,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{
MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
int numberOfPixels = source.Width * source.Height;
- Span pixels = source.GetPixelSpan();
int tileWidth = Convert.ToInt32(Math.Ceiling(source.Width / (double)this.Tiles));
int tileHeight = Convert.ToInt32(Math.Ceiling(source.Height / (double)this.Tiles));
@@ -62,12 +63,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// 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);
- int cdfX = 0;
+ var tileYStartPositions = new List<(int y, int cdfY)>();
int cdfY = 0;
- int tileX = 0;
- int tileY = 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;
+
cdfX = 0;
for (int x = halfTileWidth; x < source.Width - halfTileWidth; x += tileWidth)
{
@@ -76,11 +86,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
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++)
{
- float luminanceEqualized = this.InterpolateBetweenFourTiles(source[dx, dy], cdfData, tileX, tileY, cdfX, cdfY, tileWidth, tileHeight, pixelsInTile);
- pixels[(dy * source.Width) + dx].FromVector4(new Vector4(luminanceEqualized));
+ float luminanceEqualized = this.InterpolateBetweenFourTiles(source[dx, dy], cdfData, tileX, tileY, cdfX, tileYStartPosition.cdfY, tileWidth, tileHeight, pixelsInTile);
+ pixelRow[dx].FromVector4(new Vector4(luminanceEqualized));
tileX++;
}
@@ -89,9 +100,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
cdfX++;
}
+ });
- cdfY++;
- }
+ Span pixels = source.GetPixelSpan();
// fix left column
this.ProcessBorderColumn(source, pixels, cdfData, 0, tileWidth, tileHeight, xStart: 0, xEnd: halfTileWidth);
diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
index dbdeb8c91b..c96b590248 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs
@@ -27,11 +27,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// or 65536 for 16-bit grayscale images.
/// Indicating whether to clip the histogram bins at a specific value.
/// Histogram clip limit in percent of the total pixels in the grid. Histogram bins which exceed this limit, will be capped at this value.
- /// The number of tiles the image is split into (horizontal and vertically).
+ /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2.
public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
{
- Guard.MustBeGreaterThanOrEqualTo(tiles, 0, nameof(tiles));
+ Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles));
this.Tiles = tiles;
}