From 6856a26b4e959274944a69ebcc319dcbbde4dd1e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 29 Jul 2016 16:29:28 +1000 Subject: [PATCH] Edge detection Former-commit-id: c2a7aaf6443e721d1ef18fff5a7c8e9cf91b962a Former-commit-id: 5022db552fc42c6dabdc66b65e96e604f2222013 Former-commit-id: a9a34ac649b51cb6cedaabc13d7a31b1bb88aed3 --- src/ImageProcessorCore/Filters/DetectEdges.cs | 131 ++++++++++++++++++ .../Filters/Options/ColorBlindness.cs | 2 +- .../Filters/Options/EdgeDetection.cs | 58 ++++++++ .../Filters/Options/GreyscaleMode.cs | 4 +- .../EdgeDetection/IEdgeDetectorFilter.cs | 9 +- .../EdgeDetection/KayyaliProcessor.cs | 34 +++++ .../EdgeDetection/KirschProcessor.cs | 34 +++++ .../EdgeDetection/Laplacian3X3Processor.cs | 26 ++++ .../EdgeDetection/Laplacian5X5Processor.cs | 28 ++++ .../LaplacianOfGaussianProcessor.cs | 28 ++++ .../EdgeDetection/PrewittProcessor.cs | 34 +++++ .../EdgeDetection/RobertsCrossProcessor.cs | 32 +++++ .../EdgeDetection/ScharrProcessor.cs | 34 +++++ .../EdgeDetection/SobelProcessor.cs | 2 + .../Processors/Filters/DetectEdgesTest.cs | 53 +++++++ 15 files changed, 505 insertions(+), 4 deletions(-) create mode 100644 src/ImageProcessorCore/Filters/DetectEdges.cs create mode 100644 src/ImageProcessorCore/Filters/Options/EdgeDetection.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs create mode 100644 tests/ImageProcessorCore.Tests/Processors/Filters/DetectEdgesTest.cs diff --git a/src/ImageProcessorCore/Filters/DetectEdges.cs b/src/ImageProcessorCore/Filters/DetectEdges.cs new file mode 100644 index 000000000..2912e04f7 --- /dev/null +++ b/src/ImageProcessorCore/Filters/DetectEdges.cs @@ -0,0 +1,131 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Detects any edges within the image. Uses the filter + /// operating in greyscale mode. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image this method extends. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image DetectEdges(this Image source, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + return DetectEdges(source, source.Bounds, new SobelProcessor { Greyscale = true }, progressHandler); + } + + /// + /// Detects any edges within the image. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image this method extends. + /// The filter for detecting edges. + /// Whether to convert the image to greyscale first. Defaults to true. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image DetectEdges(this Image source, EdgeDetection filter, bool greyscale = true, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + IEdgeDetectorFilter processor; + + switch (filter) + { + case EdgeDetection.Kayyali: + processor = new KayyaliProcessor { Greyscale = greyscale }; + break; + + case EdgeDetection.Kirsch: + processor = new KirschProcessor { Greyscale = greyscale }; + break; + + case EdgeDetection.Lapacian3X3: + processor = new Laplacian3X3Processor { Greyscale = greyscale }; + break; + + case EdgeDetection.Lapacian5X5: + processor = new Laplacian5X5Processor { Greyscale = greyscale }; + break; + + case EdgeDetection.LaplacianOfGaussian: + processor = new LaplacianOfGaussianProcessor { Greyscale = greyscale }; + break; + + case EdgeDetection.Prewitt: + processor = new PrewittProcessor { Greyscale = greyscale }; + break; + + case EdgeDetection.RobertsCross: + processor = new RobertsCrossProcessor { Greyscale = greyscale }; + break; + + case EdgeDetection.Scharr: + processor = new ScharrProcessor { Greyscale = greyscale }; + break; + + default: + processor = new ScharrProcessor { Greyscale = greyscale }; + break; + } + + return DetectEdges(source, source.Bounds, processor, progressHandler); + } + + /// + /// Detects any edges within the image. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image this method extends. + /// The filter for detecting edges. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image DetectEdges(this Image source, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + return DetectEdges(source, source.Bounds, filter, progressHandler); + } + + /// + /// Detects any edges within the image. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The filter for detecting edges. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + filter.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, filter); + } + finally + { + filter.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Options/ColorBlindness.cs b/src/ImageProcessorCore/Filters/Options/ColorBlindness.cs index 6d7fe849b..e128044cd 100644 --- a/src/ImageProcessorCore/Filters/Options/ColorBlindness.cs +++ b/src/ImageProcessorCore/Filters/Options/ColorBlindness.cs @@ -6,7 +6,7 @@ namespace ImageProcessorCore { /// - /// Enumerates the various types of color blindness. + /// Enumerates the various types of defined color blindness filters. /// public enum ColorBlindness { diff --git a/src/ImageProcessorCore/Filters/Options/EdgeDetection.cs b/src/ImageProcessorCore/Filters/Options/EdgeDetection.cs new file mode 100644 index 000000000..f637e4573 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Options/EdgeDetection.cs @@ -0,0 +1,58 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + /// + /// Enumerates the various types of defined edge detection filters. + /// + public enum EdgeDetection + { + /// + /// The Kayyali operator filter. + /// + Kayyali, + + /// + /// The Kirsch operator filter. + /// + Kirsch, + + /// + /// The Lapacian3X3 operator filter. + /// + Lapacian3X3, + + /// + /// The Lapacian5X5 operator filter. + /// + Lapacian5X5, + + /// + /// The LaplacianOfGaussian operator filter. + /// + LaplacianOfGaussian, + + /// + /// The Prewitt operator filter. + /// + Prewitt, + + /// + /// The RobertsCross operator filter. + /// + RobertsCross, + + /// + /// The Scharr operator filter. + /// + Scharr, + + /// + /// The Sobel operator filter. + /// + Sobel + } +} diff --git a/src/ImageProcessorCore/Filters/Options/GreyscaleMode.cs b/src/ImageProcessorCore/Filters/Options/GreyscaleMode.cs index 269c1179e..e1cc5917e 100644 --- a/src/ImageProcessorCore/Filters/Options/GreyscaleMode.cs +++ b/src/ImageProcessorCore/Filters/Options/GreyscaleMode.cs @@ -3,10 +3,10 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Processors +namespace ImageProcessorCore { /// - /// Provides enumeration over the various greyscale methods available. + /// Enumerates the various types of defined greyscale filters. /// public enum GreyscaleMode { diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs index d2e2979f9..ef0ceea9b 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs @@ -8,9 +8,16 @@ namespace ImageProcessorCore.Processors /// /// Provides properties and methods allowing the detection of edges within an image. /// - public interface IEdgeDetectorFilter : IImageProcessor + public interface IEdgeDetectorFilter : IImageProcessor, IEdgeDetectorFilter where T : IPackedVector where TP : struct + { + } + + /// + /// Provides properties and methods allowing the detection of edges within an image. + /// + public interface IEdgeDetectorFilter { /// /// Gets or sets a value indicating whether to convert the diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs new file mode 100644 index 000000000..59327467e --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + /// + /// The Kayyali operator filter. + /// + /// + /// The pixel format. + /// The packed format. long, float. + public class KayyaliProcessor : EdgeDetector2DFilter + where T : IPackedVector + where TP : struct + { + /// + public override float[,] KernelX => new float[,] + { + { 6, 0, -6 }, + { 0, 0, 0 }, + { -6, 0, 6 } + }; + + /// + public override float[,] KernelY => new float[,] + { + { -6, 0, 6 }, + { 0, 0, 0 }, + { 6, 0, -6 } + }; + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs new file mode 100644 index 000000000..aba7e26d2 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + /// + /// The Kirsch operator filter. + /// + /// + /// The pixel format. + /// The packed format. long, float. + public class KirschProcessor : EdgeDetector2DFilter + where T : IPackedVector + where TP : struct + { + /// + public override float[,] KernelX => new float[,] + { + { 5, 5, 5 }, + { -3, 0, -3 }, + { -3, -3, -3 } + }; + + /// + public override float[,] KernelY => new float[,] + { + { 5, -3, -3 }, + { 5, 0, -3 }, + { 5, -3, -3 } + }; + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs new file mode 100644 index 000000000..31829a422 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + /// + /// The Laplacian 3 x 3 operator filter. + /// + /// + /// The pixel format. + /// The packed format. long, float. + public class Laplacian3X3Processor : EdgeDetectorFilter + where T : IPackedVector + where TP : struct + { + /// + public override float[,] KernelXY => new float[,] + { + { -1, -1, -1 }, + { -1, 8, -1 }, + { -1, -1, -1 } + }; + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs new file mode 100644 index 000000000..d43876b56 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + /// + /// The Laplacian 5 x 5 operator filter. + /// + /// + /// The pixel format. + /// The packed format. long, float. + public class Laplacian5X5Processor : EdgeDetectorFilter + where T : IPackedVector + where TP : struct + { + /// + public override float[,] KernelXY => new float[,] + { + { -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1 }, + { -1, -1, 24, -1, -1 }, + { -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1 } + }; + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs new file mode 100644 index 000000000..e47013610 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + /// + /// The Laplacian of Gaussian operator filter. + /// + /// + /// The pixel format. + /// The packed format. long, float. + public class LaplacianOfGaussianProcessor : EdgeDetectorFilter + where T : IPackedVector + where TP : struct + { + /// + public override float[,] KernelXY => new float[,] + { + { 0, 0, -1, 0, 0 }, + { 0, -1, -2, -1, 0 }, + { -1, -2, 16, -2, -1 }, + { 0, -1, -2, -1, 0 }, + { 0, 0, -1, 0, 0 } + }; + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs new file mode 100644 index 000000000..4ce4c7675 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + /// + /// The Prewitt operator filter. + /// + /// + /// The pixel format. + /// The packed format. long, float. + public class PrewittProcessor : EdgeDetector2DFilter + where T : IPackedVector + where TP : struct + { + /// + public override float[,] KernelX => new float[,] + { + { -1, 0, 1 }, + { -1, 0, 1 }, + { -1, 0, 1 } + }; + + /// + public override float[,] KernelY => new float[,] + { + { 1, 1, 1 }, + { 0, 0, 0 }, + { -1, -1, -1 } + }; + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs new file mode 100644 index 000000000..b933280c6 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + /// + /// The Roberts Cross operator filter. + /// + /// + /// The pixel format. + /// The packed format. long, float. + public class RobertsCrossProcessor : EdgeDetector2DFilter + where T : IPackedVector + where TP : struct + { + /// + public override float[,] KernelX => new float[,] + { + { 1, 0 }, + { 0, -1 } + }; + + /// + public override float[,] KernelY => new float[,] + { + { 0, 1 }, + { -1, 0 } + }; + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs new file mode 100644 index 000000000..1dd4408a0 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + /// + /// The Scharr operator filter. + /// + /// + /// The pixel format. + /// The packed format. long, float. + public class ScharrProcessor : EdgeDetector2DFilter + where T : IPackedVector + where TP : struct + { + /// + public override float[,] KernelX => new float[,] + { + { -3, 0, 3 }, + { -10, 0, 10 }, + { -3, 0, 3 } + }; + + /// + public override float[,] KernelY => new float[,] + { + { 3, 10, 3 }, + { 0, 0, 0 }, + { -3, -10, -3 } + }; + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs index a323b7cfe..9459e2ed8 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -9,6 +9,8 @@ namespace ImageProcessorCore.Processors /// The Sobel operator filter. /// /// + /// The pixel format. + /// The packed format. long, float. public class SobelProcessor : EdgeDetector2DFilter where T : IPackedVector where TP : struct diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/DetectEdgesTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/DetectEdgesTest.cs new file mode 100644 index 000000000..944413007 --- /dev/null +++ b/tests/ImageProcessorCore.Tests/Processors/Filters/DetectEdgesTest.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Tests +{ + using System.IO; + + using Xunit; + + public class DetectEdgesTest : FileTestBase + { + public static readonly TheoryData DetectEdgesFilters + = new TheoryData + { + EdgeDetection.Kayyali, + EdgeDetection.Kirsch, + EdgeDetection.Lapacian3X3, + EdgeDetection.Lapacian5X5, + EdgeDetection.LaplacianOfGaussian, + EdgeDetection.Prewitt, + EdgeDetection.RobertsCross, + EdgeDetection.Scharr, + EdgeDetection.Sobel, + }; + + [Theory] + [MemberData("DetectEdgesFilters")] + public void ImageShouldApplyDetectEdgesFilter(EdgeDetection detector) + { + const string path = "TestOutput/DetectEdges"; + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + foreach (string file in Files) + { + using (FileStream stream = File.OpenRead(file)) + { + string filename = Path.GetFileNameWithoutExtension(file) + "-" + detector + Path.GetExtension(file); + Image image = new Image(stream); + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.DetectEdges(detector) + .Save(output); + } + } + } + } + } +} \ No newline at end of file