diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 64a496141..41ecee503 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/ImageSharp/Processing/EdgeDetectionOperators.cs b/src/ImageSharp/Processing/EdgeDetectionOperators.cs deleted file mode 100644 index 3e986f802..000000000 --- a/src/ImageSharp/Processing/EdgeDetectionOperators.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Enumerates the various types of defined edge detection filters. - /// - public enum EdgeDetectionOperators - { - /// - /// The Kayyali operator filter. - /// - Kayyali, - - /// - /// The Kirsch operator filter. - /// - Kirsch, - - /// - /// The Laplacian3X3 operator filter. - /// - Laplacian3x3, - - /// - /// The Laplacian5X5 operator filter. - /// - Laplacian5x5, - - /// - /// The LaplacianOfGaussian operator filter. - /// - LaplacianOfGaussian, - - /// - /// The Prewitt operator filter. - /// - Prewitt, - - /// - /// The RobertsCross operator filter. - /// - RobertsCross, - - /// - /// The Robinson operator filter. - /// - Robinson, - - /// - /// The Scharr operator filter. - /// - Scharr, - - /// - /// The Sobel operator filter. - /// - Sobel - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs index 61b900848..f30d8ad57 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors; @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - EdgeDetectionOperators filter) => + KnownEdgeDetectionOperators filter) => DetectEdges(source, GetProcessor(filter, true)); /// @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - EdgeDetectionOperators filter, + KnownEdgeDetectionOperators filter, bool grayscale) => DetectEdges(source, GetProcessor(filter, grayscale)); @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - EdgeDetectionOperators filter, + KnownEdgeDetectionOperators filter, Rectangle rectangle, bool grayscale = true) => DetectEdges(source, rectangle, GetProcessor(filter, grayscale)); @@ -102,45 +102,45 @@ namespace SixLabors.ImageSharp.Processing return source; } - private static IImageProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) + private static IImageProcessor GetProcessor(KnownEdgeDetectionOperators filter, bool grayscale) { IImageProcessor processor; switch (filter) { - case EdgeDetectionOperators.Kayyali: + case KnownEdgeDetectionOperators.Kayyali: processor = new KayyaliProcessor(grayscale); break; - case EdgeDetectionOperators.Kirsch: + case KnownEdgeDetectionOperators.Kirsch: processor = new KirschProcessor(grayscale); break; - case EdgeDetectionOperators.Laplacian3x3: + case KnownEdgeDetectionOperators.Laplacian3x3: processor = new Laplacian3x3Processor(grayscale); break; - case EdgeDetectionOperators.Laplacian5x5: + case KnownEdgeDetectionOperators.Laplacian5x5: processor = new Laplacian5x5Processor(grayscale); break; - case EdgeDetectionOperators.LaplacianOfGaussian: + case KnownEdgeDetectionOperators.LaplacianOfGaussian: processor = new LaplacianOfGaussianProcessor(grayscale); break; - case EdgeDetectionOperators.Prewitt: + case KnownEdgeDetectionOperators.Prewitt: processor = new PrewittProcessor(grayscale); break; - case EdgeDetectionOperators.RobertsCross: + case KnownEdgeDetectionOperators.RobertsCross: processor = new RobertsCrossProcessor(grayscale); break; - case EdgeDetectionOperators.Robinson: + case KnownEdgeDetectionOperators.Robinson: processor = new RobinsonProcessor(grayscale); break; - case EdgeDetectionOperators.Scharr: + case KnownEdgeDetectionOperators.Scharr: processor = new ScharrProcessor(grayscale); break; @@ -152,4 +152,4 @@ namespace SixLabors.ImageSharp.Processing return processor; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/KnownEdgeDetectionOperators.cs b/src/ImageSharp/Processing/KnownEdgeDetectionOperators.cs new file mode 100644 index 000000000..e41fb00ce --- /dev/null +++ b/src/ImageSharp/Processing/KnownEdgeDetectionOperators.cs @@ -0,0 +1,63 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors.Convolution; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Contains reusable static instances of known edge detection kernels. + /// + public static class KnownEdgeDetectionOperators + { + /// + /// Gets the Kayyali edge detector kernel. + /// + public static EdgeDetector2DKernel Kayyali { get; } = EdgeDetector2DKernel.KayyaliKernel; + + /// + /// Gets the Kirsch edge detector kernel. + /// + public static EdgeDetectorCompassKernel Kirsch { get; } = EdgeDetectorCompassKernel.Kirsch; + + /// + /// Gets the Laplacian 3x3 edge detector kernel. + /// + public static EdgeDetectorKernel Laplacian3x3 { get; } = EdgeDetectorKernel.Laplacian3x3; + + /// + /// Gets the Laplacian 5x5 edge detector kernel. + /// + public static EdgeDetectorKernel Laplacian5x5 { get; } = EdgeDetectorKernel.Laplacian5x5; + + /// + /// Gets the Laplacian of Gaussian edge detector kernel. + /// + public static EdgeDetectorKernel LaplacianOfGaussian { get; } = EdgeDetectorKernel.LaplacianOfGaussian; + + /// + /// Gets the Prewitt edge detector kernel. + /// + public static EdgeDetector2DKernel Prewitt { get; } = EdgeDetector2DKernel.PrewittKernel; + + /// + /// Gets the Roberts-Cross edge detector kernel. + /// + public static EdgeDetector2DKernel RobertsCross { get; } = EdgeDetector2DKernel.RobertsCrossKernel; + + /// + /// Gets the Robinson edge detector kernel. + /// + public static EdgeDetectorCompassKernel Robinson { get; } = EdgeDetectorCompassKernel.Robinson; + + /// + /// Gets the Scharr edge detector kernel. + /// + public static EdgeDetector2DKernel Scharr { get; } = EdgeDetector2DKernel.ScharrKernel; + + /// + /// Gets the Sobel edge detector kernel. + /// + public static EdgeDetector2DKernel Sobel { get; } = EdgeDetector2DKernel.SobelKernel; + } +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 164488155..14d728814 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -18,25 +18,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution internal class EdgeDetectorCompassProcessor : ImageProcessor where TPixel : unmanaged, IPixel { + private readonly DenseMatrix[] kernels; + private readonly bool grayscale; + /// /// Initializes a new instance of the class. /// /// The configuration which allows altering default behaviour or extending the library. - /// Gets the kernels to use. + /// The kernels to use. /// Whether to convert the image to grayscale before performing edge detection. /// The source for the current processor instance. /// The source area to process for the current processor instance. - internal EdgeDetectorCompassProcessor(Configuration configuration, CompassKernels kernels, bool grayscale, Image source, Rectangle sourceRectangle) + internal EdgeDetectorCompassProcessor(Configuration configuration, in EdgeDetectorCompassKernel kernels, bool grayscale, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { - this.Grayscale = grayscale; - this.Kernels = kernels; + this.grayscale = grayscale; + this.kernels = kernels.Flatten(); } - private CompassKernels Kernels { get; } - - private bool Grayscale { get; } - /// protected override void BeforeImageApply() { @@ -45,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution opaque.Execute(); } - if (this.Grayscale) + if (this.grayscale) { new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); } @@ -56,29 +55,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source) { - DenseMatrix[] kernels = this.Kernels.Flatten(); - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); // We need a clean copy for each pass to start from using ImageFrame cleanCopy = source.Clone(); - using (var processor = new ConvolutionProcessor(this.Configuration, kernels[0], true, this.Source, interest)) + using (var processor = new ConvolutionProcessor(this.Configuration, in this.kernels[0], true, this.Source, interest)) { processor.Apply(source); } - if (kernels.Length == 1) + if (this.kernels.Length == 1) { return; } // Additional runs - for (int i = 1; i < kernels.Length; i++) + for (int i = 1; i < this.kernels.Length; i++) { using ImageFrame pass = cleanCopy.Clone(); - using (var processor = new ConvolutionProcessor(this.Configuration, kernels[i], true, this.Source, interest)) + using (var processor = new ConvolutionProcessor(this.Configuration, in this.kernels[i], true, this.Source, interest)) { processor.Apply(pass); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index 45639d93a..e56e0d1a7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -13,6 +13,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution internal class EdgeDetectorProcessor : ImageProcessor where TPixel : unmanaged, IPixel { + private readonly bool grayscale; + private readonly DenseMatrix kernelXY; + /// /// Initializes a new instance of the class. /// @@ -23,23 +26,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The target area to process for the current processor instance. public EdgeDetectorProcessor( Configuration configuration, - in DenseMatrix kernelXY, + in EdgeDetectorKernel kernelXY, bool grayscale, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { - this.KernelXY = kernelXY; - this.Grayscale = grayscale; + this.kernelXY = kernelXY.KernelXY; + this.grayscale = grayscale; } - public bool Grayscale { get; } - - /// - /// Gets the 2d gradient operator. - /// - public DenseMatrix KernelXY { get; } - /// protected override void BeforeImageApply() { @@ -48,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution opaque.Execute(); } - if (this.Grayscale) + if (this.grayscale) { new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); } @@ -59,10 +55,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source) { - using (var processor = new ConvolutionProcessor(this.Configuration, this.KernelXY, true, this.Source, this.SourceRectangle)) - { - processor.Apply(source); - } + using var processor = new ConvolutionProcessor(this.Configuration, in this.kernelXY, true, this.Source, this.SourceRectangle); + processor.Apply(source); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs deleted file mode 100644 index 24caa40f8..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - internal abstract class CompassKernels - { - /// - /// Gets the North gradient operator. - /// - public abstract DenseMatrix North { get; } - - /// - /// Gets the NorthWest gradient operator. - /// - public abstract DenseMatrix NorthWest { get; } - - /// - /// Gets the West gradient operator. - /// - public abstract DenseMatrix West { get; } - - /// - /// Gets the SouthWest gradient operator. - /// - public abstract DenseMatrix SouthWest { get; } - - /// - /// Gets the South gradient operator. - /// - public abstract DenseMatrix South { get; } - - /// - /// Gets the SouthEast gradient operator. - /// - public abstract DenseMatrix SouthEast { get; } - - /// - /// Gets the East gradient operator. - /// - public abstract DenseMatrix East { get; } - - /// - /// Gets the NorthEast gradient operator. - /// - public abstract DenseMatrix NorthEast { get; } - - public DenseMatrix[] Flatten() => - new[] - { - this.North, this.NorthWest, this.West, this.SouthWest, - this.South, this.SouthEast, this.East, this.NorthEast - }; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetector2DKernel.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetector2DKernel.cs new file mode 100644 index 000000000..ed363595d --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetector2DKernel.cs @@ -0,0 +1,103 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Represents an edge detection convolution kernel consisting of two 1D gradient operators. + /// + public readonly struct EdgeDetector2DKernel : IEquatable + { + /// + /// An edge detection kernel containing two Kayyali operators. + /// + public static EdgeDetector2DKernel KayyaliKernel = new EdgeDetector2DKernel(KayyaliKernels.KayyaliX, KayyaliKernels.KayyaliY); + + /// + /// An edge detection kernel containing two Prewitt operators. + /// . + /// + public static EdgeDetector2DKernel PrewittKernel = new EdgeDetector2DKernel(PrewittKernels.PrewittX, PrewittKernels.PrewittY); + + /// + /// An edge detection kernel containing two Roberts-Cross operators. + /// . + /// + public static EdgeDetector2DKernel RobertsCrossKernel = new EdgeDetector2DKernel(RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY); + + /// + /// An edge detection kernel containing two Scharr operators. + /// + public static EdgeDetector2DKernel ScharrKernel = new EdgeDetector2DKernel(ScharrKernels.ScharrX, ScharrKernels.ScharrY); + + /// + /// An edge detection kernel containing two Sobel operators. + /// . + /// + public static EdgeDetector2DKernel SobelKernel = new EdgeDetector2DKernel(SobelKernels.SobelX, SobelKernels.SobelY); + + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal gradient operator. + /// The vertical gradient operator. + public EdgeDetector2DKernel(DenseMatrix kernelX, DenseMatrix kernelY) + { + Guard.IsTrue( + kernelX.Size.Equals(kernelY.Size), + $"{nameof(kernelX)} {nameof(kernelY)}", + "Kernel sizes must be the same."); + + this.KernelX = kernelX; + this.KernelY = kernelY; + } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + /// + /// Checks whether two structures are equal. + /// + /// The left hand operand. + /// The right hand operand. + /// + /// True if the parameter is equal to the parameter; + /// otherwise, false. + /// + public static bool operator ==(EdgeDetector2DKernel left, EdgeDetector2DKernel right) + => left.Equals(right); + + /// + /// Checks whether two structures are equal. + /// + /// The left hand operand. + /// The right hand operand. + /// + /// True if the parameter is not equal to the parameter; + /// otherwise, false. + /// + public static bool operator !=(EdgeDetector2DKernel left, EdgeDetector2DKernel right) + => !(left == right); + + /// + public override bool Equals(object obj) + => obj is EdgeDetector2DKernel kernel && this.Equals(kernel); + + /// + public bool Equals(EdgeDetector2DKernel other) + => this.KernelX.Equals(other.KernelX) + && this.KernelY.Equals(other.KernelY); + + /// + public override int GetHashCode() => HashCode.Combine(this.KernelX, this.KernelY); + } +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs new file mode 100644 index 000000000..34fca9192 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs @@ -0,0 +1,161 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Represents an edge detection convolution kernel consisting of eight gradient operators. + /// + public readonly struct EdgeDetectorCompassKernel : IEquatable + { + /// + /// An edge detection kenel comprised of Kirsch gradient operators. + /// + public static EdgeDetectorCompassKernel Kirsch = + new EdgeDetectorCompassKernel( + KirschKernels.North, + KirschKernels.NorthWest, + KirschKernels.West, + KirschKernels.SouthWest, + KirschKernels.South, + KirschKernels.SouthEast, + KirschKernels.East, + KirschKernels.NorthEast); + + /// + /// An edge detection kenel comprised of Robinson gradient operators. + /// + public static EdgeDetectorCompassKernel Robinson = + new EdgeDetectorCompassKernel( + RobinsonKernels.North, + RobinsonKernels.NorthWest, + RobinsonKernels.West, + RobinsonKernels.SouthWest, + RobinsonKernels.South, + RobinsonKernels.SouthEast, + RobinsonKernels.East, + RobinsonKernels.NorthEast); + + /// + /// Initializes a new instance of the struct. + /// + /// The north gradient operator. + /// The north-west gradient operator. + /// The west gradient operator. + /// The south-west gradient operator. + /// The south gradient operator. + /// The south-east gradient operator. + /// The east gradient operator. + /// The north-east gradient operator. + public EdgeDetectorCompassKernel( + DenseMatrix north, + DenseMatrix northWest, + DenseMatrix west, + DenseMatrix southWest, + DenseMatrix south, + DenseMatrix southEast, + DenseMatrix east, + DenseMatrix northEast) + { + this.North = north; + this.NorthWest = northWest; + this.West = west; + this.SouthWest = southWest; + this.South = south; + this.SouthEast = southEast; + this.East = east; + this.NorthEast = northEast; + } + + /// + /// Gets the North gradient operator. + /// + public DenseMatrix North { get; } + + /// + /// Gets the NorthWest gradient operator. + /// + public DenseMatrix NorthWest { get; } + + /// + /// Gets the West gradient operator. + /// + public DenseMatrix West { get; } + + /// + /// Gets the SouthWest gradient operator. + /// + public DenseMatrix SouthWest { get; } + + /// + /// Gets the South gradient operator. + /// + public DenseMatrix South { get; } + + /// + /// Gets the SouthEast gradient operator. + /// + public DenseMatrix SouthEast { get; } + + /// + /// Gets the East gradient operator. + /// + public DenseMatrix East { get; } + + /// + /// Gets the NorthEast gradient operator. + /// + public DenseMatrix NorthEast { get; } + + /// + /// Checks whether two structures are equal. + /// + /// The left hand operand. + /// The right hand operand. + /// + /// True if the parameter is equal to the parameter; + /// otherwise, false. + /// + public static bool operator ==(EdgeDetectorCompassKernel left, EdgeDetectorCompassKernel right) + => left.Equals(right); + + /// + /// Checks whether two structures are equal. + /// + /// The left hand operand. + /// The right hand operand. + /// + /// True if the parameter is not equal to the parameter; + /// otherwise, false. + /// + public static bool operator !=(EdgeDetectorCompassKernel left, EdgeDetectorCompassKernel right) + => !(left == right); + + /// + public override bool Equals(object obj) => obj is EdgeDetectorCompassKernel kernel && this.Equals(kernel); + + /// + public bool Equals(EdgeDetectorCompassKernel other) => this.North.Equals(other.North) && this.NorthWest.Equals(other.NorthWest) && this.West.Equals(other.West) && this.SouthWest.Equals(other.SouthWest) && this.South.Equals(other.South) && this.SouthEast.Equals(other.SouthEast) && this.East.Equals(other.East) && this.NorthEast.Equals(other.NorthEast); + + /// + public override int GetHashCode() + => HashCode.Combine( + this.North, + this.NorthWest, + this.West, + this.SouthWest, + this.South, + this.SouthEast, + this.East, + this.NorthEast); + + internal DenseMatrix[] Flatten() => + new[] + { + this.North, this.NorthWest, this.West, this.SouthWest, + this.South, this.SouthEast, this.East, this.NorthEast + }; + } +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorKernel.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorKernel.cs new file mode 100644 index 000000000..7b1f91263 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorKernel.cs @@ -0,0 +1,75 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Represents an edge detection convolution kernel consisting of a single 2D gradient operator. + /// + public readonly struct EdgeDetectorKernel : IEquatable + { + /// + /// An edge detection kernel containing a 3x3 Laplacian operator. + /// + public static EdgeDetectorKernel Laplacian3x3 = new EdgeDetectorKernel(LaplacianKernels.Laplacian3x3); + + /// + /// An edge detection kernel containing a 5x5 Laplacian operator. + /// + public static EdgeDetectorKernel Laplacian5x5 = new EdgeDetectorKernel(LaplacianKernels.Laplacian5x5); + + /// + /// An edge detection kernel containing a Laplacian of Gaussian operator. + /// + public static EdgeDetectorKernel LaplacianOfGaussian = new EdgeDetectorKernel(LaplacianKernels.LaplacianOfGaussianXY); + + /// + /// Initializes a new instance of the struct. + /// + /// The 2D gradient operator. + public EdgeDetectorKernel(DenseMatrix kernelXY) + => this.KernelXY = kernelXY; + + /// + /// Gets the 2D gradient operator. + /// + public DenseMatrix KernelXY { get; } + + /// + /// Checks whether two structures are equal. + /// + /// The left hand operand. + /// The right hand operand. + /// + /// True if the parameter is equal to the parameter; + /// otherwise, false. + /// + public static bool operator ==(EdgeDetectorKernel left, EdgeDetectorKernel right) + => left.Equals(right); + + /// + /// Checks whether two structures are equal. + /// + /// The left hand operand. + /// The right hand operand. + /// + /// True if the parameter is not equal to the parameter; + /// otherwise, false. + /// + public static bool operator !=(EdgeDetectorKernel left, EdgeDetectorKernel right) + => !(left == right); + + /// + public override bool Equals(object obj) + => obj is EdgeDetectorKernel kernel && this.Equals(kernel); + + /// + public bool Equals(EdgeDetectorKernel other) + => this.KernelXY.Equals(other.KernelXY); + + /// + public override int GetHashCode() => this.KernelXY.GetHashCode(); + } +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KayyaliKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KayyaliKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KirschKernels.cs similarity index 80% rename from src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KirschKernels.cs index 28c5590ef..ccd8a6c8d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KirschKernels.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -6,12 +6,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Contains the eight matrices used for Kirsch edge detection /// - internal class KirschKernels : CompassKernels + internal static class KirschKernels { /// /// Gets the North gradient operator /// - public override DenseMatrix North => + public static DenseMatrix North => new float[,] { { 5, 5, 5 }, @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthWest gradient operator /// - public override DenseMatrix NorthWest => + public static DenseMatrix NorthWest => new float[,] { { 5, 5, -3 }, @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the West gradient operator /// - public override DenseMatrix West => + public static DenseMatrix West => new float[,] { { 5, -3, -3 }, @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthWest gradient operator /// - public override DenseMatrix SouthWest => + public static DenseMatrix SouthWest => new float[,] { { -3, -3, -3 }, @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the South gradient operator /// - public override DenseMatrix South => + public static DenseMatrix South => new float[,] { { -3, -3, -3 }, @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthEast gradient operator /// - public override DenseMatrix SouthEast => + public static DenseMatrix SouthEast => new float[,] { { -3, -3, -3 }, @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the East gradient operator /// - public override DenseMatrix East => + public static DenseMatrix East => new float[,] { { -3, -3, 5 }, @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthEast gradient operator /// - public override DenseMatrix NorthEast => + public static DenseMatrix NorthEast => new float[,] { { -3, 5, 5 }, @@ -96,4 +96,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { -3, -3, -3 } }; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernelFactory.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernelFactory.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/PrewittKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/PrewittKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobertsCrossKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobertsCrossKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobinsonKernels.cs similarity index 80% rename from src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobinsonKernels.cs index 857c772b0..0dde36f02 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobinsonKernels.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -6,12 +6,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Contains the kernels used for Robinson edge detection. /// - internal class RobinsonKernels : CompassKernels + internal static class RobinsonKernels { /// /// Gets the North gradient operator /// - public override DenseMatrix North => + public static DenseMatrix North => new float[,] { { 1, 2, 1 }, @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthWest gradient operator /// - public override DenseMatrix NorthWest => + public static DenseMatrix NorthWest => new float[,] { { 2, 1, 0 }, @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the West gradient operator /// - public override DenseMatrix West => + public static DenseMatrix West => new float[,] { { 1, 0, -1 }, @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthWest gradient operator /// - public override DenseMatrix SouthWest => + public static DenseMatrix SouthWest => new float[,] { { 0, -1, -2 }, @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the South gradient operator /// - public override DenseMatrix South => + public static DenseMatrix South => new float[,] { { -1, -2, -1 }, @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthEast gradient operator /// - public override DenseMatrix SouthEast => + public static DenseMatrix SouthEast => new float[,] { { -2, -1, 0 }, @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the East gradient operator /// - public override DenseMatrix East => + public static DenseMatrix East => new float[,] { { -1, 0, 1 }, @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthEast gradient operator /// - public override DenseMatrix NorthEast => + public static DenseMatrix NorthEast => new float[,] { { 0, 1, 2 }, @@ -96,4 +96,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { -2, -1, 0 } }; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/ScharrKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/ScharrKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/SobelKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/SobelKernels.cs diff --git a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs index d40201bd8..cf46c4f52 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs @@ -37,17 +37,17 @@ namespace SixLabors.ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp DetectEdges")] public void ImageProcessorCoreDetectEdges() { - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Kayyali)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Kayyali)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Kirsch)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Laplacian3x3)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Laplacian5x5)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.LaplacianOfGaussian)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Prewitt)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.RobertsCross)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Robinson)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Scharr)); - this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Sobel)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Kayyali)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Kayyali)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Kirsch)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Laplacian3x3)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Laplacian5x5)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.LaplacianOfGaussian)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Prewitt)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.RobertsCross)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Robinson)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Scharr)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Sobel)); } } } diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index a0e9be110..fd41a04e2 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -35,21 +35,21 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public static IEnumerable EdgeDetectionTheoryData => new[] { - new object[] { new TestType(), EdgeDetectionOperators.Kayyali }, - new object[] { new TestType(), EdgeDetectionOperators.Kirsch }, - new object[] { new TestType(), EdgeDetectionOperators.Laplacian3x3 }, - new object[] { new TestType(), EdgeDetectionOperators.Laplacian5x5 }, - new object[] { new TestType(), EdgeDetectionOperators.LaplacianOfGaussian }, - new object[] { new TestType(), EdgeDetectionOperators.Prewitt }, - new object[] { new TestType(), EdgeDetectionOperators.RobertsCross }, - new object[] { new TestType(), EdgeDetectionOperators.Robinson }, - new object[] { new TestType(), EdgeDetectionOperators.Scharr }, - new object[] { new TestType(), EdgeDetectionOperators.Sobel }, + new object[] { new TestType(), KnownEdgeDetectionOperators.Kayyali }, + new object[] { new TestType(), KnownEdgeDetectionOperators.Kirsch }, + new object[] { new TestType(), KnownEdgeDetectionOperators.Laplacian3x3 }, + new object[] { new TestType(), KnownEdgeDetectionOperators.Laplacian5x5 }, + new object[] { new TestType(), KnownEdgeDetectionOperators.LaplacianOfGaussian }, + new object[] { new TestType(), KnownEdgeDetectionOperators.Prewitt }, + new object[] { new TestType(), KnownEdgeDetectionOperators.RobertsCross }, + new object[] { new TestType(), KnownEdgeDetectionOperators.Robinson }, + new object[] { new TestType(), KnownEdgeDetectionOperators.Scharr }, + new object[] { new TestType(), KnownEdgeDetectionOperators.Sobel }, }; [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] - public void DetectEdges_filter_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) + public void DetectEdges_filter_SobelProcessorDefaultsSet(TestType type, KnownEdgeDetectionOperators filter) where TProcessor : EdgeDetectorProcessor { this.operations.DetectEdges(filter); @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] - public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) + public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet(TestType type, KnownEdgeDetectionOperators filter) where TProcessor : EdgeDetectorProcessor { bool grey = (int)filter % 2 == 0; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index dbbf624db..a96b05d63 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -20,18 +20,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; - public static readonly TheoryData DetectEdgesFilters = new TheoryData + public static readonly TheoryData DetectEdgesFilters = new TheoryData { - EdgeDetectionOperators.Kayyali, - EdgeDetectionOperators.Kirsch, - EdgeDetectionOperators.Laplacian3x3, - EdgeDetectionOperators.Laplacian5x5, - EdgeDetectionOperators.LaplacianOfGaussian, - EdgeDetectionOperators.Prewitt, - EdgeDetectionOperators.RobertsCross, - EdgeDetectionOperators.Robinson, - EdgeDetectionOperators.Scharr, - EdgeDetectionOperators.Sobel + KnownEdgeDetectionOperators.Kayyali, + KnownEdgeDetectionOperators.Kirsch, + KnownEdgeDetectionOperators.Laplacian3x3, + KnownEdgeDetectionOperators.Laplacian5x5, + KnownEdgeDetectionOperators.LaplacianOfGaussian, + KnownEdgeDetectionOperators.Prewitt, + KnownEdgeDetectionOperators.RobertsCross, + KnownEdgeDetectionOperators.Robinson, + KnownEdgeDetectionOperators.Scharr, + KnownEdgeDetectionOperators.Sobel }; [Theory] @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution [Theory] [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, PixelTypes.Rgba32)] [WithFileCollection(nameof(TestImages), nameof(DetectEdgesFilters), PixelTypes.Rgba32)] - public void DetectEdges_WorksWithAllFilters(TestImageProvider provider, EdgeDetectionOperators detector) + public void DetectEdges_WorksWithAllFilters(TestImageProvider provider, KnownEdgeDetectionOperators detector) where TPixel : unmanaged, IPixel { bool hasAlpha = provider.SourceFileOrDescription.Contains("TestPattern"); @@ -115,7 +115,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution [Theory] [WithFile(Tests.TestImages.Png.Bike, nameof(DetectEdgesFilters), PixelTypes.Rgba32)] - public void WorksWithDiscoBuffers(TestImageProvider provider, EdgeDetectionOperators detector) + public void WorksWithDiscoBuffers(TestImageProvider provider, KnownEdgeDetectionOperators detector) where TPixel : unmanaged, IPixel { provider.RunBufferCapacityLimitProcessorTest(