diff --git a/src/ImageProcessor.Playground/Program.cs b/src/ImageProcessor.Playground/Program.cs index 13dbc37f8..9cf8c673f 100644 --- a/src/ImageProcessor.Playground/Program.cs +++ b/src/ImageProcessor.Playground/Program.cs @@ -50,7 +50,7 @@ namespace ImageProcessor.PlayGround } Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask.png")); - IEnumerable files = GetFilesByExtensions(di, ".jpg"); + IEnumerable files = GetFilesByExtensions(di, ".png"); //IEnumerable files = GetFilesByExtensions(di, ".gif", ".webp", ".bmp", ".jpg", ".png", ".tif"); foreach (FileInfo fileInfo in files) @@ -85,9 +85,9 @@ namespace ImageProcessor.PlayGround //.Resize(layer) //.DetectEdges(new SobelEdgeFilter(), false) //.DetectEdges(new LaplacianOfGaussianEdgeFilter()) - //.EntropyCrop() + .EntropyCrop() //.Filter(MatrixFilters.Invert) - .Filter(MatrixFilters.Comic) + //.Filter(MatrixFilters.Comic) //.Filter(MatrixFilters.HiSatch) //.Pixelate(8) //.GaussianSharpen(10) diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 52d2aa27a..9d0a96b57 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -133,6 +133,7 @@ + diff --git a/src/ImageProcessor/Imaging/Colors/RgbaComponent.cs b/src/ImageProcessor/Imaging/Colors/RgbaComponent.cs new file mode 100644 index 000000000..03d077fa8 --- /dev/null +++ b/src/ImageProcessor/Imaging/Colors/RgbaComponent.cs @@ -0,0 +1,38 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Enumerates the RGBA (red, green, blue, alpha) color components. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Imaging.Colors +{ + /// + /// Enumerates the RGBA (red, green, blue, alpha) color components. + /// + public enum RgbaComponent + { + /// + /// The blue component. + /// + B = 0, + + /// + /// The green component. + /// + G = 1, + + /// + /// The red component. + /// + R = 2, + + /// + /// The alpha component. + /// + A = 3 + } +} diff --git a/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs b/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs index c801cb5e3..2e552f35c 100644 --- a/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs +++ b/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs @@ -10,8 +10,11 @@ namespace ImageProcessor.Imaging.Helpers { + using System; using System.Drawing; + using ImageProcessor.Imaging.Colors; + /// /// Provides reusable mathematical methods to apply to images. /// @@ -34,6 +37,123 @@ namespace ImageProcessor.Imaging.Helpers return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); } + /// + /// Finds the bounding rectangle based on the first instance of any color component other + /// than the given one. + /// + /// + /// The to search within. + /// + /// + /// The color component value to remove. + /// + /// + /// The channel to test against. + /// + /// + /// The . + /// + public static Rectangle GetFilteredBoundingRectangle(Image bitmap, byte componentValue, RgbaComponent channel = RgbaComponent.B) + { + int width = bitmap.Width; + int height = bitmap.Height; + Point topLeft = new Point(); + Point bottomRight = new Point(); + + Func delegateFunc; + + // Determine which channel to check against + switch (channel) + { + case RgbaComponent.R: + delegateFunc = (fastBitmap, x, y, b) => fastBitmap.GetPixel(x, y).R != b; + break; + case RgbaComponent.G: + delegateFunc = (fastBitmap, x, y, b) => fastBitmap.GetPixel(x, y).G != b; + break; + case RgbaComponent.A: + delegateFunc = (fastBitmap, x, y, b) => fastBitmap.GetPixel(x, y).A != b; + break; + default: + delegateFunc = (fastBitmap, x, y, b) => fastBitmap.GetPixel(x, y).B != b; + break; + } + + Func getMinY = fastBitmap => + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + if (delegateFunc(fastBitmap, x, y, componentValue)) + { + return y; + } + } + } + + return 0; + }; + + Func getMaxY = fastBitmap => + { + for (int y = height - 1; y > -1; y--) + { + for (int x = 0; x < width; x++) + { + if (delegateFunc(fastBitmap, x, y, componentValue)) + { + return y; + } + } + } + + return height; + }; + + Func getMinX = fastBitmap => + { + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + if (delegateFunc(fastBitmap, x, y, componentValue)) + { + return x; + } + } + } + + return 0; + }; + + Func getMaxX = fastBitmap => + { + for (int x = width - 1; x > -1; x--) + { + for (int y = 0; y < height; y++) + { + if (delegateFunc(fastBitmap, x, y, componentValue)) + { + return x; + } + } + } + + return height; + }; + + using (FastBitmap fastBitmap = new FastBitmap(bitmap)) + { + topLeft.Y = getMinY(fastBitmap) + 1; + topLeft.X = getMinX(fastBitmap) + 1; + bottomRight.Y = getMaxY(fastBitmap); + bottomRight.X = getMaxX(fastBitmap); + } + + return ImageMaths.GetBoundingRectangle(topLeft, bottomRight); + } + /// /// Gets a representing the child centered relative to the parent. /// diff --git a/src/ImageProcessor/Imaging/PixelData.cs b/src/ImageProcessor/Imaging/PixelData.cs index 78d494edc..513174c66 100644 --- a/src/ImageProcessor/Imaging/PixelData.cs +++ b/src/ImageProcessor/Imaging/PixelData.cs @@ -10,29 +10,36 @@ namespace ImageProcessor.Imaging { + using System.Runtime.InteropServices; + /// /// Contains the component parts that make up a single pixel. /// + [StructLayout(LayoutKind.Explicit)] public struct PixelData { /// /// The blue component. /// + [FieldOffset(0)] public byte B; /// /// The green component. /// + [FieldOffset(1)] public byte G; /// /// The red component. /// + [FieldOffset(2)] public byte R; /// /// The alpha component. /// + [FieldOffset(3)] public byte A; /// diff --git a/src/ImageProcessor/Processors/EntropyCrop.cs b/src/ImageProcessor/Processors/EntropyCrop.cs index 19eb65e6a..28210fe1e 100644 --- a/src/ImageProcessor/Processors/EntropyCrop.cs +++ b/src/ImageProcessor/Processors/EntropyCrop.cs @@ -12,9 +12,9 @@ namespace ImageProcessor.Processors using System.Drawing; using ImageProcessor.Common.Exceptions; - using ImageProcessor.Imaging; using ImageProcessor.Imaging.Filters.Binarization; using ImageProcessor.Imaging.Filters.EdgeDetection; + using ImageProcessor.Imaging.Helpers; /// /// Performs a crop on an image to the area of greatest entropy. @@ -61,8 +61,9 @@ namespace ImageProcessor.Processors // Detect the edges then strip out middle shades. grey = new ConvolutionFilter(new SobelEdgeFilter(), true).Process2DFilter(image); grey = new BinaryThreshold(threshold).ProcessFilter(grey); - - Rectangle rectangle = this.FindBoundingBox(grey, 0); + + // Search for the first white pixels + Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(grey, 0); newImage = new Bitmap(rectangle.Width, rectangle.Height); using (Graphics graphics = Graphics.FromImage(newImage)) @@ -99,102 +100,5 @@ namespace ImageProcessor.Processors return image; } - - /// - /// Finds the bounding rectangle based on the first instance of any color component other - /// than the given one. - /// - /// - /// The bitmap. - /// - /// - /// The color component to remove. - /// - /// - /// The . - /// - private Rectangle FindBoundingBox(Bitmap bitmap, byte componentToRemove) - { - int width = bitmap.Width; - int height = bitmap.Height; - int startX; - int startY; - int stopX; - int stopY; - - Func getMinY = fastBitmap => - { - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - if (fastBitmap.GetPixel(x, y).B != componentToRemove) - { - return y; - } - } - } - - return 0; - }; - - Func getMaxY = fastBitmap => - { - for (int y = height - 1; y > -1; y--) - { - for (int x = 0; x < width; x++) - { - if (fastBitmap.GetPixel(x, y).B != componentToRemove) - { - return y; - } - } - } - - return height; - }; - - Func getMinX = fastBitmap => - { - for (int x = 0; x < width; x++) - { - for (int y = 0; y < height; y++) - { - if (fastBitmap.GetPixel(x, y).B != componentToRemove) - { - return x; - } - } - } - - return 0; - }; - - Func getMaxX = fastBitmap => - { - for (int x = width - 1; x > -1; x--) - { - for (int y = 0; y < height; y++) - { - if (fastBitmap.GetPixel(x, y).B != componentToRemove) - { - return x; - } - } - } - - return height; - }; - - using (FastBitmap fastBitmap = new FastBitmap(bitmap)) - { - startY = getMinY(fastBitmap); - stopY = getMaxY(fastBitmap); - startX = getMinX(fastBitmap); - stopX = getMaxX(fastBitmap); - } - - return new Rectangle(startX + 1, startY + 1, stopX - (startX + 1), stopY - (startY + 1)); - } } } \ No newline at end of file