Browse Source

Optimize Convolution processors

af/merge-core
James Jackson-South 9 years ago
parent
commit
2ed67f0562
  1. 9
      src/ImageSharp/Image/ImageBase{TPixel}.cs
  2. 26
      src/ImageSharp/Image/PixelAccessor{TPixel}.cs
  3. 2
      src/ImageSharp/Memory/SpanHelper.cs
  4. 18
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
  5. 14
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
  6. 16
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs

9
src/ImageSharp/Image/ImageBase{TPixel}.cs

@ -231,6 +231,15 @@ namespace ImageSharp
return new PixelAccessor<TPixel>(this);
}
/// <summary>
/// Copies the pixels to another <see cref="PixelAccessor{TPixel}"/> of the same size.
/// </summary>
/// <param name="target">The target pixel buffer accessor.</param>
internal void CopyTo(PixelAccessor<TPixel> target)
{
SpanHelper.Copy(this.span, target.PixelBuffer.Span);
}
/// <summary>
/// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer.
/// </summary>

26
src/ImageSharp/Image/PixelAccessor{TPixel}.cs

@ -20,6 +20,13 @@ namespace ImageSharp
internal sealed class PixelAccessor<TPixel> : IDisposable, IBuffer2D<TPixel>
where TPixel : struct, IPixel<TPixel>
{
#pragma warning disable SA1401 // Fields must be private
/// <summary>
/// The <see cref="Buffer{T}"/> containing the pixel data.
/// </summary>
internal Buffer2D<TPixel> PixelBuffer;
#pragma warning restore SA1401 // Fields must be private
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
@ -31,11 +38,6 @@ namespace ImageSharp
/// </remarks>
private bool isDisposed;
/// <summary>
/// The <see cref="Buffer{T}"/> containing the pixel data.
/// </summary>
private Buffer2D<TPixel> pixelBuffer;
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TPixel}"/> class.
/// </summary>
@ -88,7 +90,7 @@ namespace ImageSharp
/// <summary>
/// Gets the pixel buffer array.
/// </summary>
public TPixel[] PixelArray => this.pixelBuffer.Array;
public TPixel[] PixelArray => this.PixelBuffer.Array;
/// <summary>
/// Gets the size of a single pixel in the number of bytes.
@ -116,7 +118,7 @@ namespace ImageSharp
public ParallelOptions ParallelOptions { get; }
/// <inheritdoc />
Span<TPixel> IBuffer2D<TPixel>.Span => this.pixelBuffer;
Span<TPixel> IBuffer2D<TPixel>.Span => this.PixelBuffer;
private static PixelOperations<TPixel> Operations => PixelOperations<TPixel>.Instance;
@ -156,7 +158,7 @@ namespace ImageSharp
// Note disposing is done.
this.isDisposed = true;
this.pixelBuffer.Dispose();
this.PixelBuffer.Dispose();
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SuppressFinalize to
@ -171,7 +173,7 @@ namespace ImageSharp
/// </summary>
public void Reset()
{
this.pixelBuffer.Clear();
this.PixelBuffer.Clear();
}
/// <summary>
@ -243,7 +245,7 @@ namespace ImageSharp
/// <remarks>If <see cref="M:PixelAccessor.PooledMemory"/> is true then caller is responsible for ensuring <see cref="M:PixelDataPool.Return()"/> is called.</remarks>
internal TPixel[] ReturnCurrentColorsAndReplaceThemInternally(int width, int height, TPixel[] pixels)
{
TPixel[] oldPixels = this.pixelBuffer.TakeArrayOwnership();
TPixel[] oldPixels = this.PixelBuffer.TakeArrayOwnership();
this.SetPixelBufferUnsafe(width, height, pixels);
return oldPixels;
}
@ -254,7 +256,7 @@ namespace ImageSharp
/// <param name="target">The target pixel buffer accessor.</param>
internal void CopyTo(PixelAccessor<TPixel> target)
{
SpanHelper.Copy(this.pixelBuffer.Span, target.pixelBuffer.Span);
SpanHelper.Copy(this.PixelBuffer.Span, target.PixelBuffer.Span);
}
/// <summary>
@ -425,7 +427,7 @@ namespace ImageSharp
/// <param name="pixels">The pixel buffer</param>
private void SetPixelBufferUnsafe(int width, int height, Buffer2D<TPixel> pixels)
{
this.pixelBuffer = pixels;
this.PixelBuffer = pixels;
this.Width = width;
this.Height = height;

2
src/ImageSharp/Memory/SpanHelper.cs

@ -70,7 +70,7 @@ namespace ImageSharp.Memory
public static void Copy<T>(Span<T> source, Span<T> destination)
where T : struct
{
Copy(source, destination, source.Length);
Copy(source, destination, Math.Min(source.Length, destination.Length));
}
/// <summary>

18
src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs

@ -5,6 +5,7 @@
namespace ImageSharp.Processing.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
@ -56,10 +57,9 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1;
int maxX = endX - 1;
using (PixelAccessor<TPixel> targetPixels = new PixelAccessor<TPixel>(source.Width, source.Height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
using (var targetPixels = new PixelAccessor<TPixel>(source.Width, source.Height))
{
sourcePixels.CopyTo(targetPixels);
source.CopyTo(targetPixels);
Parallel.For(
startY,
@ -67,6 +67,9 @@ namespace ImageSharp.Processing.Processors
this.ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetRowSpan(y);
Span<TPixel> targetRow = targetPixels.GetRowSpan(y);
for (int x = startX; x < endX; x++)
{
float rX = 0;
@ -83,6 +86,7 @@ namespace ImageSharp.Processing.Processors
int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY);
Span<TPixel> sourceOffsetRow = source.GetRowSpan(offsetY);
for (int fx = 0; fx < kernelXWidth; fx++)
{
@ -90,8 +94,7 @@ namespace ImageSharp.Processing.Processors
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
var currentColor = sourceOffsetRow[offsetX].ToVector4();
if (fy < kernelXHeight)
{
@ -115,9 +118,8 @@ namespace ImageSharp.Processing.Processors
float green = MathF.Sqrt((gX * gX) + (gY * gY));
float blue = MathF.Sqrt((bX * bX) + (bY * bY));
TPixel packed = default(TPixel);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed;
ref TPixel pixel = ref targetRow[x];
pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W));
}
});

14
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs

@ -46,7 +46,7 @@ namespace ImageSharp.Processing.Processors
int width = source.Width;
int height = source.Height;
using (PixelAccessor<TPixel> firstPassPixels = new PixelAccessor<TPixel>(width, height))
using (var firstPassPixels = new PixelAccessor<TPixel>(width, height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds, this.KernelX);
@ -84,9 +84,11 @@ namespace ImageSharp.Processing.Processors
this.ParallelOptions,
y =>
{
Span<TPixel> targetRow = targetPixels.GetRowSpan(y);
for (int x = startX; x < endX; x++)
{
Vector4 destination = default(Vector4);
var destination = default(Vector4);
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelHeight; fy++)
@ -95,6 +97,7 @@ namespace ImageSharp.Processing.Processors
int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY);
Span<TPixel> row = sourcePixels.GetRowSpan(offsetY);
for (int fx = 0; fx < kernelWidth; fx++)
{
@ -103,14 +106,13 @@ namespace ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
var currentColor = row[offsetX].ToVector4();
destination += kernel[fy, fx] * currentColor;
}
}
TPixel packed = default(TPixel);
packed.PackFromVector4(destination);
targetPixels[x, y] = packed;
ref TPixel pixel = ref targetRow[x];
pixel.PackFromVector4(destination);
}
});
}

16
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs

@ -46,10 +46,9 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1;
int maxX = endX - 1;
using (PixelAccessor<TPixel> targetPixels = new PixelAccessor<TPixel>(source.Width, source.Height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
using (var targetPixels = new PixelAccessor<TPixel>(source.Width, source.Height))
{
sourcePixels.CopyTo(targetPixels);
source.CopyTo(targetPixels);
Parallel.For(
startY,
@ -57,6 +56,9 @@ namespace ImageSharp.Processing.Processors
this.ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetRowSpan(y);
Span<TPixel> targetRow = targetPixels.GetRowSpan(y);
for (int x = startX; x < endX; x++)
{
float red = 0;
@ -70,6 +72,7 @@ namespace ImageSharp.Processing.Processors
int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY);
Span<TPixel> sourceOffsetRow = source.GetRowSpan(offsetY);
for (int fx = 0; fx < kernelLength; fx++)
{
@ -78,7 +81,7 @@ namespace ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
var currentColor = sourceOffsetRow[offsetX].ToVector4();
currentColor *= this.KernelXY[fy, fx];
red += currentColor.X;
@ -87,9 +90,8 @@ namespace ImageSharp.Processing.Processors
}
}
TPixel packed = default(TPixel);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed;
ref TPixel pixel = ref targetRow[x];
pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W));
}
});

Loading…
Cancel
Save