diff --git a/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs b/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs index c142b62cd..0532e7b7f 100644 --- a/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs +++ b/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs @@ -8,7 +8,7 @@ namespace ImageProcessor.Filters using System; /// - /// Applies a Gaussian blur to the image. + /// Applies a Gaussian blur filter to the image. /// public class GuassianBlur : Convolution2PassFilter { diff --git a/src/ImageProcessor/Filters/Convolution/GuassianSharpen.cs b/src/ImageProcessor/Filters/Convolution/GuassianSharpen.cs new file mode 100644 index 000000000..fd7befdad --- /dev/null +++ b/src/ImageProcessor/Filters/Convolution/GuassianSharpen.cs @@ -0,0 +1,153 @@ +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Filters +{ + using System; + + /// + /// Applies a Gaussian sharpening filter to the image. + /// + public class GuassianSharpen : Convolution2PassFilter + { + /// + /// The maximum size of the kernal in either direction. + /// + private readonly int kernelSize; + + /// + /// The spread of the blur. + /// + private readonly float sigma; + + /// + /// The vertical kernel + /// + private float[,] kernelY; + + /// + /// The horizontal kernel + /// + private float[,] kernelX; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The 'sigma' value representing the weight of the sharpening. + /// + public GuassianSharpen(float sigma = 3f) + { + this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1; + this.sigma = sigma; + } + + /// + public override float[,] KernelX => this.kernelX; + + /// + public override float[,] KernelY => this.kernelY; + + /// + public override int Parallelism => 1; + + /// + protected override void OnApply(Rectangle targetRectangle, Rectangle sourceRectangle) + { + if (this.kernelY == null) + { + this.kernelY = this.CreateGaussianKernel(false); + } + + if (this.kernelX == null) + { + this.kernelX = this.CreateGaussianKernel(true); + } + } + + /// + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// + /// Whether to calculate a horizontal kernel. + /// The + private float[,] CreateGaussianKernel(bool horizontal) + { + int size = this.kernelSize; + float weight = this.sigma; + float[,] kernel = horizontal ? new float[1, size] : new float[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; + if (horizontal) + { + kernel[0, i] = gx; + } + else + { + kernel[i, 0] = gx; + } + } + + // Invert the kernel for sharpening. + int midpointRounded = (int)midpoint; + + if (horizontal) + { + 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]; + } + } + } + else + { + for (int i = 0; i < size; i++) + { + if (i == midpointRounded) + { + // Calculate central value + kernel[i, 0] = (2 * sum) - kernel[i, 0]; + } + else + { + // invert value + kernel[i, 0] = -kernel[i, 0]; + } + } + } + + // Normalise kernel so that the sum of all weights equals 1 + if (horizontal) + { + for (int i = 0; i < size; i++) + { + kernel[0, i] = kernel[0, i] / sum; + } + } + else + { + for (int i = 0; i < size; i++) + { + kernel[i, 0] = kernel[i, 0] / sum; + } + } + + return kernel; + } + } +} diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 19f6584e8..8e9ef8c0a 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -66,6 +66,7 @@ + diff --git a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs b/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs index 4db94c522..98102f9b1 100644 --- a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs @@ -38,7 +38,8 @@ namespace ImageProcessor.Tests //{ "RobertsCross", new RobertsCross() }, //{ "Scharr", new Scharr() }, //{ "Sobel", new Sobel() }, - { "GuassianBlur", new GuassianBlur(10) } + //{ "GuassianBlur", new GuassianBlur() }, + { "GuassianSharpen", new GuassianSharpen() } }; [Theory]