public SlidingWindowOperation( Configuration configuration, AdaptiveHistogramEqualizationSlidingWindowProcessor <TPixel> processor, ImageFrame <TPixel> source, MemoryAllocator memoryAllocator, Buffer2D <TPixel> targetPixels, SlidingWindowInfos swInfos, int yStart, int yEnd, bool useFastPath) { this.configuration = configuration; this.processor = processor; this.source = source; this.memoryAllocator = memoryAllocator; this.targetPixels = targetPixels; this.swInfos = swInfos; this.yStart = yStart; this.yEnd = yEnd; this.useFastPath = useFastPath; }
/// <summary> /// Applies the sliding window equalization to one column of the image. The window is moved from top to bottom. /// Moving the window one pixel down requires to remove one row from the top of the window from the histogram and /// adding a new row at the bottom. /// </summary> /// <param name="source">The source image.</param> /// <param name="memoryAllocator">The memory allocator.</param> /// <param name="targetPixels">The target pixels.</param> /// <param name="swInfos"><see cref="SlidingWindowInfos"/> about the sliding window dimensions.</param> /// <param name="yStart">The y start position.</param> /// <param name="yEnd">The y end position.</param> /// <param name="useFastPath">if set to true the borders of the image will not be checked.</param> /// <param name="configuration">The configuration.</param> /// <returns>Action Delegate.</returns> private Action <int> ProcessSlidingWindow( ImageFrame <TPixel> source, MemoryAllocator memoryAllocator, Buffer2D <TPixel> targetPixels, SlidingWindowInfos swInfos, int yStart, int yEnd, bool useFastPath, Configuration configuration) { return(x => { using (IMemoryOwner <int> histogramBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner <int> histogramBufferCopy = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner <int> cdfBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner <Vector4> pixelRowBuffer = memoryAllocator.Allocate <Vector4>(swInfos.TileWidth, AllocationOptions.Clean)) { Span <int> histogram = histogramBuffer.GetSpan(); ref int histogramBase = ref MemoryMarshal.GetReference(histogram); Span <int> histogramCopy = histogramBufferCopy.GetSpan(); ref int histogramCopyBase = ref MemoryMarshal.GetReference(histogramCopy); ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan());
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator; var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism }; int tileWidth = source.Width / this.Tiles; int tileHeight = tileWidth; int pixelInTile = tileWidth * tileHeight; int halfTileHeight = tileHeight / 2; int halfTileWidth = halfTileHeight; var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixelInTile); using (Buffer2D <TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D <TPixel>(source.Width, source.Height)) { // Process the inner tiles, which do not require to check the borders. Parallel.For( halfTileWidth, source.Width - halfTileWidth, parallelOptions, this.ProcessSlidingWindow( source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: halfTileHeight, yEnd: source.Height - halfTileHeight, useFastPath: true, this.Configuration)); // Process the left border of the image. Parallel.For( 0, halfTileWidth, parallelOptions, this.ProcessSlidingWindow( source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: 0, yEnd: source.Height, useFastPath: false, this.Configuration)); // Process the right border of the image. Parallel.For( source.Width - halfTileWidth, source.Width, parallelOptions, this.ProcessSlidingWindow( source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: 0, yEnd: source.Height, useFastPath: false, this.Configuration)); // Process the top border of the image. Parallel.For( halfTileWidth, source.Width - halfTileWidth, parallelOptions, this.ProcessSlidingWindow( source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: 0, yEnd: halfTileHeight, useFastPath: false, this.Configuration)); // Process the bottom border of the image. Parallel.For( halfTileWidth, source.Width - halfTileWidth, parallelOptions, this.ProcessSlidingWindow( source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: source.Height - halfTileHeight, yEnd: source.Height, useFastPath: false, this.Configuration)); Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels); } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator; var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism }; int tileWidth = source.Width / this.Tiles; int tileHeight = tileWidth; int pixelInTile = tileWidth * tileHeight; int halfTileHeight = tileHeight / 2; int halfTileWidth = halfTileHeight; var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixelInTile); // TODO: If the process was able to be switched to operate in parallel rows instead of columns // then we could take advantage of batching and allocate per-row buffers only once per batch. using Buffer2D <TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D <TPixel>(source.Width, source.Height); // Process the inner tiles, which do not require to check the borders. var innerOperation = new SlidingWindowOperation( this.Configuration, this, source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: halfTileHeight, yEnd: source.Height - halfTileHeight, useFastPath: true); Parallel.For( halfTileWidth, source.Width - halfTileWidth, parallelOptions, innerOperation.Invoke); // Process the left border of the image. var leftBorderOperation = new SlidingWindowOperation( this.Configuration, this, source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: 0, yEnd: source.Height, useFastPath: false); Parallel.For( 0, halfTileWidth, parallelOptions, leftBorderOperation.Invoke); // Process the right border of the image. var rightBorderOperation = new SlidingWindowOperation( this.Configuration, this, source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: 0, yEnd: source.Height, useFastPath: false); Parallel.For( source.Width - halfTileWidth, source.Width, parallelOptions, rightBorderOperation.Invoke); // Process the top border of the image. var topBorderOperation = new SlidingWindowOperation( this.Configuration, this, source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: 0, yEnd: halfTileHeight, useFastPath: false); Parallel.For( halfTileWidth, source.Width - halfTileWidth, parallelOptions, topBorderOperation.Invoke); // Process the bottom border of the image. var bottomBorderOperation = new SlidingWindowOperation( this.Configuration, this, source, memoryAllocator, targetPixels, slidingWindowInfos, yStart: source.Height - halfTileHeight, yEnd: source.Height, useFastPath: false); Parallel.For( halfTileWidth, source.Width - halfTileWidth, parallelOptions, bottomBorderOperation.Invoke); Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels); }