// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Processing.Processors { using System; /// /// Applies a Gaussian sharpening sampler to the image. /// /// The pixel format. public class GaussianSharpenProcessor : 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 sharpening. /// public GaussianSharpenProcessor(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 GaussianSharpenProcessor(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 sharpen. /// /// /// The 'radius' value representing the size of the area to sample. /// This should be at least twice the sigma value. /// public GaussianSharpenProcessor(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 Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// public Fast2DArray 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 Fast2DArray CreateGaussianKernel(bool horizontal) { int size = this.kernelSize; float weight = this.sigma; Fast2DArray kernel = horizontal ? new Fast2DArray(size, 1) : new Fast2DArray(1, size); 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; if (horizontal) { kernel[0, i] = gx; } else { kernel[i, 0] = gx; } } // Invert the kernel for sharpening. int midpointRounded = (int)midpoint; if (horizontal) { 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]; } } } else { for (int i = 0; i < size; i++) { if (i == midpointRounded) { // Calculate central value kernel[i, 0] = (2 * sum) - kernel[i, 0]; } else { // invert value kernel[i, 0] = -kernel[i, 0]; } } } // 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; } } }