|
|
|
@ -6,56 +6,50 @@ using System.Numerics; |
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
using SixLabors.ImageSharp.Memory; |
|
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
|
using SixLabors.ImageSharp.Processing.Processors.Convolution; |
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Extension methods for <see cref="DenseMatrix{T}"/>.
|
|
|
|
/// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement.
|
|
|
|
/// Provides methods to perform convolution operations.
|
|
|
|
/// </summary>
|
|
|
|
internal static class DenseMatrixUtils |
|
|
|
internal static class Convolver |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Computes the sum of vectors in the span referenced by <paramref name="targetRowRef"/> weighted by the two kernel weight values.
|
|
|
|
/// Using this method the convolution filter is not applied to alpha in addition to the color channels.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
|
|
/// <param name="matrixY">The vertical dense matrix.</param>
|
|
|
|
/// <param name="matrixX">The horizontal dense matrix.</param>
|
|
|
|
/// <param name="kernelY">The vertical convolution kernel.</param>
|
|
|
|
/// <param name="kernelX">The horizontal convolution kernel.</param>
|
|
|
|
/// <param name="rowSampleOffsets">The span containing precalculated kernel y-sampling offsets.</param>
|
|
|
|
/// <param name="columnSampleOffsets">The span containing precalculated kernel x-sampling offsets.</param>
|
|
|
|
/// <param name="sourcePixels">The source frame.</param>
|
|
|
|
/// <param name="targetRowRef">The target row base reference.</param>
|
|
|
|
/// <param name="row">The current row.</param>
|
|
|
|
/// <param name="column">The current column.</param>
|
|
|
|
/// <param name="minRow">The minimum working area row.</param>
|
|
|
|
/// <param name="maxRow">The maximum working area row.</param>
|
|
|
|
/// <param name="minColumn">The minimum working area column.</param>
|
|
|
|
/// <param name="maxColumn">The maximum working area column.</param>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public static void Convolve2D3<TPixel>( |
|
|
|
in DenseMatrix<float> matrixY, |
|
|
|
in DenseMatrix<float> matrixX, |
|
|
|
in DenseMatrix<float> kernelY, |
|
|
|
in DenseMatrix<float> kernelX, |
|
|
|
Span<int> rowSampleOffsets, |
|
|
|
Span<int> columnSampleOffsets, |
|
|
|
Buffer2D<TPixel> sourcePixels, |
|
|
|
ref Vector4 targetRowRef, |
|
|
|
int row, |
|
|
|
int column, |
|
|
|
int minRow, |
|
|
|
int maxRow, |
|
|
|
int minColumn, |
|
|
|
int maxColumn) |
|
|
|
int column) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
Vector4 vector = default; |
|
|
|
|
|
|
|
Convolve2DImpl( |
|
|
|
in matrixY, |
|
|
|
in matrixX, |
|
|
|
in kernelY, |
|
|
|
in kernelX, |
|
|
|
rowSampleOffsets, |
|
|
|
columnSampleOffsets, |
|
|
|
sourcePixels, |
|
|
|
row, |
|
|
|
column, |
|
|
|
minRow, |
|
|
|
maxRow, |
|
|
|
minColumn, |
|
|
|
maxColumn, |
|
|
|
out Vector4 vector); |
|
|
|
ref vector); |
|
|
|
|
|
|
|
ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); |
|
|
|
vector.W = target.W; |
|
|
|
@ -69,41 +63,37 @@ namespace SixLabors.ImageSharp |
|
|
|
/// Using this method the convolution filter is applied to alpha in addition to the color channels.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
|
|
/// <param name="matrixY">The vertical dense matrix.</param>
|
|
|
|
/// <param name="matrixX">The horizontal dense matrix.</param>
|
|
|
|
/// <param name="kernelY">The vertical convolution kernel.</param>
|
|
|
|
/// <param name="kernelX">The horizontal convolution kernel.</param>
|
|
|
|
/// <param name="rowSampleOffsets">The span containing precalculated kernel y-sampling offsets.</param>
|
|
|
|
/// <param name="columnSampleOffsets">The span containing precalculated kernel x-sampling offsets.</param>
|
|
|
|
/// <param name="sourcePixels">The source frame.</param>
|
|
|
|
/// <param name="targetRowRef">The target row base reference.</param>
|
|
|
|
/// <param name="row">The current row.</param>
|
|
|
|
/// <param name="column">The current column.</param>
|
|
|
|
/// <param name="minRow">The minimum working area row.</param>
|
|
|
|
/// <param name="maxRow">The maximum working area row.</param>
|
|
|
|
/// <param name="minColumn">The minimum working area column.</param>
|
|
|
|
/// <param name="maxColumn">The maximum working area column.</param>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public static void Convolve2D4<TPixel>( |
|
|
|
in DenseMatrix<float> matrixY, |
|
|
|
in DenseMatrix<float> matrixX, |
|
|
|
in DenseMatrix<float> kernelY, |
|
|
|
in DenseMatrix<float> kernelX, |
|
|
|
Span<int> rowSampleOffsets, |
|
|
|
Span<int> columnSampleOffsets, |
|
|
|
Buffer2D<TPixel> sourcePixels, |
|
|
|
ref Vector4 targetRowRef, |
|
|
|
int row, |
|
|
|
int column, |
|
|
|
int minRow, |
|
|
|
int maxRow, |
|
|
|
int minColumn, |
|
|
|
int maxColumn) |
|
|
|
int column) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
Vector4 vector = default; |
|
|
|
|
|
|
|
Convolve2DImpl( |
|
|
|
in matrixY, |
|
|
|
in matrixX, |
|
|
|
in kernelY, |
|
|
|
in kernelX, |
|
|
|
rowSampleOffsets, |
|
|
|
columnSampleOffsets, |
|
|
|
sourcePixels, |
|
|
|
row, |
|
|
|
column, |
|
|
|
minRow, |
|
|
|
maxRow, |
|
|
|
minColumn, |
|
|
|
maxColumn, |
|
|
|
out Vector4 vector); |
|
|
|
ref vector); |
|
|
|
|
|
|
|
ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); |
|
|
|
Numerics.UnPremultiply(ref vector); |
|
|
|
@ -112,43 +102,38 @@ namespace SixLabors.ImageSharp |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public static void Convolve2DImpl<TPixel>( |
|
|
|
in DenseMatrix<float> matrixY, |
|
|
|
in DenseMatrix<float> matrixX, |
|
|
|
in DenseMatrix<float> kernelY, |
|
|
|
in DenseMatrix<float> kernelX, |
|
|
|
Span<int> rowSampleOffsets, |
|
|
|
Span<int> columnSampleOffsets, |
|
|
|
Buffer2D<TPixel> sourcePixels, |
|
|
|
int row, |
|
|
|
int column, |
|
|
|
int minRow, |
|
|
|
int maxRow, |
|
|
|
int minColumn, |
|
|
|
int maxColumn, |
|
|
|
out Vector4 vector) |
|
|
|
ref Vector4 targetVector) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
Vector4 vectorY = default; |
|
|
|
Vector4 vectorX = default; |
|
|
|
int matrixHeight = matrixY.Rows; |
|
|
|
int matrixWidth = matrixY.Columns; |
|
|
|
int radiusY = matrixHeight >> 1; |
|
|
|
int radiusX = matrixWidth >> 1; |
|
|
|
int sourceOffsetColumnBase = column + minColumn; |
|
|
|
int kernelHeight = kernelY.Rows; |
|
|
|
int kernelWidth = kernelY.Columns; |
|
|
|
|
|
|
|
for (int y = 0; y < matrixHeight; y++) |
|
|
|
for (int y = 0; y < kernelHeight; y++) |
|
|
|
{ |
|
|
|
int offsetY = Numerics.Clamp(row + y - radiusY, minRow, maxRow); |
|
|
|
int offsetY = rowSampleOffsets[(row * kernelHeight) + y]; |
|
|
|
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY); |
|
|
|
|
|
|
|
for (int x = 0; x < matrixWidth; x++) |
|
|
|
for (int x = 0; x < kernelWidth; x++) |
|
|
|
{ |
|
|
|
int offsetX = Numerics.Clamp(sourceOffsetColumnBase + x - radiusX, minColumn, maxColumn); |
|
|
|
var currentColor = sourceRowSpan[offsetX].ToVector4(); |
|
|
|
Numerics.Premultiply(ref currentColor); |
|
|
|
int offsetX = columnSampleOffsets[(column * kernelWidth) + x]; |
|
|
|
var sample = sourceRowSpan[offsetX].ToVector4(); |
|
|
|
Numerics.Premultiply(ref sample); |
|
|
|
|
|
|
|
vectorX += matrixX[y, x] * currentColor; |
|
|
|
vectorY += matrixY[y, x] * currentColor; |
|
|
|
vectorX += kernelX[y, x] * sample; |
|
|
|
vectorY += kernelY[y, x] * sample; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
vector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY)); |
|
|
|
targetVector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -156,18 +141,18 @@ namespace SixLabors.ImageSharp |
|
|
|
/// Using this method the convolution filter is not applied to alpha in addition to the color channels.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
|
|
/// <param name="matrix">The dense matrix.</param>
|
|
|
|
/// <param name="yOffsetSpan">The span containing precalculated kernel y-offsets.</param>
|
|
|
|
/// <param name="xOffsetSpan">The span containing precalculated kernel x-offsets.</param>
|
|
|
|
/// <param name="kernel">The convolution kernel.</param>
|
|
|
|
/// <param name="rowSampleOffsets">The span containing precalculated kernel y-sampling offsets.</param>
|
|
|
|
/// <param name="columnSampleOffsets">The span containing precalculated kernel x-sampling offsets.</param>
|
|
|
|
/// <param name="sourcePixels">The source frame.</param>
|
|
|
|
/// <param name="targetRowRef">The target row base reference.</param>
|
|
|
|
/// <param name="row">The current row.</param>
|
|
|
|
/// <param name="column">The current column.</param>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public static void Convolve3<TPixel>( |
|
|
|
in DenseMatrix<float> matrix, |
|
|
|
Span<int> yOffsetSpan, |
|
|
|
Span<int> xOffsetSpan, |
|
|
|
in DenseMatrix<float> kernel, |
|
|
|
Span<int> rowSampleOffsets, |
|
|
|
Span<int> columnSampleOffsets, |
|
|
|
Buffer2D<TPixel> sourcePixels, |
|
|
|
ref Vector4 targetRowRef, |
|
|
|
int row, |
|
|
|
@ -177,9 +162,9 @@ namespace SixLabors.ImageSharp |
|
|
|
Vector4 vector = default; |
|
|
|
|
|
|
|
ConvolveImpl( |
|
|
|
in matrix, |
|
|
|
yOffsetSpan, |
|
|
|
xOffsetSpan, |
|
|
|
in kernel, |
|
|
|
rowSampleOffsets, |
|
|
|
columnSampleOffsets, |
|
|
|
sourcePixels, |
|
|
|
row, |
|
|
|
column, |
|
|
|
@ -197,18 +182,18 @@ namespace SixLabors.ImageSharp |
|
|
|
/// Using this method the convolution filter is applied to alpha in addition to the color channels.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
|
|
/// <param name="matrix">The dense matrix.</param>
|
|
|
|
/// <param name="yOffsetSpan">The span containing precalculated kernel y-offsets.</param>
|
|
|
|
/// <param name="xOffsetSpan">The span containing precalculated kernel x-offsets.</param>
|
|
|
|
/// <param name="kernel">The convolution kernel.</param>
|
|
|
|
/// <param name="rowSampleOffsets">The span containing precalculated kernel y-offsets.</param>
|
|
|
|
/// <param name="columnSampleOffsets">The span containing precalculated kernel x-offsets.</param>
|
|
|
|
/// <param name="sourcePixels">The source frame.</param>
|
|
|
|
/// <param name="targetRowRef">The target row base reference.</param>
|
|
|
|
/// <param name="row">The current row.</param>
|
|
|
|
/// <param name="column">The current column.</param>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public static void Convolve4<TPixel>( |
|
|
|
in DenseMatrix<float> matrix, |
|
|
|
Span<int> yOffsetSpan, |
|
|
|
Span<int> xOffsetSpan, |
|
|
|
in DenseMatrix<float> kernel, |
|
|
|
Span<int> rowSampleOffsets, |
|
|
|
Span<int> columnSampleOffsets, |
|
|
|
Buffer2D<TPixel> sourcePixels, |
|
|
|
ref Vector4 targetRowRef, |
|
|
|
int row, |
|
|
|
@ -218,9 +203,9 @@ namespace SixLabors.ImageSharp |
|
|
|
Vector4 vector = default; |
|
|
|
|
|
|
|
ConvolveImpl( |
|
|
|
in matrix, |
|
|
|
yOffsetSpan, |
|
|
|
xOffsetSpan, |
|
|
|
in kernel, |
|
|
|
rowSampleOffsets, |
|
|
|
columnSampleOffsets, |
|
|
|
sourcePixels, |
|
|
|
row, |
|
|
|
column, |
|
|
|
@ -233,29 +218,29 @@ namespace SixLabors.ImageSharp |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
private static void ConvolveImpl<TPixel>( |
|
|
|
in DenseMatrix<float> matrix, |
|
|
|
Span<int> yOffsetSpan, |
|
|
|
Span<int> xOffsetSpan, |
|
|
|
in DenseMatrix<float> kernel, |
|
|
|
Span<int> rowSampleOffsets, |
|
|
|
Span<int> columnSampleOffsets, |
|
|
|
Buffer2D<TPixel> sourcePixels, |
|
|
|
int row, |
|
|
|
int column, |
|
|
|
ref Vector4 targetVector) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
int matrixHeight = matrix.Rows; |
|
|
|
int matrixWidth = matrix.Columns; |
|
|
|
int kernelHeight = kernel.Rows; |
|
|
|
int kernelWidth = kernel.Columns; |
|
|
|
|
|
|
|
for (int y = 0; y < matrixHeight; y++) |
|
|
|
for (int y = 0; y < kernelHeight; y++) |
|
|
|
{ |
|
|
|
int offsetY = yOffsetSpan[(row * matrixHeight) + y]; |
|
|
|
int offsetY = rowSampleOffsets[(row * kernelHeight) + y]; |
|
|
|
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY); |
|
|
|
|
|
|
|
for (int x = 0; x < matrixWidth; x++) |
|
|
|
for (int x = 0; x < kernelWidth; x++) |
|
|
|
{ |
|
|
|
int offsetX = xOffsetSpan[(column * matrixWidth) + x]; |
|
|
|
var currentColor = sourceRowSpan[offsetX].ToVector4(); |
|
|
|
Numerics.Premultiply(ref currentColor); |
|
|
|
targetVector += matrix[y, x] * currentColor; |
|
|
|
int offsetX = columnSampleOffsets[(column * kernelWidth) + x]; |
|
|
|
var sample = sourceRowSpan[offsetX].ToVector4(); |
|
|
|
Numerics.Premultiply(ref sample); |
|
|
|
targetVector += kernel[y, x] * sample; |
|
|
|
} |
|
|
|
} |
|
|
|
} |