|
|
@ -45,23 +45,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
/// <inheritdoc/>
|
|
|
/// <inheritdoc/>
|
|
|
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) |
|
|
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) |
|
|
{ |
|
|
{ |
|
|
MemoryAllocator memoryAllocator = configuration.MemoryAllocator; |
|
|
|
|
|
int numberOfPixels = source.Width * source.Height; |
|
|
int numberOfPixels = source.Width * source.Height; |
|
|
|
|
|
|
|
|
int tileWidth = Convert.ToInt32(Math.Ceiling(source.Width / (double)this.Tiles)); |
|
|
int tileWidth = Convert.ToInt32(Math.Ceiling(source.Width / (double)this.Tiles)); |
|
|
int tileHeight = Convert.ToInt32(Math.Ceiling(source.Height / (double)this.Tiles)); |
|
|
int tileHeight = Convert.ToInt32(Math.Ceiling(source.Height / (double)this.Tiles)); |
|
|
int pixelsInTile = tileWidth * tileHeight; |
|
|
int pixelsInTile = tileWidth * tileHeight; |
|
|
int halfTileWidth = tileWidth / 2; |
|
|
int halfTileWidth = tileWidth / 2; |
|
|
int halfTileHeight = tileHeight / 2; |
|
|
int halfTileHeight = tileHeight / 2; |
|
|
|
|
|
|
|
|
using (System.Buffers.IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean)) |
|
|
|
|
|
using (System.Buffers.IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean)) |
|
|
|
|
|
{ |
|
|
|
|
|
Span<int> histogram = histogramBuffer.GetSpan(); |
|
|
|
|
|
Span<int> cdf = cdfBuffer.GetSpan(); |
|
|
|
|
|
|
|
|
|
|
|
// The image is split up into tiles. 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, this.Tiles, this.Tiles, tileWidth, tileHeight); |
|
|
CdfData[,] cdfData = this.CalculateLookupTables(source, configuration, this.Tiles, this.Tiles, tileWidth, tileHeight); |
|
|
|
|
|
|
|
|
var tileYStartPositions = new List<(int y, int cdfY)>(); |
|
|
var tileYStartPositions = new List<(int y, int cdfY)>(); |
|
|
int cdfY = 0; |
|
|
int cdfY = 0; |
|
|
@ -128,7 +120,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
// right bottom corner
|
|
|
// 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); |
|
|
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); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Processes the part of a corner tile which was previously left out. It consists of 1 / 4 of a tile and does not need interpolation.
|
|
|
/// Processes the part of a corner tile which was previously left out. It consists of 1 / 4 of a tile and does not need interpolation.
|
|
|
@ -286,28 +277,68 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
return luminanceEqualized; |
|
|
return luminanceEqualized; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Bilinear interpolation between four tiles.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="tx">The interpolation value in x direction in the range of [0, 1].</param>
|
|
|
|
|
|
/// <param name="ty">The interpolation value in y direction in the range of [0, 1].</param>
|
|
|
|
|
|
/// <param name="lt">Luminance from top left tile.</param>
|
|
|
|
|
|
/// <param name="rt">Luminance from right top tile.</param>
|
|
|
|
|
|
/// <param name="lb">Luminance from left bottom tile.</param>
|
|
|
|
|
|
/// <param name="rb">Luminance from right bottom tile.</param>
|
|
|
|
|
|
/// <returns>Interpolated Luminance.</returns>
|
|
|
|
|
|
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); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Linear interpolation between two grey values.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="left">The left value.</param>
|
|
|
|
|
|
/// <param name="right">The right value.</param>
|
|
|
|
|
|
/// <param name="t">The interpolation value between the two values in the range of [0, 1].</param>
|
|
|
|
|
|
/// <returns>The interpolated value.</returns>
|
|
|
|
|
|
private float LinearInterpolation(float left, float right, float t) |
|
|
|
|
|
{ |
|
|
|
|
|
return left + ((right - left) * t); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Calculates the lookup tables for each tile of the image.
|
|
|
/// Calculates the lookup tables for each tile of the image.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
/// <param name="source">The input image for which the tiles will be calculated.</param>
|
|
|
/// <param name="source">The input image for which the tiles will be calculated.</param>
|
|
|
/// <param name="histogram">Histogram buffer.</param>
|
|
|
/// <param name="configuration">The configuration.</param>
|
|
|
/// <param name="cdf">Buffer for calculating the cumulative distribution function.</param>
|
|
|
|
|
|
/// <param name="numTilesX">Number of tiles in the X Direction.</param>
|
|
|
/// <param name="numTilesX">Number of tiles in the X Direction.</param>
|
|
|
/// <param name="numTilesY">Number of tiles in Y Direction</param>
|
|
|
/// <param name="numTilesY">Number of tiles in Y Direction.</param>
|
|
|
/// <param name="tileWidth">Width in pixels of one tile.</param>
|
|
|
/// <param name="tileWidth">Width in pixels of one tile.</param>
|
|
|
/// <param name="tileHeight">Height in pixels of one tile.</param>
|
|
|
/// <param name="tileHeight">Height in pixels of one tile.</param>
|
|
|
/// <returns>All lookup tables for each tile in the image.</returns>
|
|
|
/// <returns>All lookup tables for each tile in the image.</returns>
|
|
|
private CdfData[,] CalculateLookupTables(ImageFrame<TPixel> source, Span<int> histogram, Span<int> cdf, int numTilesX, int numTilesY, int tileWidth, int tileHeight) |
|
|
private CdfData[,] CalculateLookupTables(ImageFrame<TPixel> source, Configuration configuration, int numTilesX, int numTilesY, int tileWidth, int tileHeight) |
|
|
{ |
|
|
{ |
|
|
|
|
|
MemoryAllocator memoryAllocator = configuration.MemoryAllocator; |
|
|
var cdfData = new CdfData[numTilesX, numTilesY]; |
|
|
var cdfData = new CdfData[numTilesX, numTilesY]; |
|
|
int pixelsInTile = tileWidth * tileHeight; |
|
|
int pixelsInTile = tileWidth * tileHeight; |
|
|
int tileX = 0; |
|
|
|
|
|
int tileY = 0; |
|
|
var tileYStartPositions = new List<(int y, int cdfY)>(); |
|
|
|
|
|
int cdfY = 0; |
|
|
for (int y = 0; y < source.Height; y += tileHeight) |
|
|
for (int y = 0; y < source.Height; y += tileHeight) |
|
|
{ |
|
|
{ |
|
|
tileX = 0; |
|
|
tileYStartPositions.Add((y, cdfY)); |
|
|
|
|
|
cdfY++; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Parallel.ForEach(tileYStartPositions, new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, (tileYStartPosition) => |
|
|
|
|
|
{ |
|
|
|
|
|
using (System.Buffers.IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean)) |
|
|
|
|
|
using (System.Buffers.IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean)) |
|
|
|
|
|
{ |
|
|
|
|
|
int cdfX = 0; |
|
|
|
|
|
int y = tileYStartPosition.y; |
|
|
for (int x = 0; x < source.Width; x += tileWidth) |
|
|
for (int x = 0; x < source.Width; x += tileWidth) |
|
|
{ |
|
|
{ |
|
|
|
|
|
Span<int> histogram = histogramBuffer.GetSpan(); |
|
|
|
|
|
Span<int> cdf = cdfBuffer.GetSpan(); |
|
|
histogram.Clear(); |
|
|
histogram.Clear(); |
|
|
cdf.Clear(); |
|
|
cdf.Clear(); |
|
|
int ylimit = Math.Min(y + tileHeight, source.Height); |
|
|
int ylimit = Math.Min(y + tileHeight, source.Height); |
|
|
@ -328,44 +359,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
|
|
|
|
|
|
int cdfMin = this.CalculateCdf(cdf, histogram, histogram.Length - 1); |
|
|
int cdfMin = this.CalculateCdf(cdf, histogram, histogram.Length - 1); |
|
|
var currentCdf = new CdfData(cdf.ToArray(), cdfMin); |
|
|
var currentCdf = new CdfData(cdf.ToArray(), cdfMin); |
|
|
cdfData[tileX, tileY] = currentCdf; |
|
|
cdfData[cdfX, tileYStartPosition.cdfY] = currentCdf; |
|
|
|
|
|
|
|
|
tileX++; |
|
|
cdfX++; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
tileY++; |
|
|
cdfY++; |
|
|
} |
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
return cdfData; |
|
|
return cdfData; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Bilinear interpolation between four tiles.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="tx">The interpolation value in x direction in the range of [0, 1].</param>
|
|
|
|
|
|
/// <param name="ty">The interpolation value in y direction in the range of [0, 1].</param>
|
|
|
|
|
|
/// <param name="lt">Luminance from top left tile.</param>
|
|
|
|
|
|
/// <param name="rt">Luminance from right top tile.</param>
|
|
|
|
|
|
/// <param name="lb">Luminance from left bottom tile.</param>
|
|
|
|
|
|
/// <param name="rb">Luminance from right bottom tile.</param>
|
|
|
|
|
|
/// <returns>Interpolated Luminance.</returns>
|
|
|
|
|
|
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); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Linear interpolation between two grey values.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="left">The left value.</param>
|
|
|
|
|
|
/// <param name="right">The right value.</param>
|
|
|
|
|
|
/// <param name="t">The interpolation value between the two values in the range of [0, 1].</param>
|
|
|
|
|
|
/// <returns>The interpolated value.</returns>
|
|
|
|
|
|
private float LinearInterpolation(float left, float right, float t) |
|
|
|
|
|
{ |
|
|
|
|
|
return left + ((right - left) * t); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Lookup table for remapping the grey values of one tile.
|
|
|
/// Lookup table for remapping the grey values of one tile.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
|