mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 5ac93d8e7ce1bb397a8ee7a68307aa423a85882a Former-commit-id: 7b8b0c704f7f272867516203133bb1c3fbbbf5a5 Former-commit-id: e495b72e24e1f723023cc537749e03a987b9eb8caf/merge-core
3 changed files with 213 additions and 0 deletions
@ -0,0 +1,59 @@ |
|||||
|
// <copyright file="BoxBlur.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
namespace ImageProcessorCore |
||||
|
{ |
||||
|
using Processors; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Extension methods for the <see cref="Image{T,TP}"/> type.
|
||||
|
/// </summary>
|
||||
|
public static partial class ImageExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Applies a box 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="radius">The 'radius' value representing the size of the area to sample.</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> BoxBlur<T, TP>(this Image<T, TP> source, int radius = 7, ProgressEventHandler progressHandler = null) |
||||
|
where T : IPackedVector<TP> |
||||
|
where TP : struct |
||||
|
{ |
||||
|
return BoxBlur(source, radius, source.Bounds, progressHandler); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Applies a box 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="radius">The 'radius' value representing the size of the area to sample.</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> BoxBlur<T, TP>(this Image<T, TP> source, int radius, Rectangle rectangle, ProgressEventHandler progressHandler = null) |
||||
|
where T : IPackedVector<TP> |
||||
|
where TP : struct |
||||
|
{ |
||||
|
BoxBlurProcessor<T, TP> processor = new BoxBlurProcessor<T, TP>(radius); |
||||
|
processor.OnProgress += progressHandler; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
return source.Process(rectangle, processor); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
processor.OnProgress -= progressHandler; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
// <copyright file="BoxBlurProcessor.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>
|
||||
|
/// Applies a Box 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 BoxBlurProcessor<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 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="radius">
|
||||
|
/// The 'radius' value representing the size of the area to sample.
|
||||
|
/// </param>
|
||||
|
public BoxBlurProcessor(int radius = 7) |
||||
|
{ |
||||
|
this.kernelSize = (radius * 2) + 1; |
||||
|
} |
||||
|
|
||||
|
/// <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.CreateBoxKernel(false); |
||||
|
} |
||||
|
|
||||
|
if (this.kernelX == null) |
||||
|
{ |
||||
|
this.kernelX = this.CreateBoxKernel(true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a 1 dimensional Box kernel.
|
||||
|
/// </summary>
|
||||
|
/// <param name="horizontal">Whether to calculate a horizontal kernel.</param>
|
||||
|
/// <returns>The <see cref="T:float[,]"/></returns>
|
||||
|
private float[,] CreateBoxKernel(bool horizontal) |
||||
|
{ |
||||
|
int size = this.kernelSize; |
||||
|
float[,] kernel = horizontal ? new float[1, size] : new float[size, 1]; |
||||
|
float sum = 0.0f; |
||||
|
|
||||
|
for (int i = 0; i < size; i++) |
||||
|
{ |
||||
|
float x = 1; |
||||
|
sum += x; |
||||
|
if (horizontal) |
||||
|
{ |
||||
|
kernel[0, i] = x; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
kernel[i, 0] = x; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 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="BoxBlurTest.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 BoxBlurTest : FileTestBase |
||||
|
{ |
||||
|
public static readonly TheoryData<int> BoxBlurValues |
||||
|
= new TheoryData<int> |
||||
|
{ |
||||
|
3 , |
||||
|
5 , |
||||
|
}; |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData("BoxBlurValues")] |
||||
|
public void ImageShouldApplyBoxBlurFilter(int value) |
||||
|
{ |
||||
|
const string path = "TestOutput/BoxBlur"; |
||||
|
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.BoxBlur(value) |
||||
|
.Save(output); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue