Browse Source

Faster pixelate and oilpaint

Former-commit-id: 14e4fa2fdef88ce05c76ee4a0446536310ea6eff
Former-commit-id: 09ccde9cc33ffeae0de019e3310507cabbb01009
af/merge-core
James South 12 years ago
parent
commit
0cffee7da2
  1. 4
      src/ImageProcessor.Playground/Program.cs
  2. 93
      src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs
  3. 1
      src/ImageProcessor/ImageProcessor.csproj
  4. 110
      src/ImageProcessor/Imaging/Filters/Artistic/OilPaintingFilter.cs
  5. 29
      src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs
  6. 55
      src/ImageProcessor/Processors/Pixelate.cs

4
src/ImageProcessor.Playground/Program.cs

@ -77,12 +77,12 @@ namespace ImageProcessor.PlayGround
//.ReplaceColor(Color.FromArgb(255, 1, 107, 165), Color.FromArgb(255, 1, 165, 13), 80) //.ReplaceColor(Color.FromArgb(255, 1, 107, 165), Color.FromArgb(255, 1, 165, 13), 80)
//.Resize(layer) //.Resize(layer)
//.DetectEdges(new KirschEdgeFilter()) //.DetectEdges(new KirschEdgeFilter())
.DetectEdges(new LaplacianOfGaussianEdgeFilter()) //.DetectEdges(new LaplacianOfGaussianEdgeFilter())
//.EntropyCrop() //.EntropyCrop()
//.Filter(MatrixFilters.Comic) //.Filter(MatrixFilters.Comic)
//.Filter(MatrixFilters.Comic) //.Filter(MatrixFilters.Comic)
//.Filter(MatrixFilters.HiSatch) //.Filter(MatrixFilters.HiSatch)
//.Pixelate(8) .Pixelate(8)
//.GaussianSharpen(10) //.GaussianSharpen(10)
.Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", fileInfo.Name))); .Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", fileInfo.Name)));

93
src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs

@ -0,0 +1,93 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="EnumerableExtensions.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates a series of time saving extension methods to the <see cref="T:System.Collections.IEnumerable" /> interface.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Common.Extensions
{
using System;
using System.Collections.Generic;
/// <summary>
/// Encapsulates a series of time saving extension methods to the <see cref="T:System.Collections.IEnumerable"/> interface.
/// </summary>
public static class EnumerableExtensions
{
/// <summary>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="toExclusive">
/// The end index, exclusive.
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns>
public static IEnumerable<int> SteppedRange(int fromInclusive, int toExclusive, int step)
{
// Borrowed from Enumerable.Range
long num = (fromInclusive + toExclusive) - 1L;
if ((toExclusive < 0) || (num > 0x7fffffffL))
{
throw new ArgumentOutOfRangeException("toExclusive");
}
return RangeIterator(fromInclusive, i => i < toExclusive, step);
}
/// <summary>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="toDelegate">
/// A method that has one parameter and returns a <see cref="System.Boolean"/> calculating the end index
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns>
public static IEnumerable<int> SteppedRange(int fromInclusive, Func<int, bool> toDelegate, int step)
{
return RangeIterator(fromInclusive, toDelegate, step);
}
/// <summary>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="toDelegate">
/// A method that has one parameter and returns a <see cref="System.Boolean"/> calculating the end index
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns>
private static IEnumerable<int> RangeIterator(int fromInclusive, Func<int, bool> toDelegate, int step)
{
int i = fromInclusive;
while (toDelegate(i))
{
yield return i;
i += step;
}
}
}
}

1
src/ImageProcessor/ImageProcessor.csproj

@ -125,6 +125,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Common\Extensions\AssemblyExtensions.cs" /> <Compile Include="Common\Extensions\AssemblyExtensions.cs" />
<Compile Include="Common\Extensions\EnumerableExtensions.cs" />
<Compile Include="Configuration\ImageProcessorBootstrapper.cs" /> <Compile Include="Configuration\ImageProcessorBootstrapper.cs" />
<Compile Include="Common\Exceptions\ImageProcessingException.cs" /> <Compile Include="Common\Exceptions\ImageProcessingException.cs" />
<Compile Include="Common\Extensions\DoubleExtensions.cs" /> <Compile Include="Common\Extensions\DoubleExtensions.cs" />

110
src/ImageProcessor/Imaging/Filters/Artistic/OilPaintingFilter.cs

@ -12,6 +12,7 @@ namespace ImageProcessor.Imaging.Filters.Artistic
{ {
using System; using System;
using System.Drawing; using System.Drawing;
using System.Threading.Tasks;
using ImageProcessor.Common.Extensions; using ImageProcessor.Common.Extensions;
@ -104,76 +105,81 @@ namespace ImageProcessor.Imaging.Filters.Artistic
{ {
using (FastBitmap destinationBitmap = new FastBitmap(destination)) using (FastBitmap destinationBitmap = new FastBitmap(destination))
{ {
for (int y = 0; y < height; y++) Parallel.For(
{ 0,
for (int x = 0; x < width; x++) height,
y =>
{ {
int maxIntensity = 0; for (int x = 0; x < width; x++)
int maxIndex = 0;
int[] intensityBin = new int[this.levels];
int[] blueBin = new int[this.levels];
int[] greenBin = new int[this.levels];
int[] redBin = new int[this.levels];
for (int i = 0; i <= radius; i++)
{ {
int ir = i - radius; int maxIntensity = 0;
int offsetY = y + ir; int maxIndex = 0;
int[] intensityBin = new int[this.levels];
// Skip the current row int[] blueBin = new int[this.levels];
if (offsetY < 0) int[] greenBin = new int[this.levels];
int[] redBin = new int[this.levels];
for (int i = 0; i <= radius; i++)
{ {
continue; int ir = i - radius;
} int offsetY = y + ir;
// Outwith the current bounds so break.
if (offsetY >= height)
{
break;
}
for (int fx = 0; fx <= radius; fx++) // Skip the current row
{ if (offsetY < 0)
int jr = fx - 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 = sourceBitmap.GetPixel(offsetX, offsetY); break;
}
byte sourceBlue = color.B;
byte sourceGreen = color.G;
byte sourceRed = color.R;
int currentIntensity = (int)Math.Round(((sourceBlue + sourceGreen + sourceRed) / 3.0 * (this.levels - 1)) / 255.0); for (int fx = 0; fx <= radius; fx++)
{
int jr = fx - radius;
int offsetX = x + jr;
intensityBin[currentIntensity] += 1; // Skip the column
blueBin[currentIntensity] += sourceBlue; if (offsetX < 0)
greenBin[currentIntensity] += sourceGreen; {
redBin[currentIntensity] += sourceRed; continue;
}
if (intensityBin[currentIntensity] > maxIntensity) if (offsetX < width)
{ {
maxIntensity = intensityBin[currentIntensity]; // ReSharper disable once AccessToDisposedClosure
maxIndex = currentIntensity; Color color = sourceBitmap.GetPixel(offsetX, offsetY);
byte sourceBlue = color.B;
byte sourceGreen = color.G;
byte sourceRed = color.R;
int currentIntensity = (int)Math.Round(((sourceBlue + sourceGreen + sourceRed) / 3.0 * (this.levels - 1)) / 255.0);
intensityBin[currentIntensity] += 1;
blueBin[currentIntensity] += sourceBlue;
greenBin[currentIntensity] += sourceGreen;
redBin[currentIntensity] += sourceRed;
if (intensityBin[currentIntensity] > maxIntensity)
{
maxIntensity = intensityBin[currentIntensity];
maxIndex = currentIntensity;
}
} }
} }
} }
}
byte blue = Math.Abs(blueBin[maxIndex] / maxIntensity).ToByte(); byte blue = Math.Abs(blueBin[maxIndex] / maxIntensity).ToByte();
byte green = Math.Abs(greenBin[maxIndex] / maxIntensity).ToByte(); byte green = Math.Abs(greenBin[maxIndex] / maxIntensity).ToByte();
byte red = Math.Abs(redBin[maxIndex] / maxIntensity).ToByte(); byte red = Math.Abs(redBin[maxIndex] / maxIntensity).ToByte();
destinationBitmap.SetPixel(x, y, Color.FromArgb(red, green, blue)); // ReSharper disable once AccessToDisposedClosure
} destinationBitmap.SetPixel(x, y, Color.FromArgb(red, green, blue));
} }
});
} }
} }

29
src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs

@ -15,6 +15,7 @@ namespace ImageProcessor.Imaging.Filters.Photo
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks;
using ImageProcessor.Common.Extensions; using ImageProcessor.Common.Extensions;
using ImageProcessor.Imaging.Filters.Artistic; using ImageProcessor.Imaging.Filters.Artistic;
@ -61,7 +62,7 @@ namespace ImageProcessor.Imaging.Filters.Photo
// Apply a oil painting filter to the image. // Apply a oil painting filter to the image.
highBitmap = new OilPaintingFilter(3, 5).ApplyFilter((Bitmap)image); highBitmap = new OilPaintingFilter(3, 5).ApplyFilter((Bitmap)image);
// Draw the edges. // Draw the edges.
edgeBitmap = DrawEdges((Bitmap)image, 120); edgeBitmap = DrawEdges((Bitmap)image, 120);
@ -383,19 +384,25 @@ namespace ImageProcessor.Imaging.Filters.Photo
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
for (int y = 0; y < height; y++) Parallel.For(
{ 0,
for (int x = 0; x < width; x++) height,
y =>
{ {
Color sourceColor = sourceBitmap.GetPixel(x, y); for (int x = 0; x < width; x++)
Color destinationColor = destinationBitmap.GetPixel(x, y);
if (destinationColor.A != 0)
{ {
destinationBitmap.SetPixel(x, y, Color.FromArgb(sourceColor.B, destinationColor.R, destinationColor.G, destinationColor.B)); // ReSharper disable AccessToDisposedClosure
Color sourceColor = sourceBitmap.GetPixel(x, y);
Color destinationColor = destinationBitmap.GetPixel(x, y);
if (destinationColor.A != 0)
{
destinationBitmap.SetPixel(x, y, Color.FromArgb(sourceColor.B, destinationColor.R, destinationColor.G, destinationColor.B));
}
// ReSharper restore AccessToDisposedClosure
} }
} });
}
} }
} }
} }

55
src/ImageProcessor/Processors/Pixelate.cs

@ -13,8 +13,10 @@ namespace ImageProcessor.Processors
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Threading.Tasks;
using ImageProcessor.Common.Exceptions; using ImageProcessor.Common.Exceptions;
using ImageProcessor.Common.Extensions;
using ImageProcessor.Imaging; using ImageProcessor.Imaging;
/// <summary> /// <summary>
@ -82,37 +84,44 @@ namespace ImageProcessor.Processors
using (FastBitmap fastBitmap = new FastBitmap(newImage)) using (FastBitmap fastBitmap = new FastBitmap(newImage))
{ {
for (int j = y; j < y + height && j < maxHeight; j += size) // Get the range of on the y-plane to choose from.
{ IEnumerable<int> range = EnumerableExtensions.SteppedRange(y, i => i < y + height && i < maxHeight, size);
for (int i = x; i < x + width && i < maxWidth; i += size)
{
int offsetX = offset;
int offsetY = offset;
// Make sure that the offset is within the boundary of the image. Parallel.ForEach(
while (j + offsetY >= maxHeight) range,
j =>
{
for (int i = x; i < x + width && i < maxWidth; i += size)
{ {
offsetY--; int offsetX = offset;
} int offsetY = offset;
while (i + offsetX >= maxWidth) // Make sure that the offset is within the boundary of the image.
{ while (j + offsetY >= maxHeight)
offsetX--; {
} offsetY--;
}
// Get the pixel color in the centre of the soon to be pixelated area. while (i + offsetX >= maxWidth)
Color pixel = fastBitmap.GetPixel(i + offsetX, j + offsetY); {
offsetX--;
}
// For each pixel in the pixelate size, set it to the centre color. // Get the pixel color in the centre of the soon to be pixelated area.
for (int l = j; l < j + size && l < maxHeight; l++) // ReSharper disable AccessToDisposedClosure
{ Color pixel = fastBitmap.GetPixel(i + offsetX, j + offsetY);
for (int k = i; k < i + size && k < maxWidth; k++)
// For each pixel in the pixelate size, set it to the centre color.
for (int l = j; l < j + size && l < maxHeight; l++)
{ {
fastBitmap.SetPixel(k, l, pixel); for (int k = i; k < i + size && k < maxWidth; k++)
{
fastBitmap.SetPixel(k, l, pixel);
}
} }
// ReSharper restore AccessToDisposedClosure
} }
} });
}
} }
image.Dispose(); image.Dispose();

Loading…
Cancel
Save