// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Processing.Processors { using System; /// /// Applies a Gaussian blur sampler to the image. /// /// The pixel format. public class GaussianBlurProcessor : ImageProcessor where TColor : struct, IPackedPixel, IEquatable { /// /// The maximum size of the kernel in either direction. /// private readonly int kernelSize; /// /// The spread of the blur. /// private readonly float sigma; /// /// Initializes a new instance of the class. /// /// The 'sigma' value representing the weight of the blur. public GaussianBlurProcessor(float sigma = 3f) { this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1; this.sigma = sigma; this.KernelX = this.CreateGaussianKernel(true); this.KernelY = this.CreateGaussianKernel(false); } /// /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. /// public GaussianBlurProcessor(int radius) { this.kernelSize = (radius * 2) + 1; this.sigma = radius; this.KernelX = this.CreateGaussianKernel(true); this.KernelY = this.CreateGaussianKernel(false); } /// /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the blur. /// /// /// The 'radius' value representing the size of the area to sample. /// This should be at least twice the sigma value. /// public GaussianBlurProcessor(float sigma, int radius) { this.kernelSize = (radius * 2) + 1; this.sigma = sigma; this.KernelX = this.CreateGaussianKernel(true); this.KernelY = this.CreateGaussianKernel(false); } /// /// Gets the horizontal gradient operator. /// public float[][] KernelX { get; } /// /// Gets the vertical gradient operator. /// public float[][] KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); } /// /// 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][] : new float[size][]; if (horizontal) { kernel[0] = new float[size]; } float sum = 0.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; if (horizontal) { kernel[0][i] = gx; } else { kernel[i] = new[] { gx }; } } // 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; } } }