From 643c0b6054ea21ae37327d95f7f87c2813cbb728 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 1 Aug 2020 13:41:19 +0100 Subject: [PATCH 1/7] Move kernels to reusable struct --- src/ImageSharp/ImageSharp.csproj | 2 +- .../Processing/EdgeDetectionOperators.cs | 61 ------- .../Convolution/DetectEdgesExtensions.cs | 30 ++-- .../Processing/KnownEdgeDetectionOperators.cs | 63 +++++++ .../EdgeDetectorCompassProcessor{TPixel}.cs | 27 ++- .../EdgeDetectorProcessor{TPixel}.cs | 24 +-- .../Convolution/Kernels/CompassKernels.cs | 55 ------ .../Kernels/EdgeDetector2DKernel.cs | 103 +++++++++++ .../Kernels/EdgeDetectorCompassKernel.cs | 161 ++++++++++++++++++ .../Convolution/Kernels/EdgeDetectorKernel.cs | 75 ++++++++ .../{ => Implementation}/KayyaliKernels.cs | 0 .../{ => Implementation}/KirschKernels.cs | 22 +-- .../LaplacianKernelFactory.cs | 0 .../{ => Implementation}/LaplacianKernels.cs | 0 .../{ => Implementation}/PrewittKernels.cs | 0 .../RobertsCrossKernels.cs | 0 .../{ => Implementation}/RobinsonKernels.cs | 22 +-- .../{ => Implementation}/ScharrKernels.cs | 0 .../{ => Implementation}/SobelKernels.cs | 0 .../Samplers/DetectEdges.cs | 22 +-- .../Processing/Convolution/DetectEdgesTest.cs | 24 +-- .../Processors/Convolution/DetectEdgesTest.cs | 26 +-- 22 files changed, 497 insertions(+), 220 deletions(-) delete mode 100644 src/ImageSharp/Processing/EdgeDetectionOperators.cs create mode 100644 src/ImageSharp/Processing/KnownEdgeDetectionOperators.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetector2DKernel.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorKernel.cs rename src/ImageSharp/Processing/Processors/Convolution/Kernels/{ => Implementation}/KayyaliKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/Kernels/{ => Implementation}/KirschKernels.cs (80%) rename src/ImageSharp/Processing/Processors/Convolution/Kernels/{ => Implementation}/LaplacianKernelFactory.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/Kernels/{ => Implementation}/LaplacianKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/Kernels/{ => Implementation}/PrewittKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/Kernels/{ => Implementation}/RobertsCrossKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/Kernels/{ => Implementation}/RobinsonKernels.cs (80%) rename src/ImageSharp/Processing/Processors/Convolution/Kernels/{ => Implementation}/ScharrKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/Kernels/{ => Implementation}/SobelKernels.cs (100%) 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( From 6485be853cd8962085be3295ff2ac2d815acda53 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 1 Aug 2020 14:15:36 +0100 Subject: [PATCH 2/7] Replace individual processors --- .../Convolution/EdgeDetector2DProcessor.cs | 42 ++++++++++++++++++ .../EdgeDetector2DProcessor{TPixel}.cs | 43 ++++++++----------- .../EdgeDetectorCompassProcessor.cs | 42 ++++++++++++++++++ .../EdgeDetectorCompassProcessor{TPixel}.cs | 13 +++--- .../Convolution/EdgeDetectorProcessor.cs | 25 ++++++++--- .../EdgeDetectorProcessor{TPixel}.cs | 10 ++--- .../Convolution/KayyaliProcessor.cs | 31 ------------- .../Kernels/EdgeDetectorCompassKernel.cs | 2 + .../Convolution/Kernels/EdgeDetectorKernel.cs | 3 ++ .../Kernels/Implementation/KirschKernels.cs | 3 +- .../Implementation/LaplacianKernels.cs | 8 ++-- .../Kernels/Implementation/RobinsonKernels.cs | 1 + .../Processors/Convolution/KirschProcessor.cs | 25 ----------- .../Convolution/Laplacian3x3Processor.cs | 25 ----------- .../Convolution/Laplacian5x5Processor.cs | 25 ----------- .../LaplacianOfGaussianProcessor.cs | 25 ----------- .../Convolution/PrewittProcessor.cs | 31 ------------- .../Convolution/RobertsCrossProcessor.cs | 31 ------------- .../Convolution/RobinsonProcessor.cs | 25 ----------- .../Processors/Convolution/ScharrProcessor.cs | 31 ------------- .../Processors/Convolution/SobelProcessor.cs | 31 ------------- 21 files changed, 145 insertions(+), 327 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs new file mode 100644 index 000000000..1d0a29a35 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Defines edge detection using the two 1D gradient operators. + /// + public sealed class EdgeDetector2DProcessor : IImageProcessor + { + /// + /// Initializes a new instance of the class. + /// + /// The 2D edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. + /// + public EdgeDetector2DProcessor(EdgeDetector2DKernel kernel, bool grayscale) + { + this.Kernel = kernel; + this.Grayscale = grayscale; + } + + /// + /// Gets the 2D edge detector kernel. + /// + public EdgeDetector2DKernel Kernel { get; } + + /// + /// Gets a value indicating whether to convert the image to grayscale before performing + /// edge detection. + /// + public bool Grayscale { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + where TPixel : unmanaged, IPixel + => new EdgeDetector2DProcessor(configuration, this, source, sourceRectangle); + } +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs index 6c831e727..80f8a7706 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs @@ -13,42 +13,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution internal class EdgeDetector2DProcessor : ImageProcessor where TPixel : unmanaged, IPixel { + private readonly DenseMatrix kernelX; + private readonly DenseMatrix kernelY; + private readonly bool grayscale; + /// /// Initializes a new instance of the class. /// /// The configuration which allows altering default behaviour or extending the library. - /// The horizontal gradient operator. - /// The vertical gradient operator. - /// Whether to convert the image to grayscale before performing edge detection. + /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - internal EdgeDetector2DProcessor( + public EdgeDetector2DProcessor( Configuration configuration, - in DenseMatrix kernelX, - in DenseMatrix kernelY, - bool grayscale, + EdgeDetector2DProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { - Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); - this.KernelX = kernelX; - this.KernelY = kernelY; - this.Grayscale = grayscale; + this.kernelX = definition.Kernel.KernelX; + this.kernelY = definition.Kernel.KernelY; + this.grayscale = definition.Grayscale; } - /// - /// Gets the horizontal gradient operator. - /// - public DenseMatrix KernelX { get; } - - /// - /// Gets the vertical gradient operator. - /// - public DenseMatrix KernelY { get; } - - public bool Grayscale { get; } - /// protected override void BeforeImageApply() { @@ -57,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); } @@ -68,7 +55,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source) { - using var processor = new Convolution2DProcessor(this.Configuration, this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle); + using var processor = new Convolution2DProcessor( + this.Configuration, + in this.kernelX, + in this.kernelY, + true, + this.Source, + this.SourceRectangle); processor.Apply(source); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs new file mode 100644 index 000000000..083a69bd2 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Defines edge detection using eight gradient operators. + /// + public sealed class EdgeDetectorCompassProcessor : IImageProcessor + { + /// + /// Initializes a new instance of the class. + /// + /// The edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. + /// + public EdgeDetectorCompassProcessor(EdgeDetectorCompassKernel kernel, bool grayscale) + { + this.Kernel = kernel; + this.Grayscale = grayscale; + } + + /// + /// Gets the edge detector kernel. + /// + public EdgeDetectorCompassKernel Kernel { get; } + + /// + /// Gets a value indicating whether to convert the image to grayscale before performing + /// edge detection. + /// + public bool Grayscale { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + where TPixel : unmanaged, IPixel + => new EdgeDetectorCompassProcessor(configuration, this, source, sourceRectangle); + } +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 14d728814..27963613e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -25,15 +25,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// /// The configuration which allows altering default behaviour or extending the library. - /// The kernels to use. - /// Whether to convert the image to grayscale before performing edge detection. + /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - internal EdgeDetectorCompassProcessor(Configuration configuration, in EdgeDetectorCompassKernel kernels, bool grayscale, Image source, Rectangle sourceRectangle) + internal EdgeDetectorCompassProcessor( + Configuration configuration, + EdgeDetectorCompassProcessor definition, + Image source, + Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { - this.grayscale = grayscale; - this.kernels = kernels.Flatten(); + this.grayscale = definition.Grayscale; + this.kernels = definition.Kernel.Flatten(); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index a9d20b547..6fcfca662 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -6,26 +6,37 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Defines a processor that detects edges within an image using a single two dimensional matrix. + /// Defines edge detection using a single 2D gradient operator. /// - public abstract class EdgeDetectorProcessor : IImageProcessor + public sealed class EdgeDetectorProcessor : IImageProcessor { /// /// Initializes a new instance of the class. /// - /// A value indicating whether to convert the image to grayscale before performing edge detection. - protected EdgeDetectorProcessor(bool grayscale) + /// The edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. + /// + public EdgeDetectorProcessor(EdgeDetectorKernel kernel, bool grayscale) { + this.Kernel = kernel; this.Grayscale = grayscale; } /// - /// Gets a value indicating whether to convert the image to grayscale before performing edge detection. + /// Gets the edge detector kernel. + /// + public EdgeDetectorKernel Kernel { get; } + + /// + /// Gets a value indicating whether to convert the image to grayscale before performing + /// edge detection. /// public bool Grayscale { get; } /// - public abstract IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - where TPixel : unmanaged, IPixel; + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + where TPixel : unmanaged, IPixel + => new EdgeDetectorProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index e56e0d1a7..62dd54919 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -20,20 +20,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// /// The configuration which allows altering default behaviour or extending the library. - /// The 2d gradient operator. - /// Whether to convert the image to grayscale before performing edge detection. + /// The defining the processor parameters. /// The source for the current processor instance. /// The target area to process for the current processor instance. public EdgeDetectorProcessor( Configuration configuration, - in EdgeDetectorKernel kernelXY, - bool grayscale, + EdgeDetectorProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { - this.kernelXY = kernelXY.KernelXY; - this.grayscale = grayscale; + this.kernelXY = definition.Kernel.KernelXY; + this.grayscale = definition.Grayscale; } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs deleted file mode 100644 index c13e8b543..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Defines edge detection processing using the Kayyali operator filter. - /// See . - /// - public sealed class KayyaliProcessor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public KayyaliProcessor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetector2DProcessor( - configuration, - KayyaliKernels.KayyaliX, - KayyaliKernels.KayyaliY, - this.Grayscale, - source, - sourceRectangle); - } -} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs index 34fca9192..bda861799 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs @@ -12,6 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// An edge detection kenel comprised of Kirsch gradient operators. + /// . /// public static EdgeDetectorCompassKernel Kirsch = new EdgeDetectorCompassKernel( @@ -26,6 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// An edge detection kenel comprised of Robinson gradient operators. + /// /// public static EdgeDetectorCompassKernel Robinson = new EdgeDetectorCompassKernel( diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorKernel.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorKernel.cs index 7b1f91263..86b8a24d9 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorKernel.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorKernel.cs @@ -12,16 +12,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// 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); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KirschKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KirschKernels.cs index ccd8a6c8d..87ccb174d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KirschKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KirschKernels.cs @@ -4,7 +4,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Contains the eight matrices used for Kirsch edge detection + /// Contains the eight matrices used for Kirsch edge detection. + /// . /// internal static class KirschKernels { diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernels.cs index ce574341f..7036300c7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernels.cs @@ -1,10 +1,12 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Contains Laplacian kernels of different sizes + /// Contains Laplacian kernels of different sizes. + /// + /// . /// internal static class LaplacianKernels { @@ -31,4 +33,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { 0, 0, -1, 0, 0 } }; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobinsonKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobinsonKernels.cs index 0dde36f02..7d0478aa5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobinsonKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobinsonKernels.cs @@ -5,6 +5,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Contains the kernels used for Robinson edge detection. + /// /// internal static class RobinsonKernels { diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs deleted file mode 100644 index 62bd17016..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Defines edge detection using the Kirsch operator filter. - /// See . - /// - public sealed class KirschProcessor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public KirschProcessor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetectorCompassProcessor(configuration, new KirschKernels(), this.Grayscale, source, sourceRectangle); - } -} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs deleted file mode 100644 index 957c61b07..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Applies edge detection processing to the image using the Laplacian 3x3 operator filter. - /// - /// - public sealed class Laplacian3x3Processor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public Laplacian3x3Processor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetectorProcessor(configuration, LaplacianKernels.Laplacian3x3, this.Grayscale, source, sourceRectangle); - } -} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs deleted file mode 100644 index 8b28662ae..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Defines edge detection processing using the Laplacian 5x5 operator filter. - /// . - /// - public sealed class Laplacian5x5Processor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public Laplacian5x5Processor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetectorProcessor(configuration, LaplacianKernels.Laplacian5x5, this.Grayscale, source, sourceRectangle); - } -} diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs deleted file mode 100644 index 5c8f7c40d..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Applies edge detection processing to the image using the Laplacian of Gaussian operator filter. - /// See . - /// - public sealed class LaplacianOfGaussianProcessor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public LaplacianOfGaussianProcessor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetectorProcessor(configuration, LaplacianKernels.LaplacianOfGaussianXY, this.Grayscale, source, sourceRectangle); - } -} diff --git a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs deleted file mode 100644 index c5ae14935..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Defines edge detection using the Prewitt operator filter. - /// See . - /// - public sealed class PrewittProcessor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public PrewittProcessor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetector2DProcessor( - configuration, - PrewittKernels.PrewittX, - PrewittKernels.PrewittY, - this.Grayscale, - source, - sourceRectangle); - } -} diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs deleted file mode 100644 index 57df83a14..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Defines edge detection processing using the Roberts Cross operator filter. - /// See . - /// - public sealed class RobertsCrossProcessor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public RobertsCrossProcessor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetector2DProcessor( - configuration, - RobertsCrossKernels.RobertsCrossX, - RobertsCrossKernels.RobertsCrossY, - this.Grayscale, - source, - sourceRectangle); - } -} diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs deleted file mode 100644 index 22c8562e2..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Defines edge detection using the Robinson operator filter. - /// See . - /// - public sealed class RobinsonProcessor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public RobinsonProcessor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetectorCompassProcessor(configuration, new RobinsonKernels(), this.Grayscale, source, sourceRectangle); - } -} diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs deleted file mode 100644 index d9a0745e2..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Defines edge detection processing using the Scharr operator filter. - /// - /// - public sealed class ScharrProcessor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public ScharrProcessor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetector2DProcessor( - configuration, - ScharrKernels.ScharrX, - ScharrKernels.ScharrY, - this.Grayscale, - source, - sourceRectangle); - } -} diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs deleted file mode 100644 index 73632392e..000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Defines edge detection using the Sobel operator filter. - /// See . - /// - public sealed class SobelProcessor : EdgeDetectorProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// Whether to convert the image to grayscale before performing edge detection. - public SobelProcessor(bool grayscale) - : base(grayscale) - { - } - - /// - public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - => new EdgeDetector2DProcessor( - configuration, - SobelKernels.SobelX, - SobelKernels.SobelY, - this.Grayscale, - source, - sourceRectangle); - } -} From a9aa3e5ef6b1d1f8759063d1ba6661cdb580fa81 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 1 Aug 2020 21:51:32 +0100 Subject: [PATCH 3/7] Update tests --- .../Convolution/DetectEdgesExtensions.cs | 245 ++++++++++++------ ...erators.cs => KnownEdgeDetectorKernels.cs} | 2 +- .../EntropyCropProcessor{TPixel}.cs | 2 +- .../Samplers/DetectEdges.cs | 22 +- .../Processing/Convolution/DetectEdgesTest.cs | 196 +++++++++++--- .../Processors/Convolution/DetectEdgesTest.cs | 92 ++++++- 6 files changed, 417 insertions(+), 142 deletions(-) rename src/ImageSharp/Processing/{KnownEdgeDetectionOperators.cs => KnownEdgeDetectorKernels.cs} (97%) diff --git a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs index f30d8ad57..2377151bb 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Convolution; namespace SixLabors.ImageSharp.Processing @@ -12,144 +11,230 @@ namespace SixLabors.ImageSharp.Processing public static class DetectEdgesExtensions { /// - /// Detects any edges within the image. Uses the filter - /// operating in grayscale mode. + /// Detects any edges within the image. + /// Uses the kernel operating in grayscale mode. /// /// The image this method extends. /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) => - DetectEdges(source, new SobelProcessor(true)); + DetectEdges(source, KnownEdgeDetectorKernels.Sobel); /// - /// Detects any edges within the image. Uses the filter - /// operating in grayscale mode. + /// Detects any edges within the image. + /// Uses the kernel operating in grayscale mode. /// /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The to allow chaining of operations. - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) => - DetectEdges(source, rectangle, new SobelProcessor(true)); + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + Rectangle rectangle) => + DetectEdges(source, KnownEdgeDetectorKernels.Sobel, rectangle); /// - /// Detects any edges within the image. + /// Detects any edges within the image operating in grayscale mode. /// /// The image this method extends. - /// The filter for detecting edges. + /// The 2D edge detector kernel. /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - KnownEdgeDetectionOperators filter) => - DetectEdges(source, GetProcessor(filter, true)); + EdgeDetector2DKernel kernel) => + DetectEdges(source, kernel, true); /// - /// Detects any edges within the image. + /// Detects any edges within the image using a . /// /// The image this method extends. - /// The filter for detecting edges. - /// Whether to convert the image to grayscale first. Defaults to true. + /// The 2D edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. + /// /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - KnownEdgeDetectionOperators filter, - bool grayscale) => - DetectEdges(source, GetProcessor(filter, grayscale)); + EdgeDetector2DKernel kernel, + bool grayscale) + { + var processor = new EdgeDetector2DProcessor(kernel, grayscale); + source.ApplyProcessor(processor); + return source; + } /// - /// Detects any edges within the image. + /// Detects any edges within the image operating in grayscale mode. /// /// The image this method extends. - /// The filter for detecting edges. + /// The 2D edge detector kernel. /// /// The structure that specifies the portion of the image object to alter. /// - /// Whether to convert the image to grayscale first. Defaults to true. /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - KnownEdgeDetectionOperators filter, - Rectangle rectangle, - bool grayscale = true) => - DetectEdges(source, rectangle, GetProcessor(filter, grayscale)); + EdgeDetector2DKernel kernel, + Rectangle rectangle) => + DetectEdges(source, kernel, true, rectangle); /// - /// Detects any edges within the image. + /// Detects any edges within the image using a . /// /// The image this method extends. - /// The filter for detecting edges. + /// The 2D edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. + /// + /// + /// The structure that specifies the portion of the image object to alter. + /// /// The to allow chaining of operations. - private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IImageProcessor filter) + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetector2DKernel kernel, + bool grayscale, + Rectangle rectangle) { - return source.ApplyProcessor(filter); + var processor = new EdgeDetector2DProcessor(kernel, grayscale); + source.ApplyProcessor(processor, rectangle); + return source; } /// - /// Detects any edges within the image. + /// Detects any edges within the image operating in grayscale mode. /// /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. + /// The edge detector kernel. + /// The to allow chaining of operations. + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectorKernel kernel) => + DetectEdges(source, kernel, true); + + /// + /// Detects any edges within the image using a . + /// + /// The image this method extends. + /// The edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. /// - /// The filter for detecting edges. /// The to allow chaining of operations. - private static IImageProcessingContext DetectEdges( + public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - Rectangle rectangle, - IImageProcessor filter) + EdgeDetectorKernel kernel, + bool grayscale) { - source.ApplyProcessor(filter, rectangle); + var processor = new EdgeDetectorProcessor(kernel, grayscale); + source.ApplyProcessor(processor); return source; } - private static IImageProcessor GetProcessor(KnownEdgeDetectionOperators filter, bool grayscale) - { - IImageProcessor processor; - - switch (filter) - { - case KnownEdgeDetectionOperators.Kayyali: - processor = new KayyaliProcessor(grayscale); - break; - - case KnownEdgeDetectionOperators.Kirsch: - processor = new KirschProcessor(grayscale); - break; - - case KnownEdgeDetectionOperators.Laplacian3x3: - processor = new Laplacian3x3Processor(grayscale); - break; - - case KnownEdgeDetectionOperators.Laplacian5x5: - processor = new Laplacian5x5Processor(grayscale); - break; - - case KnownEdgeDetectionOperators.LaplacianOfGaussian: - processor = new LaplacianOfGaussianProcessor(grayscale); - break; - - case KnownEdgeDetectionOperators.Prewitt: - processor = new PrewittProcessor(grayscale); - break; + /// + /// Detects any edges within the image operating in grayscale mode. + /// + /// The image this method extends. + /// The edge detector kernel. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectorKernel kernel, + Rectangle rectangle) => + DetectEdges(source, kernel, true, rectangle); - case KnownEdgeDetectionOperators.RobertsCross: - processor = new RobertsCrossProcessor(grayscale); - break; + /// + /// Detects any edges within the image using a . + /// + /// The image this method extends. + /// The edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. + /// + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectorKernel kernel, + bool grayscale, + Rectangle rectangle) + { + var processor = new EdgeDetectorProcessor(kernel, grayscale); + source.ApplyProcessor(processor, rectangle); + return source; + } - case KnownEdgeDetectionOperators.Robinson: - processor = new RobinsonProcessor(grayscale); - break; + /// + /// Detects any edges within the image operating in grayscale mode. + /// + /// The image this method extends. + /// Thecompass edge detector kernel. + /// The to allow chaining of operations. + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectorCompassKernel kernel) => + DetectEdges(source, kernel, true); - case KnownEdgeDetectionOperators.Scharr: - processor = new ScharrProcessor(grayscale); - break; + /// + /// Detects any edges within the image using a . + /// + /// The image this method extends. + /// Thecompass edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectorCompassKernel kernel, + bool grayscale) + { + var processor = new EdgeDetectorCompassProcessor(kernel, grayscale); + source.ApplyProcessor(processor); + return source; + } - default: - processor = new SobelProcessor(grayscale); - break; - } + /// + /// Detects any edges within the image operating in grayscale mode. + /// + /// The image this method extends. + /// Thecompass edge detector kernel. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectorCompassKernel kernel, + Rectangle rectangle) => + DetectEdges(source, kernel, true, rectangle); - return processor; + /// + /// Detects any edges within the image using a . + /// + /// The image this method extends. + /// Thecompass edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. + /// + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectorCompassKernel kernel, + bool grayscale, + Rectangle rectangle) + { + var processor = new EdgeDetectorCompassProcessor(kernel, grayscale); + source.ApplyProcessor(processor, rectangle); + return source; } } } diff --git a/src/ImageSharp/Processing/KnownEdgeDetectionOperators.cs b/src/ImageSharp/Processing/KnownEdgeDetectorKernels.cs similarity index 97% rename from src/ImageSharp/Processing/KnownEdgeDetectionOperators.cs rename to src/ImageSharp/Processing/KnownEdgeDetectorKernels.cs index e41fb00ce..2e279d340 100644 --- a/src/ImageSharp/Processing/KnownEdgeDetectionOperators.cs +++ b/src/ImageSharp/Processing/KnownEdgeDetectorKernels.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Contains reusable static instances of known edge detection kernels. /// - public static class KnownEdgeDetectionOperators + public static class KnownEdgeDetectorKernels { /// /// Gets the Kayyali edge detector kernel. diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs index ebc8f0e4f..dd9c06938 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration = this.Source.GetConfiguration(); // Detect the edges. - new SobelProcessor(false).Execute(this.Configuration, temp, this.SourceRectangle); + new EdgeDetector2DProcessor(KnownEdgeDetectorKernels.Sobel, false).Execute(this.Configuration, temp, this.SourceRectangle); // Apply threshold binarization filter. new BinaryThresholdProcessor(this.definition.Threshold).Execute(this.Configuration, temp, this.SourceRectangle); diff --git a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs index cf46c4f52..ce2fa988c 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(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)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Kayyali)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Kayyali)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Kirsch)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Laplacian3x3)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Laplacian5x5)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.LaplacianOfGaussian)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Prewitt)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.RobertsCross)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Robinson)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Scharr)); + this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectorKernels.Sobel)); } } } diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index fd41a04e2..e14ab748f 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -1,12 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; - using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.ImageSharp.Tests.TestUtilities; - using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Convolution @@ -14,62 +10,190 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public class DetectEdgesTest : BaseImageOperationsExtensionTest { [Fact] - public void DetectEdges_SobelProcessorDefaultsSet() + public void DetectEdges_EdgeDetector2DProcessorDefaultsSet() { this.operations.DetectEdges(); + EdgeDetector2DProcessor processor = this.Verify(); - // TODO: Enable once we have updated the images - SobelProcessor processor = this.Verify(); Assert.True(processor.Grayscale); + Assert.Equal(KnownEdgeDetectorKernels.Sobel, processor.Kernel); } [Fact] - public void DetectEdges_Rect_SobelProcessorDefaultsSet() + public void DetectEdges_Rect_EdgeDetector2DProcessorDefaultsSet() { this.operations.DetectEdges(this.rect); + EdgeDetector2DProcessor processor = this.Verify(this.rect); + + Assert.True(processor.Grayscale); + Assert.Equal(KnownEdgeDetectorKernels.Sobel, processor.Kernel); + } + + public static TheoryData EdgeDetector2DKernelData = + new TheoryData + { + { KnownEdgeDetectorKernels.Kayyali, true }, + { KnownEdgeDetectorKernels.Kayyali, false }, + { KnownEdgeDetectorKernels.Prewitt, true }, + { KnownEdgeDetectorKernels.Prewitt, false }, + { KnownEdgeDetectorKernels.RobertsCross, true }, + { KnownEdgeDetectorKernels.RobertsCross, false }, + { KnownEdgeDetectorKernels.Scharr, true }, + { KnownEdgeDetectorKernels.Scharr, false }, + { KnownEdgeDetectorKernels.Sobel, true }, + { KnownEdgeDetectorKernels.Sobel, false }, + }; + + [Theory] + [MemberData(nameof(EdgeDetector2DKernelData))] + public void DetectEdges_EdgeDetector2DProcessor_DefaultGrayScale_Set(EdgeDetector2DKernel kernel, bool _) + { + this.operations.DetectEdges(kernel); + EdgeDetector2DProcessor processor = this.Verify(); + + Assert.True(processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); + } + + [Theory] + [MemberData(nameof(EdgeDetector2DKernelData))] + public void DetectEdges_Rect_EdgeDetector2DProcessor_DefaultGrayScale_Set(EdgeDetector2DKernel kernel, bool _) + { + this.operations.DetectEdges(kernel, this.rect); + EdgeDetector2DProcessor processor = this.Verify(this.rect); + + Assert.True(processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); + } + + [Theory] + [MemberData(nameof(EdgeDetector2DKernelData))] + public void DetectEdges_EdgeDetector2DProcessorSet(EdgeDetector2DKernel kernel, bool grayscale) + { + this.operations.DetectEdges(kernel, grayscale); + EdgeDetector2DProcessor processor = this.Verify(); + + Assert.Equal(grayscale, processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); + } + + [Theory] + [MemberData(nameof(EdgeDetector2DKernelData))] + public void DetectEdges_Rect_EdgeDetector2DProcessorSet(EdgeDetector2DKernel kernel, bool grayscale) + { + this.operations.DetectEdges(kernel, grayscale, this.rect); + EdgeDetector2DProcessor processor = this.Verify(this.rect); + + Assert.Equal(grayscale, processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); + } + + public static TheoryData EdgeDetectorKernelData = + new TheoryData + { + { KnownEdgeDetectorKernels.Laplacian3x3, true }, + { KnownEdgeDetectorKernels.Laplacian3x3, false }, + { KnownEdgeDetectorKernels.Laplacian5x5, true }, + { KnownEdgeDetectorKernels.Laplacian5x5, false }, + { KnownEdgeDetectorKernels.LaplacianOfGaussian, true }, + { KnownEdgeDetectorKernels.LaplacianOfGaussian, false }, + }; + + [Theory] + [MemberData(nameof(EdgeDetectorKernelData))] + public void DetectEdges_EdgeDetectorProcessor_DefaultGrayScale_Set(EdgeDetectorKernel kernel, bool _) + { + this.operations.DetectEdges(kernel); + EdgeDetectorProcessor processor = this.Verify(); - // TODO: Enable once we have updated the images - SobelProcessor processor = this.Verify(this.rect); Assert.True(processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); } - public static IEnumerable EdgeDetectionTheoryData => new[] + [Theory] + [MemberData(nameof(EdgeDetectorKernelData))] + public void DetectEdges_Rect_EdgeDetectorProcessor_DefaultGrayScale_Set(EdgeDetectorKernel kernel, bool _) { - 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 }, - }; + this.operations.DetectEdges(kernel, this.rect); + EdgeDetectorProcessor processor = this.Verify(this.rect); + + Assert.True(processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); + } [Theory] - [MemberData(nameof(EdgeDetectionTheoryData))] - public void DetectEdges_filter_SobelProcessorDefaultsSet(TestType type, KnownEdgeDetectionOperators filter) - where TProcessor : EdgeDetectorProcessor + [MemberData(nameof(EdgeDetectorKernelData))] + public void DetectEdges_EdgeDetectorProcessorSet(EdgeDetectorKernel kernel, bool grayscale) { - this.operations.DetectEdges(filter); + this.operations.DetectEdges(kernel, grayscale); + EdgeDetectorProcessor processor = this.Verify(); + + Assert.Equal(grayscale, processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); + } + + [Theory] + [MemberData(nameof(EdgeDetectorKernelData))] + public void DetectEdges_Rect_EdgeDetectorProcessorSet(EdgeDetectorKernel kernel, bool grayscale) + { + this.operations.DetectEdges(kernel, grayscale, this.rect); + EdgeDetectorProcessor processor = this.Verify(this.rect); + + Assert.Equal(grayscale, processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); + } + + public static TheoryData EdgeDetectorCompassKernelData = + new TheoryData + { + { KnownEdgeDetectorKernels.Kirsch, true }, + { KnownEdgeDetectorKernels.Kirsch, false }, + { KnownEdgeDetectorKernels.Robinson, true }, + { KnownEdgeDetectorKernels.Robinson, false }, + }; + + [Theory] + [MemberData(nameof(EdgeDetectorCompassKernelData))] + public void DetectEdges_EdgeDetectorCompassProcessor_DefaultGrayScale_Set(EdgeDetectorCompassKernel kernel, bool _) + { + this.operations.DetectEdges(kernel); + EdgeDetectorCompassProcessor processor = this.Verify(); + + Assert.True(processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); + } + + [Theory] + [MemberData(nameof(EdgeDetectorCompassKernelData))] + public void DetectEdges_Rect_EdgeDetectorCompassProcessor_DefaultGrayScale_Set(EdgeDetectorCompassKernel kernel, bool _) + { + this.operations.DetectEdges(kernel, this.rect); + EdgeDetectorCompassProcessor processor = this.Verify(this.rect); - // TODO: Enable once we have updated the images - var processor = this.Verify(); Assert.True(processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); + } + + [Theory] + [MemberData(nameof(EdgeDetectorCompassKernelData))] + public void DetectEdges_EdgeDetectorCompassProcessorSet(EdgeDetectorCompassKernel kernel, bool grayscale) + { + this.operations.DetectEdges(kernel, grayscale); + EdgeDetectorCompassProcessor processor = this.Verify(); + + Assert.Equal(grayscale, processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); } [Theory] - [MemberData(nameof(EdgeDetectionTheoryData))] - public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet(TestType type, KnownEdgeDetectionOperators filter) - where TProcessor : EdgeDetectorProcessor + [MemberData(nameof(EdgeDetectorCompassKernelData))] + public void DetectEdges_Rect_EdgeDetectorCompassProcessorSet(EdgeDetectorCompassKernel kernel, bool grayscale) { - bool grey = (int)filter % 2 == 0; - this.operations.DetectEdges(filter, grey); + this.operations.DetectEdges(kernel, grayscale, this.rect); + EdgeDetectorCompassProcessor processor = this.Verify(this.rect); - // TODO: Enable once we have updated the images - var processor = this.Verify(); - Assert.Equal(grey, processor.Grayscale); + Assert.Equal(grayscale, processor.Grayscale); + Assert.Equal(kernel, processor.Kernel); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index a96b05d63..7a3b104b3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; @@ -20,18 +21,29 @@ 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 { - KnownEdgeDetectionOperators.Kayyali, - KnownEdgeDetectionOperators.Kirsch, - KnownEdgeDetectionOperators.Laplacian3x3, - KnownEdgeDetectionOperators.Laplacian5x5, - KnownEdgeDetectionOperators.LaplacianOfGaussian, - KnownEdgeDetectionOperators.Prewitt, - KnownEdgeDetectionOperators.RobertsCross, - KnownEdgeDetectionOperators.Robinson, - KnownEdgeDetectionOperators.Scharr, - KnownEdgeDetectionOperators.Sobel + KnownEdgeDetectorKernels.Laplacian3x3, + KnownEdgeDetectorKernels.Laplacian5x5, + KnownEdgeDetectorKernels.LaplacianOfGaussian, + }; + + public static readonly TheoryData DetectEdges2DFilters + = new TheoryData + { + KnownEdgeDetectorKernels.Kayyali, + KnownEdgeDetectorKernels.Prewitt, + KnownEdgeDetectorKernels.RobertsCross, + KnownEdgeDetectorKernels.Scharr, + KnownEdgeDetectorKernels.Sobel + }; + + public static readonly TheoryData DetectEdgesCompassFilters + = new TheoryData + { + KnownEdgeDetectorKernels.Kirsch, + KnownEdgeDetectorKernels.Robinson, }; [Theory] @@ -53,7 +65,39 @@ 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, KnownEdgeDetectionOperators detector) + public void DetectEdges_WorksWithAllFilters(TestImageProvider provider, EdgeDetectorKernel detector) + where TPixel : unmanaged, IPixel + { + bool hasAlpha = provider.SourceFileOrDescription.Contains("TestPattern"); + ImageComparer comparer = hasAlpha ? TransparentComparer : OpaqueComparer; + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.DetectEdges(detector)); + image.DebugSave(provider, detector.ToString()); + image.CompareToReferenceOutput(comparer, provider, detector.ToString()); + } + } + + [Theory] + [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, PixelTypes.Rgba32)] + [WithFileCollection(nameof(TestImages), nameof(DetectEdges2DFilters), PixelTypes.Rgba32)] + public void DetectEdges2D_WorksWithAllFilters(TestImageProvider provider, EdgeDetector2DKernel detector) + where TPixel : unmanaged, IPixel + { + bool hasAlpha = provider.SourceFileOrDescription.Contains("TestPattern"); + ImageComparer comparer = hasAlpha ? TransparentComparer : OpaqueComparer; + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.DetectEdges(detector)); + image.DebugSave(provider, detector.ToString()); + image.CompareToReferenceOutput(comparer, provider, detector.ToString()); + } + } + + [Theory] + [WithTestPatternImages(nameof(DetectEdgesCompassFilters), 100, 100, PixelTypes.Rgba32)] + [WithFileCollection(nameof(TestImages), nameof(DetectEdgesFilters), PixelTypes.Rgba32)] + public void DetectEdgesCompass_WorksWithAllFilters(TestImageProvider provider, EdgeDetectorCompassKernel detector) where TPixel : unmanaged, IPixel { bool hasAlpha = provider.SourceFileOrDescription.Contains("TestPattern"); @@ -115,7 +159,29 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution [Theory] [WithFile(Tests.TestImages.Png.Bike, nameof(DetectEdgesFilters), PixelTypes.Rgba32)] - public void WorksWithDiscoBuffers(TestImageProvider provider, KnownEdgeDetectionOperators detector) + public void WorksWithDiscoBuffers(TestImageProvider provider, EdgeDetectorKernel detector) + where TPixel : unmanaged, IPixel + { + provider.RunBufferCapacityLimitProcessorTest( + 41, + c => c.DetectEdges(detector), + detector); + } + + [Theory] + [WithFile(Tests.TestImages.Png.Bike, nameof(DetectEdges2DFilters), PixelTypes.Rgba32)] + public void WorksWithDiscoBuffers2D(TestImageProvider provider, EdgeDetector2DKernel detector) + where TPixel : unmanaged, IPixel + { + provider.RunBufferCapacityLimitProcessorTest( + 41, + c => c.DetectEdges(detector), + detector); + } + + [Theory] + [WithFile(Tests.TestImages.Png.Bike, nameof(DetectEdgesCompassFilters), PixelTypes.Rgba32)] + public void WorksWithDiscoBuffersCompass(TestImageProvider provider, EdgeDetectorCompassKernel detector) where TPixel : unmanaged, IPixel { provider.RunBufferCapacityLimitProcessorTest( From b130bcd5e3d8dd5db5d32305879566ace01d9d43 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 1 Aug 2020 22:37:15 +0100 Subject: [PATCH 4/7] Update skipped tests --- .../Processors/Convolution/DetectEdgesTest.cs | 78 ++++++++++++------- tests/Images/External | 2 +- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index 7a3b104b3..33619415b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -21,29 +21,29 @@ 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 { - KnownEdgeDetectorKernels.Laplacian3x3, - KnownEdgeDetectorKernels.Laplacian5x5, - KnownEdgeDetectorKernels.LaplacianOfGaussian, + { KnownEdgeDetectorKernels.Laplacian3x3, nameof(KnownEdgeDetectorKernels.Laplacian3x3) }, + { KnownEdgeDetectorKernels.Laplacian5x5, nameof(KnownEdgeDetectorKernels.Laplacian5x5) }, + { KnownEdgeDetectorKernels.LaplacianOfGaussian, nameof(KnownEdgeDetectorKernels.LaplacianOfGaussian) }, }; - public static readonly TheoryData DetectEdges2DFilters - = new TheoryData + public static readonly TheoryData DetectEdges2DFilters + = new TheoryData { - KnownEdgeDetectorKernels.Kayyali, - KnownEdgeDetectorKernels.Prewitt, - KnownEdgeDetectorKernels.RobertsCross, - KnownEdgeDetectorKernels.Scharr, - KnownEdgeDetectorKernels.Sobel + { KnownEdgeDetectorKernels.Kayyali, nameof(KnownEdgeDetectorKernels.Kayyali) }, + { KnownEdgeDetectorKernels.Prewitt, nameof(KnownEdgeDetectorKernels.Prewitt) }, + { KnownEdgeDetectorKernels.RobertsCross, nameof(KnownEdgeDetectorKernels.RobertsCross) }, + { KnownEdgeDetectorKernels.Scharr, nameof(KnownEdgeDetectorKernels.Scharr) }, + { KnownEdgeDetectorKernels.Sobel, nameof(KnownEdgeDetectorKernels.Sobel) }, }; - public static readonly TheoryData DetectEdgesCompassFilters - = new TheoryData + public static readonly TheoryData DetectEdgesCompassFilters + = new TheoryData { - KnownEdgeDetectorKernels.Kirsch, - KnownEdgeDetectorKernels.Robinson, + { KnownEdgeDetectorKernels.Kirsch, nameof(KnownEdgeDetectorKernels.Kirsch) }, + { KnownEdgeDetectorKernels.Robinson, nameof(KnownEdgeDetectorKernels.Robinson) }, }; [Theory] @@ -65,7 +65,10 @@ 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, EdgeDetectorKernel detector) + public void DetectEdges_WorksWithAllFilters( + TestImageProvider provider, + EdgeDetectorKernel detector, + string name) where TPixel : unmanaged, IPixel { bool hasAlpha = provider.SourceFileOrDescription.Contains("TestPattern"); @@ -73,15 +76,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution using (Image image = provider.GetImage()) { image.Mutate(x => x.DetectEdges(detector)); - image.DebugSave(provider, detector.ToString()); - image.CompareToReferenceOutput(comparer, provider, detector.ToString()); + image.DebugSave(provider, name); + image.CompareToReferenceOutput(comparer, provider, name); } } [Theory] - [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(DetectEdges2DFilters), 100, 100, PixelTypes.Rgba32)] [WithFileCollection(nameof(TestImages), nameof(DetectEdges2DFilters), PixelTypes.Rgba32)] - public void DetectEdges2D_WorksWithAllFilters(TestImageProvider provider, EdgeDetector2DKernel detector) + public void DetectEdges2D_WorksWithAllFilters( + TestImageProvider provider, + EdgeDetector2DKernel detector, + string name) where TPixel : unmanaged, IPixel { bool hasAlpha = provider.SourceFileOrDescription.Contains("TestPattern"); @@ -89,15 +95,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution using (Image image = provider.GetImage()) { image.Mutate(x => x.DetectEdges(detector)); - image.DebugSave(provider, detector.ToString()); - image.CompareToReferenceOutput(comparer, provider, detector.ToString()); + image.DebugSave(provider, name); + image.CompareToReferenceOutput(comparer, provider, name); } } [Theory] [WithTestPatternImages(nameof(DetectEdgesCompassFilters), 100, 100, PixelTypes.Rgba32)] - [WithFileCollection(nameof(TestImages), nameof(DetectEdgesFilters), PixelTypes.Rgba32)] - public void DetectEdgesCompass_WorksWithAllFilters(TestImageProvider provider, EdgeDetectorCompassKernel detector) + [WithFileCollection(nameof(TestImages), nameof(DetectEdgesCompassFilters), PixelTypes.Rgba32)] + public void DetectEdgesCompass_WorksWithAllFilters( + TestImageProvider provider, + EdgeDetectorCompassKernel detector, + string name) where TPixel : unmanaged, IPixel { bool hasAlpha = provider.SourceFileOrDescription.Contains("TestPattern"); @@ -105,8 +114,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution using (Image image = provider.GetImage()) { image.Mutate(x => x.DetectEdges(detector)); - image.DebugSave(provider, detector.ToString()); - image.CompareToReferenceOutput(comparer, provider, detector.ToString()); + image.DebugSave(provider, name); + image.CompareToReferenceOutput(comparer, provider, name); } } @@ -159,7 +168,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution [Theory] [WithFile(Tests.TestImages.Png.Bike, nameof(DetectEdgesFilters), PixelTypes.Rgba32)] - public void WorksWithDiscoBuffers(TestImageProvider provider, EdgeDetectorKernel detector) + public void WorksWithDiscoBuffers( + TestImageProvider provider, + EdgeDetectorKernel detector, + string _) where TPixel : unmanaged, IPixel { provider.RunBufferCapacityLimitProcessorTest( @@ -170,7 +182,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution [Theory] [WithFile(Tests.TestImages.Png.Bike, nameof(DetectEdges2DFilters), PixelTypes.Rgba32)] - public void WorksWithDiscoBuffers2D(TestImageProvider provider, EdgeDetector2DKernel detector) + public void WorksWithDiscoBuffers2D( + TestImageProvider provider, + EdgeDetector2DKernel detector, + string _) where TPixel : unmanaged, IPixel { provider.RunBufferCapacityLimitProcessorTest( @@ -181,7 +196,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution [Theory] [WithFile(Tests.TestImages.Png.Bike, nameof(DetectEdgesCompassFilters), PixelTypes.Rgba32)] - public void WorksWithDiscoBuffersCompass(TestImageProvider provider, EdgeDetectorCompassKernel detector) + public void WorksWithDiscoBuffersCompass( + TestImageProvider provider, + EdgeDetectorCompassKernel detector, + string _) where TPixel : unmanaged, IPixel { provider.RunBufferCapacityLimitProcessorTest( diff --git a/tests/Images/External b/tests/Images/External index 0d1f91e2f..6a0030806 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 0d1f91e2fe1491f6dc2c137a8ea20460fde4404c +Subproject commit 6a003080674d1fedc66292c13ce5a357b2a33083 From 59139d72e3259ddfd9b5d422eeb3e2abfe8cf09a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 1 Aug 2020 23:22:36 +0100 Subject: [PATCH 5/7] Create EdgeDetectorKernelTests.cs --- .../Processors/EdgeDetectorKernelTests.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/ImageSharp.Tests/Processing/Convolution/Processors/EdgeDetectorKernelTests.cs diff --git a/tests/ImageSharp.Tests/Processing/Convolution/Processors/EdgeDetectorKernelTests.cs b/tests/ImageSharp.Tests/Processing/Convolution/Processors/EdgeDetectorKernelTests.cs new file mode 100644 index 000000000..b50e5d7cc --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Convolution/Processors/EdgeDetectorKernelTests.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Convolution; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Convolution.Processors +{ + public class EdgeDetectorKernelTests + { + [Fact] + public void EdgeDetectorKernelEqualityOperatorTest() + { + EdgeDetectorKernel kernel0 = KnownEdgeDetectorKernels.Laplacian3x3; + EdgeDetectorKernel kernel1 = KnownEdgeDetectorKernels.Laplacian3x3; + EdgeDetectorKernel kernel2 = KnownEdgeDetectorKernels.Laplacian5x5; + + Assert.True(kernel0 == kernel1); + Assert.False(kernel0 != kernel1); + + Assert.True(kernel0 != kernel2); + Assert.False(kernel0 == kernel2); + + Assert.True(kernel0.Equals((object)kernel1)); + Assert.True(kernel0.Equals(kernel1)); + + Assert.False(kernel0.Equals((object)kernel2)); + Assert.False(kernel0.Equals(kernel2)); + + Assert.Equal(kernel0.GetHashCode(), kernel1.GetHashCode()); + Assert.NotEqual(kernel0.GetHashCode(), kernel2.GetHashCode()); + } + + [Fact] + public void EdgeDetector2DKernelEqualityOperatorTest() + { + EdgeDetector2DKernel kernel0 = KnownEdgeDetectorKernels.Prewitt; + EdgeDetector2DKernel kernel1 = KnownEdgeDetectorKernels.Prewitt; + EdgeDetector2DKernel kernel2 = KnownEdgeDetectorKernels.RobertsCross; + + Assert.True(kernel0 == kernel1); + Assert.False(kernel0 != kernel1); + + Assert.True(kernel0 != kernel2); + Assert.False(kernel0 == kernel2); + + Assert.True(kernel0.Equals((object)kernel1)); + Assert.True(kernel0.Equals(kernel1)); + + Assert.False(kernel0.Equals((object)kernel2)); + Assert.False(kernel0.Equals(kernel2)); + + Assert.Equal(kernel0.GetHashCode(), kernel1.GetHashCode()); + Assert.NotEqual(kernel0.GetHashCode(), kernel2.GetHashCode()); + } + + [Fact] + public void EdgeDetectorCompassKernelEqualityOperatorTest() + { + EdgeDetectorCompassKernel kernel0 = KnownEdgeDetectorKernels.Kirsch; + EdgeDetectorCompassKernel kernel1 = KnownEdgeDetectorKernels.Kirsch; + EdgeDetectorCompassKernel kernel2 = KnownEdgeDetectorKernels.Robinson; + + Assert.True(kernel0 == kernel1); + Assert.False(kernel0 != kernel1); + + Assert.True(kernel0 != kernel2); + Assert.False(kernel0 == kernel2); + + Assert.True(kernel0.Equals((object)kernel1)); + Assert.True(kernel0.Equals(kernel1)); + + Assert.False(kernel0.Equals((object)kernel2)); + Assert.False(kernel0.Equals(kernel2)); + + Assert.Equal(kernel0.GetHashCode(), kernel1.GetHashCode()); + Assert.NotEqual(kernel0.GetHashCode(), kernel2.GetHashCode()); + } + } +} From 84e6112b9daf85a79706375ed8ef938afdf4c81d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 2 Aug 2020 11:29:33 +0100 Subject: [PATCH 6/7] Remove warnings --- .../Processing/Processors/Convolution/DetectEdgesTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index 33619415b..e468778de 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Convolution; @@ -11,6 +12,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { [GroupOutput("Convolution")] + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "OK. Used for TheoryData compatibility.")] public class DetectEdgesTest { private static readonly ImageComparer OpaqueComparer = ImageComparer.TolerantPercentage(0.01F); From 9f3ea01c19359afd35de72615d13a4affd0155d0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 2 Aug 2020 12:22:15 +0100 Subject: [PATCH 7/7] Remove all warnings --- .../ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index e14ab748f..3ffb8f4e3 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Convolution; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Convolution { + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "OK. Used for TheoryData compatibility.")] public class DetectEdgesTest : BaseImageOperationsExtensionTest { [Fact]