From 55479b0dee1bcb57bf0d641a0fc23b95d0896ef1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 22 Sep 2018 00:55:42 +0200 Subject: [PATCH] WIP applying ParallelHelper to Convolution processors --- .../Convolution/Convolution2DProcessor.cs | 174 +++++++++++++----- .../Convolution/Convolution2PassProcessor.cs | 45 +++++ .../Convolution/ConvolutionProcessor.cs | 53 ++++++ .../EdgeDetectorCompassProcessor.cs | 37 ++++ .../Processors/Convolution/DetectEdgesTest.cs | 2 - 5 files changed, 259 insertions(+), 52 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index b5a272543..24505eac0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -3,12 +3,12 @@ using System; using System.Numerics; -using System.Threading.Tasks; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -42,7 +42,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) { int kernelYHeight = this.KernelY.Rows; int kernelYWidth = this.KernelY.Columns; @@ -58,71 +61,142 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution int maxY = endY - 1; int maxX = endX - 1; - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) + using (Buffer2D targetPixels = + configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) { source.CopyTo(targetPixels); + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + +#if true + ParallelHelper.IterateRows( + workingRectangle, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = targetPixels.GetRowSpan(y); + + for (int x = startX; x < endX; x++) + { + float rX = 0; + float gX = 0; + float bX = 0; + float rY = 0; + float gY = 0; + float bY = 0; + + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelYHeight; fy++) + { + int fyr = fy - radiusY; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + + for (int fx = 0; fx < kernelXWidth; fx++) + { + int fxr = fx - radiusX; + int offsetX = x + fxr; + + offsetX = offsetX.Clamp(0, maxX); + Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); + + if (fy < kernelXHeight) + { + Vector4 kx = this.KernelX[fy, fx] * currentColor; + rX += kx.X; + gX += kx.Y; + bX += kx.Z; + } + + if (fx < kernelYWidth) + { + Vector4 ky = this.KernelY[fy, fx] * currentColor; + rY += ky.X; + gY += ky.Y; + bY += ky.Z; + } + } + } + + float red = MathF.Sqrt((rX * rX) + (rY * rY)); + float green = MathF.Sqrt((gX * gX) + (gY * gY)); + float blue = MathF.Sqrt((bX * bX) + (bY * bY)); + + ref TPixel pixel = ref targetRow[x]; + pixel.PackFromVector4( + new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); + } + } + }); +#else ParallelFor.WithConfiguration( startY, endY, configuration, y => - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = startX; x < endX; x++) { - float rX = 0; - float gX = 0; - float bX = 0; - float rY = 0; - float gY = 0; - float bY = 0; - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelYHeight; fy++) - { - int fyr = fy - radiusY; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = targetPixels.GetRowSpan(y); - for (int fx = 0; fx < kernelXWidth; fx++) + for (int x = startX; x < endX; x++) + { + float rX = 0; + float gX = 0; + float bX = 0; + float rY = 0; + float gY = 0; + float bY = 0; + + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelYHeight; fy++) { - int fxr = fx - radiusX; - int offsetX = x + fxr; + int fyr = fy - radiusY; + int offsetY = y + fyr; - offsetX = offsetX.Clamp(0, maxX); - Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); + offsetY = offsetY.Clamp(0, maxY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - if (fy < kernelXHeight) + for (int fx = 0; fx < kernelXWidth; fx++) { - Vector4 kx = this.KernelX[fy, fx] * currentColor; - rX += kx.X; - gX += kx.Y; - bX += kx.Z; - } - - if (fx < kernelYWidth) - { - Vector4 ky = this.KernelY[fy, fx] * currentColor; - rY += ky.X; - gY += ky.Y; - bY += ky.Z; + int fxr = fx - radiusX; + int offsetX = x + fxr; + + offsetX = offsetX.Clamp(0, maxX); + Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); + + if (fy < kernelXHeight) + { + Vector4 kx = this.KernelX[fy, fx] * currentColor; + rX += kx.X; + gX += kx.Y; + bX += kx.Z; + } + + if (fx < kernelYWidth) + { + Vector4 ky = this.KernelY[fy, fx] * currentColor; + rY += ky.X; + gY += ky.Y; + bY += ky.Z; + } } } - } - float red = MathF.Sqrt((rX * rX) + (rY * rY)); - float green = MathF.Sqrt((gX * gX) + (gY * gY)); - float blue = MathF.Sqrt((bX * bX) + (bY * bY)); + float red = MathF.Sqrt((rX * rX) + (rY * rY)); + float green = MathF.Sqrt((gX * gX) + (gY * gY)); + float blue = MathF.Sqrt((bX * bX) + (bY * bY)); - ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); - } - }); + ref TPixel pixel = ref targetRow[x]; + pixel.PackFromVector4( + new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); + } + }); +#endif Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index 0808c07d0..9263c262d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -6,6 +6,7 @@ using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors; @@ -82,6 +83,49 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution int maxY = endY - 1; int maxX = endX - 1; + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + +#if true + ParallelHelper.IterateRows( + workingRectangle, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRow = targetPixels.GetRowSpan(y); + + for (int x = startX; x < endX; x++) + { + Vector4 destination = default; + + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelHeight; fy++) + { + int fyr = fy - radiusY; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); + Span row = sourcePixels.GetRowSpan(offsetY); + + for (int fx = 0; fx < kernelWidth; fx++) + { + int fxr = fx - radiusX; + int offsetX = x + fxr; + + offsetX = offsetX.Clamp(0, maxX); + + Vector4 currentColor = row[offsetX].ToVector4().Premultiply(); + destination += kernel[fy, fx] * currentColor; + } + } + + ref TPixel pixel = ref targetRow[x]; + pixel.PackFromVector4(destination.UnPremultiply()); + } + } + }); +#else ParallelFor.WithConfiguration( startY, endY, @@ -119,6 +163,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution pixel.PackFromVector4(destination.UnPremultiply()); } }); +#endif } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index 31e638a0a..64c9f4632 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -6,6 +6,7 @@ using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors; @@ -52,6 +53,57 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { source.CopyTo(targetPixels); + var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); + +#if true + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = targetPixels.GetRowSpan(y); + + for (int x = startX; x < endX; x++) + { + float red = 0; + float green = 0; + float blue = 0; + + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelLength; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + + for (int fx = 0; fx < kernelLength; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + + offsetX = offsetX.Clamp(0, maxX); + + Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); + currentColor *= this.KernelXY[fy, fx]; + + red += currentColor.X; + green += currentColor.Y; + blue += currentColor.Z; + } + } + + ref TPixel pixel = ref targetRow[x]; + pixel.PackFromVector4( + new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); + } + } + }); +#else ParallelFor.WithConfiguration( startY, endY, @@ -96,6 +148,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); } }); +#endif Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs index 316de422f..28a72922f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -124,6 +125,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution shiftY = 0; } + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + // Additional runs. // ReSharper disable once ForCanBeConvertedToForeach for (int i = 1; i < kernels.Length; i++) @@ -135,6 +138,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution Buffer2D passPixels = pass.PixelBuffer; Buffer2D targetPixels = source.PixelBuffer; +#if true + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + int offsetY = y - shiftY; + + ref TPixel passPixelsBase = + ref MemoryMarshal.GetReference(passPixels.GetRowSpan(offsetY)); + ref TPixel targetPixelsBase = + ref MemoryMarshal.GetReference(targetPixels.GetRowSpan(offsetY)); + + for (int x = minX; x < maxX; x++) + { + int offsetX = x - shiftX; + + // Grab the max components of the two pixels + ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, offsetX); + ref TPixel currentTargetPixel = + ref Unsafe.Add(ref targetPixelsBase, offsetX); + + var pixelValue = Vector4.Max( + currentPassPixel.ToVector4(), + currentTargetPixel.ToVector4()); + + currentTargetPixel.PackFromVector4(pixelValue); + } + } + }); +#else ParallelFor.WithConfiguration( minY, maxY, @@ -161,6 +197,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution currentTargetPixel.PackFromVector4(pixelValue); } }); +#endif } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index a32239d96..de72f6d09 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -10,8 +10,6 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - using SixLabors.ImageSharp.Advanced; - public class DetectEdgesTest : FileTestBase { private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F);