Browse Source

More speed improvements

Former-commit-id: b2e663bca1492bf9e28a1da8051731646a19feef
Former-commit-id: ec08860c20b0eb2e8423d834b3548fd939d90a76
af/merge-core
James South 12 years ago
parent
commit
0cf4ca8b92
  1. 3
      src/ImageProcessor.Playground/images/input/circle3.png
  2. 158
      src/ImageProcessor/Imaging/Convolution.cs
  3. 6
      src/ImageProcessor/Imaging/FastBitmap.cs
  4. 25
      src/ImageProcessor/Imaging/Filters/Binarization/BinaryThreshold.cs
  5. 111
      src/ImageProcessor/Imaging/Filters/EdgeDetection/ConvolutionFilter.cs
  6. 6
      src/ImageProcessor/Processors/EntropyCrop.cs

3
src/ImageProcessor.Playground/images/input/circle3.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f6f6f0e7876609f12e7549a4fb605102e62027661a1fe93bbffad96ec5e5731
size 5322

158
src/ImageProcessor/Imaging/Convolution.cs

@ -12,6 +12,7 @@ namespace ImageProcessor.Imaging
{ {
using System; using System;
using System.Drawing; using System.Drawing;
using System.Threading.Tasks;
using ImageProcessor.Common.Extensions; using ImageProcessor.Common.Extensions;
@ -274,101 +275,106 @@ namespace ImageProcessor.Imaging
int threshold = this.Threshold; int threshold = this.Threshold;
// For each line // For each line
for (int y = 0; y < height; y++) Parallel.For(
{ 0,
// For each pixel height,
for (int x = 0; x < width; x++) y =>
{ {
// The number of kernel elements taken into account // For each pixel
int processedKernelSize; for (int x = 0; x < width; x++)
// 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++)
{ {
int ir = i - radius; // The number of kernel elements taken into account
int offsetY = y + ir; int processedKernelSize;
// Skip the current row // Colour sums
if (offsetY < 0) 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. // Skip the current row
if (offsetY >= height) if (offsetY < 0)
{
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)
{ {
continue; continue;
} }
if (offsetX < width) // Outwith the current bounds so break.
if (offsetY >= height)
{ {
Color color = sourceFastBitmap.GetPixel(offsetX, offsetY); break;
double k = kernel[i, j]; }
divider += k;
red += k * color.R;
green += k * color.G;
blue += k * color.B;
alpha += k * color.A;
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 // Check to see if all kernel elements were processed
if (processedKernelSize == kernelSize) 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)
{ {
// Apply the set divider. // All kernel elements are processed; we are not on the edge.
divider = this.Divider; 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 // Check and apply the divider
if ((long)divider != 0) if ((long)divider != 0)
{ {
red /= divider; red /= divider;
green /= divider; green /= divider;
blue /= divider; blue /= divider;
alpha /= divider; alpha /= divider;
} }
// Add any applicable threshold. // Add any applicable threshold.
red += threshold; red += threshold;
green += threshold; green += threshold;
blue += threshold; blue += threshold;
alpha += 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()));
} }
});
} }
} }

6
src/ImageProcessor/Imaging/FastBitmap.cs

@ -156,6 +156,7 @@ namespace ImageProcessor.Imaging
/// <returns>The <see cref="System.Drawing.Color"/> at the given pixel.</returns> /// <returns>The <see cref="System.Drawing.Color"/> at the given pixel.</returns>
public Color GetPixel(int x, int y) public Color GetPixel(int x, int y)
{ {
#if DEBUG
if ((x < 0) || (x >= this.width)) if ((x < 0) || (x >= this.width))
{ {
throw new ArgumentOutOfRangeException("x", "Value cannot be less than zero or greater than the bitmap 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."); throw new ArgumentOutOfRangeException("y", "Value cannot be less than zero or greater than the bitmap height.");
} }
#endif
PixelData* data = this[x, y]; PixelData* data = this[x, y];
return Color.FromArgb(data->A, data->R, data->G, data->B); return Color.FromArgb(data->A, data->R, data->G, data->B);
} }
@ -181,6 +182,7 @@ namespace ImageProcessor.Imaging
/// </param> /// </param>
public void SetPixel(int x, int y, Color color) public void SetPixel(int x, int y, Color color)
{ {
#if DEBUG
if ((x < 0) || (x >= this.width)) if ((x < 0) || (x >= this.width))
{ {
throw new ArgumentOutOfRangeException("x", "Value cannot be less than zero or greater than the bitmap 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."); throw new ArgumentOutOfRangeException("y", "Value cannot be less than zero or greater than the bitmap height.");
} }
#endif
PixelData* data = this[x, y]; PixelData* data = this[x, y];
data->R = color.R; data->R = color.R;
data->G = color.G; data->G = color.G;

25
src/ImageProcessor/Imaging/Filters/Binarization/BinaryThreshold.cs

@ -11,6 +11,10 @@
namespace ImageProcessor.Imaging.Filters.Binarization namespace ImageProcessor.Imaging.Filters.Binarization
{ {
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging;
using System.Threading.Tasks;
using ImageProcessor.Imaging.Filters.Photo;
/// <summary> /// <summary>
/// Performs binary threshold filtering against a given greyscale image. /// Performs binary threshold filtering against a given greyscale image.
@ -20,7 +24,7 @@ namespace ImageProcessor.Imaging.Filters.Binarization
/// <summary> /// <summary>
/// The threshold value. /// The threshold value.
/// </summary> /// </summary>
private byte threshold = 128; private byte threshold = 10;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BinaryThreshold"/> class. /// Initializes a new instance of the <see cref="BinaryThreshold"/> class.
@ -61,14 +65,19 @@ namespace ImageProcessor.Imaging.Filters.Binarization
using (FastBitmap sourceBitmap = new FastBitmap(source)) using (FastBitmap sourceBitmap = new FastBitmap(source))
{ {
for (int y = 0; y < height; y++) Parallel.For(
{ 0,
for (int x = 0; x < width; x++) height,
y =>
{ {
Color color = sourceBitmap.GetPixel(x, y); for (int x = 0; x < width; x++)
sourceBitmap.SetPixel(x, y, color.B >= this.threshold ? Color.White : Color.Black); {
} // 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; return source;

111
src/ImageProcessor/Imaging/Filters/EdgeDetection/ConvolutionFilter.cs

@ -49,7 +49,7 @@ namespace ImageProcessor.Imaging.Filters.EdgeDetection
} }
/// <summary> /// <summary>
/// Processes the given bitmap to apply the current instances <see cref="IEdgeFilter"/>. /// Processes the given bitmap to apply the current instance of <see cref="IEdgeFilter"/>.
/// </summary> /// </summary>
/// <param name="source">The image to process.</param> /// <param name="source">The image to process.</param>
/// <returns>A processed bitmap.</returns> /// <returns>A processed bitmap.</returns>
@ -184,7 +184,7 @@ namespace ImageProcessor.Imaging.Filters.EdgeDetection
} }
/// <summary> /// <summary>
/// Processes the given bitmap to apply the current instances <see cref="IEdgeFilter"/>. /// Processes the given bitmap to apply the current instance of <see cref="I2DEdgeFilter"/>.
/// </summary> /// </summary>
/// <param name="source">The image to process.</param> /// <param name="source">The image to process.</param>
/// <returns>A processed bitmap.</returns> /// <returns>A processed bitmap.</returns>
@ -229,74 +229,79 @@ namespace ImageProcessor.Imaging.Filters.EdgeDetection
using (FastBitmap destinationBitmap = new FastBitmap(destination)) using (FastBitmap destinationBitmap = new FastBitmap(destination))
{ {
// Loop through the pixels. // Loop through the pixels.
for (int y = 0; y < height; y++) Parallel.For(
{ 0,
for (int x = 0; x < width; x++) height,
y =>
{ {
double rX = 0; for (int x = 0; x < width; x++)
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++)
{ {
int fyr = fy - radius; double rX = 0;
int offsetY = y + fyr; double rY = 0;
double gX = 0;
// Skip the current row double gY = 0;
if (offsetY < 0) double bX = 0;
{ double bY = 0;
continue;
}
// Outwith the current bounds so break.
if (offsetY >= height)
{
break;
}
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 fyr = fy - radius;
int offsetX = x + fxr; int offsetY = y + fyr;
// Skip the column // Skip the current row
if (offsetX < 0) if (offsetY < 0)
{ {
continue; 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); int fxr = fx - radius;
double r = currentColor.R; int offsetX = x + fxr;
double g = currentColor.G;
double b = currentColor.B;
rX += horizontalFilter[fy, fx] * r; // Skip the column
rY += verticalFilter[fy, fx] * r; if (offsetX < 0)
{
continue;
}
gX += horizontalFilter[fy, fx] * g; if (offsetX < width)
gY += verticalFilter[fy, fx] * g; {
// 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; rX += horizontalFilter[fy, fx] * r;
bY += verticalFilter[fy, fx] * b; 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. // Apply the equation and sanitize.
byte red = Math.Sqrt((rX * rX) + (rY * rY)).ToByte(); byte red = Math.Sqrt((rX * rX) + (rY * rY)).ToByte();
byte green = Math.Sqrt((gX * gX) + (gY * gY)).ToByte(); byte green = Math.Sqrt((gX * gX) + (gY * gY)).ToByte();
byte blue = Math.Sqrt((bX * bX) + (bY * bY)).ToByte(); byte blue = Math.Sqrt((bX * bX) + (bY * bY)).ToByte();
Color newColor = Color.FromArgb(red, green, blue); Color newColor = Color.FromArgb(red, green, blue);
destinationBitmap.SetPixel(x, y, newColor); // ReSharper disable once AccessToDisposedClosure
} destinationBitmap.SetPixel(x, y, newColor);
} }
});
} }
} }
} }

6
src/ImageProcessor/Processors/EntropyCrop.cs

@ -11,6 +11,7 @@ namespace ImageProcessor.Processors
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO;
using ImageProcessor.Common.Exceptions; using ImageProcessor.Common.Exceptions;
using ImageProcessor.Imaging; using ImageProcessor.Imaging;
@ -59,9 +60,10 @@ namespace ImageProcessor.Processors
try 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); grey = new BinaryThreshold(threshold).ProcessFilter(grey);
Rectangle rectangle = this.FindBoundingBox(grey, 0); Rectangle rectangle = this.FindBoundingBox(grey, 0);
newImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb); newImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppPArgb);

Loading…
Cancel
Save