mirror of https://github.com/SixLabors/ImageSharp
12 changed files with 316 additions and 67 deletions
@ -0,0 +1,89 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Processing.Processors.Convolution; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Extensions.Convolution; |
|||
|
|||
/// <summary>
|
|||
/// Defines general convolution extensions to apply on an <see cref="Image"/>
|
|||
/// using Mutate/Clone.
|
|||
/// </summary>
|
|||
public static class ConvolutionExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Applies a convolution filter to the image.
|
|||
/// </summary>
|
|||
/// <param name="source">The current image processing context.</param>
|
|||
/// <param name="kernelXY">The convolution kernel to apply.</param>
|
|||
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
|
|||
public static IImageProcessingContext Convolve(this IImageProcessingContext source, DenseMatrix<float> kernelXY) |
|||
=> Convolve(source, kernelXY, false); |
|||
|
|||
/// <summary>
|
|||
/// Applies a convolution filter to the image.
|
|||
/// </summary>
|
|||
/// <param name="source">The current image processing context.</param>
|
|||
/// <param name="kernelXY">The convolution kernel to apply.</param>
|
|||
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
|
|||
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
|
|||
public static IImageProcessingContext Convolve(this IImageProcessingContext source, DenseMatrix<float> kernelXY, bool preserveAlpha) |
|||
=> Convolve(source, kernelXY, preserveAlpha, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat); |
|||
|
|||
/// <summary>
|
|||
/// Applies a convolution filter to the image.
|
|||
/// </summary>
|
|||
/// <param name="source">The current image processing context.</param>
|
|||
/// <param name="kernelXY">The convolution kernel to apply.</param>
|
|||
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
|
|||
/// <param name="borderWrapModeX">The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in X direction.</param>
|
|||
/// <param name="borderWrapModeY">The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.</param>
|
|||
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
|
|||
public static IImageProcessingContext Convolve( |
|||
this IImageProcessingContext source, |
|||
DenseMatrix<float> kernelXY, |
|||
bool preserveAlpha, |
|||
BorderWrappingMode borderWrapModeX, |
|||
BorderWrappingMode borderWrapModeY) |
|||
=> source.ApplyProcessor(new ConvolutionProcessor(kernelXY, preserveAlpha, borderWrapModeX, borderWrapModeY)); |
|||
|
|||
/// <summary>
|
|||
/// Applies a convolution filter to the image.
|
|||
/// </summary>
|
|||
/// <param name="source">The current image processing context.</param>
|
|||
/// <param name="rectangle">The rectangle structure that specifies the portion of the image object to alter.</param>
|
|||
/// <param name="kernelXY">The convolution kernel to apply.</param>
|
|||
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
|
|||
public static IImageProcessingContext Convolve(this IImageProcessingContext source, Rectangle rectangle, DenseMatrix<float> kernelXY) |
|||
=> Convolve(source, rectangle, kernelXY, false); |
|||
|
|||
/// <summary>
|
|||
/// Applies a convolution filter to the image.
|
|||
/// </summary>
|
|||
/// <param name="source">The current image processing context.</param>
|
|||
/// <param name="rectangle">The rectangle structure that specifies the portion of the image object to alter.</param>
|
|||
/// <param name="kernelXY">The convolution kernel to apply.</param>
|
|||
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
|
|||
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
|
|||
public static IImageProcessingContext Convolve(this IImageProcessingContext source, Rectangle rectangle, DenseMatrix<float> kernelXY, bool preserveAlpha) |
|||
=> Convolve(source, rectangle, kernelXY, preserveAlpha, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat); |
|||
|
|||
/// <summary>
|
|||
/// Applies a convolution filter to the image.
|
|||
/// </summary>
|
|||
/// <param name="source">The current image processing context.</param>
|
|||
/// <param name="rectangle">The rectangle structure that specifies the portion of the image object to alter.</param>
|
|||
/// <param name="kernelXY">The convolution kernel to apply.</param>
|
|||
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
|
|||
/// <param name="borderWrapModeX">The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in X direction.</param>
|
|||
/// <param name="borderWrapModeY">The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.</param>
|
|||
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
|
|||
public static IImageProcessingContext Convolve( |
|||
this IImageProcessingContext source, |
|||
Rectangle rectangle, |
|||
DenseMatrix<float> kernelXY, |
|||
bool preserveAlpha, |
|||
BorderWrappingMode borderWrapModeX, |
|||
BorderWrappingMode borderWrapModeY) |
|||
=> source.ApplyProcessor(new ConvolutionProcessor(kernelXY, preserveAlpha, borderWrapModeX, borderWrapModeY), rectangle); |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Convolution; |
|||
|
|||
/// <summary>
|
|||
/// Defines a processor that uses a 2 dimensional matrix to perform convolution against an image.
|
|||
/// </summary>
|
|||
public class ConvolutionProcessor : IImageProcessor |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ConvolutionProcessor"/> class.
|
|||
/// </summary>
|
|||
/// <param name="kernelXY">The 2d gradient operator.</param>
|
|||
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
|
|||
/// <param name="borderWrapModeX">The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in X direction.</param>
|
|||
/// <param name="borderWrapModeY">The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.</param>
|
|||
public ConvolutionProcessor( |
|||
in DenseMatrix<float> kernelXY, |
|||
bool preserveAlpha, |
|||
BorderWrappingMode borderWrapModeX, |
|||
BorderWrappingMode borderWrapModeY) |
|||
{ |
|||
this.KernelXY = kernelXY; |
|||
this.PreserveAlpha = preserveAlpha; |
|||
this.BorderWrapModeX = borderWrapModeX; |
|||
this.BorderWrapModeY = borderWrapModeY; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the 2d convolution kernel.
|
|||
/// </summary>
|
|||
public DenseMatrix<float> KernelXY { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels.
|
|||
/// </summary>
|
|||
public bool PreserveAlpha { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in X direction.
|
|||
/// </summary>
|
|||
public BorderWrappingMode BorderWrapModeX { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.
|
|||
/// </summary>
|
|||
public BorderWrappingMode BorderWrapModeY { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle) |
|||
where TPixel : unmanaged, |
|||
IPixel<TPixel> |
|||
{ |
|||
if (this.KernelXY.TryGetLinearlySeparableComponents(out float[]? kernelX, out float[]? kernelY)) |
|||
{ |
|||
return new Convolution2PassProcessor<TPixel>( |
|||
configuration, |
|||
kernelX, |
|||
kernelY, |
|||
this.PreserveAlpha, |
|||
source, |
|||
sourceRectangle, |
|||
this.BorderWrapModeX, |
|||
this.BorderWrapModeY); |
|||
} |
|||
|
|||
return new ConvolutionProcessor<TPixel>( |
|||
configuration, |
|||
this.KernelXY, |
|||
this.PreserveAlpha, |
|||
source, |
|||
sourceRectangle, |
|||
this.BorderWrapModeX, |
|||
this.BorderWrapModeY); |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Processing.Extensions.Convolution; |
|||
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution; |
|||
|
|||
public class ConvolutionTests |
|||
{ |
|||
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05F); |
|||
|
|||
public static readonly TheoryData<DenseMatrix<float>> Values = new TheoryData<DenseMatrix<float>> |
|||
{ |
|||
// Sharpening kernel.
|
|||
new float[,] |
|||
{ |
|||
{ -1, -1, -1 }, |
|||
{ -1, 16, -1 }, |
|||
{ -1, -1, -1 } |
|||
} |
|||
}; |
|||
|
|||
public static readonly string[] InputImages = |
|||
[ |
|||
TestImages.Bmp.Car, |
|||
TestImages.Png.CalliphoraPartial, |
|||
TestImages.Png.Blur |
|||
]; |
|||
|
|||
[Theory] |
|||
[WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] |
|||
public void OnFullImage<TPixel>(TestImageProvider<TPixel> provider, DenseMatrix<float> value) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
=> provider.RunValidatingProcessorTest( |
|||
x => x.Convolve(value), |
|||
string.Join('_', value.Data), |
|||
ValidatorComparer); |
|||
|
|||
[Theory] |
|||
[WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] |
|||
public void InBox<TPixel>(TestImageProvider<TPixel> provider, DenseMatrix<float> value) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
=> provider.RunRectangleConstrainedValidatingProcessorTest( |
|||
(x, rect) => x.Convolve(rect, value), |
|||
string.Join('_', value.Data), |
|||
ValidatorComparer); |
|||
} |
|||
Loading…
Reference in new issue