diff --git a/src/ImageProcessor/Filters/Convolution/Convolution2DFilter - Copy.cs b/src/ImageProcessor/Filters/Convolution/Convolution2DFilter - Copy.cs deleted file mode 100644 index d67b86462..000000000 --- a/src/ImageProcessor/Filters/Convolution/Convolution2DFilter - Copy.cs +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright (c) James South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Filters -{ - using System; - using System.Threading.Tasks; - - /// - /// Defines a filter that uses a matrix to perform convolution across two dimensions against an image. - /// - public abstract class Convolution2DFilter : ParallelImageProcessor - { - /// - /// Gets the horizontal gradient operator. - /// - public abstract float[,] KernelX { get; } - - /// - /// Gets the vertical gradient operator. - /// - public abstract float[,] KernelY { get; } - - /// - protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) - { - float[,] kernelX = this.KernelX; - float[,] kernelY = this.KernelY; - int kernelYLength = kernelY.GetLength(0); - int kernelXLength = kernelX.GetLength(0); - int radiusY = kernelYLength >> 1; - int radiusX = kernelXLength >> 1; - - int sourceY = sourceRectangle.Y; - int sourceBottom = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int maxY = sourceBottom - 1; - int maxX = endX - 1; - - Parallel.For( - startY, - endY, - y => - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) - { - float rX = 0; - float gX = 0; - float bX = 0; - float rY = 0; - float gY = 0; - float bY = 0; - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelYLength; fy++) - { - int fyr = fy - radiusY; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); - - for (int fx = 0; fx < kernelXLength; fx++) - { - int fxr = fx - radiusX; - int offsetX = x + fxr; - - offsetX = offsetX.Clamp(0, maxX); - - Color currentColor = source[offsetX, offsetY]; - float r = currentColor.R; - float g = currentColor.G; - float b = currentColor.B; - - rX += kernelX[fx, fy] * r; - gX += kernelX[fx, fy] * g; - bX += kernelX[fx, fy] * b; - - rY += kernelY[fy, fx] * r; - gY += kernelY[fy, fx] * g; - bY += kernelY[fy, fx] * b; - } - } - - float red = (float)Math.Sqrt((rX * rX) + (rY * rY)); - float green = (float)Math.Sqrt((gX * gX) + (gY * gY)); - float blue = (float)Math.Sqrt((bX * bX) + (bY * bY)); - - target[x, y] = new Color(red, green, blue); - } - } - }); - } - } -} diff --git a/src/ImageProcessor/Filters/Convolution/Convolution2DFilter.cs b/src/ImageProcessor/Filters/Convolution/Convolution2DFilter.cs index 47cf25a7c..c50bc4e45 100644 --- a/src/ImageProcessor/Filters/Convolution/Convolution2DFilter.cs +++ b/src/ImageProcessor/Filters/Convolution/Convolution2DFilter.cs @@ -28,8 +28,12 @@ namespace ImageProcessor.Filters { float[,] kernelX = this.KernelX; float[,] kernelY = this.KernelY; - int kernelLength = kernelX.GetLength(0); - int radius = kernelLength >> 1; + int kernelYHeight = kernelY.GetLength(0); + int kernelYWidth = kernelY.GetLength(1); + int kernelXHeight = kernelX.GetLength(0); + int kernelXWidth = kernelX.GetLength(0); + int radiusY = kernelYHeight >> 1; + int radiusX = kernelXWidth >> 1; int sourceY = sourceRectangle.Y; int sourceBottom = sourceRectangle.Bottom; @@ -55,16 +59,16 @@ namespace ImageProcessor.Filters float bY = 0; // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelLength; fy++) + for (int fy = 0; fy < kernelYHeight; fy++) { - int fyr = fy - radius; + int fyr = fy - radiusY; int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); - for (int fx = 0; fx < kernelLength; fx++) + for (int fx = 0; fx < kernelXWidth; fx++) { - int fxr = fx - radius; + int fxr = fx - radiusX; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); @@ -74,13 +78,19 @@ namespace ImageProcessor.Filters float g = currentColor.G; float b = currentColor.B; - rX += kernelX[fy, fx] * r; - gX += kernelX[fy, fx] * g; - bX += kernelX[fy, fx] * b; + if (fy < kernelXHeight) + { + rX += kernelX[fy, fx] * r; + gX += kernelX[fy, fx] * g; + bX += kernelX[fy, fx] * b; + } - rY += kernelY[fy, fx] * r; - gY += kernelY[fy, fx] * g; - bY += kernelY[fy, fx] * b; + if (fx < kernelYWidth) + { + rY += kernelY[fy, fx] * r; + gY += kernelY[fy, fx] * g; + bY += kernelY[fy, fx] * b; + } } } diff --git a/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs b/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs index d760221e3..e95f848ac 100644 --- a/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs +++ b/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs @@ -1,28 +1,47 @@ -namespace ImageProcessor.Filters.Convolution +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Filters { using System; + /// + /// Applies a Gaussian blur to the image. + /// TODO: Something is not right here. The output blur is more like a motion blur. + /// public class GuassianBlur : Convolution2DFilter { - private int kernelSize; + /// + /// The maximum size of the kernal in either direction. + /// + private readonly int kernelSize; - private float standardDeviation; + /// + /// The standard deviation (weight) + /// + private readonly float standardDeviation; + /// + /// The vertical kernel + /// private float[,] kernelY; + + /// + /// The horizontal kernel + /// private float[,] kernelX; /// /// Initializes a new instance of the class. /// - /// - /// The size. - /// /// /// The standard deviation 'sigma' value for calculating Gaussian curves. /// - public GuassianBlur(int size, float standardDeviation = 1.4f) + public GuassianBlur(float standardDeviation = 3f) { - this.kernelSize = size; + this.kernelSize = ((int)Math.Ceiling(standardDeviation * 3) * 2) + 1; this.standardDeviation = standardDeviation; } @@ -46,44 +65,10 @@ } } - /// - /// Create a 2 dimensional Gaussian kernel using the Gaussian G(x y) function - /// - private void CreateGaussianKernel2D() - { - int size = this.kernelSize; - float[,] kernel = new float[size, size]; - - int midpoint = size / 2; - float sum = 0; - for (int i = 0; i < size; i++) - { - int x = i - midpoint; - - for (int j = 0; j < size; j++) - { - int y = j - midpoint; - float gxy = this.Gaussian2D(x, y); - sum += gxy; - kernel[i, j] = gxy; - } - } - - // Normalise kernel so that the sum of all weights equals 1 - //for (int i = 0; i < size; i++) - //{ - // for (int j = 0; j < size; j++) - // { - // kernel[i, 0] = kernel[i, j] / sum; - // } - //} - - this.kernelY = kernel; - } - /// /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// + /// Whether to calculate a horizontal kernel. /// The private float[,] CreateGaussianKernel(bool horizontal) { @@ -91,10 +76,10 @@ float[,] kernel = horizontal ? new float[1, size] : new float[size, 1]; float sum = 0.0f; - int midpoint = size / 2; + float midpoint = (size - 1) / 2f; for (int i = 0; i < size; i++) { - int x = i - midpoint; + float x = i - midpoint; float gx = this.Gaussian(x); sum += gx; if (horizontal) @@ -145,27 +130,5 @@ return left * right; } - - /// - /// Implementation of 2D Gaussian G(x) function - /// - /// The x provided to G(x y) - /// The y provided to G(x y) - /// The Gaussian G(x y) - private float Gaussian2D(float x, float y) - { - const float Numerator = 1.0f; - float deviation = this.standardDeviation; - double pow = Math.Pow(deviation, 2); - float denominator = (float)((2 * Math.PI) * pow); - - float exponentNumerator = (-x * x) + (-y * y); - float exponentDenominator = (float)(2 * pow); - - float left = Numerator / denominator; - float right = (float)Math.Exp(exponentNumerator / exponentDenominator); - - return left * right; - } } } diff --git a/src/ImageProcessor/project.lock.json.REMOVED.git-id b/src/ImageProcessor/project.lock.json.REMOVED.git-id index 24339fed2..dba2656f5 100644 --- a/src/ImageProcessor/project.lock.json.REMOVED.git-id +++ b/src/ImageProcessor/project.lock.json.REMOVED.git-id @@ -1 +1 @@ -3f05708641eb3ed085d4689aae4a960eb067fd16 \ No newline at end of file +eb00c54ee74016c2b70f81963e7e8f83cb2dd54b \ No newline at end of file diff --git a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs b/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs index 204210249..517738f10 100644 --- a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs @@ -6,7 +6,6 @@ namespace ImageProcessor.Tests using System.Numerics; using ImageProcessor.Filters; - using ImageProcessor.Filters.Convolution; using Xunit; @@ -39,7 +38,7 @@ namespace ImageProcessor.Tests //{ "RobertsCross", new RobertsCross() }, //{ "Scharr", new Scharr() }, //{ "Sobel", new Sobel() }, - { "GuassianBlur", new GuassianBlur(20) } + { "GuassianBlur", new GuassianBlur(5) } }; [Theory] diff --git a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs index 7ba500e5e..45339591e 100644 --- a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs +++ b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs @@ -22,6 +22,7 @@ namespace ImageProcessor.Tests //"../../TestImages/Formats/Jpg/Backdrop.jpg", //"../../TestImages/Formats/Jpg/Calliphora.jpg", "../../TestImages/Formats/Jpg/ant.jpg", + "../../TestImages/Formats/Jpg/parachute.jpg", //"../../TestImages/Formats/Jpg/lomo.jpg", //"../../TestImages/Formats/Jpg/shaftesbury.jpg", //"../../TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg", @@ -31,6 +32,7 @@ namespace ImageProcessor.Tests //"../../TestImages/Formats/Png/gamma-1.0-or-2.2.png", //"../../TestImages/Formats/Png/splash.png", //"../../TestImages/Formats/Gif/leaf.gif", + "../../TestImages/Formats/Gif/ben2.gif", //"../../TestImages/Formats/Gif/rings.gif", //"../../TestImages/Formats/Gif/ani2.gif", //"../../TestImages/Formats/Gif/giphy.gif" diff --git a/tests/ImageProcessor.Tests/TestImages/Formats/Gif/ben2.gif b/tests/ImageProcessor.Tests/TestImages/Formats/Gif/ben2.gif new file mode 100644 index 000000000..a91ea4a8e --- /dev/null +++ b/tests/ImageProcessor.Tests/TestImages/Formats/Gif/ben2.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e69e47e78320e848efd07d83e265328a749cafde65291c0d7e7bdba90ba658c3 +size 50960 diff --git a/tests/ImageProcessor.Tests/TestImages/Formats/Jpg/parachute.jpg b/tests/ImageProcessor.Tests/TestImages/Formats/Jpg/parachute.jpg new file mode 100644 index 000000000..0b155b75f --- /dev/null +++ b/tests/ImageProcessor.Tests/TestImages/Formats/Jpg/parachute.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88f4e06fb00ecdcc663a8ba4227dc89be2e3b000bd39246d6bc4adb6450520b3 +size 28324