From 7d7c9fef6c6685171e98c7cf819e02bbdcd35e6b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 19 Oct 2015 17:53:36 +1100 Subject: [PATCH] Adding some resamplers Former-commit-id: 81377f2d574fe5778b5a24c968c53bd967e8f08d Former-commit-id: 0dbdf15a7d41f51a7ca109a1b6fc39954c6cfa69 Former-commit-id: 48d223ff221f92f49f12685d44c09839bb76b2e4 --- .../Common/Helpers/ImageMaths.cs | 35 +++++++++++++++++++ src/ImageProcessor/ImageProcessor.csproj | 17 ++++++--- .../ImageProcessor.csproj.DotSettings | 3 +- .../{ => Resamplers}/BicubicResampler.cs | 2 +- .../Samplers/Resamplers/BoxResampler.cs | 32 +++++++++++++++++ .../Resamplers/CatmullRomResampler.cs | 26 ++++++++++++++ .../Samplers/Resamplers/HermiteResampler.cs | 26 ++++++++++++++ .../Samplers/{ => Resamplers}/IResampler.cs | 0 .../Lanczos3Resampler.cs} | 6 ++-- .../Resamplers/MitchellNetravaliResampler.cs | 26 ++++++++++++++ .../Samplers/Resamplers/RobidouxResampler.cs | 26 ++++++++++++++ .../Resamplers/RobidouxSharpResampler.cs | 26 ++++++++++++++ .../Resamplers/RobidouxSoftResampler.cs | 26 ++++++++++++++ .../Samplers/Resamplers/SplineResampler.cs | 26 ++++++++++++++ .../Samplers/Resamplers/TriangleResampler.cs | 32 +++++++++++++++++ .../Samplers/Resamplers/WelchResampler.cs | 33 +++++++++++++++++ src/ImageProcessor/Samplers/Resize.cs | 19 ++++++++-- .../Processors/Samplers/SamplerTests.cs | 13 +++++-- 18 files changed, 361 insertions(+), 13 deletions(-) rename src/ImageProcessor/Samplers/{ => Resamplers}/BicubicResampler.cs (96%) create mode 100644 src/ImageProcessor/Samplers/Resamplers/BoxResampler.cs create mode 100644 src/ImageProcessor/Samplers/Resamplers/CatmullRomResampler.cs create mode 100644 src/ImageProcessor/Samplers/Resamplers/HermiteResampler.cs rename src/ImageProcessor/Samplers/{ => Resamplers}/IResampler.cs (100%) rename src/ImageProcessor/Samplers/{Lanczos5Resampler.cs => Resamplers/Lanczos3Resampler.cs} (82%) create mode 100644 src/ImageProcessor/Samplers/Resamplers/MitchellNetravaliResampler.cs create mode 100644 src/ImageProcessor/Samplers/Resamplers/RobidouxResampler.cs create mode 100644 src/ImageProcessor/Samplers/Resamplers/RobidouxSharpResampler.cs create mode 100644 src/ImageProcessor/Samplers/Resamplers/RobidouxSoftResampler.cs create mode 100644 src/ImageProcessor/Samplers/Resamplers/SplineResampler.cs create mode 100644 src/ImageProcessor/Samplers/Resamplers/TriangleResampler.cs create mode 100644 src/ImageProcessor/Samplers/Resamplers/WelchResampler.cs diff --git a/src/ImageProcessor/Common/Helpers/ImageMaths.cs b/src/ImageProcessor/Common/Helpers/ImageMaths.cs index 2e2f49bee..e0362992a 100644 --- a/src/ImageProcessor/Common/Helpers/ImageMaths.cs +++ b/src/ImageProcessor/Common/Helpers/ImageMaths.cs @@ -12,6 +12,41 @@ namespace ImageProcessor /// internal static class ImageMaths { + /// + /// Returns the result of a B-C filter against the given value. + /// + /// + /// The value to process. + /// The B-Spline curve variable. + /// The Cardinal curve variable. + /// + /// The . + /// + public static double GetBcValue(double x, double b, double c) + { + double temp; + + if (x < 0) + { + x = -x; + } + + temp = x * x; + if (x < 1) + { + x = ((12 - (9 * b) - (6 * c)) * (x * temp)) + ((-18 + (12 * b) + (6 * c)) * temp) + (6 - (2 * b)); + return x / 6; + } + + if (x < 2) + { + x = ((-b - (6 * c)) * (x * temp)) + (((6 * b) + (30 * c)) * temp) + (((-12 * b) - (48 * c)) * x) + ((8 * b) + (24 * c)); + return x / 6; + } + + return 0; + } + /// /// Gets the result of a sine cardinal function for the given value. /// diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 7843b88bf..bc3b47826 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -181,10 +181,20 @@ - - + + + + + + + + + + + + - + @@ -197,7 +207,6 @@ - diff --git a/src/ImageProcessor/ImageProcessor.csproj.DotSettings b/src/ImageProcessor/ImageProcessor.csproj.DotSettings index 33397fdd2..9d4487f0f 100644 --- a/src/ImageProcessor/ImageProcessor.csproj.DotSettings +++ b/src/ImageProcessor/ImageProcessor.csproj.DotSettings @@ -12,4 +12,5 @@ True True True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/src/ImageProcessor/Samplers/BicubicResampler.cs b/src/ImageProcessor/Samplers/Resamplers/BicubicResampler.cs similarity index 96% rename from src/ImageProcessor/Samplers/BicubicResampler.cs rename to src/ImageProcessor/Samplers/Resamplers/BicubicResampler.cs index 4c0d285af..20c4643cb 100644 --- a/src/ImageProcessor/Samplers/BicubicResampler.cs +++ b/src/ImageProcessor/Samplers/Resamplers/BicubicResampler.cs @@ -12,7 +12,7 @@ namespace ImageProcessor.Samplers public class BicubicResampler : IResampler { /// - public double Radius => 4; + public double Radius => 2; /// public double GetValue(double x) diff --git a/src/ImageProcessor/Samplers/Resamplers/BoxResampler.cs b/src/ImageProcessor/Samplers/Resamplers/BoxResampler.cs new file mode 100644 index 000000000..7983261da --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/BoxResampler.cs @@ -0,0 +1,32 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the box (nearest neighbour) algorithm. + /// + public class BoxResampler : IResampler + { + /// + public double Radius => 0.5; + + /// + public double GetValue(double x) + { + if (x < 0) + { + x = -x; + } + + if (x <= 0.5) + { + return 1; + } + + return 0; + } + } +} diff --git a/src/ImageProcessor/Samplers/Resamplers/CatmullRomResampler.cs b/src/ImageProcessor/Samplers/Resamplers/CatmullRomResampler.cs new file mode 100644 index 000000000..816cf0607 --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/CatmullRomResampler.cs @@ -0,0 +1,26 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the Catmull-Rom algorithm. + /// + /// + public class CatmullRomResampler : IResampler + { + /// + public double Radius => 2; + + /// + public double GetValue(double x) + { + const double B = 0; + const double C = 1 / 2d; + + return ImageMaths.GetBcValue(x, B, C); + } + } +} diff --git a/src/ImageProcessor/Samplers/Resamplers/HermiteResampler.cs b/src/ImageProcessor/Samplers/Resamplers/HermiteResampler.cs new file mode 100644 index 000000000..666c3372d --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/HermiteResampler.cs @@ -0,0 +1,26 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the hermite algorithm. + /// + /// + public class HermiteResampler : IResampler + { + /// + public double Radius => 2; + + /// + public double GetValue(double x) + { + const double B = 0; + const double C = 0; + + return ImageMaths.GetBcValue(x, B, C); + } + } +} diff --git a/src/ImageProcessor/Samplers/IResampler.cs b/src/ImageProcessor/Samplers/Resamplers/IResampler.cs similarity index 100% rename from src/ImageProcessor/Samplers/IResampler.cs rename to src/ImageProcessor/Samplers/Resamplers/IResampler.cs diff --git a/src/ImageProcessor/Samplers/Lanczos5Resampler.cs b/src/ImageProcessor/Samplers/Resamplers/Lanczos3Resampler.cs similarity index 82% rename from src/ImageProcessor/Samplers/Lanczos5Resampler.cs rename to src/ImageProcessor/Samplers/Resamplers/Lanczos3Resampler.cs index 1d7008d58..3a154303d 100644 --- a/src/ImageProcessor/Samplers/Lanczos5Resampler.cs +++ b/src/ImageProcessor/Samplers/Resamplers/Lanczos3Resampler.cs @@ -1,4 +1,4 @@ -// +// // Copyright © James South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -9,10 +9,10 @@ namespace ImageProcessor.Samplers /// The function implements the Lanczos kernel algorithm as described on /// Wikipedia /// - public class Lanczos5Resampler : IResampler + public class Lanczos3Resampler : IResampler { /// - public double Radius => 5; + public double Radius => 3; /// public double GetValue(double x) diff --git a/src/ImageProcessor/Samplers/Resamplers/MitchellNetravaliResampler.cs b/src/ImageProcessor/Samplers/Resamplers/MitchellNetravaliResampler.cs new file mode 100644 index 000000000..acf5c51d6 --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/MitchellNetravaliResampler.cs @@ -0,0 +1,26 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the mitchell algorithm as described on + /// Wikipedia + /// + public class MitchellNetravaliResampler : IResampler + { + /// + public double Radius => 2; + + /// + public double GetValue(double x) + { + const double B = 1 / 3d; + const double C = 1 / 3d; + + return ImageMaths.GetBcValue(x, B, C); + } + } +} diff --git a/src/ImageProcessor/Samplers/Resamplers/RobidouxResampler.cs b/src/ImageProcessor/Samplers/Resamplers/RobidouxResampler.cs new file mode 100644 index 000000000..dcfea2870 --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/RobidouxResampler.cs @@ -0,0 +1,26 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the Robidoux algorithm. + /// + /// + public class RobidouxResampler : IResampler + { + /// + public double Radius => 2; + + /// + public double GetValue(double x) + { + const double B = 0.3782; + const double C = 0.3109; + + return ImageMaths.GetBcValue(x, B, C); + } + } +} diff --git a/src/ImageProcessor/Samplers/Resamplers/RobidouxSharpResampler.cs b/src/ImageProcessor/Samplers/Resamplers/RobidouxSharpResampler.cs new file mode 100644 index 000000000..dfb8d9bfd --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/RobidouxSharpResampler.cs @@ -0,0 +1,26 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the Robidoux Sharp algorithm. + /// + /// + public class RobidouxSharpResampler : IResampler + { + /// + public double Radius => 2; + + /// + public double GetValue(double x) + { + const double B = 0.2620; + const double C = 0.3690; + + return ImageMaths.GetBcValue(x, B, C); + } + } +} diff --git a/src/ImageProcessor/Samplers/Resamplers/RobidouxSoftResampler.cs b/src/ImageProcessor/Samplers/Resamplers/RobidouxSoftResampler.cs new file mode 100644 index 000000000..a6bbfada8 --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/RobidouxSoftResampler.cs @@ -0,0 +1,26 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the Robidoux Soft algorithm. + /// + /// + public class RobidouxSoftResampler : IResampler + { + /// + public double Radius => 2; + + /// + public double GetValue(double x) + { + const double B = 0.6796; + const double C = 0.1602; + + return ImageMaths.GetBcValue(x, B, C); + } + } +} diff --git a/src/ImageProcessor/Samplers/Resamplers/SplineResampler.cs b/src/ImageProcessor/Samplers/Resamplers/SplineResampler.cs new file mode 100644 index 000000000..f3ed32ef0 --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/SplineResampler.cs @@ -0,0 +1,26 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the spline algorithm. + /// + /// + public class SplineResampler : IResampler + { + /// + public double Radius => 2; + + /// + public double GetValue(double x) + { + const double B = 1; + const double C = 0; + + return ImageMaths.GetBcValue(x, B, C); + } + } +} diff --git a/src/ImageProcessor/Samplers/Resamplers/TriangleResampler.cs b/src/ImageProcessor/Samplers/Resamplers/TriangleResampler.cs new file mode 100644 index 000000000..66ff6a1ae --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/TriangleResampler.cs @@ -0,0 +1,32 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the triangle (bilinear) algorithm. + /// + public class TriangleResampler : IResampler + { + /// + public double Radius => 1; + + /// + public double GetValue(double x) + { + if (x < 0) + { + x = -x; + } + + if (x < 1) + { + return 1 - x; + } + + return 0; + } + } +} diff --git a/src/ImageProcessor/Samplers/Resamplers/WelchResampler.cs b/src/ImageProcessor/Samplers/Resamplers/WelchResampler.cs new file mode 100644 index 000000000..db395ba85 --- /dev/null +++ b/src/ImageProcessor/Samplers/Resamplers/WelchResampler.cs @@ -0,0 +1,33 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + /// + /// The function implements the welch algorithm. + /// + /// + public class WelchResampler : IResampler + { + /// + public double Radius => 3; + + /// + public double GetValue(double x) + { + if (x < 0) + { + x = -x; + } + + if (x < 3) + { + return ImageMaths.SinC(x) * (1.0 - (x * x / 9.0)); + } + + return 0; + } + } +} diff --git a/src/ImageProcessor/Samplers/Resize.cs b/src/ImageProcessor/Samplers/Resize.cs index 3dc3b166f..811294eb5 100644 --- a/src/ImageProcessor/Samplers/Resize.cs +++ b/src/ImageProcessor/Samplers/Resize.cs @@ -12,6 +12,11 @@ namespace ImageProcessor.Samplers /// public class Resize : ParallelImageProcessor { + /// + /// The epsilon for comparing floating point numbers. + /// + private const float Epsilon = 0.0001f; + /// /// Initializes a new instance of the class. /// @@ -78,11 +83,16 @@ namespace ImageProcessor.Samplers double b = 0; double a = 0; - for (int yy = left; yy < right; yy++) + for (int yy = left; yy <= right; yy++) { // Get Y cooefficient double kernel1 = this.Sampler.GetValue(dy - yy); + if (Math.Abs(kernel1) < Epsilon) + { + continue; + } + int originY2 = originY1 + yy; if (originY2 < 0) { @@ -94,11 +104,16 @@ namespace ImageProcessor.Samplers originY2 = maxHeight; } - for (int xx = left; xx < right; xx++) + for (int xx = left; xx <= right; xx++) { // Get X cooefficient double kernel2 = kernel1 * this.Sampler.GetValue(xx - dx); + if (Math.Abs(kernel2) < Epsilon) + { + continue; + } + int originX2 = originX1 + xx; if (originX2 < 0) { diff --git a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs index ab4511ad3..7a9e0f732 100644 --- a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs @@ -14,7 +14,16 @@ namespace ImageProcessor.Tests new TheoryData { { "Bicubic", new BicubicResampler() }, - //{ "Lanczos3", new Lanczos5Resampler() } + { "Bilinear", new TriangleResampler() }, + { "NearestNeighbour", new BoxResampler() }, + { "Lanczos3", new Lanczos3Resampler() }, + { "MitchellNetravali", new MitchellNetravaliResampler() }, + { "Hermite", new HermiteResampler() }, + { "Spline", new SplineResampler() }, + { "Robidoux", new RobidouxResampler() }, + { "RobidouxSharp", new RobidouxSharpResampler() }, + { "RobidouxSoft", new RobidouxSoftResampler() }, + { "Welch", new WelchResampler() } }; [Theory] @@ -52,7 +61,7 @@ namespace ImageProcessor.Tests [InlineData(2, 0)] public static void Lanczos3WindowOscillatesCorrectly(double x, double expected) { - Lanczos5Resampler sampler = new Lanczos5Resampler(); + Lanczos3Resampler sampler = new Lanczos3Resampler(); double result = sampler.GetValue(x); Assert.Equal(result, expected);