Browse Source

using Parallel.ForEach to calculate the lookup tables

pull/673/head
popow 8 years ago
parent
commit
09955daa33
  1. 227
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs
  2. 8
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs

227
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs

@ -45,89 +45,80 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> 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<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.
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<TPixel> pixelRow = source.GetPixelRowSpan(dy);
tileX = 0;
for (int dx = x; dx < xEnd; dx++)
{
Span<TPixel> 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<TPixel> pixels = source.GetPixelSpan();
cdfX++;
}
});
Span<TPixel> 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);
}
/// <summary>
@ -286,59 +277,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return luminanceEqualized;
}
/// <summary>
/// Calculates the lookup tables for each tile of the image.
/// </summary>
/// <param name="source">The input image for which the tiles will be calculated.</param>
/// <param name="histogram">Histogram buffer.</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="numTilesY">Number of tiles in Y Direction</param>
/// <param name="tileWidth">Width 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>
private CdfData[,] CalculateLookupTables(ImageFrame<TPixel> source, Span<int> histogram, Span<int> 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;
}
/// <summary>
/// Bilinear interpolation between four tiles.
/// </summary>
@ -366,6 +304,73 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return left + ((right - left) * t);
}
/// <summary>
/// Calculates the lookup tables for each tile of the image.
/// </summary>
/// <param name="source">The input image for which the tiles will be calculated.</param>
/// <param name="configuration">The configuration.</param>
/// <param name="numTilesX">Number of tiles in the X Direction.</param>
/// <param name="numTilesY">Number of tiles in Y Direction.</param>
/// <param name="tileWidth">Width 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>
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];
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<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)
{
Span<int> histogram = histogramBuffer.GetSpan();
Span<int> 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;
}
/// <summary>
/// Lookup table for remapping the grey values of one tile.
/// </summary>

8
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs

@ -178,8 +178,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary>
/// Adds a row of grey values to the histogram.
/// </summary>
/// <param name="greyValues">The grey values to add</param>
/// <param name="histogram">The histogram</param>
/// <param name="greyValues">The grey values to add.</param>
/// <param name="histogram">The histogram.</param>
/// <param name="luminanceLevels">The number of different luminance levels.</param>
/// <returns>The maximum index where a value was changed.</returns>
private int AddPixelsToHistogram(Span<TPixel> greyValues, Span<int> histogram, int luminanceLevels)
@ -201,8 +201,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary>
/// Removes a row of grey values from the histogram.
/// </summary>
/// <param name="greyValues">The grey values to remove</param>
/// <param name="histogram">The histogram</param>
/// <param name="greyValues">The grey values to remove.</param>
/// <param name="histogram">The histogram.</param>
/// <param name="luminanceLevels">The number of different luminance levels.</param>
/// <param name="maxHistIdx">The current maximum index of the histogram.</param>
/// <returns>The (maybe changed) maximum index of the histogram.</returns>

Loading…
Cancel
Save