|
|
@ -86,42 +86,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count), |
|
|
new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count), |
|
|
in operation); |
|
|
in operation); |
|
|
|
|
|
|
|
|
ref TPixel pixelsBase = ref source.GetPixelReference(0, 0); |
|
|
|
|
|
|
|
|
|
|
|
// Fix left column
|
|
|
// Fix left column
|
|
|
ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); |
|
|
ProcessBorderColumn(source, cdfData, 0, sourceHeight, this.Tiles, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); |
|
|
|
|
|
|
|
|
// Fix right column
|
|
|
// Fix right column
|
|
|
int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; |
|
|
int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; |
|
|
ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); |
|
|
ProcessBorderColumn(source, cdfData, this.Tiles - 1, sourceHeight, this.Tiles, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); |
|
|
|
|
|
|
|
|
// Fix top row
|
|
|
// Fix top row
|
|
|
ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, this.Tiles, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); |
|
|
ProcessBorderRow(source, cdfData, 0, sourceWidth, this.Tiles, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); |
|
|
|
|
|
|
|
|
// Fix bottom row
|
|
|
// Fix bottom row
|
|
|
int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; |
|
|
int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; |
|
|
ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, this.Tiles, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); |
|
|
ProcessBorderRow(source, cdfData, this.Tiles - 1, sourceWidth, this.Tiles, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); |
|
|
|
|
|
|
|
|
// Left top corner
|
|
|
// Left top corner
|
|
|
ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); |
|
|
ProcessCornerTile(source, cdfData, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); |
|
|
|
|
|
|
|
|
// Left bottom corner
|
|
|
// Left bottom corner
|
|
|
ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); |
|
|
ProcessCornerTile(source, cdfData, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); |
|
|
|
|
|
|
|
|
// Right top corner
|
|
|
// Right top corner
|
|
|
ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); |
|
|
ProcessCornerTile(source, cdfData, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); |
|
|
|
|
|
|
|
|
// Right bottom corner
|
|
|
// Right bottom corner
|
|
|
ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); |
|
|
ProcessCornerTile(source, cdfData, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <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.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
/// <param name="pixelsBase">The output pixels base reference.</param>
|
|
|
/// <param name="source">The source image.</param>
|
|
|
/// <param name="cdfData">The lookup table to remap the grey values.</param>
|
|
|
/// <param name="cdfData">The lookup table to remap the grey values.</param>
|
|
|
/// <param name="sourceWidth">The source image width.</param>
|
|
|
|
|
|
/// <param name="cdfX">The x-position in the CDF lookup map.</param>
|
|
|
/// <param name="cdfX">The x-position in the CDF lookup map.</param>
|
|
|
/// <param name="cdfY">The y-position in the CDF lookup map.</param>
|
|
|
/// <param name="cdfY">The y-position in the CDF lookup map.</param>
|
|
|
/// <param name="xStart">X start position.</param>
|
|
|
/// <param name="xStart">X start position.</param>
|
|
|
@ -133,9 +130,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
/// or 65536 for 16-bit grayscale images.
|
|
|
/// or 65536 for 16-bit grayscale images.
|
|
|
/// </param>
|
|
|
/// </param>
|
|
|
private static void ProcessCornerTile( |
|
|
private static void ProcessCornerTile( |
|
|
ref TPixel pixelsBase, |
|
|
ImageFrame<TPixel> source, |
|
|
CdfTileData cdfData, |
|
|
CdfTileData cdfData, |
|
|
int sourceWidth, |
|
|
|
|
|
int cdfX, |
|
|
int cdfX, |
|
|
int cdfY, |
|
|
int cdfY, |
|
|
int xStart, |
|
|
int xStart, |
|
|
@ -146,10 +142,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
{ |
|
|
{ |
|
|
for (int dy = yStart; dy < yEnd; dy++) |
|
|
for (int dy = yStart; dy < yEnd; dy++) |
|
|
{ |
|
|
{ |
|
|
int dyOffSet = dy * sourceWidth; |
|
|
Span<TPixel> rowSpan = source.GetPixelRowSpan(dy); |
|
|
for (int dx = xStart; dx < xEnd; dx++) |
|
|
for (int dx = xStart; dx < xEnd; dx++) |
|
|
{ |
|
|
{ |
|
|
ref TPixel pixel = ref Unsafe.Add(ref pixelsBase, dyOffSet + dx); |
|
|
ref TPixel pixel = ref rowSpan[dx]; |
|
|
float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, GetLuminance(pixel, luminanceLevels)); |
|
|
float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, GetLuminance(pixel, luminanceLevels)); |
|
|
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); |
|
|
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); |
|
|
} |
|
|
} |
|
|
@ -159,10 +155,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Processes a border column of the image which is half the size of the tile width.
|
|
|
/// Processes a border column of the image which is half the size of the tile width.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
/// <param name="pixelBase">The output pixels reference.</param>
|
|
|
/// <param name="source">The source image.</param>
|
|
|
/// <param name="cdfData">The pre-computed lookup tables to remap the grey values for each tiles.</param>
|
|
|
/// <param name="cdfData">The pre-computed lookup tables to remap the grey values for each tiles.</param>
|
|
|
/// <param name="cdfX">The X index of the lookup table to use.</param>
|
|
|
/// <param name="cdfX">The X index of the lookup table to use.</param>
|
|
|
/// <param name="sourceWidth">The source image width.</param>
|
|
|
|
|
|
/// <param name="sourceHeight">The source image height.</param>
|
|
|
/// <param name="sourceHeight">The source image height.</param>
|
|
|
/// <param name="tileCount">The number of vertical tiles.</param>
|
|
|
/// <param name="tileCount">The number of vertical tiles.</param>
|
|
|
/// <param name="tileHeight">The height of a tile.</param>
|
|
|
/// <param name="tileHeight">The height of a tile.</param>
|
|
|
@ -173,10 +168,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
/// or 65536 for 16-bit grayscale images.
|
|
|
/// or 65536 for 16-bit grayscale images.
|
|
|
/// </param>
|
|
|
/// </param>
|
|
|
private static void ProcessBorderColumn( |
|
|
private static void ProcessBorderColumn( |
|
|
ref TPixel pixelBase, |
|
|
ImageFrame<TPixel> source, |
|
|
CdfTileData cdfData, |
|
|
CdfTileData cdfData, |
|
|
int cdfX, |
|
|
int cdfX, |
|
|
int sourceWidth, |
|
|
|
|
|
int sourceHeight, |
|
|
int sourceHeight, |
|
|
int tileCount, |
|
|
int tileCount, |
|
|
int tileHeight, |
|
|
int tileHeight, |
|
|
@ -194,10 +188,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
int tileY = 0; |
|
|
int tileY = 0; |
|
|
for (int dy = y; dy < yLimit; dy++) |
|
|
for (int dy = y; dy < yLimit; dy++) |
|
|
{ |
|
|
{ |
|
|
int dyOffSet = dy * sourceWidth; |
|
|
Span<TPixel> rowSpan = source.GetPixelRowSpan(dy); |
|
|
for (int dx = xStart; dx < xEnd; dx++) |
|
|
for (int dx = xStart; dx < xEnd; dx++) |
|
|
{ |
|
|
{ |
|
|
ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); |
|
|
ref TPixel pixel = ref rowSpan[dx]; |
|
|
float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); |
|
|
float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); |
|
|
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); |
|
|
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); |
|
|
} |
|
|
} |
|
|
@ -213,7 +207,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Processes a border row of the image which is half of the size of the tile height.
|
|
|
/// Processes a border row of the image which is half of the size of the tile height.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
/// <param name="pixelBase">The output pixels base reference.</param>
|
|
|
/// <param name="source">The source image.</param>
|
|
|
/// <param name="cdfData">The pre-computed lookup tables to remap the grey values for each tiles.</param>
|
|
|
/// <param name="cdfData">The pre-computed lookup tables to remap the grey values for each tiles.</param>
|
|
|
/// <param name="cdfY">The Y index of the lookup table to use.</param>
|
|
|
/// <param name="cdfY">The Y index of the lookup table to use.</param>
|
|
|
/// <param name="sourceWidth">The source image width.</param>
|
|
|
/// <param name="sourceWidth">The source image width.</param>
|
|
|
@ -226,7 +220,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
/// or 65536 for 16-bit grayscale images.
|
|
|
/// or 65536 for 16-bit grayscale images.
|
|
|
/// </param>
|
|
|
/// </param>
|
|
|
private static void ProcessBorderRow( |
|
|
private static void ProcessBorderRow( |
|
|
ref TPixel pixelBase, |
|
|
ImageFrame<TPixel> source, |
|
|
CdfTileData cdfData, |
|
|
CdfTileData cdfData, |
|
|
int cdfY, |
|
|
int cdfY, |
|
|
int sourceWidth, |
|
|
int sourceWidth, |
|
|
@ -244,12 +238,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
{ |
|
|
{ |
|
|
for (int dy = yStart; dy < yEnd; dy++) |
|
|
for (int dy = yStart; dy < yEnd; dy++) |
|
|
{ |
|
|
{ |
|
|
int dyOffSet = dy * sourceWidth; |
|
|
Span<TPixel> rowSpan = source.GetPixelRowSpan(dy); |
|
|
int tileX = 0; |
|
|
int tileX = 0; |
|
|
int xLimit = Math.Min(x + tileWidth, sourceWidth - 1); |
|
|
int xLimit = Math.Min(x + tileWidth, sourceWidth - 1); |
|
|
for (int dx = x; dx < xLimit; dx++) |
|
|
for (int dx = x; dx < xLimit; dx++) |
|
|
{ |
|
|
{ |
|
|
ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); |
|
|
ref TPixel pixel = ref rowSpan[dx]; |
|
|
float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX + 1, cdfY, tileX, tileWidth, luminanceLevels); |
|
|
float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX + 1, cdfY, tileX, tileWidth, luminanceLevels); |
|
|
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); |
|
|
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); |
|
|
tileX++; |
|
|
tileX++; |
|
|
@ -410,8 +404,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
public void Invoke(in RowInterval rows) |
|
|
public void Invoke(in RowInterval rows) |
|
|
{ |
|
|
{ |
|
|
ref TPixel sourceBase = ref this.source.GetPixelReference(0, 0); |
|
|
|
|
|
|
|
|
|
|
|
for (int index = rows.Min; index < rows.Max; index++) |
|
|
for (int index = rows.Min; index < rows.Max; index++) |
|
|
{ |
|
|
{ |
|
|
(int y, int cdfY) tileYStartPosition = this.tileYStartPositions[index]; |
|
|
(int y, int cdfY) tileYStartPosition = this.tileYStartPositions[index]; |
|
|
@ -427,11 +419,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
int xEnd = Math.Min(x + this.tileWidth, this.sourceWidth); |
|
|
int xEnd = Math.Min(x + this.tileWidth, this.sourceWidth); |
|
|
for (int dy = y; dy < yEnd; dy++) |
|
|
for (int dy = y; dy < yEnd; dy++) |
|
|
{ |
|
|
{ |
|
|
int dyOffSet = dy * this.sourceWidth; |
|
|
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(dy); |
|
|
int tileX = 0; |
|
|
int tileX = 0; |
|
|
for (int dx = x; dx < xEnd; dx++) |
|
|
for (int dx = x; dx < xEnd; dx++) |
|
|
{ |
|
|
{ |
|
|
ref TPixel pixel = ref Unsafe.Add(ref sourceBase, dyOffSet + dx); |
|
|
ref TPixel pixel = ref rowSpan[dx]; |
|
|
float luminanceEqualized = InterpolateBetweenFourTiles( |
|
|
float luminanceEqualized = InterpolateBetweenFourTiles( |
|
|
pixel, |
|
|
pixel, |
|
|
this.cdfData, |
|
|
this.cdfData, |
|
|
@ -597,15 +589,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
public void Invoke(in RowInterval rows) |
|
|
public void Invoke(in RowInterval rows) |
|
|
{ |
|
|
{ |
|
|
ref TPixel sourceBase = ref this.source.GetPixelReference(0, 0); |
|
|
|
|
|
|
|
|
|
|
|
for (int index = rows.Min; index < rows.Max; index++) |
|
|
for (int index = rows.Min; index < rows.Max; index++) |
|
|
{ |
|
|
{ |
|
|
int cdfX = 0; |
|
|
int cdfX = 0; |
|
|
int cdfY = this.tileYStartPositions[index].cdfY; |
|
|
int cdfY = this.tileYStartPositions[index].cdfY; |
|
|
int y = this.tileYStartPositions[index].y; |
|
|
int y = this.tileYStartPositions[index].y; |
|
|
int endY = Math.Min(y + this.tileHeight, this.sourceHeight); |
|
|
int endY = Math.Min(y + this.tileHeight, this.sourceHeight); |
|
|
ref int cdfMinBase = ref MemoryMarshal.GetReference(this.cdfMinBuffer2D.GetRowSpan(cdfY)); |
|
|
Span<int> cdfMinSpan = this.cdfMinBuffer2D.GetRowSpan(cdfY); |
|
|
|
|
|
|
|
|
using IMemoryOwner<int> histogramBuffer = this.allocator.Allocate<int>(this.luminanceLevels); |
|
|
using IMemoryOwner<int> histogramBuffer = this.allocator.Allocate<int>(this.luminanceLevels); |
|
|
Span<int> histogram = histogramBuffer.GetSpan(); |
|
|
Span<int> histogram = histogramBuffer.GetSpan(); |
|
|
@ -620,10 +610,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
int xlimit = Math.Min(x + this.tileWidth, this.sourceWidth); |
|
|
int xlimit = Math.Min(x + this.tileWidth, this.sourceWidth); |
|
|
for (int dy = y; dy < endY; dy++) |
|
|
for (int dy = y; dy < endY; dy++) |
|
|
{ |
|
|
{ |
|
|
int dyOffset = dy * this.sourceWidth; |
|
|
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(dy); |
|
|
for (int dx = x; dx < xlimit; dx++) |
|
|
for (int dx = x; dx < xlimit; dx++) |
|
|
{ |
|
|
{ |
|
|
int luminance = GetLuminance(Unsafe.Add(ref sourceBase, dyOffset + dx), this.luminanceLevels); |
|
|
int luminance = GetLuminance(rowSpan[dx], this.luminanceLevels); |
|
|
histogram[luminance]++; |
|
|
histogram[luminance]++; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -633,7 +623,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization |
|
|
this.processor.ClipHistogram(histogram, this.processor.ClipLimit); |
|
|
this.processor.ClipHistogram(histogram, this.processor.ClipLimit); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Unsafe.Add(ref cdfMinBase, cdfX) = this.processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); |
|
|
cdfMinSpan[cdfX] += this.processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); |
|
|
|
|
|
|
|
|
cdfX++; |
|
|
cdfX++; |
|
|
} |
|
|
} |
|
|
|