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