|
|
|
@ -14,14 +14,14 @@ namespace ImageSharp.Processing.Processors |
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TColor">The pixel format.</typeparam>
|
|
|
|
public class Convolution2PassProcessor<TColor> : ImageProcessor<TColor> |
|
|
|
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|
|
|
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="Convolution2PassProcessor{TColor}"/> class.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="kernelX">The horizontal gradient operator.</param>
|
|
|
|
/// <param name="kernelY">The vertical gradient operator.</param>
|
|
|
|
public Convolution2PassProcessor(float[][] kernelX, float[][] kernelY) |
|
|
|
public Convolution2PassProcessor(Fast2DArray<float> kernelX, Fast2DArray<float> kernelY) |
|
|
|
{ |
|
|
|
this.KernelX = kernelX; |
|
|
|
this.KernelY = kernelY; |
|
|
|
@ -30,18 +30,16 @@ namespace ImageSharp.Processing.Processors |
|
|
|
/// <summary>
|
|
|
|
/// Gets the horizontal gradient operator.
|
|
|
|
/// </summary>
|
|
|
|
public float[][] KernelX { get; } |
|
|
|
public Fast2DArray<float> KernelX { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the vertical gradient operator.
|
|
|
|
/// </summary>
|
|
|
|
public float[][] KernelY { get; } |
|
|
|
public Fast2DArray<float> KernelY { get; } |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle) |
|
|
|
{ |
|
|
|
float[][] kernelX = this.KernelX; |
|
|
|
float[][] kernelY = this.KernelY; |
|
|
|
int width = source.Width; |
|
|
|
int height = source.Height; |
|
|
|
|
|
|
|
@ -50,8 +48,8 @@ namespace ImageSharp.Processing.Processors |
|
|
|
using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, height)) |
|
|
|
using (PixelAccessor<TColor> sourcePixels = source.Lock()) |
|
|
|
{ |
|
|
|
this.ApplyConvolution(width, height, firstPassPixels, sourcePixels, sourceRectangle, kernelX); |
|
|
|
this.ApplyConvolution(width, height, targetPixels, firstPassPixels, sourceRectangle, kernelY); |
|
|
|
this.ApplyConvolution(firstPassPixels, sourcePixels, sourceRectangle, this.KernelX); |
|
|
|
this.ApplyConvolution(targetPixels, firstPassPixels, sourceRectangle, this.KernelY); |
|
|
|
} |
|
|
|
|
|
|
|
source.SwapPixelsBuffers(targetPixels); |
|
|
|
@ -62,18 +60,16 @@ namespace ImageSharp.Processing.Processors |
|
|
|
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor}"/> at the specified location
|
|
|
|
/// and with the specified size.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="width">The image width.</param>
|
|
|
|
/// <param name="height">The image height.</param>
|
|
|
|
/// <param name="targetPixels">The target pixels to apply the process to.</param>
|
|
|
|
/// <param name="sourcePixels">The source pixels. Cannot be null.</param>
|
|
|
|
/// <param name="sourceRectangle">
|
|
|
|
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
|
|
|
/// </param>
|
|
|
|
/// <param name="kernel">The kernel operator.</param>
|
|
|
|
private void ApplyConvolution(int width, int height, PixelAccessor<TColor> targetPixels, PixelAccessor<TColor> sourcePixels, Rectangle sourceRectangle, float[][] kernel) |
|
|
|
private void ApplyConvolution(PixelAccessor<TColor> targetPixels, PixelAccessor<TColor> sourcePixels, Rectangle sourceRectangle, Fast2DArray<float> kernel) |
|
|
|
{ |
|
|
|
int kernelHeight = kernel.Length; |
|
|
|
int kernelWidth = kernel[0].Length; |
|
|
|
int kernelHeight = kernel.Height; |
|
|
|
int kernelWidth = kernel.Width; |
|
|
|
int radiusY = kernelHeight >> 1; |
|
|
|
int radiusX = kernelWidth >> 1; |
|
|
|
|
|
|
|
@ -85,40 +81,40 @@ namespace ImageSharp.Processing.Processors |
|
|
|
int maxX = endX - 1; |
|
|
|
|
|
|
|
Parallel.For( |
|
|
|
startY, |
|
|
|
endY, |
|
|
|
this.ParallelOptions, |
|
|
|
y => |
|
|
|
{ |
|
|
|
for (int x = startX; x < endX; x++) |
|
|
|
startY, |
|
|
|
endY, |
|
|
|
this.ParallelOptions, |
|
|
|
y => |
|
|
|
{ |
|
|
|
Vector4 destination = default(Vector4); |
|
|
|
|
|
|
|
// Apply each matrix multiplier to the color components for each pixel.
|
|
|
|
for (int fy = 0; fy < kernelHeight; fy++) |
|
|
|
for (int x = startX; x < endX; x++) |
|
|
|
{ |
|
|
|
int fyr = fy - radiusY; |
|
|
|
int offsetY = y + fyr; |
|
|
|
Vector4 destination = default(Vector4); |
|
|
|
|
|
|
|
offsetY = offsetY.Clamp(0, maxY); |
|
|
|
|
|
|
|
for (int fx = 0; fx < kernelWidth; fx++) |
|
|
|
// Apply each matrix multiplier to the color components for each pixel.
|
|
|
|
for (int fy = 0; fy < kernelHeight; fy++) |
|
|
|
{ |
|
|
|
int fxr = fx - radiusX; |
|
|
|
int offsetX = x + fxr; |
|
|
|
int fyr = fy - radiusY; |
|
|
|
int offsetY = y + fyr; |
|
|
|
|
|
|
|
offsetY = offsetY.Clamp(0, maxY); |
|
|
|
|
|
|
|
offsetX = offsetX.Clamp(0, maxX); |
|
|
|
for (int fx = 0; fx < kernelWidth; fx++) |
|
|
|
{ |
|
|
|
int fxr = fx - radiusX; |
|
|
|
int offsetX = x + fxr; |
|
|
|
|
|
|
|
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); |
|
|
|
destination += kernel[fy][fx] * currentColor; |
|
|
|
offsetX = offsetX.Clamp(0, maxX); |
|
|
|
|
|
|
|
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); |
|
|
|
destination += kernel[fy, fx] * currentColor; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
TColor packed = default(TColor); |
|
|
|
packed.PackFromVector4(destination); |
|
|
|
targetPixels[x, y] = packed; |
|
|
|
} |
|
|
|
}); |
|
|
|
TColor packed = default(TColor); |
|
|
|
packed.PackFromVector4(destination); |
|
|
|
targetPixels[x, y] = packed; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} |