mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: c2a7aaf6443e721d1ef18fff5a7c8e9cf91b962a Former-commit-id: 5022db552fc42c6dabdc66b65e96e604f2222013 Former-commit-id: a9a34ac649b51cb6cedaabc13d7a31b1bb88aed3af/merge-core
15 changed files with 505 additions and 4 deletions
@ -0,0 +1,131 @@ |
|||
// <copyright file="DetectEdges.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using Processors; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods for the <see cref="Image{T,TP}"/> type.
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Detects any edges within the image. Uses the <see cref="SobelProcessor"/> filter
|
|||
/// operating in greyscale mode.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
|
|||
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
|
|||
public static Image<T, TP> DetectEdges<T, TP>(this Image<T, TP> source, ProgressEventHandler progressHandler = null) |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
return DetectEdges(source, source.Bounds, new SobelProcessor<T, TP> { Greyscale = true }, progressHandler); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Detects any edges within the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="filter">The filter for detecting edges.</param>
|
|||
/// <param name="greyscale">Whether to convert the image to greyscale first. Defaults to true.</param>
|
|||
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
|
|||
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
|
|||
public static Image<T, TP> DetectEdges<T, TP>(this Image<T, TP> source, EdgeDetection filter, bool greyscale = true, ProgressEventHandler progressHandler = null) |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
IEdgeDetectorFilter<T, TP> processor; |
|||
|
|||
switch (filter) |
|||
{ |
|||
case EdgeDetection.Kayyali: |
|||
processor = new KayyaliProcessor<T, TP> { Greyscale = greyscale }; |
|||
break; |
|||
|
|||
case EdgeDetection.Kirsch: |
|||
processor = new KirschProcessor<T, TP> { Greyscale = greyscale }; |
|||
break; |
|||
|
|||
case EdgeDetection.Lapacian3X3: |
|||
processor = new Laplacian3X3Processor<T, TP> { Greyscale = greyscale }; |
|||
break; |
|||
|
|||
case EdgeDetection.Lapacian5X5: |
|||
processor = new Laplacian5X5Processor<T, TP> { Greyscale = greyscale }; |
|||
break; |
|||
|
|||
case EdgeDetection.LaplacianOfGaussian: |
|||
processor = new LaplacianOfGaussianProcessor<T, TP> { Greyscale = greyscale }; |
|||
break; |
|||
|
|||
case EdgeDetection.Prewitt: |
|||
processor = new PrewittProcessor<T, TP> { Greyscale = greyscale }; |
|||
break; |
|||
|
|||
case EdgeDetection.RobertsCross: |
|||
processor = new RobertsCrossProcessor<T, TP> { Greyscale = greyscale }; |
|||
break; |
|||
|
|||
case EdgeDetection.Scharr: |
|||
processor = new ScharrProcessor<T, TP> { Greyscale = greyscale }; |
|||
break; |
|||
|
|||
default: |
|||
processor = new ScharrProcessor<T, TP> { Greyscale = greyscale }; |
|||
break; |
|||
} |
|||
|
|||
return DetectEdges(source, source.Bounds, processor, progressHandler); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Detects any edges within the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="filter">The filter for detecting edges.</param>
|
|||
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
|
|||
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
|
|||
public static Image<T, TP> DetectEdges<T, TP>(this Image<T, TP> source, IEdgeDetectorFilter<T, TP> filter, ProgressEventHandler progressHandler = null) |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
return DetectEdges(source, source.Bounds, filter, progressHandler); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Detects any edges within the image.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="rectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
|
|||
/// </param>
|
|||
/// <param name="filter">The filter for detecting edges.</param>
|
|||
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
|
|||
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
|
|||
public static Image<T, TP> DetectEdges<T, TP>(this Image<T, TP> source, Rectangle rectangle, IEdgeDetectorFilter<T, TP> filter, ProgressEventHandler progressHandler = null) |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
filter.OnProgress += progressHandler; |
|||
|
|||
try |
|||
{ |
|||
return source.Process(rectangle, filter); |
|||
} |
|||
finally |
|||
{ |
|||
filter.OnProgress -= progressHandler; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
// <copyright file="EdgeDetection.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the various types of defined edge detection filters.
|
|||
/// </summary>
|
|||
public enum EdgeDetection |
|||
{ |
|||
/// <summary>
|
|||
/// The Kayyali operator filter.
|
|||
/// </summary>
|
|||
Kayyali, |
|||
|
|||
/// <summary>
|
|||
/// The Kirsch operator filter.
|
|||
/// </summary>
|
|||
Kirsch, |
|||
|
|||
/// <summary>
|
|||
/// The Lapacian3X3 operator filter.
|
|||
/// </summary>
|
|||
Lapacian3X3, |
|||
|
|||
/// <summary>
|
|||
/// The Lapacian5X5 operator filter.
|
|||
/// </summary>
|
|||
Lapacian5X5, |
|||
|
|||
/// <summary>
|
|||
/// The LaplacianOfGaussian operator filter.
|
|||
/// </summary>
|
|||
LaplacianOfGaussian, |
|||
|
|||
/// <summary>
|
|||
/// The Prewitt operator filter.
|
|||
/// </summary>
|
|||
Prewitt, |
|||
|
|||
/// <summary>
|
|||
/// The RobertsCross operator filter.
|
|||
/// </summary>
|
|||
RobertsCross, |
|||
|
|||
/// <summary>
|
|||
/// The Scharr operator filter.
|
|||
/// </summary>
|
|||
Scharr, |
|||
|
|||
/// <summary>
|
|||
/// The Sobel operator filter.
|
|||
/// </summary>
|
|||
Sobel |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// <copyright file="KayyaliProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
/// <summary>
|
|||
/// The Kayyali operator filter.
|
|||
/// <see href="http://edgedetection.webs.com/"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class KayyaliProcessor<T, TP> : EdgeDetector2DFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelX => new float[,] |
|||
{ |
|||
{ 6, 0, -6 }, |
|||
{ 0, 0, 0 }, |
|||
{ -6, 0, 6 } |
|||
}; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelY => new float[,] |
|||
{ |
|||
{ -6, 0, 6 }, |
|||
{ 0, 0, 0 }, |
|||
{ 6, 0, -6 } |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// <copyright file="KirschProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
/// <summary>
|
|||
/// The Kirsch operator filter.
|
|||
/// <see href="http://en.wikipedia.org/wiki/Kirsch_operator"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class KirschProcessor<T, TP> : EdgeDetector2DFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelX => new float[,] |
|||
{ |
|||
{ 5, 5, 5 }, |
|||
{ -3, 0, -3 }, |
|||
{ -3, -3, -3 } |
|||
}; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelY => new float[,] |
|||
{ |
|||
{ 5, -3, -3 }, |
|||
{ 5, 0, -3 }, |
|||
{ 5, -3, -3 } |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// <copyright file="Laplacian3X3Processor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
/// <summary>
|
|||
/// The Laplacian 3 x 3 operator filter.
|
|||
/// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class Laplacian3X3Processor<T, TP> : EdgeDetectorFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelXY => new float[,] |
|||
{ |
|||
{ -1, -1, -1 }, |
|||
{ -1, 8, -1 }, |
|||
{ -1, -1, -1 } |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// <copyright file="Laplacian5X5Processor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
/// <summary>
|
|||
/// The Laplacian 5 x 5 operator filter.
|
|||
/// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class Laplacian5X5Processor<T, TP> : EdgeDetectorFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <inheritdoc/>
|
|||
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 } |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// <copyright file="LaplacianOfGaussianProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
/// <summary>
|
|||
/// The Laplacian of Gaussian operator filter.
|
|||
/// <see href="http://fourier.eng.hmc.edu/e161/lectures/gradient/node8.html"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class LaplacianOfGaussianProcessor<T, TP> : EdgeDetectorFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <inheritdoc/>
|
|||
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 } |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// <copyright file="PrewittProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
/// <summary>
|
|||
/// The Prewitt operator filter.
|
|||
/// <see href="http://en.wikipedia.org/wiki/Prewitt_operator"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class PrewittProcessor<T, TP> : EdgeDetector2DFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelX => new float[,] |
|||
{ |
|||
{ -1, 0, 1 }, |
|||
{ -1, 0, 1 }, |
|||
{ -1, 0, 1 } |
|||
}; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelY => new float[,] |
|||
{ |
|||
{ 1, 1, 1 }, |
|||
{ 0, 0, 0 }, |
|||
{ -1, -1, -1 } |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// <copyright file="RobertsCrossProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
/// <summary>
|
|||
/// The Roberts Cross operator filter.
|
|||
/// <see href="http://en.wikipedia.org/wiki/Roberts_cross"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class RobertsCrossProcessor<T, TP> : EdgeDetector2DFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelX => new float[,] |
|||
{ |
|||
{ 1, 0 }, |
|||
{ 0, -1 } |
|||
}; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelY => new float[,] |
|||
{ |
|||
{ 0, 1 }, |
|||
{ -1, 0 } |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// <copyright file="ScharrProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
/// <summary>
|
|||
/// The Scharr operator filter.
|
|||
/// <see href="http://en.wikipedia.org/wiki/Sobel_operator#Alternative_operators"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class ScharrProcessor<T, TP> : EdgeDetector2DFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelX => new float[,] |
|||
{ |
|||
{ -3, 0, 3 }, |
|||
{ -10, 0, 10 }, |
|||
{ -3, 0, 3 } |
|||
}; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelY => new float[,] |
|||
{ |
|||
{ 3, 10, 3 }, |
|||
{ 0, 0, 0 }, |
|||
{ -3, -10, -3 } |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
// <copyright file="DetectEdgesTest.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Tests |
|||
{ |
|||
using System.IO; |
|||
|
|||
using Xunit; |
|||
|
|||
public class DetectEdgesTest : FileTestBase |
|||
{ |
|||
public static readonly TheoryData<EdgeDetection> DetectEdgesFilters |
|||
= new TheoryData<EdgeDetection> |
|||
{ |
|||
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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue