Browse Source

Move kernels to reusable struct

pull/1299/head
James Jackson-South 6 years ago
parent
commit
b5d772ddf4
  1. 2
      src/ImageSharp/ImageSharp.csproj
  2. 61
      src/ImageSharp/Processing/EdgeDetectionOperators.cs
  3. 30
      src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs
  4. 63
      src/ImageSharp/Processing/KnownEdgeDetectionOperators.cs
  5. 27
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
  6. 24
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs
  7. 55
      src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs
  8. 103
      src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetector2DKernel.cs
  9. 161
      src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs
  10. 75
      src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorKernel.cs
  11. 0
      src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KayyaliKernels.cs
  12. 22
      src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KirschKernels.cs
  13. 0
      src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernelFactory.cs
  14. 0
      src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernels.cs
  15. 0
      src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/PrewittKernels.cs
  16. 0
      src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobertsCrossKernels.cs
  17. 22
      src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobinsonKernels.cs
  18. 0
      src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/ScharrKernels.cs
  19. 0
      src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/SobelKernels.cs
  20. 22
      tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs
  21. 24
      tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs
  22. 26
      tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs

2
src/ImageSharp/ImageSharp.csproj

@ -20,7 +20,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub"/> <PackageReference Include="Microsoft.SourceLink.GitHub" />
<PackageReference Include="MinVer" PrivateAssets="All" /> <PackageReference Include="MinVer" PrivateAssets="All" />
</ItemGroup> </ItemGroup>

61
src/ImageSharp/Processing/EdgeDetectionOperators.cs

@ -1,61 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Enumerates the various types of defined edge detection filters.
/// </summary>
public enum EdgeDetectionOperators
{
/// <summary>
/// The Kayyali operator filter.
/// </summary>
Kayyali,
/// <summary>
/// The Kirsch operator filter.
/// </summary>
Kirsch,
/// <summary>
/// The Laplacian3X3 operator filter.
/// </summary>
Laplacian3x3,
/// <summary>
/// The Laplacian5X5 operator filter.
/// </summary>
Laplacian5x5,
/// <summary>
/// The LaplacianOfGaussian operator filter.
/// </summary>
LaplacianOfGaussian,
/// <summary>
/// The Prewitt operator filter.
/// </summary>
Prewitt,
/// <summary>
/// The RobertsCross operator filter.
/// </summary>
RobertsCross,
/// <summary>
/// The Robinson operator filter.
/// </summary>
Robinson,
/// <summary>
/// The Scharr operator filter.
/// </summary>
Scharr,
/// <summary>
/// The Sobel operator filter.
/// </summary>
Sobel
}
}

30
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. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectionOperators filter) => KnownEdgeDetectionOperators filter) =>
DetectEdges(source, GetProcessor(filter, true)); DetectEdges(source, GetProcessor(filter, true));
/// <summary> /// <summary>
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectionOperators filter, KnownEdgeDetectionOperators filter,
bool grayscale) => bool grayscale) =>
DetectEdges(source, GetProcessor(filter, grayscale)); DetectEdges(source, GetProcessor(filter, grayscale));
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectionOperators filter, KnownEdgeDetectionOperators filter,
Rectangle rectangle, Rectangle rectangle,
bool grayscale = true) => bool grayscale = true) =>
DetectEdges(source, rectangle, GetProcessor(filter, grayscale)); DetectEdges(source, rectangle, GetProcessor(filter, grayscale));
@ -102,45 +102,45 @@ namespace SixLabors.ImageSharp.Processing
return source; return source;
} }
private static IImageProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) private static IImageProcessor GetProcessor(KnownEdgeDetectionOperators filter, bool grayscale)
{ {
IImageProcessor processor; IImageProcessor processor;
switch (filter) switch (filter)
{ {
case EdgeDetectionOperators.Kayyali: case KnownEdgeDetectionOperators.Kayyali:
processor = new KayyaliProcessor(grayscale); processor = new KayyaliProcessor(grayscale);
break; break;
case EdgeDetectionOperators.Kirsch: case KnownEdgeDetectionOperators.Kirsch:
processor = new KirschProcessor(grayscale); processor = new KirschProcessor(grayscale);
break; break;
case EdgeDetectionOperators.Laplacian3x3: case KnownEdgeDetectionOperators.Laplacian3x3:
processor = new Laplacian3x3Processor(grayscale); processor = new Laplacian3x3Processor(grayscale);
break; break;
case EdgeDetectionOperators.Laplacian5x5: case KnownEdgeDetectionOperators.Laplacian5x5:
processor = new Laplacian5x5Processor(grayscale); processor = new Laplacian5x5Processor(grayscale);
break; break;
case EdgeDetectionOperators.LaplacianOfGaussian: case KnownEdgeDetectionOperators.LaplacianOfGaussian:
processor = new LaplacianOfGaussianProcessor(grayscale); processor = new LaplacianOfGaussianProcessor(grayscale);
break; break;
case EdgeDetectionOperators.Prewitt: case KnownEdgeDetectionOperators.Prewitt:
processor = new PrewittProcessor(grayscale); processor = new PrewittProcessor(grayscale);
break; break;
case EdgeDetectionOperators.RobertsCross: case KnownEdgeDetectionOperators.RobertsCross:
processor = new RobertsCrossProcessor(grayscale); processor = new RobertsCrossProcessor(grayscale);
break; break;
case EdgeDetectionOperators.Robinson: case KnownEdgeDetectionOperators.Robinson:
processor = new RobinsonProcessor(grayscale); processor = new RobinsonProcessor(grayscale);
break; break;
case EdgeDetectionOperators.Scharr: case KnownEdgeDetectionOperators.Scharr:
processor = new ScharrProcessor(grayscale); processor = new ScharrProcessor(grayscale);
break; break;
@ -152,4 +152,4 @@ namespace SixLabors.ImageSharp.Processing
return processor; return processor;
} }
} }
} }

63
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
{
/// <summary>
/// Contains reusable static instances of known edge detection kernels.
/// </summary>
public static class KnownEdgeDetectionOperators
{
/// <summary>
/// Gets the Kayyali edge detector kernel.
/// </summary>
public static EdgeDetector2DKernel Kayyali { get; } = EdgeDetector2DKernel.KayyaliKernel;
/// <summary>
/// Gets the Kirsch edge detector kernel.
/// </summary>
public static EdgeDetectorCompassKernel Kirsch { get; } = EdgeDetectorCompassKernel.Kirsch;
/// <summary>
/// Gets the Laplacian 3x3 edge detector kernel.
/// </summary>
public static EdgeDetectorKernel Laplacian3x3 { get; } = EdgeDetectorKernel.Laplacian3x3;
/// <summary>
/// Gets the Laplacian 5x5 edge detector kernel.
/// </summary>
public static EdgeDetectorKernel Laplacian5x5 { get; } = EdgeDetectorKernel.Laplacian5x5;
/// <summary>
/// Gets the Laplacian of Gaussian edge detector kernel.
/// </summary>
public static EdgeDetectorKernel LaplacianOfGaussian { get; } = EdgeDetectorKernel.LaplacianOfGaussian;
/// <summary>
/// Gets the Prewitt edge detector kernel.
/// </summary>
public static EdgeDetector2DKernel Prewitt { get; } = EdgeDetector2DKernel.PrewittKernel;
/// <summary>
/// Gets the Roberts-Cross edge detector kernel.
/// </summary>
public static EdgeDetector2DKernel RobertsCross { get; } = EdgeDetector2DKernel.RobertsCrossKernel;
/// <summary>
/// Gets the Robinson edge detector kernel.
/// </summary>
public static EdgeDetectorCompassKernel Robinson { get; } = EdgeDetectorCompassKernel.Robinson;
/// <summary>
/// Gets the Scharr edge detector kernel.
/// </summary>
public static EdgeDetector2DKernel Scharr { get; } = EdgeDetector2DKernel.ScharrKernel;
/// <summary>
/// Gets the Sobel edge detector kernel.
/// </summary>
public static EdgeDetector2DKernel Sobel { get; } = EdgeDetector2DKernel.SobelKernel;
}
}

27
src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs

@ -18,25 +18,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
internal class EdgeDetectorCompassProcessor<TPixel> : ImageProcessor<TPixel> internal class EdgeDetectorCompassProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private readonly DenseMatrix<float>[] kernels;
private readonly bool grayscale;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="EdgeDetectorCompassProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="EdgeDetectorCompassProcessor{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="kernels">Gets the kernels to use.</param> /// <param name="kernels">The kernels to use.</param>
/// <param name="grayscale">Whether to convert the image to grayscale before performing edge detection.</param> /// <param name="grayscale">Whether to convert the image to grayscale before performing edge detection.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param> /// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param> /// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
internal EdgeDetectorCompassProcessor(Configuration configuration, CompassKernels kernels, bool grayscale, Image<TPixel> source, Rectangle sourceRectangle) internal EdgeDetectorCompassProcessor(Configuration configuration, in EdgeDetectorCompassKernel kernels, bool grayscale, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle) : base(configuration, source, sourceRectangle)
{ {
this.Grayscale = grayscale; this.grayscale = grayscale;
this.Kernels = kernels; this.kernels = kernels.Flatten();
} }
private CompassKernels Kernels { get; }
private bool Grayscale { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void BeforeImageApply() protected override void BeforeImageApply()
{ {
@ -45,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
opaque.Execute(); opaque.Execute();
} }
if (this.Grayscale) if (this.grayscale)
{ {
new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle);
} }
@ -56,29 +55,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc /> /// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
DenseMatrix<float>[] kernels = this.Kernels.Flatten();
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
// We need a clean copy for each pass to start from // We need a clean copy for each pass to start from
using ImageFrame<TPixel> cleanCopy = source.Clone(); using ImageFrame<TPixel> cleanCopy = source.Clone();
using (var processor = new ConvolutionProcessor<TPixel>(this.Configuration, kernels[0], true, this.Source, interest)) using (var processor = new ConvolutionProcessor<TPixel>(this.Configuration, in this.kernels[0], true, this.Source, interest))
{ {
processor.Apply(source); processor.Apply(source);
} }
if (kernels.Length == 1) if (this.kernels.Length == 1)
{ {
return; return;
} }
// Additional runs // Additional runs
for (int i = 1; i < kernels.Length; i++) for (int i = 1; i < this.kernels.Length; i++)
{ {
using ImageFrame<TPixel> pass = cleanCopy.Clone(); using ImageFrame<TPixel> pass = cleanCopy.Clone();
using (var processor = new ConvolutionProcessor<TPixel>(this.Configuration, kernels[i], true, this.Source, interest)) using (var processor = new ConvolutionProcessor<TPixel>(this.Configuration, in this.kernels[i], true, this.Source, interest))
{ {
processor.Apply(pass); processor.Apply(pass);
} }

24
src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs

@ -13,6 +13,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
internal class EdgeDetectorProcessor<TPixel> : ImageProcessor<TPixel> internal class EdgeDetectorProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private readonly bool grayscale;
private readonly DenseMatrix<float> kernelXY;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="EdgeDetectorProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="EdgeDetectorProcessor{TPixel}"/> class.
/// </summary> /// </summary>
@ -23,23 +26,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <param name="sourceRectangle">The target area to process for the current processor instance.</param> /// <param name="sourceRectangle">The target area to process for the current processor instance.</param>
public EdgeDetectorProcessor( public EdgeDetectorProcessor(
Configuration configuration, Configuration configuration,
in DenseMatrix<float> kernelXY, in EdgeDetectorKernel kernelXY,
bool grayscale, bool grayscale,
Image<TPixel> source, Image<TPixel> source,
Rectangle sourceRectangle) Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle) : base(configuration, source, sourceRectangle)
{ {
this.KernelXY = kernelXY; this.kernelXY = kernelXY.KernelXY;
this.Grayscale = grayscale; this.grayscale = grayscale;
} }
public bool Grayscale { get; }
/// <summary>
/// Gets the 2d gradient operator.
/// </summary>
public DenseMatrix<float> KernelXY { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void BeforeImageApply() protected override void BeforeImageApply()
{ {
@ -48,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
opaque.Execute(); opaque.Execute();
} }
if (this.Grayscale) if (this.grayscale)
{ {
new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle);
} }
@ -59,10 +55,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
using (var processor = new ConvolutionProcessor<TPixel>(this.Configuration, this.KernelXY, true, this.Source, this.SourceRectangle)) using var processor = new ConvolutionProcessor<TPixel>(this.Configuration, in this.kernelXY, true, this.Source, this.SourceRectangle);
{ processor.Apply(source);
processor.Apply(source);
}
} }
} }
} }

55
src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs

@ -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
{
/// <summary>
/// Gets the North gradient operator.
/// </summary>
public abstract DenseMatrix<float> North { get; }
/// <summary>
/// Gets the NorthWest gradient operator.
/// </summary>
public abstract DenseMatrix<float> NorthWest { get; }
/// <summary>
/// Gets the West gradient operator.
/// </summary>
public abstract DenseMatrix<float> West { get; }
/// <summary>
/// Gets the SouthWest gradient operator.
/// </summary>
public abstract DenseMatrix<float> SouthWest { get; }
/// <summary>
/// Gets the South gradient operator.
/// </summary>
public abstract DenseMatrix<float> South { get; }
/// <summary>
/// Gets the SouthEast gradient operator.
/// </summary>
public abstract DenseMatrix<float> SouthEast { get; }
/// <summary>
/// Gets the East gradient operator.
/// </summary>
public abstract DenseMatrix<float> East { get; }
/// <summary>
/// Gets the NorthEast gradient operator.
/// </summary>
public abstract DenseMatrix<float> NorthEast { get; }
public DenseMatrix<float>[] Flatten() =>
new[]
{
this.North, this.NorthWest, this.West, this.SouthWest,
this.South, this.SouthEast, this.East, this.NorthEast
};
}
}

103
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
{
/// <summary>
/// Represents an edge detection convolution kernel consisting of two 1D gradient operators.
/// </summary>
public readonly struct EdgeDetector2DKernel : IEquatable<EdgeDetector2DKernel>
{
/// <summary>
/// An edge detection kernel containing two Kayyali operators.
/// </summary>
public static EdgeDetector2DKernel KayyaliKernel = new EdgeDetector2DKernel(KayyaliKernels.KayyaliX, KayyaliKernels.KayyaliY);
/// <summary>
/// An edge detection kernel containing two Prewitt operators.
/// <see href="https://en.wikipedia.org/wiki/Prewitt_operator"/>.
/// </summary>
public static EdgeDetector2DKernel PrewittKernel = new EdgeDetector2DKernel(PrewittKernels.PrewittX, PrewittKernels.PrewittY);
/// <summary>
/// An edge detection kernel containing two Roberts-Cross operators.
/// <see href="https://en.wikipedia.org/wiki/Roberts_cross"/>.
/// </summary>
public static EdgeDetector2DKernel RobertsCrossKernel = new EdgeDetector2DKernel(RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY);
/// <summary>
/// An edge detection kernel containing two Scharr operators.
/// </summary>
public static EdgeDetector2DKernel ScharrKernel = new EdgeDetector2DKernel(ScharrKernels.ScharrX, ScharrKernels.ScharrY);
/// <summary>
/// An edge detection kernel containing two Sobel operators.
/// <see href="https://en.wikipedia.org/wiki/Sobel_operator"/>.
/// </summary>
public static EdgeDetector2DKernel SobelKernel = new EdgeDetector2DKernel(SobelKernels.SobelX, SobelKernels.SobelY);
/// <summary>
/// Initializes a new instance of the <see cref="EdgeDetector2DKernel"/> struct.
/// </summary>
/// <param name="kernelX">The horizontal gradient operator.</param>
/// <param name="kernelY">The vertical gradient operator.</param>
public EdgeDetector2DKernel(DenseMatrix<float> kernelX, DenseMatrix<float> kernelY)
{
Guard.IsTrue(
kernelX.Size.Equals(kernelY.Size),
$"{nameof(kernelX)} {nameof(kernelY)}",
"Kernel sizes must be the same.");
this.KernelX = kernelX;
this.KernelY = kernelY;
}
/// <summary>
/// Gets the horizontal gradient operator.
/// </summary>
public DenseMatrix<float> KernelX { get; }
/// <summary>
/// Gets the vertical gradient operator.
/// </summary>
public DenseMatrix<float> KernelY { get; }
/// <summary>
/// Checks whether two <see cref="EdgeDetector2DKernel"/> structures are equal.
/// </summary>
/// <param name="left">The left hand <see cref="EdgeDetector2DKernel"/> operand.</param>
/// <param name="right">The right hand <see cref="EdgeDetector2DKernel"/> operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter;
/// otherwise, false.
/// </returns>
public static bool operator ==(EdgeDetector2DKernel left, EdgeDetector2DKernel right)
=> left.Equals(right);
/// <summary>
/// Checks whether two <see cref="EdgeDetector2DKernel"/> structures are equal.
/// </summary>
/// <param name="left">The left hand <see cref="EdgeDetector2DKernel"/> operand.</param>
/// <param name="right">The right hand <see cref="EdgeDetector2DKernel"/> operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter;
/// otherwise, false.
/// </returns>
public static bool operator !=(EdgeDetector2DKernel left, EdgeDetector2DKernel right)
=> !(left == right);
/// <inheritdoc/>
public override bool Equals(object obj)
=> obj is EdgeDetector2DKernel kernel && this.Equals(kernel);
/// <inheritdoc/>
public bool Equals(EdgeDetector2DKernel other)
=> this.KernelX.Equals(other.KernelX)
&& this.KernelY.Equals(other.KernelY);
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.KernelX, this.KernelY);
}
}

161
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
{
/// <summary>
/// Represents an edge detection convolution kernel consisting of eight gradient operators.
/// </summary>
public readonly struct EdgeDetectorCompassKernel : IEquatable<EdgeDetectorCompassKernel>
{
/// <summary>
/// An edge detection kenel comprised of Kirsch gradient operators.
/// </summary>
public static EdgeDetectorCompassKernel Kirsch =
new EdgeDetectorCompassKernel(
KirschKernels.North,
KirschKernels.NorthWest,
KirschKernels.West,
KirschKernels.SouthWest,
KirschKernels.South,
KirschKernels.SouthEast,
KirschKernels.East,
KirschKernels.NorthEast);
/// <summary>
/// An edge detection kenel comprised of Robinson gradient operators.
/// </summary>
public static EdgeDetectorCompassKernel Robinson =
new EdgeDetectorCompassKernel(
RobinsonKernels.North,
RobinsonKernels.NorthWest,
RobinsonKernels.West,
RobinsonKernels.SouthWest,
RobinsonKernels.South,
RobinsonKernels.SouthEast,
RobinsonKernels.East,
RobinsonKernels.NorthEast);
/// <summary>
/// Initializes a new instance of the <see cref="EdgeDetectorCompassKernel"/> struct.
/// </summary>
/// <param name="north">The north gradient operator.</param>
/// <param name="northWest">The north-west gradient operator.</param>
/// <param name="west">The west gradient operator.</param>
/// <param name="southWest">The south-west gradient operator.</param>
/// <param name="south">The south gradient operator.</param>
/// <param name="southEast">The south-east gradient operator.</param>
/// <param name="east">The east gradient operator.</param>
/// <param name="northEast">The north-east gradient operator.</param>
public EdgeDetectorCompassKernel(
DenseMatrix<float> north,
DenseMatrix<float> northWest,
DenseMatrix<float> west,
DenseMatrix<float> southWest,
DenseMatrix<float> south,
DenseMatrix<float> southEast,
DenseMatrix<float> east,
DenseMatrix<float> 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;
}
/// <summary>
/// Gets the North gradient operator.
/// </summary>
public DenseMatrix<float> North { get; }
/// <summary>
/// Gets the NorthWest gradient operator.
/// </summary>
public DenseMatrix<float> NorthWest { get; }
/// <summary>
/// Gets the West gradient operator.
/// </summary>
public DenseMatrix<float> West { get; }
/// <summary>
/// Gets the SouthWest gradient operator.
/// </summary>
public DenseMatrix<float> SouthWest { get; }
/// <summary>
/// Gets the South gradient operator.
/// </summary>
public DenseMatrix<float> South { get; }
/// <summary>
/// Gets the SouthEast gradient operator.
/// </summary>
public DenseMatrix<float> SouthEast { get; }
/// <summary>
/// Gets the East gradient operator.
/// </summary>
public DenseMatrix<float> East { get; }
/// <summary>
/// Gets the NorthEast gradient operator.
/// </summary>
public DenseMatrix<float> NorthEast { get; }
/// <summary>
/// Checks whether two <see cref="EdgeDetectorCompassKernel"/> structures are equal.
/// </summary>
/// <param name="left">The left hand <see cref="EdgeDetectorCompassKernel"/> operand.</param>
/// <param name="right">The right hand <see cref="EdgeDetectorCompassKernel"/> operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter;
/// otherwise, false.
/// </returns>
public static bool operator ==(EdgeDetectorCompassKernel left, EdgeDetectorCompassKernel right)
=> left.Equals(right);
/// <summary>
/// Checks whether two <see cref="EdgeDetectorCompassKernel"/> structures are equal.
/// </summary>
/// <param name="left">The left hand <see cref="EdgeDetectorCompassKernel"/> operand.</param>
/// <param name="right">The right hand <see cref="EdgeDetectorCompassKernel"/> operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter;
/// otherwise, false.
/// </returns>
public static bool operator !=(EdgeDetectorCompassKernel left, EdgeDetectorCompassKernel right)
=> !(left == right);
/// <inheritdoc/>
public override bool Equals(object obj) => obj is EdgeDetectorCompassKernel kernel && this.Equals(kernel);
/// <inheritdoc/>
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);
/// <inheritdoc/>
public override int GetHashCode()
=> HashCode.Combine(
this.North,
this.NorthWest,
this.West,
this.SouthWest,
this.South,
this.SouthEast,
this.East,
this.NorthEast);
internal DenseMatrix<float>[] Flatten() =>
new[]
{
this.North, this.NorthWest, this.West, this.SouthWest,
this.South, this.SouthEast, this.East, this.NorthEast
};
}
}

75
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
{
/// <summary>
/// Represents an edge detection convolution kernel consisting of a single 2D gradient operator.
/// </summary>
public readonly struct EdgeDetectorKernel : IEquatable<EdgeDetectorKernel>
{
/// <summary>
/// An edge detection kernel containing a 3x3 Laplacian operator.
/// </summary>
public static EdgeDetectorKernel Laplacian3x3 = new EdgeDetectorKernel(LaplacianKernels.Laplacian3x3);
/// <summary>
/// An edge detection kernel containing a 5x5 Laplacian operator.
/// </summary>
public static EdgeDetectorKernel Laplacian5x5 = new EdgeDetectorKernel(LaplacianKernels.Laplacian5x5);
/// <summary>
/// An edge detection kernel containing a Laplacian of Gaussian operator.
/// </summary>
public static EdgeDetectorKernel LaplacianOfGaussian = new EdgeDetectorKernel(LaplacianKernels.LaplacianOfGaussianXY);
/// <summary>
/// Initializes a new instance of the <see cref="EdgeDetectorKernel"/> struct.
/// </summary>
/// <param name="kernelXY">The 2D gradient operator.</param>
public EdgeDetectorKernel(DenseMatrix<float> kernelXY)
=> this.KernelXY = kernelXY;
/// <summary>
/// Gets the 2D gradient operator.
/// </summary>
public DenseMatrix<float> KernelXY { get; }
/// <summary>
/// Checks whether two <see cref="EdgeDetectorKernel"/> structures are equal.
/// </summary>
/// <param name="left">The left hand <see cref="EdgeDetectorKernel"/> operand.</param>
/// <param name="right">The right hand <see cref="EdgeDetectorKernel"/> operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter;
/// otherwise, false.
/// </returns>
public static bool operator ==(EdgeDetectorKernel left, EdgeDetectorKernel right)
=> left.Equals(right);
/// <summary>
/// Checks whether two <see cref="EdgeDetectorKernel"/> structures are equal.
/// </summary>
/// <param name="left">The left hand <see cref="EdgeDetectorKernel"/> operand.</param>
/// <param name="right">The right hand <see cref="EdgeDetectorKernel"/> operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter;
/// otherwise, false.
/// </returns>
public static bool operator !=(EdgeDetectorKernel left, EdgeDetectorKernel right)
=> !(left == right);
/// <inheritdoc/>
public override bool Equals(object obj)
=> obj is EdgeDetectorKernel kernel && this.Equals(kernel);
/// <inheritdoc/>
public bool Equals(EdgeDetectorKernel other)
=> this.KernelXY.Equals(other.KernelXY);
/// <inheritdoc/>
public override int GetHashCode() => this.KernelXY.GetHashCode();
}
}

0
src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs → src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/KayyaliKernels.cs

22
src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs → 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. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing.Processors.Convolution namespace SixLabors.ImageSharp.Processing.Processors.Convolution
@ -6,12 +6,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Contains the eight matrices used for Kirsch edge detection /// Contains the eight matrices used for Kirsch edge detection
/// </summary> /// </summary>
internal class KirschKernels : CompassKernels internal static class KirschKernels
{ {
/// <summary> /// <summary>
/// Gets the North gradient operator /// Gets the North gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> North => public static DenseMatrix<float> North =>
new float[,] new float[,]
{ {
{ 5, 5, 5 }, { 5, 5, 5 },
@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the NorthWest gradient operator /// Gets the NorthWest gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> NorthWest => public static DenseMatrix<float> NorthWest =>
new float[,] new float[,]
{ {
{ 5, 5, -3 }, { 5, 5, -3 },
@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the West gradient operator /// Gets the West gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> West => public static DenseMatrix<float> West =>
new float[,] new float[,]
{ {
{ 5, -3, -3 }, { 5, -3, -3 },
@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the SouthWest gradient operator /// Gets the SouthWest gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> SouthWest => public static DenseMatrix<float> SouthWest =>
new float[,] new float[,]
{ {
{ -3, -3, -3 }, { -3, -3, -3 },
@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the South gradient operator /// Gets the South gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> South => public static DenseMatrix<float> South =>
new float[,] new float[,]
{ {
{ -3, -3, -3 }, { -3, -3, -3 },
@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the SouthEast gradient operator /// Gets the SouthEast gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> SouthEast => public static DenseMatrix<float> SouthEast =>
new float[,] new float[,]
{ {
{ -3, -3, -3 }, { -3, -3, -3 },
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the East gradient operator /// Gets the East gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> East => public static DenseMatrix<float> East =>
new float[,] new float[,]
{ {
{ -3, -3, 5 }, { -3, -3, 5 },
@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the NorthEast gradient operator /// Gets the NorthEast gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> NorthEast => public static DenseMatrix<float> NorthEast =>
new float[,] new float[,]
{ {
{ -3, 5, 5 }, { -3, 5, 5 },
@ -96,4 +96,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{ -3, -3, -3 } { -3, -3, -3 }
}; };
} }
} }

0
src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs → src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernelFactory.cs

0
src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs → src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernels.cs

0
src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs → src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/PrewittKernels.cs

0
src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs → src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/RobertsCrossKernels.cs

22
src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs → 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. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing.Processors.Convolution namespace SixLabors.ImageSharp.Processing.Processors.Convolution
@ -6,12 +6,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Contains the kernels used for Robinson edge detection. /// Contains the kernels used for Robinson edge detection.
/// </summary> /// </summary>
internal class RobinsonKernels : CompassKernels internal static class RobinsonKernels
{ {
/// <summary> /// <summary>
/// Gets the North gradient operator /// Gets the North gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> North => public static DenseMatrix<float> North =>
new float[,] new float[,]
{ {
{ 1, 2, 1 }, { 1, 2, 1 },
@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the NorthWest gradient operator /// Gets the NorthWest gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> NorthWest => public static DenseMatrix<float> NorthWest =>
new float[,] new float[,]
{ {
{ 2, 1, 0 }, { 2, 1, 0 },
@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the West gradient operator /// Gets the West gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> West => public static DenseMatrix<float> West =>
new float[,] new float[,]
{ {
{ 1, 0, -1 }, { 1, 0, -1 },
@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the SouthWest gradient operator /// Gets the SouthWest gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> SouthWest => public static DenseMatrix<float> SouthWest =>
new float[,] new float[,]
{ {
{ 0, -1, -2 }, { 0, -1, -2 },
@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the South gradient operator /// Gets the South gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> South => public static DenseMatrix<float> South =>
new float[,] new float[,]
{ {
{ -1, -2, -1 }, { -1, -2, -1 },
@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the SouthEast gradient operator /// Gets the SouthEast gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> SouthEast => public static DenseMatrix<float> SouthEast =>
new float[,] new float[,]
{ {
{ -2, -1, 0 }, { -2, -1, 0 },
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the East gradient operator /// Gets the East gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> East => public static DenseMatrix<float> East =>
new float[,] new float[,]
{ {
{ -1, 0, 1 }, { -1, 0, 1 },
@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <summary> /// <summary>
/// Gets the NorthEast gradient operator /// Gets the NorthEast gradient operator
/// </summary> /// </summary>
public override DenseMatrix<float> NorthEast => public static DenseMatrix<float> NorthEast =>
new float[,] new float[,]
{ {
{ 0, 1, 2 }, { 0, 1, 2 },
@ -96,4 +96,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{ -2, -1, 0 } { -2, -1, 0 }
}; };
} }
} }

0
src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs → src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/ScharrKernels.cs

0
src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs → src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/SobelKernels.cs

22
tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs

@ -37,17 +37,17 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp DetectEdges")] [Benchmark(Description = "ImageSharp DetectEdges")]
public void ImageProcessorCoreDetectEdges() public void ImageProcessorCoreDetectEdges()
{ {
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Kayyali)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Kayyali));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Kayyali)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Kayyali));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Kirsch)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Kirsch));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Laplacian3x3)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Laplacian3x3));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Laplacian5x5)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Laplacian5x5));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.LaplacianOfGaussian)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.LaplacianOfGaussian));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Prewitt)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Prewitt));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.RobertsCross)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.RobertsCross));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Robinson)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Robinson));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Scharr)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Scharr));
this.image.Mutate(x => x.DetectEdges(EdgeDetectionOperators.Sobel)); this.image.Mutate(x => x.DetectEdges(KnownEdgeDetectionOperators.Sobel));
} }
} }
} }

24
tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs

@ -35,21 +35,21 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution
public static IEnumerable<object[]> EdgeDetectionTheoryData => new[] public static IEnumerable<object[]> EdgeDetectionTheoryData => new[]
{ {
new object[] { new TestType<KayyaliProcessor>(), EdgeDetectionOperators.Kayyali }, new object[] { new TestType<KayyaliProcessor>(), KnownEdgeDetectionOperators.Kayyali },
new object[] { new TestType<KirschProcessor>(), EdgeDetectionOperators.Kirsch }, new object[] { new TestType<KirschProcessor>(), KnownEdgeDetectionOperators.Kirsch },
new object[] { new TestType<Laplacian3x3Processor>(), EdgeDetectionOperators.Laplacian3x3 }, new object[] { new TestType<Laplacian3x3Processor>(), KnownEdgeDetectionOperators.Laplacian3x3 },
new object[] { new TestType<Laplacian5x5Processor>(), EdgeDetectionOperators.Laplacian5x5 }, new object[] { new TestType<Laplacian5x5Processor>(), KnownEdgeDetectionOperators.Laplacian5x5 },
new object[] { new TestType<LaplacianOfGaussianProcessor>(), EdgeDetectionOperators.LaplacianOfGaussian }, new object[] { new TestType<LaplacianOfGaussianProcessor>(), KnownEdgeDetectionOperators.LaplacianOfGaussian },
new object[] { new TestType<PrewittProcessor>(), EdgeDetectionOperators.Prewitt }, new object[] { new TestType<PrewittProcessor>(), KnownEdgeDetectionOperators.Prewitt },
new object[] { new TestType<RobertsCrossProcessor>(), EdgeDetectionOperators.RobertsCross }, new object[] { new TestType<RobertsCrossProcessor>(), KnownEdgeDetectionOperators.RobertsCross },
new object[] { new TestType<RobinsonProcessor>(), EdgeDetectionOperators.Robinson }, new object[] { new TestType<RobinsonProcessor>(), KnownEdgeDetectionOperators.Robinson },
new object[] { new TestType<ScharrProcessor>(), EdgeDetectionOperators.Scharr }, new object[] { new TestType<ScharrProcessor>(), KnownEdgeDetectionOperators.Scharr },
new object[] { new TestType<SobelProcessor>(), EdgeDetectionOperators.Sobel }, new object[] { new TestType<SobelProcessor>(), KnownEdgeDetectionOperators.Sobel },
}; };
[Theory] [Theory]
[MemberData(nameof(EdgeDetectionTheoryData))] [MemberData(nameof(EdgeDetectionTheoryData))]
public void DetectEdges_filter_SobelProcessorDefaultsSet<TProcessor>(TestType<TProcessor> type, EdgeDetectionOperators filter) public void DetectEdges_filter_SobelProcessorDefaultsSet<TProcessor>(TestType<TProcessor> type, KnownEdgeDetectionOperators filter)
where TProcessor : EdgeDetectorProcessor where TProcessor : EdgeDetectorProcessor
{ {
this.operations.DetectEdges(filter); this.operations.DetectEdges(filter);
@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution
[Theory] [Theory]
[MemberData(nameof(EdgeDetectionTheoryData))] [MemberData(nameof(EdgeDetectionTheoryData))]
public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet<TProcessor>(TestType<TProcessor> type, EdgeDetectionOperators filter) public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet<TProcessor>(TestType<TProcessor> type, KnownEdgeDetectionOperators filter)
where TProcessor : EdgeDetectorProcessor where TProcessor : EdgeDetectorProcessor
{ {
bool grey = (int)filter % 2 == 0; bool grey = (int)filter % 2 == 0;

26
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 const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector;
public static readonly TheoryData<EdgeDetectionOperators> DetectEdgesFilters = new TheoryData<EdgeDetectionOperators> public static readonly TheoryData<KnownEdgeDetectionOperators> DetectEdgesFilters = new TheoryData<KnownEdgeDetectionOperators>
{ {
EdgeDetectionOperators.Kayyali, KnownEdgeDetectionOperators.Kayyali,
EdgeDetectionOperators.Kirsch, KnownEdgeDetectionOperators.Kirsch,
EdgeDetectionOperators.Laplacian3x3, KnownEdgeDetectionOperators.Laplacian3x3,
EdgeDetectionOperators.Laplacian5x5, KnownEdgeDetectionOperators.Laplacian5x5,
EdgeDetectionOperators.LaplacianOfGaussian, KnownEdgeDetectionOperators.LaplacianOfGaussian,
EdgeDetectionOperators.Prewitt, KnownEdgeDetectionOperators.Prewitt,
EdgeDetectionOperators.RobertsCross, KnownEdgeDetectionOperators.RobertsCross,
EdgeDetectionOperators.Robinson, KnownEdgeDetectionOperators.Robinson,
EdgeDetectionOperators.Scharr, KnownEdgeDetectionOperators.Scharr,
EdgeDetectionOperators.Sobel KnownEdgeDetectionOperators.Sobel
}; };
[Theory] [Theory]
@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
[Theory] [Theory]
[WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, PixelTypes.Rgba32)]
[WithFileCollection(nameof(TestImages), nameof(DetectEdgesFilters), PixelTypes.Rgba32)] [WithFileCollection(nameof(TestImages), nameof(DetectEdgesFilters), PixelTypes.Rgba32)]
public void DetectEdges_WorksWithAllFilters<TPixel>(TestImageProvider<TPixel> provider, EdgeDetectionOperators detector) public void DetectEdges_WorksWithAllFilters<TPixel>(TestImageProvider<TPixel> provider, KnownEdgeDetectionOperators detector)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
bool hasAlpha = provider.SourceFileOrDescription.Contains("TestPattern"); bool hasAlpha = provider.SourceFileOrDescription.Contains("TestPattern");
@ -115,7 +115,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
[Theory] [Theory]
[WithFile(Tests.TestImages.Png.Bike, nameof(DetectEdgesFilters), PixelTypes.Rgba32)] [WithFile(Tests.TestImages.Png.Bike, nameof(DetectEdgesFilters), PixelTypes.Rgba32)]
public void WorksWithDiscoBuffers<TPixel>(TestImageProvider<TPixel> provider, EdgeDetectionOperators detector) public void WorksWithDiscoBuffers<TPixel>(TestImageProvider<TPixel> provider, KnownEdgeDetectionOperators detector)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
provider.RunBufferCapacityLimitProcessorTest( provider.RunBufferCapacityLimitProcessorTest(

Loading…
Cancel
Save