Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Convolution Api #2

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Expose Convolution Api #2

wants to merge 3 commits into from

Conversation

lizard-boy
Copy link

@lizard-boy lizard-boy commented Nov 1, 2024

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

See the roadmap discussion at SixLabors#2533

Adds a general convolution API that accepts a DenseMatrix<float> to describe the convolution kernel.

All this stuff has been the plumbing for various processors over the years and should be exposed publicly now it's solid.

Greptile Summary

This PR exposes a new public convolution API in ImageSharp, allowing users to apply custom convolution kernels to images while maintaining the library's existing performance optimizations and border handling capabilities.

Key changes:

  • Added new ConvolutionExtensions.cs exposing public methods to apply custom convolution kernels using DenseMatrix<float>
  • Added border wrapping mode support to control how pixels outside image bounds are handled during convolution
  • Optimized implementation using two-pass approach for linearly separable kernels
  • Updated processors to consistently handle border wrapping modes and alpha channel preservation
  • Added basic test coverage for convolution operations using sharpening kernel

The changes look well-structured and maintain ImageSharp's existing patterns, though test coverage could be expanded to verify edge cases and different border handling modes.

@lizard-boy lizard-boy added enhancement New feature or request API labels Nov 1, 2024
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12 file(s) reviewed, 22 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines +58 to +64
public static IImageProcessingContext BoxBlur(
this IImageProcessingContext source,
int radius,
Rectangle rectangle,
BorderWrappingMode borderWrapModeX,
BorderWrappingMode borderWrapModeY)
=> source.ApplyProcessor(new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY), rectangle);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Consider adding parameter validation for radius >= 0 to prevent invalid kernel sizes

Comment on lines +20 to +21
public static IImageProcessingContext Convolve(this IImageProcessingContext source, DenseMatrix<float> kernelXY)
=> Convolve(source, kernelXY, false);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: No validation for null or empty kernelXY matrix. Consider adding Guard.NotNull and size validation.

/// <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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Default BorderWrappingMode.Repeat is used but not documented in XML comments

Comment on lines +9 to +11
/// Defines general convolution extensions to apply on an <see cref="Image"/>
/// using Mutate/Clone.
/// </summary>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: XML comment should mention that these are extension methods for IImageProcessingContext, not just Image


mapXY.BuildSamplingOffsetMap(this.Kernel.Length, this.Kernel.Length, interest, this.BorderWrapModeX, this.BorderWrapModeY);
mapXY.BuildSamplingOffsetMap(this.KernelX.Length, this.KernelX.Length, interest, this.BorderWrapModeX, this.BorderWrapModeY);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Using KernelX.Length for both dimensions could be incorrect if kernels have different sizes

Image<TPixel> source,
Rectangle sourceRectangle,
BorderWrappingMode borderWrapModeX,
BorderWrappingMode borderWrapModeY)
: base(configuration, source, sourceRectangle)
{
this.KernelXY = kernelXY;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: no validation that kernelXY is not empty or null


// We need a clean copy for each pass to start from
using ImageFrame<TPixel> cleanCopy = source.Clone();

using (var processor = new ConvolutionProcessor<TPixel>(this.Configuration, in this.kernels[0], true, this.Source, interest))
using (ConvolutionProcessor<TPixel> processor = new(this.Configuration, in this.kernels[0], true, this.Source, interest))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding BorderWrappingMode parameters here to match the new convolution API

@@ -78,12 +78,12 @@ protected override void OnFrameApply(ImageFrame<TPixel> source)
{
using ImageFrame<TPixel> pass = cleanCopy.Clone();

using (var processor = new ConvolutionProcessor<TPixel>(this.Configuration, in this.kernels[i], true, this.Source, interest))
using (ConvolutionProcessor<TPixel> processor = new(this.Configuration, in this.kernels[i], true, this.Source, interest))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding BorderWrappingMode parameters here to match the new convolution API

[GroupOutput("Convolution")]
public class ConvolutionTests
{
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05F);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: 5% tolerance seems high for convolution operations - could mask subtle errors in kernel application

Comment on lines +33 to +40
[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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing tests for preserveAlpha and border wrapping mode parameters that exist in the ConvolutionProcessor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants