Browse Source

Refactor Convolution2DProcessor<TPixel>

af/octree-no-pixelmap
Sergio Pedri 7 years ago
parent
commit
2abc95a6fe
  1. 5
      src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs
  2. 113
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
  3. 5
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs

5
src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs

@ -40,11 +40,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
using (var processor = new Convolution2PassProcessor<TPixel>(this.Configuration, this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) using var processor = new Convolution2PassProcessor<TPixel>(this.Configuration, this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle);
{
processor.Apply(source); processor.Apply(source);
} }
}
/// <summary> /// <summary>
/// Create a 1 dimensional Box kernel. /// Create a 1 dimensional Box kernel.

113
src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -59,79 +60,115 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
DenseMatrix<float> matrixY = this.KernelY; using Buffer2D<TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Width, source.Height);
DenseMatrix<float> matrixX = this.KernelX;
bool preserveAlpha = this.PreserveAlpha;
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int startY = interest.Y;
int endY = interest.Bottom;
int startX = interest.X;
int endX = interest.Right;
int maxY = endY - 1;
int maxX = endX - 1;
using (Buffer2D<TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Width, source.Height))
{
source.CopyTo(targetPixels); source.CopyTo(targetPixels);
var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int width = workingRectangle.Width;
ParallelRowIterator.IterateRows<Vector4>( ParallelRowIterator.IterateRows<RowIntervalAction, Vector4>(
workingRectangle, interest,
this.Configuration, this.Configuration,
(rows, vectorBuffer) => new RowIntervalAction(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha));
Buffer2D<TPixel>.SwapOrCopyContent(source.PixelBuffer, targetPixels);
}
/// <summary>
/// A <see langword="struct"/> implementing the convolution logic for <see cref="Convolution2DProcessor{T}"/>.
/// </summary>
private readonly struct RowIntervalAction : IRowIntervalAction<Vector4>
{
private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> sourcePixels;
private readonly DenseMatrix<float> kernelY;
private readonly DenseMatrix<float> kernelX;
private readonly Configuration configuration;
private readonly bool preserveAlpha;
/// <summary>
/// Initializes a new instance of the <see cref="Convolution2DProcessor{TPixel}.RowIntervalAction"/> struct.
/// </summary>
/// <param name="bounds">The target processing bounds for the current instance.</param>
/// <param name="targetPixels">The target pixel buffer to adjust.</param>
/// <param name="sourcePixels">The source pixels. Cannot be null.</param>
/// <param name="kernelY">The vertical kernel operator.</param>
/// <param name="kernelX">The horizontal kernel operator.</param>
/// <param name="configuration">The <see cref="Configuration"/></param>
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(
Rectangle bounds,
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
DenseMatrix<float> kernelY,
DenseMatrix<float> kernelX,
Configuration configuration,
bool preserveAlpha)
{ {
Span<Vector4> vectorSpan = vectorBuffer.Span; this.bounds = bounds;
this.targetPixels = targetPixels;
this.sourcePixels = sourcePixels;
this.kernelY = kernelY;
this.kernelX = kernelX;
this.configuration = configuration;
this.preserveAlpha = preserveAlpha;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
{
Span<Vector4> vectorSpan = memory.Span;
int length = vectorSpan.Length; int length = vectorSpan.Length;
ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan);
int maxY = this.bounds.Bottom - 1;
int maxX = this.bounds.Right - 1;
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); Span<TPixel> targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.Configuration, targetRowSpan.Slice(0, length), vectorSpan); PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, length), vectorSpan);
if (preserveAlpha) if (this.preserveAlpha)
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < this.bounds.Width; x++)
{ {
DenseMatrixUtils.Convolve2D3( DenseMatrixUtils.Convolve2D3(
in matrixY, in this.kernelY,
in matrixX, in this.kernelX,
source.PixelBuffer, this.sourcePixels,
ref vectorSpanRef, ref vectorSpanRef,
y, y,
x, x,
startY, this.bounds.Y,
maxY, maxY,
startX, this.bounds.X,
maxX); maxX);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < this.bounds.Width; x++)
{ {
DenseMatrixUtils.Convolve2D4( DenseMatrixUtils.Convolve2D4(
in matrixY, in this.kernelY,
in matrixX, in this.kernelX,
source.PixelBuffer, this.sourcePixels,
ref vectorSpanRef, ref vectorSpanRef,
y, y,
x, x,
startY, this.bounds.Y,
maxY, maxY,
startX, this.bounds.X,
maxX); maxX);
} }
} }
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.Configuration, vectorSpan, targetRowSpan); PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorSpan, targetRowSpan);
} }
});
Buffer2D<TPixel>.SwapOrCopyContent(source.PixelBuffer, targetPixels);
} }
} }
} }

5
src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs

@ -63,10 +63,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc /> /// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
using (var processor = new Convolution2DProcessor<TPixel>(this.Configuration, this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle)) using var processor = new Convolution2DProcessor<TPixel>(this.Configuration, this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle);
{
processor.Apply(source); processor.Apply(source);
} }
} }
}
} }

Loading…
Cancel
Save