From 400b62f9647bef6cd49cb733ea7a38513fc3575e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 29 Jul 2016 11:43:42 +1000 Subject: [PATCH] Box Blur Former-commit-id: 5ac93d8e7ce1bb397a8ee7a68307aa423a85882a Former-commit-id: 7b8b0c704f7f272867516203133bb1c3fbbbf5a5 Former-commit-id: e495b72e24e1f723023cc537749e03a987b9eb8c --- src/ImageProcessorCore/Filters/BoxBlur.cs | 59 ++++++++++ .../Convolution/BoxBlurProcessor.cs | 107 ++++++++++++++++++ .../Processors/Filters/BoxBlurTest.cs | 47 ++++++++ 3 files changed, 213 insertions(+) create mode 100644 src/ImageProcessorCore/Filters/BoxBlur.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs create mode 100644 tests/ImageProcessorCore.Tests/Processors/Filters/BoxBlurTest.cs diff --git a/src/ImageProcessorCore/Filters/BoxBlur.cs b/src/ImageProcessorCore/Filters/BoxBlur.cs new file mode 100644 index 0000000000..527950a209 --- /dev/null +++ b/src/ImageProcessorCore/Filters/BoxBlur.cs @@ -0,0 +1,59 @@ +// +// 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 + { + /// + /// Applies a box blur to the image. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image BoxBlur(this Image source, int radius = 7, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + return BoxBlur(source, radius, source.Bounds, progressHandler); + } + + /// + /// Applies a box blur to the image. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image BoxBlur(this Image source, int radius, Rectangle rectangle, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + BoxBlurProcessor processor = new BoxBlurProcessor(radius); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs new file mode 100644 index 0000000000..a4fe4da0f1 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs @@ -0,0 +1,107 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + /// + /// Applies a Box blur filter to the image. + /// + /// The pixel format. + /// The packed format. long, float. + public class BoxBlurProcessor : Convolution2PassFilter + where T : IPackedVector + where TP : struct + { + /// + /// The maximum size of the kernal in either direction. + /// + private readonly int kernelSize; + + /// + /// The vertical kernel + /// + private float[,] kernelY; + + /// + /// The horizontal kernel + /// + private float[,] kernelX; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The 'radius' value representing the size of the area to sample. + /// + public BoxBlurProcessor(int radius = 7) + { + this.kernelSize = (radius * 2) + 1; + } + + /// + public override float[,] KernelX => this.kernelX; + + /// + public override float[,] KernelY => this.kernelY; + + /// + protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) + { + if (this.kernelY == null) + { + this.kernelY = this.CreateBoxKernel(false); + } + + if (this.kernelX == null) + { + this.kernelX = this.CreateBoxKernel(true); + } + } + + /// + /// Create a 1 dimensional Box kernel. + /// + /// Whether to calculate a horizontal kernel. + /// The + 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; + } + } +} diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/BoxBlurTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/BoxBlurTest.cs new file mode 100644 index 0000000000..9c48752af0 --- /dev/null +++ b/tests/ImageProcessorCore.Tests/Processors/Filters/BoxBlurTest.cs @@ -0,0 +1,47 @@ +// +// 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 BoxBlurTest : FileTestBase + { + public static readonly TheoryData BoxBlurValues + = new TheoryData + { + 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); + } + } + } + } + } +} \ No newline at end of file