/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { int sourceWidth = source.Width; int sourceHeight = source.Height; int tileWidth = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles); int tileHeight = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles); int tileCount = this.Tiles; int halfTileWidth = tileWidth / 2; int halfTileHeight = tileHeight / 2; int luminanceLevels = this.LuminanceLevels; // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. using (var cdfData = new CdfTileData(this.Configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) { cdfData.CalculateLookupTables(source, this); var tileYStartPositions = new List <(int y, int cdfY)>(); int cdfY = 0; int yStart = halfTileHeight; for (int tile = 0; tile < tileCount - 1; tile++) { tileYStartPositions.Add((yStart, cdfY)); cdfY++; yStart += tileHeight; } var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source); ParallelRowIterator.IterateRowIntervals( this.Configuration, new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count), in operation); ref TPixel pixelsBase = ref source.GetPixelReference(0, 0); // Fix left column ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); // Fix right column int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); // Fix top row ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, this.Tiles, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); // Fix bottom row int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, this.Tiles, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); // Left top corner ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); // Left bottom corner ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); // Right top corner ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); // Right bottom corner ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); }
/// <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. /// </summary> /// <param name="source">The source image.</param> /// <param name="cdfData">The lookup table to remap the grey values.</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="xStart">X start position.</param> /// <param name="xEnd">X end position.</param> /// <param name="yStart">Y start position.</param> /// <param name="yEnd">Y end position.</param> /// <param name="luminanceLevels"> /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// </param> private static void ProcessCornerTile( ImageFrame <TPixel> source, CdfTileData cdfData, int cdfX, int cdfY, int xStart, int xEnd, int yStart, int yEnd, int luminanceLevels) { for (int dy = yStart; dy < yEnd; dy++) { Span <TPixel> rowSpan = source.GetPixelRowSpan(dy); for (int dx = xStart; dx < xEnd; dx++) { ref TPixel pixel = ref rowSpan[dx]; float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, GetLuminance(pixel, luminanceLevels)); pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { int sourceWidth = source.Width; int sourceHeight = source.Height; int tileWidth = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles); int tileHeight = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles); int tileCount = this.Tiles; int halfTileWidth = tileWidth / 2; int halfTileHeight = tileHeight / 2; int luminanceLevels = this.LuminanceLevels; // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. using (var cdfData = new CdfTileData(this.Configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) { cdfData.CalculateLookupTables(source, this); var tileYStartPositions = new List <(int y, int cdfY)>(); int cdfY = 0; int yStart = halfTileHeight; for (int tile = 0; tile < tileCount - 1; tile++) { tileYStartPositions.Add((yStart, cdfY)); cdfY++; yStart += tileHeight; } Parallel.For( 0, tileYStartPositions.Count, new ParallelOptions { MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism }, index => { int y = tileYStartPositions[index].y; int cdfYY = tileYStartPositions[index].cdfY; // It's unfortunate that we have to do this per iteration. ref TPixel sourceBase = ref source.GetPixelReference(0, 0); int cdfX = 0; int x = halfTileWidth; for (int tile = 0; tile < tileCount - 1; tile++) { int tileY = 0; int yEnd = Math.Min(y + tileHeight, sourceHeight); int xEnd = Math.Min(x + tileWidth, sourceWidth); for (int dy = y; dy < yEnd; dy++) { int dyOffSet = dy * sourceWidth; int tileX = 0; for (int dx = x; dx < xEnd; dx++) { ref TPixel pixel = ref Unsafe.Add(ref sourceBase, dyOffSet + dx); float luminanceEqualized = InterpolateBetweenFourTiles( pixel, cdfData, tileCount, tileCount, tileX, tileY, cdfX, cdfYY, tileWidth, tileHeight, luminanceLevels); pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); tileX++; } tileY++; } cdfX++; x += tileWidth; } });