From 1fc25ae5ddbc63c6b291e296dbdfb3a066e9f4bb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 7 Nov 2015 22:18:36 +1100 Subject: [PATCH] Add rectangular crop. Also add overloads to Gaussian blur and sharpen. Former-commit-id: ff18410aefbdb605d7177dfcc0e98dadf9d85e47 Former-commit-id: 71993046259e4fbc6734345cab961c2741ccb9d6 Former-commit-id: 5a3f8b071b8e4948544db58aa1280c0df451efe2 --- README.md | 2 +- .../Filters/Convolution/GuassianBlur.cs | 28 +++++++++++ src/ImageProcessor/ImageExtensions.cs | 7 ++- src/ImageProcessor/ImageProcessor.csproj | 1 + src/ImageProcessor/Samplers/Crop.cs | 44 ++++++++++++++++++ .../Samplers/ImageSampleExtensions.cs | 46 ++++++++++++++++++- .../Processors/Samplers/SamplerTests.cs | 24 +++++++++- 7 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 src/ImageProcessor/Samplers/Crop.cs diff --git a/README.md b/README.md index 89f703cbc..0b5ff1d26 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Honestly... I don't know. I could be writing code that may be suddenly obsolete. - [x] Triangle - [x] Welch - Cropping - - [ ] Rectangular Crop + - [x] Rectangular Crop - [ ] Elliptical Crop - [ ] Entropy Crop - Rotation diff --git a/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs b/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs index 0532e7b7f..0973b1a61 100644 --- a/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs +++ b/src/ImageProcessor/Filters/Convolution/GuassianBlur.cs @@ -44,6 +44,34 @@ namespace ImageProcessor.Filters this.sigma = sigma; } + /// + /// Initializes a new instance of the class. + /// + /// + /// The 'radius' value representing the size of the area to sample. + /// + public GuassianBlur(int radius) + { + this.kernelSize = (radius * 2) + 1; + this.sigma = radius; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The 'sigma' value representing the weight of the blur. + /// + /// + /// The 'radius' value representing the size of the area to sample. + /// This should be at least twice the sigma value. + /// + public GuassianBlur(float sigma, int radius) + { + this.kernelSize = (radius * 2) + 1; + this.sigma = sigma; + } + /// public override float[,] KernelX => this.kernelX; diff --git a/src/ImageProcessor/ImageExtensions.cs b/src/ImageProcessor/ImageExtensions.cs index 2b341d766..6c52fe15e 100644 --- a/src/ImageProcessor/ImageExtensions.cs +++ b/src/ImageProcessor/ImageExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -62,6 +62,7 @@ namespace ImageProcessor /// /// Applies the collection of processors to the image. + /// This method does not resize the target image. /// /// The image this method extends. /// @@ -95,6 +96,10 @@ namespace ImageProcessor /// /// Applies the collection of processors to the image. + /// + /// This method does will resize the target image if the source and target + /// rectangles are different. + /// /// /// The source image. Cannot be null. /// The target image width. diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 8e9ef8c0a..9ce6dc4bd 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -235,6 +235,7 @@ + diff --git a/src/ImageProcessor/Samplers/Crop.cs b/src/ImageProcessor/Samplers/Crop.cs new file mode 100644 index 000000000..7ed0bb2df --- /dev/null +++ b/src/ImageProcessor/Samplers/Crop.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + using System.Threading.Tasks; + + /// + /// Provides methods to allow the cropping of an image. + /// + public class Crop : ParallelImageProcessor + { + /// + protected override void Apply( + ImageBase target, + ImageBase source, + Rectangle targetRectangle, + Rectangle sourceRectangle, + int startY, + int endY) + { + int targetY = targetRectangle.Y; + int targetBottom = targetRectangle.Bottom; + int startX = targetRectangle.X; + int endX = targetRectangle.Right; + + Parallel.For( + startY, + endY, + y => + { + if (y >= targetY && y < targetBottom) + { + for (int x = startX; x < endX; x++) + { + target[x, y] = source[x, y]; + } + } + }); + } + } +} diff --git a/src/ImageProcessor/Samplers/ImageSampleExtensions.cs b/src/ImageProcessor/Samplers/ImageSampleExtensions.cs index b560e4779..6b2c0bacf 100644 --- a/src/ImageProcessor/Samplers/ImageSampleExtensions.cs +++ b/src/ImageProcessor/Samplers/ImageSampleExtensions.cs @@ -36,7 +36,8 @@ namespace ImageProcessor.Samplers } /// - /// Resizes an image to the given width and height with the given sampler. + /// Resizes an image to the given width and height with the given sampler, + /// source rectangle, and target rectangle. /// /// The image to resize. /// The target image width. @@ -54,5 +55,48 @@ namespace ImageProcessor.Samplers { return source.Process(width, height, sourceRectangle, targetRectangle, new Resize(sampler)); } + + /// + /// Crops an image to the given width and height. + /// + /// The image to resize. + /// The target image width. + /// The target image height. + /// The + public static Image Crop(this Image source, int width, int height) + { + return Crop(source, width, height, source.Bounds, new Rectangle(0, 0, width, height)); + } + + /// + /// Crops an image to the given width and height with the given source rectangle, + /// and target rectangle. + /// + /// If the source rectangle is smaller than the target dimensions then the + /// area within the source is resized performing a zoomed crop. + /// + /// + /// The image to resize. + /// The target image width. + /// The target image height. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// + /// The structure that specifies the location and size of the drawn image. + /// The image is cropped to fit the rectangle. + /// + /// The + public static Image Crop(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle) + { + if (sourceRectangle.Width < targetRectangle.Width || sourceRectangle.Height < targetRectangle.Height) + { + // If the source rectangle is smaller than the target perform a + // cropped zoom. + source = source.Resize(sourceRectangle.Width, sourceRectangle.Height); + } + + return source.Process(width, height, sourceRectangle, targetRectangle, new Crop()); + } } } diff --git a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs index 1e71cf112..a507c5a50 100644 --- a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs @@ -1,7 +1,6 @@  namespace ImageProcessor.Tests { - using System; using System.Diagnostics; using System.IO; @@ -47,7 +46,6 @@ namespace ImageProcessor.Tests string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); using (FileStream output = File.OpenWrite($"Resized/{filename}")) { - //image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output); image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output); } @@ -56,6 +54,28 @@ namespace ImageProcessor.Tests } } + [Fact] + public void ImageShouldCrop() + { + if (!Directory.Exists("Cropped")) + { + Directory.CreateDirectory("Cropped"); + } + + foreach (string file in Files) + { + using (FileStream stream = File.OpenRead(file)) + { + Image image = new Image(stream); + string filename = Path.GetFileNameWithoutExtension(file) + "-Cropped" + Path.GetExtension(file); + using (FileStream output = File.OpenWrite($"Cropped/{filename}")) + { + image.Crop(image.Width / 2, image.Height / 2).Save(output); + } + } + } + } + [Theory] [InlineData(-2, 0)] [InlineData(-1, 0)]