Browse Source

Further refactor on Gaussian stuff

pull/904/head
Anton Firszov 7 years ago
parent
commit
a117dbb873
  1. 26
      src/ImageSharp/Processing/GaussianSharpenExtensions.cs
  2. 93
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs
  3. 16
      src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
  4. 37
      src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
  5. 101
      src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
  6. 42
      src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
  7. 6
      tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs

26
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
{
/// <summary>
/// Adds Gaussian sharpening extensions to the <see cref="Image{TPixel}"/> type.
/// Adds Gaussian sharpening extensions to the <see cref="Image"/> type.
/// </summary>
public static class GaussianSharpenExtensions
{
/// <summary>
/// Applies a Gaussian sharpening filter to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> GaussianSharpen<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new GaussianSharpenProcessor<TPixel>());
public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) =>
source.ApplyProcessor(new GaussianSharpenProcessor());
/// <summary>
/// Applies a Gaussian sharpening filter to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> GaussianSharpen<TPixel>(this IImageProcessingContext<TPixel> source, float sigma)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new GaussianSharpenProcessor<TPixel>(sigma));
public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) =>
source.ApplyProcessor(new GaussianSharpenProcessor(sigma));
/// <summary>
/// Applies a Gaussian sharpening filter to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> GaussianSharpen<TPixel>(this IImageProcessingContext<TPixel> source, float sigma, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new GaussianSharpenProcessor<TPixel>(sigma), rectangle);
public static IImageProcessingContext GaussianSharpen(
this IImageProcessingContext source,
float sigma,
Rectangle rectangle) =>
source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle);
}
}
}

93
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
{
/// <summary>
/// Kernel radius is calculated using the minimum viable value.
/// <see cref="http://chemaguerra.com/gaussian-filter-radius/"/>.
/// </summary>
internal static int GetDefaultGaussianRadius(float sigma)
{
return (int)MathF.Ceiling(sigma * 3);
}
/// <summary>
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary>
/// <returns>The <see cref="DenseMatrix{T}"/>.</returns>
internal static DenseMatrix<float> CreateGaussianBlurKernel(int size, float weight)
{
var kernel = new DenseMatrix<float>(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;
}
/// <summary>
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary>
/// <returns>The <see cref="DenseMatrix{T}"/>.</returns>
internal static DenseMatrix<float> CreateGaussianSharpenKernel(int size, float weight)
{
var kernel = new DenseMatrix<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;
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;
}
}
}

16
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
{
/// <summary>
/// Defines a gaussian blur processor with a (Sigma, Radius) pair.
/// Defines Gaussian blur by a (Sigma, Radius) pair.
/// </summary>
public class GaussianBlurProcessor : IImageProcessor
{
@ -21,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Initializes a new instance of the <see cref="GaussianBlurProcessor"/> class.
/// </summary>
public GaussianBlurProcessor()
: this(DefaultSigma, CalculateDefaultRadius(DefaultSigma))
: this(DefaultSigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(DefaultSigma))
{
}
@ -30,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// </summary>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
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<TPixel>(this);
}
/// <summary>
/// Kernel radius is calculated using the minimum viable value.
/// <see cref="http://chemaguerra.com/gaussian-filter-radius/"/>.
/// </summary>
private static int CalculateDefaultRadius(float sigma)
{
return (int)MathF.Ceiling(sigma * 3);
}
}
}

37
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<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly GaussianBlurProcessor definition;
/// <summary>
/// Initializes a new instance of the <see cref="GaussianBlurProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="GaussianBlurProcessor"/> defining the processor parameters.</param>
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);
/// <summary>
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary>
/// <returns>The <see cref="DenseMatrix{T}"/></returns>
private static DenseMatrix<float> CreateGaussianKernel(int size, float weight)
{
var kernel = new DenseMatrix<float>(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;
}
}
}

101
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
{
/// <summary>
/// Applies Gaussian sharpening processing to the image.
/// Defines Gaussian sharpening by a (Sigma, Radius) pair.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GaussianSharpenProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
public class GaussianSharpenProcessor : IImageProcessor
{
/// <summary>
/// The default value for <see cref="Sigma"/>.
/// </summary>
public const float DefaultSigma = 3f;
/// <summary>
/// The maximum size of the kernel in either direction.
/// Initializes a new instance of the <see cref="GaussianSharpenProcessor"/> class.
/// </summary>
private readonly int kernelSize;
public GaussianSharpenProcessor()
: this(DefaultSigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(DefaultSigma))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GaussianSharpenProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="GaussianSharpenProcessor"/> class.
/// </summary>
/// <param name="sigma">
/// The 'sigma' value representing the weight of the sharpening.
/// </param>
public GaussianSharpenProcessor(float sigma = 3F)
: this(sigma, (int)MathF.Ceiling(sigma * 3))
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
public GaussianSharpenProcessor(float sigma)
: this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma))
{
// Kernel radius is calculated using the minimum viable value.
// http://chemaguerra.com/gaussian-filter-radius/
}
/// <summary>
/// Initializes a new instance of the <see cref="GaussianSharpenProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="GaussianSharpenProcessor"/> class.
/// </summary>
/// <param name="radius">
/// The 'radius' value representing the size of the area to sample.
@ -45,10 +45,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <summary>
/// Initializes a new instance of the <see cref="GaussianSharpenProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="GaussianSharpenProcessor"/> class.
/// </summary>
/// <param name="sigma">
/// The 'sigma' value representing the weight of the sharpen.
/// The 'sigma' value representing the weight of the blur.
/// </param>
/// <param name="radius">
/// The 'radius' value representing the size of the area to sample.
@ -56,10 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// </param>
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;
}
/// <summary>
@ -68,63 +66,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public float Sigma { get; }
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public DenseMatrix<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// Gets the radius defining the size of the area to sample.
/// </summary>
public DenseMatrix<float> KernelY { get; }
public int Radius { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
=> new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration);
/// <summary>
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
/// </summary>
/// <returns>The <see cref="DenseMatrix{T}"/></returns>
private DenseMatrix<float> CreateGaussianKernel()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
int size = this.kernelSize;
float weight = this.Sigma;
var kernel = new DenseMatrix<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;
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<TPixel>(this);
}
}
}

42
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
{
/// <summary>
/// Applies Gaussian sharpening processing to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GaussianSharpenProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="GaussianSharpenProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="GaussianBlurProcessor"/> defining the processor parameters.</param>
public GaussianSharpenProcessor(GaussianSharpenProcessor definition)
{
int kernelSize = (definition.Radius * 2) + 1;
this.KernelX = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, definition.Sigma);
this.KernelY = this.KernelX.Transpose();
}
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public DenseMatrix<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public DenseMatrix<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
=> new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration);
}
}

6
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<GaussianSharpenProcessor<Rgba32>>();
var processor = this.Verify<GaussianSharpenProcessor>();
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<GaussianSharpenProcessor<Rgba32>>();
var processor = this.Verify<GaussianSharpenProcessor>();
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<GaussianSharpenProcessor<Rgba32>>(this.rect);
var processor = this.Verify<GaussianSharpenProcessor>(this.rect);
Assert.Equal(.6f, processor.Sigma);
}

Loading…
Cancel
Save