Browse Source

refactor BoxBlur and GaussianBlur

pull/904/head
Anton Firszov 7 years ago
parent
commit
ff48c62e71
  1. 20
      src/ImageSharp/Processing/BoxBlurExtensions.cs
  2. 20
      src/ImageSharp/Processing/GaussianBlurExtensions.cs
  3. 53
      src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
  4. 64
      src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs
  5. 82
      src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
  6. 79
      src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
  7. 6
      tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs
  8. 6
      tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs

20
src/ImageSharp/Processing/BoxBlurExtensions.cs

@ -8,43 +8,37 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds box blurring extensions to the <see cref="Image{TPixel}"/> type.
/// Adds box blurring extensions to the <see cref="Image"/> type.
/// </summary>
public static class BoxBlurExtensions
{
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> BoxBlur<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new BoxBlurProcessor<TPixel>());
public static IImageProcessingContext BoxBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new BoxBlurProcessor());
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> BoxBlur<TPixel>(this IImageProcessingContext<TPixel> source, int radius)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new BoxBlurProcessor<TPixel>(radius));
public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius)
=> source.ApplyProcessor(new BoxBlurProcessor(radius));
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> BoxBlur<TPixel>(this IImageProcessingContext<TPixel> source, int radius, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new BoxBlurProcessor<TPixel>(radius), rectangle);
public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle)
=> source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle);
}
}

20
src/ImageSharp/Processing/GaussianBlurExtensions.cs

@ -8,43 +8,37 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds Gaussian blurring extensions to the <see cref="Image{TPixel}"/> type.
/// Adds Gaussian blurring extensions to the <see cref="Image"/> type.
/// </summary>
public static class GaussianBlurExtensions
{
/// <summary>
/// Applies a Gaussian blur to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> GaussianBlur<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new GaussianBlurProcessor<TPixel>());
public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new GaussianBlurProcessor());
/// <summary>
/// Applies a Gaussian blur to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> GaussianBlur<TPixel>(this IImageProcessingContext<TPixel> source, float sigma)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new GaussianBlurProcessor<TPixel>(sigma));
public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma)
=> source.ApplyProcessor(new GaussianBlurProcessor(sigma));
/// <summary>
/// Applies a Gaussian blur to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> GaussianBlur<TPixel>(this IImageProcessingContext<TPixel> source, float sigma, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new GaussianBlurProcessor<TPixel>(sigma), rectangle);
public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle)
=> source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle);
}
}

53
src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs

@ -3,67 +3,48 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
/// Applies box blur processing to the image.
/// Defines a box blur processor of a given Radius.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class BoxBlurProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
public class BoxBlurProcessor : IImageProcessor
{
/// <summary>
/// The maximum size of the kernel in either direction.
/// The default radius used by the parameterless constructor.
/// </summary>
private readonly int kernelSize;
public const int DefaultRadius = 7;
/// <summary>
/// Initializes a new instance of the <see cref="BoxBlurProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="BoxBlurProcessor"/> class.
/// </summary>
/// <param name="radius">
/// The 'radius' value representing the size of the area to sample.
/// </param>
public BoxBlurProcessor(int radius = 7)
public BoxBlurProcessor(int radius)
{
this.Radius = radius;
this.kernelSize = (radius * 2) + 1;
this.KernelX = this.CreateBoxKernel();
this.KernelY = this.KernelX.Transpose();
}
/// <summary>
/// Gets the Radius
/// Initializes a new instance of the <see cref="BoxBlurProcessor"/> class.
/// </summary>
public int Radius { get; }
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public DenseMatrix<float> KernelX { get; }
public BoxBlurProcessor()
: this(DefaultRadius)
{
}
/// <summary>
/// Gets the vertical gradient operator.
/// Gets the Radius.
/// </summary>
public DenseMatrix<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
=> new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration);
public int Radius { get; }
/// <summary>
/// Create a 1 dimensional Box kernel.
/// </summary>
/// <returns>The <see cref="DenseMatrix{T}"/></returns>
private DenseMatrix<float> CreateBoxKernel()
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
int size = this.kernelSize;
var kernel = new DenseMatrix<float>(size, 1);
kernel.Fill(1F / size);
return kernel;
return new BoxBlurProcessor<TPixel>(this);
}
}
}

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

@ -0,0 +1,64 @@
// // Copyright (c) Six Labors and contributors.
// // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
/// Applies box blur processing to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class BoxBlurProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly BoxBlurProcessor definition;
/// <summary>
/// Initializes a new instance of the <see cref="BoxBlurProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="BoxBlurProcessor"/> defining the processor parameters.</param>
public BoxBlurProcessor(BoxBlurProcessor definition)
{
this.definition = definition;
int kernelSize = (definition.Radius * 2) + 1;
this.KernelX = CreateBoxKernel(kernelSize);
this.KernelY = this.KernelX.Transpose();
}
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public DenseMatrix<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public DenseMatrix<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration) =>
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false).Apply(
source,
sourceRectangle,
configuration);
/// <summary>
/// Create a 1 dimensional Box kernel.
/// </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)
{
var kernel = new DenseMatrix<float>(kernelSize, 1);
kernel.Fill(1F / kernelSize);
return kernel;
}
}
}

82
src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs

@ -4,35 +4,38 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
/// Applies Gaussian blur processing to the image.
/// Defines a gaussian blur processor with a (Sigma, Radius) pair.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GaussianBlurProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
public class GaussianBlurProcessor : IImageProcessor
{
/// <summary>
/// The maximum size of the kernel in either direction.
/// The default value for <see cref="Sigma"/>.
/// </summary>
private readonly int kernelSize;
public const float DefaultSigma = 3f;
/// <summary>
/// Initializes a new instance of the <see cref="GaussianBlurProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="GaussianBlurProcessor"/> class.
/// </summary>
public GaussianBlurProcessor()
: this(DefaultSigma, CalculateDefaultRadius(DefaultSigma))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GaussianBlurProcessor"/> class.
/// </summary>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
public GaussianBlurProcessor(float sigma = 3F)
: this(sigma, (int)MathF.Ceiling(sigma * 3))
public GaussianBlurProcessor(float sigma)
: this(sigma, CalculateDefaultRadius(sigma))
{
// Kernel radius is calculated using the minimum viable value.
// http://chemaguerra.com/gaussian-filter-radius/
}
/// <summary>
/// Initializes a new instance of the <see cref="GaussianBlurProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="GaussianBlurProcessor"/> class.
/// </summary>
/// <param name="radius">
/// The 'radius' value representing the size of the area to sample.
@ -43,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <summary>
/// Initializes a new instance of the <see cref="GaussianBlurProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="GaussianBlurProcessor"/> class.
/// </summary>
/// <param name="sigma">
/// The 'sigma' value representing the weight of the blur.
@ -54,10 +57,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// </param>
public GaussianBlurProcessor(float sigma, int radius)
{
this.kernelSize = (radius * 2) + 1;
this.Sigma = sigma;
this.KernelX = this.CreateGaussianKernel();
this.KernelY = this.KernelX.Transpose();
this.Radius = radius;
}
/// <summary>
@ -66,47 +67,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public float Sigma { get; }
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public DenseMatrix<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// Gets the radius defining the size of the area to sample.
/// </summary>
public DenseMatrix<float> KernelY { get; }
public int Radius { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
=> new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration);
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
return new GaussianBlurProcessor<TPixel>(this);
}
/// <summary>
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// Kernel radius is calculated using the minimum viable value.
/// <see cref="http://chemaguerra.com/gaussian-filter-radius/"/>.
/// </summary>
/// <returns>The <see cref="DenseMatrix{T}"/></returns>
private DenseMatrix<float> CreateGaussianKernel()
private static int CalculateDefaultRadius(float sigma)
{
int size = this.kernelSize;
float weight = this.Sigma;
var kernel = new DenseMatrix<float>(size, 1);
float sum = 0F;
float midpoint = (size - 1) / 2F;
for (int i = 0; i < size; i++)
{
float x = i - midpoint;
float gx = ImageMaths.Gaussian(x, weight);
sum += gx;
kernel[0, i] = gx;
}
// Normalize kernel so that the sum of all weights equals 1
for (int i = 0; i < size; i++)
{
kernel[0, i] /= sum;
}
return kernel;
return (int)MathF.Ceiling(sigma * 3);
}
}
}

79
src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs

@ -0,0 +1,79 @@
// // Copyright (c) Six Labors and contributors.
// // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
/// Applies Gaussian blur processing to an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GaussianBlurProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly GaussianBlurProcessor definition;
/// <summary>
/// Initializes a new instance of the <see cref="GaussianBlurProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="GaussianBlurProcessor"/> defining the processor parameters.</param>
public GaussianBlurProcessor(GaussianBlurProcessor definition)
{
this.definition = definition;
int kernelSize = (definition.Radius * 2) + 1;
this.KernelX = CreateGaussianKernel(kernelSize, definition.Sigma);
this.KernelY = this.KernelX.Transpose();
}
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public DenseMatrix<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public DenseMatrix<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration) =>
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false).Apply(
source,
sourceRectangle,
configuration);
/// <summary>
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary>
/// <returns>The <see cref="DenseMatrix{T}"/></returns>
private static DenseMatrix<float> CreateGaussianKernel(int size, float weight)
{
var kernel = new DenseMatrix<float>(size, 1);
float sum = 0F;
float midpoint = (size - 1) / 2F;
for (int i = 0; i < size; i++)
{
float x = i - midpoint;
float gx = ImageMaths.Gaussian(x, weight);
sum += gx;
kernel[0, i] = gx;
}
// Normalize kernel so that the sum of all weights equals 1
for (int i = 0; i < size; i++)
{
kernel[0, i] /= sum;
}
return kernel;
}
}
}

6
tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution
public void BoxBlur_BoxBlurProcessorDefaultsSet()
{
this.operations.BoxBlur();
var processor = this.Verify<BoxBlurProcessor<Rgba32>>();
var processor = this.Verify<BoxBlurProcessor>();
Assert.Equal(7, processor.Radius);
}
@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution
public void BoxBlur_amount_BoxBlurProcessorDefaultsSet()
{
this.operations.BoxBlur(34);
var processor = this.Verify<BoxBlurProcessor<Rgba32>>();
var processor = this.Verify<BoxBlurProcessor>();
Assert.Equal(34, processor.Radius);
}
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution
public void BoxBlur_amount_rect_BoxBlurProcessorDefaultsSet()
{
this.operations.BoxBlur(5, this.rect);
var processor = this.Verify<BoxBlurProcessor<Rgba32>>(this.rect);
var processor = this.Verify<BoxBlurProcessor>(this.rect);
Assert.Equal(5, processor.Radius);
}

6
tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution
public void GaussianBlur_GaussianBlurProcessorDefaultsSet()
{
this.operations.GaussianBlur();
var processor = this.Verify<GaussianBlurProcessor<Rgba32>>();
var processor = this.Verify<GaussianBlurProcessor>();
Assert.Equal(3f, processor.Sigma);
}
@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution
public void GaussianBlur_amount_GaussianBlurProcessorDefaultsSet()
{
this.operations.GaussianBlur(0.2f);
var processor = this.Verify<GaussianBlurProcessor<Rgba32>>();
var processor = this.Verify<GaussianBlurProcessor>();
Assert.Equal(.2f, processor.Sigma);
}
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution
public void GaussianBlur_amount_rect_GaussianBlurProcessorDefaultsSet()
{
this.operations.GaussianBlur(0.6f, this.rect);
var processor = this.Verify<GaussianBlurProcessor<Rgba32>>(this.rect);
var processor = this.Verify<GaussianBlurProcessor>(this.rect);
Assert.Equal(.6f, processor.Sigma);
}

Loading…
Cancel
Save