diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
index 5b659fa322..b63026df83 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
@@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
tileX = 0;
for (int dx = x; dx < xEnd; dx++)
{
- float luminanceEqualized = this.InterpolateBetweenTiles(source[dx, dy], cdfData, dx, dy, tileX, tileY, cdfX, cdfY, tileWidth, tileHeight, pixelsInTile);
+ float luminanceEqualized = this.InterpolateBetweenFourTiles(source[dx, dy], cdfData, tileX, tileY, cdfX, cdfY, tileWidth, tileHeight, pixelsInTile);
pixels[(dy * source.Width) + dx].PackFromVector4(new Vector4(luminanceEqualized));
tileX++;
}
@@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// fix left column
cdfX = 0;
cdfY = 0;
- for (int y = 0; y < source.Height; y += tileHeight)
+ for (int y = halfTileWidth; y < source.Height - halfTileWidth; y += tileHeight)
{
int yLimit = Math.Min(y + tileHeight, source.Height - 1);
tileY = 0;
@@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
tileX = 0;
for (int dx = 0; dx < halfTileWidth; dx++)
{
- float luminanceEqualized = this.InterpolateBetweenTiles(source[dx, dy], cdfData, dx, dy, tileX, tileY, cdfX, cdfY, tileWidth, tileHeight, pixelsInTile);
+ float luminanceEqualized = this.InterpolateBetweenTwoTiles(source[dx, dy], cdfData[cdfX, cdfY], cdfData[cdfX, cdfY + 1], tileY, tileHeight, pixelsInTile);
pixels[(dy * source.Width) + dx].PackFromVector4(new Vector4(luminanceEqualized));
tileX++;
}
@@ -117,9 +117,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
}
// fix right column
- cdfX = this.Tiles - 2;
+ cdfX = this.Tiles - 1;
cdfY = 0;
- for (int y = 0; y < source.Height; y += tileHeight)
+ for (int y = halfTileWidth; y < source.Height - halfTileWidth; y += tileHeight)
{
int yLimit = Math.Min(y + tileHeight, source.Height - 1);
tileY = 0;
@@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
tileX = halfTileWidth;
for (int dx = source.Width - halfTileWidth; dx < source.Width; dx++)
{
- float luminanceEqualized = this.InterpolateBetweenTiles(source[dx, dy], cdfData, dx, dy, tileX, tileY, cdfX, cdfY, tileWidth, tileHeight, pixelsInTile);
+ float luminanceEqualized = this.InterpolateBetweenTwoTiles(source[dx, dy], cdfData[cdfX, cdfY], cdfData[cdfX, cdfY + 1], tileY, tileHeight, pixelsInTile);
pixels[(dy * source.Width) + dx].PackFromVector4(new Vector4(luminanceEqualized));
tileX++;
}
@@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// fix top row
cdfX = 0;
cdfY = 0;
- for (int x = 0; x < source.Width; x += tileWidth)
+ for (int x = halfTileWidth; x < source.Width - halfTileWidth; x += tileWidth)
{
tileY = 0;
for (int dy = 0; dy < halfTileHeight; dy++)
@@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int xLimit = Math.Min(x + tileWidth, source.Width - 1);
for (int dx = x; dx < xLimit; dx++)
{
- float luminanceEqualized = this.InterpolateBetweenTiles(source[dx, dy], cdfData, dx, dy, tileX, tileY, cdfX, cdfY, tileWidth, tileHeight, pixelsInTile);
+ float luminanceEqualized = this.InterpolateBetweenTwoTiles(source[dx, dy], cdfData[cdfX, cdfY], cdfData[cdfX + 1, cdfY], tileX, tileWidth, pixelsInTile);
pixels[(dy * source.Width) + dx].PackFromVector4(new Vector4(luminanceEqualized));
tileX++;
}
@@ -164,8 +164,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// fix bottom row
cdfX = 0;
- cdfY = 0;
- for (int x = 0; x < source.Width; x += tileWidth)
+ cdfY = this.Tiles - 1;
+ for (int x = halfTileWidth; x < source.Width - halfTileWidth; x += tileWidth)
{
tileY = 0;
for (int dy = source.Height - halfTileHeight; dy < source.Height; dy++)
@@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int xLimit = Math.Min(x + tileWidth, source.Width - 1);
for (int dx = x; dx < xLimit; dx++)
{
- float luminanceEqualized = this.InterpolateBetweenTiles(source[dx, dy], cdfData, dx, dy, tileX, tileY, cdfX, cdfY, tileWidth, tileHeight, pixelsInTile);
+ float luminanceEqualized = this.InterpolateBetweenTwoTiles(source[dx, dy], cdfData[cdfX, cdfY], cdfData[cdfX + 1, cdfY], tileX, tileWidth, pixelsInTile);
pixels[(dy * source.Width) + dx].PackFromVector4(new Vector4(luminanceEqualized));
tileX++;
}
@@ -188,12 +188,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
}
///
- /// Interpolates between four adjacent tiles.
+ /// Bilinear interpolation between four adjacent tiles.
///
/// The pixel to remap the grey value from.
/// The pre-computed lookup tables to remap the grey values for each tiles.
- /// X index in the image.
- /// Y index in the image.
/// X position inside the tile.
/// Y position inside the tile.
/// X index of the top left lookup table to use.
@@ -202,7 +200,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// Height of one tile in pixels.
/// Amount of pixels in one tile.
/// A re-mapped grey value.
- private float InterpolateBetweenTiles(TPixel sourcePixel, CdfData[,] cdfData, int dx, int dy, int tileX, int tileY, int cdfX, int cdfY, int tileWidth, int tileHeight, int pixelsInTile)
+ private float InterpolateBetweenFourTiles(TPixel sourcePixel, CdfData[,] cdfData, int tileX, int tileY, int cdfX, int cdfY, int tileWidth, int tileHeight, int pixelsInTile)
{
int luminace = this.GetLuminance(sourcePixel, this.LuminanceLevels);
float tx = tileX / (float)(tileWidth - 1);
@@ -222,6 +220,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return luminanceEqualized;
}
+ ///
+ /// Linear interpolation between two tiles.
+ ///
+ /// The pixel to remap the grey value from.
+ /// First lookup table.
+ /// Second lookup table.
+ /// Position inside the tile.
+ /// Width of the tile.
+ /// Pixels in one tile.
+ /// A re-mapped grey value.
+ private float InterpolateBetweenTwoTiles(TPixel sourcePixel, CdfData cdfData1, CdfData cdfData2, int tilePos, int tileWidth, int pixelsInTile)
+ {
+ int luminace = this.GetLuminance(sourcePixel, this.LuminanceLevels);
+ float tx = tilePos / (float)(tileWidth - 1);
+
+ float cdfLuminance1 = cdfData1.RemapGreyValue(luminace, pixelsInTile);
+ float cdfLuminance2 = cdfData2.RemapGreyValue(luminace, pixelsInTile);
+ float luminanceEqualized = this.LinearInterpolation(cdfLuminance1, cdfLuminance2, tx);
+
+ return luminanceEqualized;
+ }
+
///
/// Calculates the lookup tables for each tile of the image.
///
@@ -302,6 +322,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return left + ((right - left) * t);
}
+ ///
+ /// Lookup table for remapping the grey values of one tile.
+ ///
private class CdfData
{
///