From ff48c62e7155e797a2982b37e61676da7db42319 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 23:16:11 +0200 Subject: [PATCH] refactor BoxBlur and GaussianBlur --- .../Processing/BoxBlurExtensions.cs | 20 ++--- .../Processing/GaussianBlurExtensions.cs | 20 ++--- .../Convolution/BoxBlurProcessor.cs | 53 ++++-------- .../Convolution/BoxBlurProcessor{TPixel}.cs | 64 +++++++++++++++ .../Convolution/GaussianBlurProcessor.cs | 82 +++++++------------ .../GaussianBlurProcessor{TPixel}.cs | 79 ++++++++++++++++++ .../Processing/Convolution/BoxBlurTest.cs | 6 +- .../Convolution/GaussianBlurTest.cs | 6 +- 8 files changed, 210 insertions(+), 120 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/BoxBlurExtensions.cs b/src/ImageSharp/Processing/BoxBlurExtensions.cs index 624da239b..bc6e06dd6 100644 --- a/src/ImageSharp/Processing/BoxBlurExtensions.cs +++ b/src/ImageSharp/Processing/BoxBlurExtensions.cs @@ -8,43 +8,37 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds box blurring extensions to the type. + /// Adds box blurring extensions to the type. /// public static class BoxBlurExtensions { /// /// Applies a box blur to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BoxBlurProcessor()); + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) + => source.ApplyProcessor(new BoxBlurProcessor()); /// /// Applies a box blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BoxBlurProcessor(radius)); + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) + => source.ApplyProcessor(new BoxBlurProcessor(radius)); /// /// Applies a box blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) + => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/GaussianBlurExtensions.cs index 165c4ce1a..1dab05457 100644 --- a/src/ImageSharp/Processing/GaussianBlurExtensions.cs +++ b/src/ImageSharp/Processing/GaussianBlurExtensions.cs @@ -8,43 +8,37 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian blurring extensions to the type. + /// Adds Gaussian blurring extensions to the type. /// public static class GaussianBlurExtensions { /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianBlurProcessor()); + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) + => source.ApplyProcessor(new GaussianBlurProcessor()); /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) + => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) + => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 3d5bdc42a..bb37489e1 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/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 { /// - /// Applies box blur processing to the image. + /// Defines a box blur processor of a given Radius. /// - /// The pixel format. - internal class BoxBlurProcessor : ImageProcessor - where TPixel : struct, IPixel + public class BoxBlurProcessor : IImageProcessor { /// - /// The maximum size of the kernel in either direction. + /// The default radius used by the parameterless constructor. /// - private readonly int kernelSize; + public const int DefaultRadius = 7; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. /// - 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(); } /// - /// Gets the Radius + /// Initializes a new instance of the class. /// - public int Radius { get; } - - /// - /// Gets the horizontal gradient operator. - /// - public DenseMatrix KernelX { get; } + public BoxBlurProcessor() + : this(DefaultRadius) + { + } /// - /// Gets the vertical gradient operator. + /// Gets the Radius. /// - public DenseMatrix KernelY { get; } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); + public int Radius { get; } - /// - /// Create a 1 dimensional Box kernel. - /// - /// The - private DenseMatrix CreateBoxKernel() + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - int size = this.kernelSize; - var kernel = new DenseMatrix(size, 1); - - kernel.Fill(1F / size); - - return kernel; + return new BoxBlurProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs new file mode 100644 index 000000000..ba092cb42 --- /dev/null +++ b/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 +{ + /// + /// Applies box blur processing to the image. + /// + /// The pixel format. + internal class BoxBlurProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly BoxBlurProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public BoxBlurProcessor(BoxBlurProcessor definition) + { + this.definition = definition; + int kernelSize = (definition.Radius * 2) + 1; + this.KernelX = CreateBoxKernel(kernelSize); + this.KernelY = this.KernelX.Transpose(); + } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) => + new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply( + source, + sourceRectangle, + configuration); + + /// + /// Create a 1 dimensional Box kernel. + /// + /// The maximum size of the kernel in either direction. + /// The . + private static DenseMatrix CreateBoxKernel(int kernelSize) + { + var kernel = new DenseMatrix(kernelSize, 1); + kernel.Fill(1F / kernelSize); + return kernel; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 0fc822d57..33e2ef955 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/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 { /// - /// Applies Gaussian blur processing to the image. + /// Defines a gaussian blur processor with a (Sigma, Radius) pair. /// - /// The pixel format. - internal class GaussianBlurProcessor : ImageProcessor - where TPixel : struct, IPixel + public class GaussianBlurProcessor : IImageProcessor { /// - /// The maximum size of the kernel in either direction. + /// The default value for . /// - private readonly int kernelSize; + public const float DefaultSigma = 3f; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. + /// + public GaussianBlurProcessor() + : this(DefaultSigma, CalculateDefaultRadius(DefaultSigma)) + { + } + + /// + /// Initializes a new instance of the class. /// /// The 'sigma' value representing the weight of the blur. - 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/ } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -43,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the blur. @@ -54,10 +57,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// 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; } /// @@ -66,47 +67,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public float Sigma { get; } /// - /// Gets the horizontal gradient operator. - /// - public DenseMatrix KernelX { get; } - - /// - /// Gets the vertical gradient operator. + /// Gets the radius defining the size of the area to sample. /// - public DenseMatrix KernelY { get; } + public int Radius { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + return new GaussianBlurProcessor(this); + } /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// Kernel radius is calculated using the minimum viable value. + /// . /// - /// The - private DenseMatrix CreateGaussianKernel() + private static int CalculateDefaultRadius(float sigma) { - int size = this.kernelSize; - float weight = this.Sigma; - var kernel = new DenseMatrix(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); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs new file mode 100644 index 000000000..9b60fb2d2 --- /dev/null +++ b/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 +{ + /// + /// Applies Gaussian blur processing to an image. + /// + /// The pixel format. + internal class GaussianBlurProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly GaussianBlurProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public GaussianBlurProcessor(GaussianBlurProcessor definition) + { + this.definition = definition; + int kernelSize = (definition.Radius * 2) + 1; + this.KernelX = CreateGaussianKernel(kernelSize, definition.Sigma); + this.KernelY = this.KernelX.Transpose(); + } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) => + new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply( + source, + sourceRectangle, + configuration); + + /// + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// + /// The + private static DenseMatrix CreateGaussianKernel(int size, float weight) + { + var kernel = new DenseMatrix(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; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs index e425b6315..8c659848f 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs +++ b/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>(); + var processor = this.Verify(); 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>(); + var processor = this.Verify(); 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>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(5, processor.Radius); } diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs index c87a834eb..0f64ebbeb 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs +++ b/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>(); + var processor = this.Verify(); 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>(); + var processor = this.Verify(); 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>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(.6f, processor.Sigma); }