From a117dbb873afaa993b56b8630a7817f4f67ab114 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 23:53:00 +0200 Subject: [PATCH] Further refactor on Gaussian stuff --- .../Processing/GaussianSharpenExtensions.cs | 26 ++--- .../ConvolutionProcessorHelpers.cs | 93 ++++++++++++++++ .../Convolution/GaussianBlurProcessor.cs | 16 +-- .../GaussianBlurProcessor{TPixel}.cs | 37 +------ .../Convolution/GaussianSharpenProcessor.cs | 101 +++++------------- .../GaussianSharpenProcessor{TPixel}.cs | 42 ++++++++ .../Convolution/GaussianSharpenTest.cs | 6 +- 7 files changed, 180 insertions(+), 141 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs index 675bbc142d..64aa7b5db3 100644 --- a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs +++ b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs @@ -1,50 +1,46 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian sharpening extensions to the type. + /// Adds Gaussian sharpening extensions to the type. /// public static class GaussianSharpenExtensions { /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianSharpenProcessor()); + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) => + source.ApplyProcessor(new GaussianSharpenProcessor()); /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) => + source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); /// /// Applies a Gaussian sharpening filter 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 GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); + public static IImageProcessingContext GaussianSharpen( + this IImageProcessingContext source, + float sigma, + Rectangle rectangle) => + source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs new file mode 100644 index 0000000000..bb0072b82e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs @@ -0,0 +1,93 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + internal static class ConvolutionProcessorHelpers + { + /// + /// Kernel radius is calculated using the minimum viable value. + /// . + /// + internal static int GetDefaultGaussianRadius(float sigma) + { + return (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) + { + 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; + } + + /// + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// + /// The . + internal static DenseMatrix CreateGaussianSharpenKernel(int size, float weight) + { + var kernel = new DenseMatrix(size, 1); + + float sum = 0; + + 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; + } + + // Invert the kernel for sharpening. + int midpointRounded = (int)midpoint; + for (int i = 0; i < size; i++) + { + if (i == midpointRounded) + { + // Calculate central value + kernel[0, i] = (2F * sum) - kernel[0, i]; + } + else + { + // invert value + kernel[0, i] = -kernel[0, i]; + } + } + + // 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/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 33e2ef955d..c64a024f9f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -1,14 +1,13 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Defines a gaussian blur processor with a (Sigma, Radius) pair. + /// Defines Gaussian blur by a (Sigma, Radius) pair. /// public class GaussianBlurProcessor : IImageProcessor { @@ -21,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// public GaussianBlurProcessor() - : this(DefaultSigma, CalculateDefaultRadius(DefaultSigma)) + : this(DefaultSigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(DefaultSigma)) { } @@ -30,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// The 'sigma' value representing the weight of the blur. public GaussianBlurProcessor(float sigma) - : this(sigma, CalculateDefaultRadius(sigma)) + : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma)) { } @@ -77,14 +76,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { return new GaussianBlurProcessor(this); } - - /// - /// Kernel radius is calculated using the minimum viable value. - /// . - /// - private static int CalculateDefaultRadius(float sigma) - { - 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 index 9b60fb2d2e..a129ff5473 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -14,17 +14,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution 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.KernelX = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma); this.KernelY = this.KernelX.Transpose(); } @@ -47,33 +44,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution 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/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 001471720a..bc06ee6182 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -3,38 +3,38 @@ using System; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies Gaussian sharpening processing to the image. + /// Defines Gaussian sharpening by a (Sigma, Radius) pair. /// - /// The pixel format. - internal class GaussianSharpenProcessor : ImageProcessor - where TPixel : struct, IPixel + public class GaussianSharpenProcessor : IImageProcessor { + /// + /// The default value for . + /// + public const float DefaultSigma = 3f; + /// - /// The maximum size of the kernel in either direction. + /// Initializes a new instance of the class. /// - private readonly int kernelSize; + public GaussianSharpenProcessor() + : this(DefaultSigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(DefaultSigma)) + { + } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// - /// The 'sigma' value representing the weight of the sharpening. - /// - public GaussianSharpenProcessor(float sigma = 3F) - : this(sigma, (int)MathF.Ceiling(sigma * 3)) + /// The 'sigma' value representing the weight of the blur. + public GaussianSharpenProcessor(float sigma) + : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(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. @@ -45,10 +45,10 @@ 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 sharpen. + /// The 'sigma' value representing the weight of the blur. /// /// /// The 'radius' value representing the size of the area to sample. @@ -56,10 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public GaussianSharpenProcessor(float sigma, int radius) { - this.kernelSize = (radius * 2) + 1; this.Sigma = sigma; - this.KernelX = this.CreateGaussianKernel(); - this.KernelY = this.KernelX.Transpose(); + this.Radius = radius; } /// @@ -68,63 +66,14 @@ 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); - - /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function - /// - /// The - private DenseMatrix CreateGaussianKernel() + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - int size = this.kernelSize; - float weight = this.Sigma; - var kernel = new DenseMatrix(size, 1); - - float sum = 0; - - 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; - } - - // Invert the kernel for sharpening. - int midpointRounded = (int)midpoint; - for (int i = 0; i < size; i++) - { - if (i == midpointRounded) - { - // Calculate central value - kernel[0, i] = (2F * sum) - kernel[0, i]; - } - else - { - // invert value - kernel[0, i] = -kernel[0, i]; - } - } - - // 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 new GaussianSharpenProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs new file mode 100644 index 0000000000..edde9f9e7f --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs @@ -0,0 +1,42 @@ +// 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 sharpening processing to the image. + /// + /// The pixel format. + internal class GaussianSharpenProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public GaussianSharpenProcessor(GaussianSharpenProcessor definition) + { + int kernelSize = (definition.Radius * 2) + 1; + this.KernelX = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(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); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs index 675498745e..7c2205f4e4 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianSharpen_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(); - 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 GaussianSharpen_amount_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(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 GaussianSharpen_amount_rect_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(0.6f, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(.6f, processor.Sigma); }