Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Remove transposed 1D kernels, switch to float[] type
  • Loading branch information
Sergio0694 committed Dec 15, 2020
commit e57423218628abe26899fbfccaeca5397dcefa32
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -23,24 +24,18 @@ public BoxBlurProcessor(Configuration configuration, BoxBlurProcessor definition
: base(configuration, source, sourceRectangle)
{
int kernelSize = (definition.Radius * 2) + 1;
this.KernelX = CreateBoxKernel(kernelSize);
this.KernelY = this.KernelX.Transpose();
this.Kernel = CreateBoxKernel(kernelSize);
}

/// <summary>
/// Gets the horizontal gradient operator.
/// Gets the 1D convolution kernel.
/// </summary>
public DenseMatrix<float> KernelX { get; }

/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public DenseMatrix<float> KernelY { get; }
public float[] Kernel { get; }

/// <inheritdoc/>
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.Kernel, false, this.Source, this.SourceRectangle);

processor.Apply(source);
}
Expand All @@ -50,10 +45,12 @@ protected override void OnFrameApply(ImageFrame<TPixel> source)
/// </summary>
/// <param name="kernelSize">The maximum size of the kernel in either direction.</param>
/// <returns>The <see cref="DenseMatrix{T}"/>.</returns>
private static DenseMatrix<float> CreateBoxKernel(int kernelSize)
private static float[] CreateBoxKernel(int kernelSize)
{
var kernel = new DenseMatrix<float>(kernelSize, 1);
kernel.Fill(1F / kernelSize);
var kernel = new float[kernelSize];

kernel.AsSpan().Fill(1F / kernelSize);

return kernel;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,26 @@ internal class Convolution2PassProcessor<TPixel> : ImageProcessor<TPixel>
/// Initializes a new instance of the <see cref="Convolution2PassProcessor{TPixel}"/> class.
/// </summary>
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="kernelX">The horizontal gradient operator.</param>
/// <param name="kernelY">The vertical gradient operator.</param>
/// <param name="kernel">The 1D convolution kernel.</param>
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public Convolution2PassProcessor(
Configuration configuration,
in DenseMatrix<float> kernelX,
in DenseMatrix<float> kernelY,
float[] kernel,
bool preserveAlpha,
Image<TPixel> source,
Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
this.KernelX = kernelX;
this.KernelY = kernelY;
this.Kernel = kernel;
this.PreserveAlpha = preserveAlpha;
}

/// <summary>
/// Gets the horizontal convolution kernel.
/// Gets the convolution kernel.
/// </summary>
public DenseMatrix<float> KernelX { get; }

/// <summary>
/// Gets the vertical convolution kernel.
/// </summary>
public DenseMatrix<float> KernelY { get; }
public float[] Kernel { get; }

/// <summary>
/// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels.
Expand All @@ -71,15 +63,15 @@ protected override void OnFrameApply(ImageFrame<TPixel> source)
// 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(
interest,
firstPassPixels,
source.PixelBuffer,
mapXY,
this.KernelX,
this.Kernel,
this.Configuration,
this.PreserveAlpha);

Expand All @@ -94,7 +86,7 @@ protected override void OnFrameApply(ImageFrame<TPixel> source)
source.PixelBuffer,
firstPassPixels,
mapXY,
this.KernelY,
this.Kernel,
this.Configuration,
this.PreserveAlpha);

Expand All @@ -113,7 +105,7 @@ protected override void OnFrameApply(ImageFrame<TPixel> source)
private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> sourcePixels;
private readonly KernelSamplingMap map;
private readonly DenseMatrix<float> kernelMatrix;
private readonly float[] kernel;
private readonly Configuration configuration;
private readonly bool preserveAlpha;

Expand All @@ -123,15 +115,15 @@ public HorizontalConvolutionRowOperation(
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
KernelSamplingMap map,
DenseMatrix<float> kernelMatrix,
float[] kernel,
Configuration configuration,
bool preserveAlpha)
{
this.bounds = bounds;
this.targetPixels = targetPixels;
this.sourcePixels = sourcePixels;
this.map = map;
this.kernelMatrix = kernelMatrix;
this.kernel = kernel;
this.configuration = configuration;
this.preserveAlpha = preserveAlpha;
}
Expand All @@ -156,7 +148,7 @@ private void Convolve3(int y, Span<Vector4> span)
// 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<Vector4> sourceBuffer = span.Slice(0, this.bounds.Width);
Span<Vector4> targetBuffer = span.Slice(this.bounds.Width);
Expand All @@ -170,7 +162,7 @@ private void Convolve3(int y, Span<Vector4> span)
PixelOperations<TPixel>.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++)
Expand Down Expand Up @@ -210,7 +202,7 @@ private void Convolve4(int y, Span<Vector4> span)
// 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<Vector4> sourceBuffer = span.Slice(0, this.bounds.Width);
Span<Vector4> targetBuffer = span.Slice(this.bounds.Width);
Expand All @@ -226,7 +218,7 @@ private void Convolve4(int y, Span<Vector4> span)
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++)
Expand Down Expand Up @@ -261,7 +253,7 @@ private void Convolve4(int y, Span<Vector4> span)
private readonly Buffer2D<TPixel> targetPixels;
private readonly Buffer2D<TPixel> sourcePixels;
private readonly KernelSamplingMap map;
private readonly DenseMatrix<float> kernelMatrix;
private readonly float[] kernel;
private readonly Configuration configuration;
private readonly bool preserveAlpha;

Expand All @@ -271,15 +263,15 @@ public VerticalConvolutionRowOperation(
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
KernelSamplingMap map,
DenseMatrix<float> kernelMatrix,
float[] kernel,
Configuration configuration,
bool preserveAlpha)
{
this.bounds = bounds;
this.targetPixels = targetPixels;
this.sourcePixels = sourcePixels;
this.map = map;
this.kernelMatrix = kernelMatrix;
this.kernel = kernel;
this.configuration = configuration;
this.preserveAlpha = preserveAlpha;
}
Expand All @@ -304,7 +296,7 @@ private void Convolve3(int y, Span<Vector4> span)
// 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<Vector4> sourceBuffer = span.Slice(0, this.bounds.Width);
Span<Vector4> targetBuffer = span.Slice(this.bounds.Width);
Expand All @@ -315,7 +307,7 @@ private void Convolve3(int y, Span<Vector4> span)
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<TPixel> sourceRow;
for (int kY = 0; kY < kernelSize; kY++)
Expand Down Expand Up @@ -358,19 +350,18 @@ private void Convolve4(int y, Span<Vector4> span)
// 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<Vector4> sourceBuffer = span.Slice(0, this.bounds.Width);
Span<Vector4> 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++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,15 @@ internal static class ConvolutionProcessorHelpers
/// See <see href="http://chemaguerra.com/gaussian-filter-radius/"/>.
/// </summary>
internal static int GetDefaultGaussianRadius(float sigma)
{
return (int)MathF.Ceiling(sigma * 3);
}
=> (int)MathF.Ceiling(sigma * 3);

/// <summary>
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function.
/// </summary>
/// <returns>The <see cref="DenseMatrix{T}"/>.</returns>
internal static DenseMatrix<float> CreateGaussianBlurKernel(int size, float weight)
/// <returns>The convolution kernel.</returns>
internal static float[] CreateGaussianBlurKernel(int size, float weight)
{
var kernel = new DenseMatrix<float>(size, 1);
var kernel = new float[size];

float sum = 0F;
float midpoint = (size - 1) / 2F;
Expand All @@ -32,13 +30,13 @@ internal static DenseMatrix<float> CreateGaussianBlurKernel(int size, float weig
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;
Expand All @@ -47,10 +45,10 @@ internal static DenseMatrix<float> CreateGaussianBlurKernel(int size, float weig
/// <summary>
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary>
/// <returns>The <see cref="DenseMatrix{T}"/>.</returns>
internal static DenseMatrix<float> CreateGaussianSharpenKernel(int size, float weight)
/// <returns>The convolution kernel.</returns>
internal static float[] CreateGaussianSharpenKernel(int size, float weight)
{
var kernel = new DenseMatrix<float>(size, 1);
var kernel = new float[size];

float sum = 0;

Expand All @@ -60,7 +58,7 @@ internal static DenseMatrix<float> CreateGaussianSharpenKernel(int size, float w
float x = i - midpoint;
float gx = Numerics.Gaussian(x, weight);
sum += gx;
kernel[0, i] = gx;
kernel[i] = gx;
}

// Invert the kernel for sharpening.
Expand All @@ -70,19 +68,19 @@ internal static DenseMatrix<float> CreateGaussianSharpenKernel(int size, float w
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,18 @@ public GaussianBlurProcessor(
: 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);
}

/// <summary>
/// Gets the horizontal gradient operator.
/// Gets the 1D convolution kernel.
/// </summary>
public DenseMatrix<float> KernelX { get; }

/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public DenseMatrix<float> KernelY { get; }
public float[] Kernel { get; }

/// <inheritdoc/>
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.Kernel, false, this.Source, this.SourceRectangle);

processor.Apply(source);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,18 @@ public GaussianSharpenProcessor(
: 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);
}

/// <summary>
/// Gets the horizontal gradient operator.
/// Gets the 1D convolution kernel.
/// </summary>
public DenseMatrix<float> KernelX { get; }

/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public DenseMatrix<float> KernelY { get; }
public float[] Kernel { get; }

/// <inheritdoc/>
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.Kernel, false, this.Source, this.SourceRectangle);

processor.Apply(source);
}
Expand Down