mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 8a5b4cf3f2f77979e055f7244a07152f3e7911b3 Former-commit-id: 3c88eca8695868dafeb4478865530b94a4fff195 Former-commit-id: e31d42ca178b55324a3700280a67fc7acd90f0a8pull/1/head
8 changed files with 650 additions and 0 deletions
@ -0,0 +1,60 @@ |
|||
// <copyright file="GuassianBlur.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>
|
|||
/// Applies a Guassian blur to 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="sigma">The 'sigma' value representing the weight of the blur.</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> GuassianBlur<T, TP>(this Image<T, TP> source, float sigma = 3f, ProgressEventHandler progressHandler = null) |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
return GuassianBlur(source, sigma, source.Bounds, progressHandler); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies a Guassian blur to 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="sigma">The 'sigma' value representing the weight of the blur.</param>
|
|||
/// <param name="rectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
|
|||
/// </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> GuassianBlur<T, TP>(this Image<T, TP> source, float sigma, Rectangle rectangle, ProgressEventHandler progressHandler = null) |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
GuassianBlurProcessor<T, TP> processor = new GuassianBlurProcessor<T, TP>(sigma); |
|||
processor.OnProgress += progressHandler; |
|||
|
|||
try |
|||
{ |
|||
return source.Process(rectangle, processor); |
|||
} |
|||
finally |
|||
{ |
|||
processor.OnProgress -= progressHandler; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
// <copyright file="GuassianSharpen.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>
|
|||
/// Applies a Guassian sharpening filter to 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="sigma">The 'sigma' value representing the weight of the blur.</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> GuassianSharpen<T, TP>(this Image<T, TP> source, float sigma = 3f, ProgressEventHandler progressHandler = null) |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
return GuassianSharpen(source, sigma, source.Bounds, progressHandler); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies a Guassian sharpening filter to 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="sigma">The 'sigma' value representing the weight of the blur.</param>
|
|||
/// <param name="rectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
|
|||
/// </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> GuassianSharpen<T, TP>(this Image<T, TP> source, float sigma, Rectangle rectangle, ProgressEventHandler progressHandler = null) |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
GuassianSharpenProcessor<T, TP> processor = new GuassianSharpenProcessor<T, TP>(sigma); |
|||
processor.OnProgress += progressHandler; |
|||
|
|||
try |
|||
{ |
|||
return source.Process(rectangle, processor); |
|||
} |
|||
finally |
|||
{ |
|||
processor.OnProgress -= progressHandler; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,108 @@ |
|||
// <copyright file="Convolution2PassFilter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
using System.Numerics; |
|||
using System.Threading.Tasks; |
|||
|
|||
/// <summary>
|
|||
/// Defines a filter that uses two one-dimensional matrices to perform two-pass convolution against an image.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public abstract class Convolution2PassFilter<T, TP> : ImageProcessor<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the horizontal gradient operator.
|
|||
/// </summary>
|
|||
public abstract float[,] KernelX { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the vertical gradient operator.
|
|||
/// </summary>
|
|||
public abstract float[,] KernelY { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) |
|||
{ |
|||
float[,] kernelX = this.KernelX; |
|||
float[,] kernelY = this.KernelY; |
|||
|
|||
ImageBase<T, TP> firstPass = new Image<T, TP>(source.Width, source.Height); |
|||
this.ApplyConvolution(firstPass, source, sourceRectangle, startY, endY, kernelX); |
|||
this.ApplyConvolution(target, firstPass, sourceRectangle, startY, endY, kernelY); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the process to the specified portion of the specified <see cref="ImageBase{T,TP}"/> at the specified location
|
|||
/// and with the specified size.
|
|||
/// </summary>
|
|||
/// <param name="target">Target image to apply the process to.</param>
|
|||
/// <param name="source">The source image. Cannot be null.</param>
|
|||
/// <param name="sourceRectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
|||
/// </param>
|
|||
/// <param name="startY">The index of the row within the source image to start processing.</param>
|
|||
/// <param name="endY">The index of the row within the source image to end processing.</param>
|
|||
/// <param name="kernel">The kernel operator.</param>
|
|||
private void ApplyConvolution(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle sourceRectangle, int startY, int endY, float[,] kernel) |
|||
{ |
|||
int kernelHeight = kernel.GetLength(0); |
|||
int kernelWidth = kernel.GetLength(1); |
|||
int radiusY = kernelHeight >> 1; |
|||
int radiusX = kernelWidth >> 1; |
|||
|
|||
int sourceBottom = sourceRectangle.Bottom; |
|||
int startX = sourceRectangle.X; |
|||
int endX = sourceRectangle.Right; |
|||
int maxY = sourceBottom - 1; |
|||
int maxX = endX - 1; |
|||
|
|||
using (IPixelAccessor<T, TP> sourcePixels = source.Lock()) |
|||
using (IPixelAccessor<T, TP> targetPixels = target.Lock()) |
|||
{ |
|||
Parallel.For( |
|||
startY, |
|||
endY, |
|||
y => |
|||
{ |
|||
for (int x = startX; x < endX; x++) |
|||
{ |
|||
Vector4 destination = new Vector4(); |
|||
|
|||
// Apply each matrix multiplier to the color components for each pixel.
|
|||
for (int fy = 0; fy < kernelHeight; fy++) |
|||
{ |
|||
int fyr = fy - radiusY; |
|||
int offsetY = y + fyr; |
|||
|
|||
offsetY = offsetY.Clamp(0, maxY); |
|||
|
|||
for (int fx = 0; fx < kernelWidth; fx++) |
|||
{ |
|||
int fxr = fx - radiusX; |
|||
int offsetX = x + fxr; |
|||
|
|||
offsetX = offsetX.Clamp(0, maxX); |
|||
|
|||
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); |
|||
destination += kernel[fy, fx] * currentColor; |
|||
} |
|||
} |
|||
|
|||
T packed = default(T); |
|||
packed.PackVector(destination); |
|||
targetPixels[x, y] = packed; |
|||
} |
|||
|
|||
this.OnRowProcessed(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,144 @@ |
|||
// <copyright file="GuassianBlurProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Applies a Gaussian blur filter to the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class GuassianBlurProcessor<T, TP> : Convolution2PassFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <summary>
|
|||
/// The maximum size of the kernal in either direction.
|
|||
/// </summary>
|
|||
private readonly int kernelSize; |
|||
|
|||
/// <summary>
|
|||
/// The spread of the blur.
|
|||
/// </summary>
|
|||
private readonly float sigma; |
|||
|
|||
/// <summary>
|
|||
/// The vertical kernel
|
|||
/// </summary>
|
|||
private float[,] kernelY; |
|||
|
|||
/// <summary>
|
|||
/// The horizontal kernel
|
|||
/// </summary>
|
|||
private float[,] kernelX; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class.
|
|||
/// </summary>
|
|||
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
|
|||
public GuassianBlurProcessor(float sigma = 3f) |
|||
{ |
|||
this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1; |
|||
this.sigma = sigma; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class.
|
|||
/// </summary>
|
|||
/// <param name="radius">
|
|||
/// The 'radius' value representing the size of the area to sample.
|
|||
/// </param>
|
|||
public GuassianBlurProcessor(int radius) |
|||
{ |
|||
this.kernelSize = (radius * 2) + 1; |
|||
this.sigma = radius; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class.
|
|||
/// </summary>
|
|||
/// <param name="sigma">
|
|||
/// The 'sigma' value representing the weight of the blur.
|
|||
/// </param>
|
|||
/// <param name="radius">
|
|||
/// The 'radius' value representing the size of the area to sample.
|
|||
/// This should be at least twice the sigma value.
|
|||
/// </param>
|
|||
public GuassianBlurProcessor(float sigma, int radius) |
|||
{ |
|||
this.kernelSize = (radius * 2) + 1; |
|||
this.sigma = sigma; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelX => this.kernelX; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelY => this.kernelY; |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle) |
|||
{ |
|||
if (this.kernelY == null) |
|||
{ |
|||
this.kernelY = this.CreateGaussianKernel(false); |
|||
} |
|||
|
|||
if (this.kernelX == null) |
|||
{ |
|||
this.kernelX = this.CreateGaussianKernel(true); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
|
|||
/// </summary>
|
|||
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
|
|||
/// <returns>The <see cref="T:float[,]"/></returns>
|
|||
private float[,] CreateGaussianKernel(bool horizontal) |
|||
{ |
|||
int size = this.kernelSize; |
|||
float weight = this.sigma; |
|||
float[,] kernel = horizontal ? new float[1, size] : new float[size, 1]; |
|||
float sum = 0.0f; |
|||
|
|||
float midpoint = (size - 1) / 2f; |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
float x = i - midpoint; |
|||
float gx = ImageMaths.Gaussian(x, weight); |
|||
sum += gx; |
|||
if (horizontal) |
|||
{ |
|||
kernel[0, i] = gx; |
|||
} |
|||
else |
|||
{ |
|||
kernel[i, 0] = gx; |
|||
} |
|||
} |
|||
|
|||
// Normalise kernel so that the sum of all weights equals 1
|
|||
if (horizontal) |
|||
{ |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
kernel[0, i] = kernel[0, i] / sum; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
kernel[i, 0] = kernel[i, 0] / sum; |
|||
} |
|||
} |
|||
|
|||
return kernel; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,182 @@ |
|||
// <copyright file="GuassianSharpenProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Processors |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Applies a Gaussian sharpening filter to the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class GuassianSharpenProcessor<T, TP> : Convolution2PassFilter<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <summary>
|
|||
/// The maximum size of the kernal in either direction.
|
|||
/// </summary>
|
|||
private readonly int kernelSize; |
|||
|
|||
/// <summary>
|
|||
/// The spread of the blur.
|
|||
/// </summary>
|
|||
private readonly float sigma; |
|||
|
|||
/// <summary>
|
|||
/// The vertical kernel
|
|||
/// </summary>
|
|||
private float[,] kernelY; |
|||
|
|||
/// <summary>
|
|||
/// The horizontal kernel
|
|||
/// </summary>
|
|||
private float[,] kernelX; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GuassianSharpenProcessor"/> class.
|
|||
/// </summary>
|
|||
/// <param name="sigma">
|
|||
/// The 'sigma' value representing the weight of the sharpening.
|
|||
/// </param>
|
|||
public GuassianSharpenProcessor(float sigma = 3f) |
|||
{ |
|||
this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1; |
|||
this.sigma = sigma; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GuassianSharpenProcessor"/> class.
|
|||
/// </summary>
|
|||
/// <param name="radius">
|
|||
/// The 'radius' value representing the size of the area to sample.
|
|||
/// </param>
|
|||
public GuassianSharpenProcessor(int radius) |
|||
{ |
|||
this.kernelSize = (radius * 2) + 1; |
|||
this.sigma = radius; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GuassianSharpenProcessor"/> class.
|
|||
/// </summary>
|
|||
/// <param name="sigma">
|
|||
/// The 'sigma' value representing the weight of the sharpen.
|
|||
/// </param>
|
|||
/// <param name="radius">
|
|||
/// The 'radius' value representing the size of the area to sample.
|
|||
/// This should be at least twice the sigma value.
|
|||
/// </param>
|
|||
public GuassianSharpenProcessor(float sigma, int radius) |
|||
{ |
|||
this.kernelSize = (radius * 2) + 1; |
|||
this.sigma = sigma; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelX => this.kernelX; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override float[,] KernelY => this.kernelY; |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle) |
|||
{ |
|||
if (this.kernelY == null) |
|||
{ |
|||
this.kernelY = this.CreateGaussianKernel(false); |
|||
} |
|||
|
|||
if (this.kernelX == null) |
|||
{ |
|||
this.kernelX = this.CreateGaussianKernel(true); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function
|
|||
/// </summary>
|
|||
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
|
|||
/// <returns>The <see cref="T:float[,]"/></returns>
|
|||
private float[,] CreateGaussianKernel(bool horizontal) |
|||
{ |
|||
int size = this.kernelSize; |
|||
float weight = this.sigma; |
|||
float[,] kernel = horizontal ? new float[1, size] : new float[size, 1]; |
|||
float sum = 0; |
|||
|
|||
float midpoint = (size - 1) / 2f; |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
float x = i - midpoint; |
|||
float gx = ImageMaths.Gaussian(x, weight); |
|||
sum += gx; |
|||
if (horizontal) |
|||
{ |
|||
kernel[0, i] = gx; |
|||
} |
|||
else |
|||
{ |
|||
kernel[i, 0] = gx; |
|||
} |
|||
} |
|||
|
|||
// Invert the kernel for sharpening.
|
|||
int midpointRounded = (int)midpoint; |
|||
|
|||
if (horizontal) |
|||
{ |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
if (i == midpointRounded) |
|||
{ |
|||
// Calculate central value
|
|||
kernel[0, i] = (2f * sum) - kernel[0, i]; |
|||
} |
|||
else |
|||
{ |
|||
// invert value
|
|||
kernel[0, i] = -kernel[0, i]; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
if (i == midpointRounded) |
|||
{ |
|||
// Calculate central value
|
|||
kernel[i, 0] = (2 * sum) - kernel[i, 0]; |
|||
} |
|||
else |
|||
{ |
|||
// invert value
|
|||
kernel[i, 0] = -kernel[i, 0]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Normalise kernel so that the sum of all weights equals 1
|
|||
if (horizontal) |
|||
{ |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
kernel[0, i] = kernel[0, i] / sum; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
kernel[i, 0] = kernel[i, 0] / sum; |
|||
} |
|||
} |
|||
|
|||
return kernel; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
// <copyright file="GuassianBlurTest.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 GuassianBlurTest : FileTestBase |
|||
{ |
|||
public static readonly TheoryData<int> GuassianBlurValues |
|||
= new TheoryData<int> |
|||
{ |
|||
3 , |
|||
5 , |
|||
}; |
|||
|
|||
[Theory] |
|||
[MemberData("GuassianBlurValues")] |
|||
public void ImageShouldApplyGuassianBlurFilter(int value) |
|||
{ |
|||
const string path = "TestOutput/GuassianBlur"; |
|||
if (!Directory.Exists(path)) |
|||
{ |
|||
Directory.CreateDirectory(path); |
|||
} |
|||
|
|||
foreach (string file in Files) |
|||
{ |
|||
using (FileStream stream = File.OpenRead(file)) |
|||
{ |
|||
string filename = Path.GetFileNameWithoutExtension(file) + "-" + value + Path.GetExtension(file); |
|||
|
|||
Image image = new Image(stream); |
|||
using (FileStream output = File.OpenWrite($"{path}/{filename}")) |
|||
{ |
|||
image.GuassianBlur(value) |
|||
.Save(output); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
// <copyright file="GuassianSharpenTest.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 GuassianSharpenTest : FileTestBase |
|||
{ |
|||
public static readonly TheoryData<int> GuassianSharpenValues |
|||
= new TheoryData<int> |
|||
{ |
|||
3 , |
|||
5 , |
|||
}; |
|||
|
|||
[Theory] |
|||
[MemberData("GuassianSharpenValues")] |
|||
public void ImageShouldApplyGuassianSharpenFilter(int value) |
|||
{ |
|||
const string path = "TestOutput/GuassianSharpen"; |
|||
if (!Directory.Exists(path)) |
|||
{ |
|||
Directory.CreateDirectory(path); |
|||
} |
|||
|
|||
foreach (string file in Files) |
|||
{ |
|||
using (FileStream stream = File.OpenRead(file)) |
|||
{ |
|||
string filename = Path.GetFileNameWithoutExtension(file) + "-" + value + Path.GetExtension(file); |
|||
|
|||
Image image = new Image(stream); |
|||
using (FileStream output = File.OpenWrite($"{path}/{filename}")) |
|||
{ |
|||
image.GuassianSharpen(value) |
|||
.Save(output); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue