Browse Source

Clean up Gaussian Blur.

Remove old code. Something is still not right here with smoothing not
even.


Former-commit-id: f1d5ac17714dbbf71aae5de61f7ed3c4538934ed
Former-commit-id: ec7f0d53d5ef132102808bcef83aea84453b0387
Former-commit-id: 81ca8195d1c72810e776de098086c00dc7aeecfc
af/merge-core
James Jackson-South 10 years ago
parent
commit
b60e2a4c95
  1. 99
      src/ImageProcessor/Filters/Convolution/Convolution2DFilter - Copy.cs
  2. 34
      src/ImageProcessor/Filters/Convolution/Convolution2DFilter.cs
  3. 97
      src/ImageProcessor/Filters/Convolution/GuassianBlur.cs
  4. 2
      src/ImageProcessor/project.lock.json.REMOVED.git-id
  5. 3
      tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs
  6. 2
      tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs
  7. 3
      tests/ImageProcessor.Tests/TestImages/Formats/Gif/ben2.gif
  8. 3
      tests/ImageProcessor.Tests/TestImages/Formats/Jpg/parachute.jpg

99
src/ImageProcessor/Filters/Convolution/Convolution2DFilter - Copy.cs

@ -1,99 +0,0 @@
// <copyright file="Convolution2DFilter.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Filters
{
using System;
using System.Threading.Tasks;
/// <summary>
/// Defines a filter that uses a matrix to perform convolution across two dimensions against an image.
/// </summary>
public abstract class Convolution2DFilter : ParallelImageProcessor
{
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public abstract float[,] KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public abstract float[,] KernelY { get; }
/// <inheritdoc/>
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);
}
}
});
}
}
}

34
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;
}
}
}

97
src/ImageProcessor/Filters/Convolution/GuassianBlur.cs

@ -1,28 +1,47 @@
namespace ImageProcessor.Filters.Convolution
// <copyright file="GuassianBlur.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Filters
{
using System;
/// <summary>
/// Applies a Gaussian blur to the image.
/// TODO: Something is not right here. The output blur is more like a motion blur.
/// </summary>
public class GuassianBlur : Convolution2DFilter
{
private int kernelSize;
/// <summary>
/// The maximum size of the kernal in either direction.
/// </summary>
private readonly int kernelSize;
private float standardDeviation;
/// <summary>
/// The standard deviation (weight)
/// </summary>
private readonly float standardDeviation;
/// <summary>
/// The vertical kernel
/// </summary>
private float[,] kernelY;
/// <summary>
/// The horizontal kernel
/// </summary>
private float[,] kernelX;
/// <summary>
/// Initializes a new instance of the <see cref="GuassianBlur"/> class.
/// </summary>
/// <param name="size">
/// The size.
/// </param>
/// <param name="standardDeviation">
/// The standard deviation 'sigma' value for calculating Gaussian curves.
/// </param>
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 @@
}
}
/// <summary>
/// Create a 2 dimensional Gaussian kernel using the Gaussian G(x y) function
/// </summary>
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;
}
/// <summary>
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary>
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
/// <returns>The <see cref="T:float[,]"/></returns>
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;
}
/// <summary>
/// Implementation of 2D Gaussian G(x) function
/// </summary>
/// <param name="x">The x provided to G(x y)</param>
/// <param name="y">The y provided to G(x y)</param>
/// <returns>The Gaussian G(x y)</returns>
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;
}
}
}

2
src/ImageProcessor/project.lock.json.REMOVED.git-id

@ -1 +1 @@
3f05708641eb3ed085d4689aae4a960eb067fd16
eb00c54ee74016c2b70f81963e7e8f83cb2dd54b

3
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]

2
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"

3
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

3
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
Loading…
Cancel
Save