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