diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs
index 8c5358770c..5beadb0cee 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using System;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
@@ -23,24 +24,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
: base(configuration, source, sourceRectangle)
{
int kernelSize = (definition.Radius * 2) + 1;
- this.KernelX = CreateBoxKernel(kernelSize);
- this.KernelY = this.KernelX.Transpose();
+ this.Kernel = CreateBoxKernel(kernelSize);
}
///
- /// Gets the horizontal gradient operator.
+ /// Gets the 1D convolution kernel.
///
- public DenseMatrix KernelX { get; }
-
- ///
- /// Gets the vertical gradient operator.
- ///
- public DenseMatrix KernelY { get; }
+ public float[] Kernel { get; }
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle);
+ using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle);
processor.Apply(source);
}
@@ -50,10 +45,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// The maximum size of the kernel in either direction.
/// The .
- private static DenseMatrix CreateBoxKernel(int kernelSize)
+ private static float[] CreateBoxKernel(int kernelSize)
{
- var kernel = new DenseMatrix(kernelSize, 1);
- kernel.Fill(1F / kernelSize);
+ var kernel = new float[kernelSize];
+
+ kernel.AsSpan().Fill(1F / kernelSize);
+
return kernel;
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
index c7f5c94dd2..9b7ed75808 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
@@ -22,34 +22,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Initializes a new instance of the class.
///
/// The configuration which allows altering default behaviour or extending the library.
- /// The horizontal gradient operator.
- /// The vertical gradient operator.
+ /// The 1D convolution kernel.
/// Whether the convolution filter is applied to alpha as well as the color channels.
/// The source for the current processor instance.
/// The source area to process for the current processor instance.
public Convolution2PassProcessor(
Configuration configuration,
- in DenseMatrix kernelX,
- in DenseMatrix kernelY,
+ float[] kernel,
bool preserveAlpha,
Image source,
Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
- this.KernelX = kernelX;
- this.KernelY = kernelY;
+ this.Kernel = kernel;
this.PreserveAlpha = preserveAlpha;
}
///
- /// Gets the horizontal convolution kernel.
+ /// Gets the convolution kernel.
///
- public DenseMatrix KernelX { get; }
-
- ///
- /// Gets the vertical convolution kernel.
- ///
- public DenseMatrix KernelY { get; }
+ public float[] Kernel { get; }
///
/// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels.
@@ -71,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// the two 1D kernels represent, and reuse it across both convolution steps, like in the bokeh blur.
using var mapXY = new KernelSamplingMap(this.Configuration.MemoryAllocator);
- mapXY.BuildSamplingOffsetMap(this.KernelY.Rows, this.KernelX.Columns, interest);
+ mapXY.BuildSamplingOffsetMap(this.Kernel.Length, this.Kernel.Length, interest);
// Horizontal convolution
var horizontalOperation = new HorizontalConvolutionRowOperation(
@@ -79,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
firstPassPixels,
source.PixelBuffer,
mapXY,
- this.KernelX,
+ this.Kernel,
this.Configuration,
this.PreserveAlpha);
@@ -94,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
source.PixelBuffer,
firstPassPixels,
mapXY,
- this.KernelY,
+ this.Kernel,
this.Configuration,
this.PreserveAlpha);
@@ -113,7 +105,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly Buffer2D targetPixels;
private readonly Buffer2D sourcePixels;
private readonly KernelSamplingMap map;
- private readonly DenseMatrix kernelMatrix;
+ private readonly float[] kernel;
private readonly Configuration configuration;
private readonly bool preserveAlpha;
@@ -123,7 +115,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Buffer2D targetPixels,
Buffer2D sourcePixels,
KernelSamplingMap map,
- DenseMatrix kernelMatrix,
+ float[] kernel,
Configuration configuration,
bool preserveAlpha)
{
@@ -131,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
this.targetPixels = targetPixels;
this.sourcePixels = sourcePixels;
this.map = map;
- this.kernelMatrix = kernelMatrix;
+ this.kernel = kernel;
this.configuration = configuration;
this.preserveAlpha = preserveAlpha;
}
@@ -156,7 +148,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// Span is 2x bounds.
int boundsX = this.bounds.X;
int boundsWidth = this.bounds.Width;
- int kernelSize = this.kernelMatrix.Columns;
+ int kernelSize = this.kernel.Length;
Span sourceBuffer = span.Slice(0, this.bounds.Width);
Span targetBuffer = span.Slice(this.bounds.Width);
@@ -170,7 +162,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer);
ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer);
- ref float kernelBase = ref this.kernelMatrix[0, 0];
+ ref float kernelBase = ref this.kernel[0];
ref int sampleColumnBase = ref MemoryMarshal.GetReference(this.map.GetColumnOffsetSpan());
for (int x = 0; x < sourceBuffer.Length; x++)
@@ -210,7 +202,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// Span is 2x bounds.
int boundsX = this.bounds.X;
int boundsWidth = this.bounds.Width;
- int kernelSize = this.kernelMatrix.Columns;
+ int kernelSize = this.kernel.Length;
Span sourceBuffer = span.Slice(0, this.bounds.Width);
Span targetBuffer = span.Slice(this.bounds.Width);
@@ -226,7 +218,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Numerics.Premultiply(sourceBuffer);
ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer);
- ref float kernelBase = ref this.kernelMatrix[0, 0];
+ ref float kernelBase = ref this.kernel[0];
ref int sampleColumnBase = ref MemoryMarshal.GetReference(this.map.GetColumnOffsetSpan());
for (int x = 0; x < sourceBuffer.Length; x++)
@@ -261,7 +253,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly Buffer2D targetPixels;
private readonly Buffer2D sourcePixels;
private readonly KernelSamplingMap map;
- private readonly DenseMatrix kernelMatrix;
+ private readonly float[] kernel;
private readonly Configuration configuration;
private readonly bool preserveAlpha;
@@ -271,7 +263,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Buffer2D targetPixels,
Buffer2D sourcePixels,
KernelSamplingMap map,
- DenseMatrix kernelMatrix,
+ float[] kernel,
Configuration configuration,
bool preserveAlpha)
{
@@ -279,7 +271,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
this.targetPixels = targetPixels;
this.sourcePixels = sourcePixels;
this.map = map;
- this.kernelMatrix = kernelMatrix;
+ this.kernel = kernel;
this.configuration = configuration;
this.preserveAlpha = preserveAlpha;
}
@@ -304,7 +296,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// Span is 2x bounds.
int boundsX = this.bounds.X;
int boundsWidth = this.bounds.Width;
- int kernelSize = this.kernelMatrix.Rows;
+ int kernelSize = this.kernel.Length;
Span sourceBuffer = span.Slice(0, this.bounds.Width);
Span targetBuffer = span.Slice(this.bounds.Width);
@@ -315,7 +307,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
targetBuffer.Clear();
ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer);
- ref float kernelBase = ref this.kernelMatrix[0, 0];
+ ref float kernelBase = ref this.kernel[0];
Span sourceRow;
for (int kY = 0; kY < kernelSize; kY++)
@@ -358,19 +350,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// Span is 2x bounds.
int boundsX = this.bounds.X;
int boundsWidth = this.bounds.Width;
- int kernelSize = this.kernelMatrix.Rows;
+ int kernelSize = this.kernel.Length;
Span sourceBuffer = span.Slice(0, this.bounds.Width);
Span targetBuffer = span.Slice(this.bounds.Width);
- var state = new ConvolutionState(in this.kernelMatrix, this.map);
- ref int sampleRowBase = ref state.GetSampleRow(y - this.bounds.Y);
+ ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (y - this.bounds.Y) * kernelSize);
// Clear the target buffer for each row run.
targetBuffer.Clear();
ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer);
- ref float kernelBase = ref this.kernelMatrix[0, 0];
+ ref float kernelBase = ref this.kernel[0];
for (int kY = 0; kY < kernelSize; kY++)
{
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs
index 9844f99563..f93cdabc47 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs
@@ -12,17 +12,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// See .
///
internal static int GetDefaultGaussianRadius(float sigma)
- {
- return (int)MathF.Ceiling(sigma * 3);
- }
+ => (int)MathF.Ceiling(sigma * 3);
///
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function.
///
- /// The .
- internal static DenseMatrix CreateGaussianBlurKernel(int size, float weight)
+ /// The convolution kernel.
+ internal static float[] CreateGaussianBlurKernel(int size, float weight)
{
- var kernel = new DenseMatrix(size, 1);
+ var kernel = new float[size];
float sum = 0F;
float midpoint = (size - 1) / 2F;
@@ -32,13 +30,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
float x = i - midpoint;
float gx = Numerics.Gaussian(x, weight);
sum += gx;
- kernel[0, i] = gx;
+ kernel[i] = gx;
}
// Normalize kernel so that the sum of all weights equals 1
for (int i = 0; i < size; i++)
{
- kernel[0, i] /= sum;
+ kernel[i] /= sum;
}
return kernel;
@@ -47,10 +45,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
///
- /// The .
- internal static DenseMatrix CreateGaussianSharpenKernel(int size, float weight)
+ /// The convolution kernel.
+ internal static float[] CreateGaussianSharpenKernel(int size, float weight)
{
- var kernel = new DenseMatrix(size, 1);
+ var kernel = new float[size];
float sum = 0;
@@ -60,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
float x = i - midpoint;
float gx = Numerics.Gaussian(x, weight);
sum += gx;
- kernel[0, i] = gx;
+ kernel[i] = gx;
}
// Invert the kernel for sharpening.
@@ -70,19 +68,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
if (i == midpointRounded)
{
// Calculate central value
- kernel[0, i] = (2F * sum) - kernel[0, i];
+ kernel[i] = (2F * sum) - kernel[i];
}
else
{
// invert value
- kernel[0, i] = -kernel[0, i];
+ kernel[i] = -kernel[i];
}
}
// Normalize kernel so that the sum of all weights equals 1
for (int i = 0; i < size; i++)
{
- kernel[0, i] /= sum;
+ kernel[i] /= sum;
}
return kernel;
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
index a9b692a015..4ade01f914 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
@@ -27,24 +27,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
: base(configuration, source, sourceRectangle)
{
int kernelSize = (definition.Radius * 2) + 1;
- this.KernelX = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma);
- this.KernelY = this.KernelX.Transpose();
+ this.Kernel = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma);
}
///
- /// Gets the horizontal gradient operator.
+ /// Gets the 1D convolution kernel.
///
- public DenseMatrix KernelX { get; }
-
- ///
- /// Gets the vertical gradient operator.
- ///
- public DenseMatrix KernelY { get; }
+ public float[] Kernel { get; }
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle);
+ using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle);
processor.Apply(source);
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
index 5e20865e5c..73aaaec188 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
@@ -27,24 +27,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
: base(configuration, source, sourceRectangle)
{
int kernelSize = (definition.Radius * 2) + 1;
- this.KernelX = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, definition.Sigma);
- this.KernelY = this.KernelX.Transpose();
+ this.Kernel = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, definition.Sigma);
}
///
- /// Gets the horizontal gradient operator.
+ /// Gets the 1D convolution kernel.
///
- public DenseMatrix KernelX { get; }
-
- ///
- /// Gets the vertical gradient operator.
- ///
- public DenseMatrix KernelY { get; }
+ public float[] Kernel { get; }
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle);
+ using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle);
processor.Apply(source);
}