Browse Source

Performance improvement plus tidy up

Former-commit-id: dbc028215305b4eb7364fc8b04392c4c34988682
Former-commit-id: 8cf7f06b1efeba7fb9f9dfc3483f86617eab3350
pull/17/head
James South 11 years ago
parent
commit
fdab10752f
  1. 3
      src/ImageProcessor.Playground/ImageProcessor.Playground.csproj
  2. 10
      src/ImageProcessor.Playground/Program.cs
  3. 14
      src/ImageProcessor/Imaging/FastBitmap.cs
  4. 69
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs
  5. 6
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs
  6. 109
      src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs

3
src/ImageProcessor.Playground/ImageProcessor.Playground.csproj

@ -34,6 +34,9 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />

10
src/ImageProcessor.Playground/Program.cs

@ -50,12 +50,12 @@ namespace ImageProcessor.PlayGround
}
// Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask2.png"));
//FileInfo fileInfo = new FileInfo(Path.Combine(resolvedPath, "rgba.png"));
IEnumerable<FileInfo> files = GetFilesByExtensions(di, ".png");
FileInfo fileInfo = new FileInfo(Path.Combine(resolvedPath, "rgba.png"));
//IEnumerable<FileInfo> files = GetFilesByExtensions(di, ".png");
//IEnumerable<FileInfo> files = GetFilesByExtensions(di, ".gif", ".webp", ".bmp", ".jpg", ".png", ".tif");
foreach (FileInfo fileInfo in files)
{
//foreach (FileInfo fileInfo in files)
//{
byte[] photoBytes = File.ReadAllBytes(fileInfo.FullName);
Console.WriteLine("Processing: " + fileInfo.Name);
@ -109,7 +109,7 @@ namespace ImageProcessor.PlayGround
Console.WriteLine(@"Completed {0} in {1:s\.fff} secs with peak memory usage of {2}.", fileInfo.Name, stopwatch.Elapsed, Process.GetCurrentProcess().PeakWorkingSet64.ToString("#,#"));
//Console.WriteLine("Processed: " + fileInfo.Name + " in " + stopwatch.ElapsedMilliseconds + "ms");
}
//}
Console.ReadLine();
}

14
src/ImageProcessor/Imaging/FastBitmap.cs

@ -52,9 +52,9 @@ namespace ImageProcessor.Imaging
private BitmapData bitmapData;
/// <summary>
/// The pixel buffer for holding pixel data.
/// The position of the first pixel in the bitmap.
/// </summary>
private byte* pixelBuffer;
private byte* pixelBase;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
@ -117,7 +117,7 @@ namespace ImageProcessor.Imaging
/// </returns>
private Color32* this[int x, int y]
{
get { return (Color32*)(this.pixelBuffer + (y * this.bytesInARow) + (x * this.color32Size)); }
get { return (Color32*)(this.pixelBase + (y * this.bytesInARow) + (x * this.color32Size)); }
}
/// <summary>
@ -210,7 +210,7 @@ namespace ImageProcessor.Imaging
this.Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// Therefore, you should call GC.SuppressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
@ -290,8 +290,8 @@ namespace ImageProcessor.Imaging
// Lock the bitmap
this.bitmapData = this.bitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
// Copy the bitmap data across to the array for manipulation.
this.pixelBuffer = (byte*)this.bitmapData.Scan0.ToPointer();
// Set the value to the first scan line
this.pixelBase = (byte*)this.bitmapData.Scan0.ToPointer();
}
/// <summary>
@ -302,7 +302,7 @@ namespace ImageProcessor.Imaging
// Copy the RGB values back to the bitmap and unlock the bitmap.
this.bitmap.UnlockBits(this.bitmapData);
this.bitmapData = null;
this.pixelBuffer = null;
this.pixelBase = null;
}
}
}

69
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs

@ -14,7 +14,6 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
@ -43,7 +42,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
public Bitmap Image { get; private set; }
/// <summary>
/// Gets the pixel lines.
/// Gets the enumerable pixel array representing each row of pixels.
/// </summary>
/// <exception cref="QuantizationException">
/// Thrown if the given image is not a 32 bit per pixel image.
@ -64,39 +63,20 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
int width = this.Image.Width;
int height = this.Image.Height;
int[] buffer = new int[width];
Pixel[] pixels = new Pixel[width];
//using (FastBitmap bitmap = new FastBitmap(this.Image))
//{
// for (int y = 0; y < height; y++)
// {
// for (int x = 0; x < width; x++)
// {
// Color color = bitmap.GetPixel(x, y);
// pixels[x] = new Pixel(color.A, color.R, color.G, color.B);
// }
// yield return pixels;
// }
//}
for (int rowIndex = 0; rowIndex < height; rowIndex++)
using (FastBitmap bitmap = new FastBitmap(this.Image))
{
BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, rowIndex, width, rowIndex + 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
try
for (int y = 0; y < height; y++)
{
Marshal.Copy(data.Scan0, buffer, 0, width);
for (int pixelIndex = 0; pixelIndex < buffer.Length; pixelIndex++)
for (int x = 0; x < width; x++)
{
pixels[pixelIndex] = new Pixel(buffer[pixelIndex]);
Color color = bitmap.GetPixel(x, y);
pixels[x] = new Pixel(color.A, color.R, color.G, color.B);
}
}
finally
{
this.Image.UnlockBits(data);
}
yield return pixels;
yield return pixels;
}
}
}
}
@ -105,31 +85,34 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
/// Updates the pixel indexes.
/// </summary>
/// <param name="lineIndexes">
/// The line indexes.
/// The enumerable byte array representing each row of pixels.
/// </param>
public void UpdatePixelIndexes(IEnumerable<byte[]> lineIndexes)
{
int width = this.Image.Width;
int height = this.Image.Height;
int rowIndex = 0;
IEnumerator<byte[]> indexesIterator = lineIndexes.GetEnumerator();
for (int rowIndex = 0; rowIndex < height; rowIndex++)
BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
try
{
indexesIterator.MoveNext();
BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, rowIndex, width, rowIndex + 1), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
try
IntPtr pixelBase = data.Scan0;
int scanWidth = data.Stride;
foreach (byte[] scanLine in lineIndexes)
{
Marshal.Copy(indexesIterator.Current, 0, data.Scan0, width);
}
finally
{
this.Image.UnlockBits(data);
// TODO: Use unsafe code
Marshal.Copy(scanLine, 0, IntPtr.Add(pixelBase, scanWidth * rowIndex), width);
if (++rowIndex >= height)
{
break;
}
}
}
finally
{
this.Image.UnlockBits(data);
}
}
}
}

6
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs

@ -82,11 +82,11 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
int bestDistance = int.MaxValue;
byte bestMatch = 0;
foreach (var lookup in bucket)
foreach (LookupNode lookup in bucket)
{
var lookupPixel = lookup.Pixel;
Pixel lookupPixel = lookup.Pixel;
var deltaAlpha = pixel.Alpha - lookupPixel.Alpha;
int deltaAlpha = pixel.Alpha - lookupPixel.Alpha;
int distance = deltaAlpha * deltaAlpha;
var deltaRed = pixel.Red - lookupPixel.Red;

109
src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs

@ -1,16 +1,95 @@
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="WuQuantizer.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
{
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
/// <summary>
/// The wu quantizer.
/// </summary>
public class WuQuantizer : WuQuantizerBase, IWuQuantizer
{
/// <summary>
/// The get quantized image.
/// </summary>
/// <param name="image">
/// The image.
/// </param>
/// <param name="colorCount">
/// The color count.
/// </param>
/// <param name="lookups">
/// The lookups.
/// </param>
/// <param name="alphaThreshold">
/// The alpha threshold.
/// </param>
/// <returns>
/// The <see cref="Image"/>.
/// </returns>
internal override Image GetQuantizedImage(ImageBuffer image, int colorCount, Pixel[] lookups, int alphaThreshold)
{
Bitmap result = new Bitmap(image.Image.Width, image.Image.Height, PixelFormat.Format8bppIndexed);
result.SetResolution(image.Image.HorizontalResolution, image.Image.VerticalResolution);
ImageBuffer resultBuffer = new ImageBuffer(result);
PaletteColorHistory[] paletteHistogram = new PaletteColorHistory[colorCount + 1];
resultBuffer.UpdatePixelIndexes(IndexedPixels(image, lookups, alphaThreshold, paletteHistogram));
result.Palette = BuildPalette(result.Palette, paletteHistogram);
return result;
}
/// <summary>
/// The build palette.
/// </summary>
/// <param name="palette">
/// The palette.
/// </param>
/// <param name="paletteHistogram">
/// The palette histogram.
/// </param>
/// <returns>
/// The <see cref="ColorPalette"/>.
/// </returns>
private static ColorPalette BuildPalette(ColorPalette palette, PaletteColorHistory[] paletteHistogram)
{
for (int paletteColorIndex = 0; paletteColorIndex < paletteHistogram.Length; paletteColorIndex++)
{
palette.Entries[paletteColorIndex] = paletteHistogram[paletteColorIndex].ToNormalizedColor();
}
return palette;
}
/// <summary>
/// The indexed pixels.
/// </summary>
/// <param name="image">
/// The image.
/// </param>
/// <param name="lookups">
/// The lookups.
/// </param>
/// <param name="alphaThreshold">
/// The alpha threshold.
/// </param>
/// <param name="paletteHistogram">
/// The palette histogram.
/// </param>
/// <returns>
/// The <see cref="IEnumerable"/>.
/// </returns>
private static IEnumerable<byte[]> IndexedPixels(ImageBuffer image, Pixel[] lookups, int alphaThreshold, PaletteColorHistory[] paletteHistogram)
{
byte[] lineIndexes = new byte[image.Image.Width];
PaletteLookup lookup = new PaletteLookup(lookups);
foreach (var pixelLine in image.PixelLines)
foreach (Pixel[] pixelLine in image.PixelLines)
{
for (int pixelIndex = 0; pixelIndex < pixelLine.Length; pixelIndex++)
{
@ -28,25 +107,5 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer
yield return lineIndexes;
}
}
internal override Image GetQuantizedImage(ImageBuffer image, int colorCount, Pixel[] lookups, int alphaThreshold)
{
var result = new Bitmap(image.Image.Width, image.Image.Height, PixelFormat.Format8bppIndexed);
var resultBuffer = new ImageBuffer(result);
var paletteHistogram = new PaletteColorHistory[colorCount + 1];
resultBuffer.UpdatePixelIndexes(IndexedPixels(image, lookups, alphaThreshold, paletteHistogram));
result.Palette = BuildPalette(result.Palette, paletteHistogram);
return result;
}
private static ColorPalette BuildPalette(ColorPalette palette, PaletteColorHistory[] paletteHistogram)
{
for (int paletteColorIndex = 0; paletteColorIndex < paletteHistogram.Length; paletteColorIndex++)
{
palette.Entries[paletteColorIndex] = paletteHistogram[paletteColorIndex].ToNormalizedColor();
}
return palette;
}
}
}
}
Loading…
Cancel
Save