Browse Source

WIP applying ParallelHelper to Convolution processors

pull/710/head
Anton Firszov 7 years ago
parent
commit
55479b0dee
  1. 174
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
  2. 45
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
  3. 53
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
  4. 37
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs
  5. 2
      tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs

174
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<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(
ImageFrame<TPixel> 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<TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D<TPixel>(source.Width, source.Height))
using (Buffer2D<TPixel> targetPixels =
configuration.MemoryAllocator.Allocate2D<TPixel>(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<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<TPixel> 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<TPixel> 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<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<TPixel> 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<TPixel> sourceOffsetRow = source.GetPixelRowSpan(offsetY);
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<TPixel> 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<TPixel> 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<TPixel>.SwapOrCopyContent(source.PixelBuffer, targetPixels);
}

45
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<TPixel> 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<TPixel> 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
}
}
}

53
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<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<TPixel> 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<TPixel> 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<TPixel>.SwapOrCopyContent(source.PixelBuffer, targetPixels);
}

37
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<TPixel> passPixels = pass.PixelBuffer;
Buffer2D<TPixel> 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
}
}
}

2
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);

Loading…
Cancel
Save