From ef986a329e331b10976cf79c32dfdcf96933b807 Mon Sep 17 00:00:00 2001 From: James South Date: Wed, 8 Oct 2014 23:06:36 +0100 Subject: [PATCH] Fixing comic filter Former-commit-id: 3fe75f0dcb823383aee6f0b78f585e2d89443b9e Former-commit-id: f67c0028ab9e096ad63030c0e84a80f51732a950 --- src/ImageProcessor.Playground/Program.cs | 8 +- .../images/input/circle.png | 4 +- .../images/input/monster.png.REMOVED.git-id | 2 +- .../Filters/Artistic/OilPaintingFilter.cs | 4 +- .../Filters/Photo/ComicMatrixFilter.cs | 201 +++--------------- src/ImageProcessor/Imaging/PixelData.cs | 2 - 6 files changed, 41 insertions(+), 180 deletions(-) diff --git a/src/ImageProcessor.Playground/Program.cs b/src/ImageProcessor.Playground/Program.cs index d34732b34..1cc1aaf35 100644 --- a/src/ImageProcessor.Playground/Program.cs +++ b/src/ImageProcessor.Playground/Program.cs @@ -73,16 +73,16 @@ namespace ImageProcessor.PlayGround //.BackgroundColor(Color.White) //.Resize(new Size((int)(size.Width * 1.1), 0)) //.ContentAwareResize(layer) - //.Constrain(size) + // .Constrain(size) //.ReplaceColor(Color.FromArgb(255, 1, 107, 165), Color.FromArgb(255, 1, 165, 13), 80) //.Resize(layer) - //.DetectEdges(new KirschEdgeFilter()) + .DetectEdges(new SobelEdgeFilter(), false) //.DetectEdges(new LaplacianOfGaussianEdgeFilter()) //.EntropyCrop() - //.Filter(MatrixFilters.Comic) + .Filter(MatrixFilters.Invert) //.Filter(MatrixFilters.Comic) //.Filter(MatrixFilters.HiSatch) - .Pixelate(8) + //.Pixelate(8) //.GaussianSharpen(10) .Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", fileInfo.Name))); diff --git a/src/ImageProcessor.Playground/images/input/circle.png b/src/ImageProcessor.Playground/images/input/circle.png index 3d96cf303..7e46427ac 100644 --- a/src/ImageProcessor.Playground/images/input/circle.png +++ b/src/ImageProcessor.Playground/images/input/circle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3df296a3fd58930899d308558b128db760879cb776afba8f2d6511aba3934dd0 -size 6957 +oid sha256:73f4d8e08292df487a457be0765cb7994f2d91600b1824170a82e8f20f0a6d0b +size 5089 diff --git a/src/ImageProcessor.Playground/images/input/monster.png.REMOVED.git-id b/src/ImageProcessor.Playground/images/input/monster.png.REMOVED.git-id index 3d53156f1..c13b65e9e 100644 --- a/src/ImageProcessor.Playground/images/input/monster.png.REMOVED.git-id +++ b/src/ImageProcessor.Playground/images/input/monster.png.REMOVED.git-id @@ -1 +1 @@ -6b1252209f60025427722f765dbbc271d125114e \ No newline at end of file +4edf74a6857665c8efe2d3282c25907f5b20ca81 \ No newline at end of file diff --git a/src/ImageProcessor/Imaging/Filters/Artistic/OilPaintingFilter.cs b/src/ImageProcessor/Imaging/Filters/Artistic/OilPaintingFilter.cs index 2213c043a..0208edbd5 100644 --- a/src/ImageProcessor/Imaging/Filters/Artistic/OilPaintingFilter.cs +++ b/src/ImageProcessor/Imaging/Filters/Artistic/OilPaintingFilter.cs @@ -118,6 +118,7 @@ namespace ImageProcessor.Imaging.Filters.Artistic int[] blueBin = new int[this.levels]; int[] greenBin = new int[this.levels]; int[] redBin = new int[this.levels]; + byte sourceAlpha = 255; for (int i = 0; i <= radius; i++) { @@ -155,6 +156,7 @@ namespace ImageProcessor.Imaging.Filters.Artistic byte sourceBlue = color.B; byte sourceGreen = color.G; byte sourceRed = color.R; + sourceAlpha = color.A; int currentIntensity = (int)Math.Round(((sourceBlue + sourceGreen + sourceRed) / 3.0 * (this.levels - 1)) / 255.0); @@ -177,7 +179,7 @@ namespace ImageProcessor.Imaging.Filters.Artistic byte red = Math.Abs(redBin[maxIndex] / maxIntensity).ToByte(); // ReSharper disable once AccessToDisposedClosure - destinationBitmap.SetPixel(x, y, Color.FromArgb(red, green, blue)); + destinationBitmap.SetPixel(x, y, Color.FromArgb(sourceAlpha, red, green, blue)); } }); } diff --git a/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs index ac9d488af..868a4d64e 100644 --- a/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs +++ b/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs @@ -19,6 +19,7 @@ namespace ImageProcessor.Imaging.Filters.Photo using ImageProcessor.Common.Extensions; using ImageProcessor.Imaging.Filters.Artistic; + using ImageProcessor.Imaging.Filters.EdgeDetection; /// /// Encapsulates methods with which to add a comic filter to an image. @@ -64,7 +65,7 @@ namespace ImageProcessor.Imaging.Filters.Photo highBitmap = new OilPaintingFilter(3, 5).ApplyFilter((Bitmap)image); // Draw the edges. - edgeBitmap = DrawEdges((Bitmap)image, 120); + edgeBitmap = Trace((Bitmap)image, 120); using (Graphics graphics = Graphics.FromImage(highBitmap)) { @@ -169,7 +170,7 @@ namespace ImageProcessor.Imaging.Filters.Photo /// Detects and draws edges. /// TODO: Move this to another class and do edge detection. /// - /// + /// /// The source bitmap. /// /// @@ -178,184 +179,44 @@ namespace ImageProcessor.Imaging.Filters.Photo /// /// The . /// - private static Bitmap DrawEdges(Bitmap sourceBitmap, byte threshold = 0) + private static Bitmap Trace(Bitmap source, byte threshold = 0) { - Color color = Color.Black; - int width = sourceBitmap.Width; - int height = sourceBitmap.Height; - - BitmapData sourceData = sourceBitmap.LockBits( - new Rectangle(0, 0, width, height), - ImageLockMode.ReadOnly, - PixelFormat.Format32bppArgb); - - int strideWidth = sourceData.Stride; - int scanHeight = sourceData.Height; - - int bufferSize = strideWidth * scanHeight; - byte[] pixelBuffer = new byte[bufferSize]; - byte[] resultBuffer = new byte[bufferSize]; - - Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length); - - sourceBitmap.UnlockBits(sourceData); - - for (int offsetY = 1; offsetY < height - 1; offsetY++) + int width = source.Width; + int height = source.Height; + + // Grab the edges converting to greyscale, and invert the colors. + ConvolutionFilter filter = new ConvolutionFilter(new SobelEdgeFilter(), true); + Bitmap destination = filter.Process2DFilter(source); + Bitmap invert = new Bitmap(width, height, PixelFormat.Format32bppArgb); + InvertMatrixFilter matrix = new InvertMatrixFilter(); + invert = (Bitmap)matrix.TransformImage(destination, invert); + + // Loop through and replace any colors more white than the threshold + // with a transparent one. + using (FastBitmap sourceBitmap = new FastBitmap(invert)) { - for (int offsetX = 1; offsetX < width - 1; offsetX++) - { - int byteOffset = (offsetY * strideWidth) + (offsetX * 4); - - int blueGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]); - - blueGradient += - Math.Abs( - pixelBuffer[byteOffset - strideWidth] - pixelBuffer[byteOffset + strideWidth]); - - byteOffset++; - - int greenGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]); - - greenGradient += - Math.Abs( - pixelBuffer[byteOffset - strideWidth] - pixelBuffer[byteOffset + strideWidth]); - - byteOffset++; - - int redGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]); - - redGradient += - Math.Abs( - pixelBuffer[byteOffset - strideWidth] - pixelBuffer[byteOffset + strideWidth]); - - bool exceedsThreshold; - if (blueGradient + greenGradient + redGradient > threshold) + Parallel.For( + 0, + height, + y => { - exceedsThreshold = true; - } - else - { - byteOffset -= 2; - - blueGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]); - byteOffset++; - - greenGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]); - byteOffset++; - - redGradient = Math.Abs(pixelBuffer[byteOffset - 4] - pixelBuffer[byteOffset + 4]); - - if (blueGradient + greenGradient + redGradient > threshold) - { - exceedsThreshold = true; - } - else + for (int x = 0; x < width; x++) { - byteOffset -= 2; - - blueGradient = - Math.Abs(pixelBuffer[byteOffset - strideWidth] - pixelBuffer[byteOffset + strideWidth]); - - byteOffset++; - - greenGradient = - Math.Abs(pixelBuffer[byteOffset - strideWidth] - pixelBuffer[byteOffset + strideWidth]); - - byteOffset++; - - redGradient = - Math.Abs(pixelBuffer[byteOffset - strideWidth] - pixelBuffer[byteOffset + strideWidth]); - - if (blueGradient + greenGradient + redGradient > threshold) + // ReSharper disable AccessToDisposedClosure + Color color = sourceBitmap.GetPixel(x, y); + if (color.B >= threshold) { - exceedsThreshold = true; - } - else - { - byteOffset -= 2; - - blueGradient = - Math.Abs( - pixelBuffer[byteOffset - 4 - strideWidth] - - pixelBuffer[byteOffset + 4 + strideWidth]); - - blueGradient += - Math.Abs( - pixelBuffer[byteOffset - strideWidth + 4] - - pixelBuffer[byteOffset + strideWidth - 4]); - - byteOffset++; - - greenGradient = - Math.Abs( - pixelBuffer[byteOffset - 4 - strideWidth] - - pixelBuffer[byteOffset + 4 + strideWidth]); - - greenGradient += - Math.Abs( - pixelBuffer[byteOffset - strideWidth + 4] - - pixelBuffer[byteOffset + strideWidth - 4]); - - byteOffset++; - - redGradient = - Math.Abs( - pixelBuffer[byteOffset - 4 - strideWidth] - - pixelBuffer[byteOffset + 4 + strideWidth]); - - redGradient += - Math.Abs( - pixelBuffer[byteOffset - strideWidth + 4] - - pixelBuffer[byteOffset + strideWidth - 4]); - - exceedsThreshold = blueGradient + greenGradient + redGradient > threshold; + sourceBitmap.SetPixel(x, y, Color.Transparent); } + // ReSharper restore AccessToDisposedClosure } - } - - byteOffset -= 2; - - double blue; - double red; - double green; - double alpha; - if (exceedsThreshold) - { - blue = color.B; // 0; - green = color.G; // 0; - red = color.R; // 0; - alpha = 255; - } - else - { - // These would normally be used to transfer the correct value across. - // blue = pixelBuffer[byteOffset]; - // green = pixelBuffer[byteOffset + 1]; - // red = pixelBuffer[byteOffset + 2]; - blue = 255; - green = 255; - red = 255; - alpha = 0; - } - - resultBuffer[byteOffset] = blue.ToByte(); - resultBuffer[byteOffset + 1] = green.ToByte(); - resultBuffer[byteOffset + 2] = red.ToByte(); - resultBuffer[byteOffset + 3] = alpha.ToByte(); - } + }); } - Bitmap resultBitmap = new Bitmap(width, height); - - BitmapData resultData = resultBitmap.LockBits( - new Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), - ImageLockMode.WriteOnly, - PixelFormat.Format32bppArgb); - - Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length); + destination.Dispose(); + destination = invert; - resultBitmap.UnlockBits(resultData); - return resultBitmap; + return destination; } /// diff --git a/src/ImageProcessor/Imaging/PixelData.cs b/src/ImageProcessor/Imaging/PixelData.cs index 741b295cd..78d494edc 100644 --- a/src/ImageProcessor/Imaging/PixelData.cs +++ b/src/ImageProcessor/Imaging/PixelData.cs @@ -10,8 +10,6 @@ namespace ImageProcessor.Imaging { - using System.Collections.Generic; - /// /// Contains the component parts that make up a single pixel. ///