diff --git a/src/ImageProcessor.Playground/images/input/circle3.png b/src/ImageProcessor.Playground/images/input/circle3.png new file mode 100644 index 000000000..f83291fe9 --- /dev/null +++ b/src/ImageProcessor.Playground/images/input/circle3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f6f6f0e7876609f12e7549a4fb605102e62027661a1fe93bbffad96ec5e5731 +size 5322 diff --git a/src/ImageProcessor/Imaging/Convolution.cs b/src/ImageProcessor/Imaging/Convolution.cs index 8e9495637..36e57df41 100644 --- a/src/ImageProcessor/Imaging/Convolution.cs +++ b/src/ImageProcessor/Imaging/Convolution.cs @@ -12,6 +12,7 @@ namespace ImageProcessor.Imaging { using System; using System.Drawing; + using System.Threading.Tasks; using ImageProcessor.Common.Extensions; @@ -274,101 +275,106 @@ namespace ImageProcessor.Imaging int threshold = this.Threshold; // For each line - for (int y = 0; y < height; y++) - { - // For each pixel - for (int x = 0; x < width; x++) + Parallel.For( + 0, + height, + y => { - // The number of kernel elements taken into account - int processedKernelSize; - - // Colour sums - double blue; - double alpha; - double divider; - double green; - double red = green = blue = alpha = divider = processedKernelSize = 0; - - // For each kernel row - for (int i = 0; i < kernelLength; i++) + // For each pixel + for (int x = 0; x < width; x++) { - int ir = i - radius; - int offsetY = y + ir; - - // Skip the current row - if (offsetY < 0) + // The number of kernel elements taken into account + int processedKernelSize; + + // Colour sums + double blue; + double alpha; + double divider; + double green; + double red = green = blue = alpha = divider = processedKernelSize = 0; + + // For each kernel row + for (int i = 0; i < kernelLength; i++) { - continue; - } + int ir = i - radius; + int offsetY = y + ir; - // Outwith the current bounds so break. - if (offsetY >= height) - { - break; - } - - // For each kernel column - for (int j = 0; j < kernelLength; j++) - { - int jr = j - radius; - int offsetX = x + jr; - - // Skip the column - if (offsetX < 0) + // Skip the current row + if (offsetY < 0) { continue; } - if (offsetX < width) + // Outwith the current bounds so break. + if (offsetY >= height) { - Color color = sourceFastBitmap.GetPixel(offsetX, offsetY); - double k = kernel[i, j]; - divider += k; - - red += k * color.R; - green += k * color.G; - blue += k * color.B; - alpha += k * color.A; + break; + } - processedKernelSize++; + // For each kernel column + for (int j = 0; j < kernelLength; j++) + { + int jr = j - radius; + int offsetX = x + jr; + + // Skip the column + if (offsetX < 0) + { + continue; + } + + if (offsetX < width) + { + // ReSharper disable once AccessToDisposedClosure + Color color = sourceFastBitmap.GetPixel(offsetX, offsetY); + double k = kernel[i, j]; + divider += k; + + red += k * color.R; + green += k * color.G; + blue += k * color.B; + alpha += k * color.A; + + processedKernelSize++; + } } } - } - // Check to see if all kernel elements were processed - if (processedKernelSize == kernelSize) - { - // All kernel elements are processed; we are not on the edge. - divider = this.Divider; - } - else - { - // We are on an edge; do we need to use dynamic divider or not? - if (!this.UseDynamicDividerForEdges) + // Check to see if all kernel elements were processed + if (processedKernelSize == kernelSize) { - // Apply the set divider. + // All kernel elements are processed; we are not on the edge. divider = this.Divider; } - } + else + { + // We are on an edge; do we need to use dynamic divider or not? + if (!this.UseDynamicDividerForEdges) + { + // Apply the set divider. + divider = this.Divider; + } + } - // Check and apply the divider - if ((long)divider != 0) - { - red /= divider; - green /= divider; - blue /= divider; - alpha /= divider; - } + // Check and apply the divider + if ((long)divider != 0) + { + red /= divider; + green /= divider; + blue /= divider; + alpha /= divider; + } - // Add any applicable threshold. - red += threshold; - green += threshold; - blue += threshold; - alpha += threshold; + // Add any applicable threshold. + red += threshold; + green += threshold; + blue += threshold; + alpha += threshold; - destinationFastBitmap.SetPixel(x, y, Color.FromArgb(alpha.ToByte(), red.ToByte(), green.ToByte(), blue.ToByte())); - } - } + // ReSharper disable once AccessToDisposedClosure + destinationFastBitmap.SetPixel(x, y, Color.FromArgb(alpha.ToByte(), red.ToByte(), green.ToByte(), blue.ToByte())); + } + }); } } diff --git a/src/ImageProcessor/Imaging/FastBitmap.cs b/src/ImageProcessor/Imaging/FastBitmap.cs index aea18d326..8b5fd8715 100644 --- a/src/ImageProcessor/Imaging/FastBitmap.cs +++ b/src/ImageProcessor/Imaging/FastBitmap.cs @@ -156,6 +156,7 @@ namespace ImageProcessor.Imaging /// The at the given pixel. public Color GetPixel(int x, int y) { +#if DEBUG if ((x < 0) || (x >= this.width)) { throw new ArgumentOutOfRangeException("x", "Value cannot be less than zero or greater than the bitmap width."); @@ -165,7 +166,7 @@ namespace ImageProcessor.Imaging { throw new ArgumentOutOfRangeException("y", "Value cannot be less than zero or greater than the bitmap height."); } - +#endif PixelData* data = this[x, y]; return Color.FromArgb(data->A, data->R, data->G, data->B); } @@ -181,6 +182,7 @@ namespace ImageProcessor.Imaging /// public void SetPixel(int x, int y, Color color) { +#if DEBUG if ((x < 0) || (x >= this.width)) { throw new ArgumentOutOfRangeException("x", "Value cannot be less than zero or greater than the bitmap width."); @@ -190,7 +192,7 @@ namespace ImageProcessor.Imaging { throw new ArgumentOutOfRangeException("y", "Value cannot be less than zero or greater than the bitmap height."); } - +#endif PixelData* data = this[x, y]; data->R = color.R; data->G = color.G; diff --git a/src/ImageProcessor/Imaging/Filters/Binarization/BinaryThreshold.cs b/src/ImageProcessor/Imaging/Filters/Binarization/BinaryThreshold.cs index 96478497f..e8ffa61e7 100644 --- a/src/ImageProcessor/Imaging/Filters/Binarization/BinaryThreshold.cs +++ b/src/ImageProcessor/Imaging/Filters/Binarization/BinaryThreshold.cs @@ -11,6 +11,10 @@ namespace ImageProcessor.Imaging.Filters.Binarization { using System.Drawing; + using System.Drawing.Imaging; + using System.Threading.Tasks; + + using ImageProcessor.Imaging.Filters.Photo; /// /// Performs binary threshold filtering against a given greyscale image. @@ -20,7 +24,7 @@ namespace ImageProcessor.Imaging.Filters.Binarization /// /// The threshold value. /// - private byte threshold = 128; + private byte threshold = 10; /// /// Initializes a new instance of the class. @@ -61,14 +65,19 @@ namespace ImageProcessor.Imaging.Filters.Binarization using (FastBitmap sourceBitmap = new FastBitmap(source)) { - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) + Parallel.For( + 0, + height, + y => { - Color color = sourceBitmap.GetPixel(x, y); - sourceBitmap.SetPixel(x, y, color.B >= this.threshold ? Color.White : Color.Black); - } - } + for (int x = 0; x < width; x++) + { + // ReSharper disable AccessToDisposedClosure + Color color = sourceBitmap.GetPixel(x, y); + sourceBitmap.SetPixel(x, y, color.B >= this.threshold ? Color.White : Color.Black); + // ReSharper restore AccessToDisposedClosure + } + }); } return source; diff --git a/src/ImageProcessor/Imaging/Filters/EdgeDetection/ConvolutionFilter.cs b/src/ImageProcessor/Imaging/Filters/EdgeDetection/ConvolutionFilter.cs index a5ec3c24a..450262c55 100644 --- a/src/ImageProcessor/Imaging/Filters/EdgeDetection/ConvolutionFilter.cs +++ b/src/ImageProcessor/Imaging/Filters/EdgeDetection/ConvolutionFilter.cs @@ -49,7 +49,7 @@ namespace ImageProcessor.Imaging.Filters.EdgeDetection } /// - /// Processes the given bitmap to apply the current instances . + /// Processes the given bitmap to apply the current instance of . /// /// The image to process. /// A processed bitmap. @@ -184,7 +184,7 @@ namespace ImageProcessor.Imaging.Filters.EdgeDetection } /// - /// Processes the given bitmap to apply the current instances . + /// Processes the given bitmap to apply the current instance of . /// /// The image to process. /// A processed bitmap. @@ -229,74 +229,79 @@ namespace ImageProcessor.Imaging.Filters.EdgeDetection using (FastBitmap destinationBitmap = new FastBitmap(destination)) { // Loop through the pixels. - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) + Parallel.For( + 0, + height, + y => { - double rX = 0; - double rY = 0; - double gX = 0; - double gY = 0; - double bX = 0; - double bY = 0; - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelLength; fy++) + for (int x = 0; x < width; x++) { - int fyr = fy - radius; - int offsetY = y + fyr; - - // Skip the current row - if (offsetY < 0) - { - continue; - } - - // Outwith the current bounds so break. - if (offsetY >= height) - { - break; - } + double rX = 0; + double rY = 0; + double gX = 0; + double gY = 0; + double bX = 0; + double bY = 0; - for (int fx = 0; fx < kernelLength; fx++) + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelLength; fy++) { - int fxr = fx - radius; - int offsetX = x + fxr; + int fyr = fy - radius; + int offsetY = y + fyr; - // Skip the column - if (offsetX < 0) + // Skip the current row + if (offsetY < 0) { continue; } - if (offsetX < width) + // Outwith the current bounds so break. + if (offsetY >= height) + { + break; + } + + for (int fx = 0; fx < kernelLength; fx++) { - Color currentColor = sourceBitmap.GetPixel(offsetX, offsetY); - double r = currentColor.R; - double g = currentColor.G; - double b = currentColor.B; + int fxr = fx - radius; + int offsetX = x + fxr; - rX += horizontalFilter[fy, fx] * r; - rY += verticalFilter[fy, fx] * r; + // Skip the column + if (offsetX < 0) + { + continue; + } - gX += horizontalFilter[fy, fx] * g; - gY += verticalFilter[fy, fx] * g; + if (offsetX < width) + { + // ReSharper disable once AccessToDisposedClosure + Color currentColor = sourceBitmap.GetPixel(offsetX, offsetY); + double r = currentColor.R; + double g = currentColor.G; + double b = currentColor.B; - bX += horizontalFilter[fy, fx] * b; - bY += verticalFilter[fy, fx] * b; + rX += horizontalFilter[fy, fx] * r; + rY += verticalFilter[fy, fx] * r; + + gX += horizontalFilter[fy, fx] * g; + gY += verticalFilter[fy, fx] * g; + + bX += horizontalFilter[fy, fx] * b; + bY += verticalFilter[fy, fx] * b; + } } } - } - // Apply the equation and sanitize. - byte red = Math.Sqrt((rX * rX) + (rY * rY)).ToByte(); - byte green = Math.Sqrt((gX * gX) + (gY * gY)).ToByte(); - byte blue = Math.Sqrt((bX * bX) + (bY * bY)).ToByte(); + // Apply the equation and sanitize. + byte red = Math.Sqrt((rX * rX) + (rY * rY)).ToByte(); + byte green = Math.Sqrt((gX * gX) + (gY * gY)).ToByte(); + byte blue = Math.Sqrt((bX * bX) + (bY * bY)).ToByte(); - Color newColor = Color.FromArgb(red, green, blue); - destinationBitmap.SetPixel(x, y, newColor); - } - } + Color newColor = Color.FromArgb(red, green, blue); + // ReSharper disable once AccessToDisposedClosure + destinationBitmap.SetPixel(x, y, newColor); + } + }); } } } diff --git a/src/ImageProcessor/Processors/EntropyCrop.cs b/src/ImageProcessor/Processors/EntropyCrop.cs index f8e6a3199..c14709e48 100644 --- a/src/ImageProcessor/Processors/EntropyCrop.cs +++ b/src/ImageProcessor/Processors/EntropyCrop.cs @@ -11,6 +11,7 @@ namespace ImageProcessor.Processors using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; + using System.IO; using ImageProcessor.Common.Exceptions; using ImageProcessor.Imaging; @@ -59,9 +60,10 @@ namespace ImageProcessor.Processors try { - grey = new ConvolutionFilter(new SobelEdgeFilter(), true).ProcessFilter((Bitmap)image); + // Detect the edges then strip out middle shades. + grey = new ConvolutionFilter(new SobelEdgeFilter(), true).Process2DFilter((Bitmap)image); grey = new BinaryThreshold(threshold).ProcessFilter(grey); - + Rectangle rectangle = this.FindBoundingBox(grey, 0); newImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb);