Browse Source

Implement the interface for the basic quantizer. The resulting image is not correct with this commit but we have a starting point to troubleshoot the quantizer.

Former-commit-id: 9a02fda23c4ee617948798ca10df40f415469291
Former-commit-id: 2a73febf146145751efde9460f3de24086422727
Former-commit-id: 98a3c0689c9d9a7cb6ca0badf9ef4ebec4e36cac
pull/17/head
Yufei Huang 11 years ago
parent
commit
cea13f6866
  1. 4
      src/ImageProcessor/Formats/Gif/Quantizer/IQuantizer.cs
  2. 80
      src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs
  3. 40
      src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs
  4. 1
      src/ImageProcessor/ImageProcessor.csproj
  5. 20
      tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs

4
src/ImageProcessor/Formats/Gif/Quantizer/IQuantizer.cs

@ -20,8 +20,8 @@ namespace ImageProcessor.Formats
/// </summary>
/// <param name="imageBase">The image to quantize.</param>
/// <returns>
/// A <see cref="T:byte[]"/> representing a quantized version of the image pixels.
/// A <see cref="T:QuantizedImage"/> representing a quantized version of the image pixels.
/// </returns>
byte[] Quantize(ImageBase imageBase);
QuantizedImage Quantize(ImageBase imageBase);
}
}

80
src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs

@ -0,0 +1,80 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="QuantizedImage.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides methods for allowing quantization of images pixels.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
using System;
/// <summary>
/// Represents a quantized image where the pixels indexed by a color palette.
/// </summary>
public class QuantizedImage
{
/// <summary>
/// Gets the width of this <see cref="T:QuantizedImage"/>.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the height of this <see cref="T:QuantizedImage"/>.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets the color palette of this <see cref="T:QuantizedImage"/>.
/// </summary>
public Bgra[] Palette { get; }
/// <summary>
/// Gets the pixels of this <see cref="T:QuantizedImage"/>.
/// </summary>
public byte[] Pixels { get; }
/// <summary>
/// Initializes a new instance of <see cref="T:QuantizedImage"/>.
/// </summary>
public QuantizedImage(int width, int height, Bgra[] palette, byte[] pixels)
{
if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width));
if (height <= 0) throw new ArgumentOutOfRangeException(nameof(height));
if (palette == null) throw new ArgumentNullException(nameof(palette));
if (pixels == null) throw new ArgumentNullException(nameof(pixels));
if (pixels.Length != width * height) throw new ArgumentException("Pixel array size must be width * height", nameof(pixels));
this.Width = width;
this.Height = height;
this.Palette = palette;
this.Pixels = pixels;
}
/// <summary>
/// Converts this quantized image to a normal image.
/// </summary>
/// <returns></returns>
public Image ToImage()
{
Image image = new Image();
int pixelCount = Pixels.Length;
byte[] bgraPixels = new byte[pixelCount * 4];
for (int i = 0; i < pixelCount; i++)
{
Bgra color = Palette[Pixels[i]];
bgraPixels[i + 0] = color.B;
bgraPixels[i + 1] = color.G;
bgraPixels[i + 2] = color.R;
bgraPixels[i + 3] = color.A;
}
image.SetPixels(Width, Height, bgraPixels);
return image;
}
}
}

40
src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs

@ -11,6 +11,7 @@
namespace ImageProcessor.Formats
{
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Encapsulates methods to calculate the color palette of an image.
@ -45,22 +46,25 @@ namespace ImageProcessor.Formats
/// <returns>
/// A <see cref="T:byte[]"/> representing a quantized version of the image pixels.
/// </returns>
public byte[] Quantize(ImageBase imageBase)
public QuantizedImage Quantize(ImageBase imageBase)
{
// Get the size of the source image
int height = imageBase.Height;
int width = imageBase.Width;
ImageBase copy = new ImageFrame((ImageFrame)imageBase);
// Call the FirstPass function if not a single pass algorithm.
// For something like an Octree quantizer, this will run through
// all image pixels, build a data structure, and create a palette.
if (!this.singlePass)
if (!singlePass)
{
this.FirstPass(copy, width, height);
FirstPass(imageBase, width, height);
}
throw new System.NotImplementedException();
byte[] quantizedPixels = new byte[width * height];
SecondPass(imageBase, quantizedPixels, width, height);
return new QuantizedImage(width, height, GetPalette().ToArray(), quantizedPixels);
}
/// <summary>
@ -92,18 +96,34 @@ namespace ImageProcessor.Formats
/// <param name="height">The height in pixels of the image</param>
protected virtual void SecondPass(ImageBase source, byte[] output, int width, int height)
{
Bgra sourcePixel = source[0, 0];
int i = 0;
// And convert the first pixel, so that I have values going into the loop
byte pixelValue = this.QuantizePixel(sourcePixel);
// Convert the first pixel, so that I have values going into the loop
Bgra previousPixel = source[0, 0];
byte pixelValue = QuantizePixel(previousPixel);
output[0] = pixelValue;
for (int y = 0; y < height; y++)
{
// TODO: Translate this from the old method.
}
for (int x = 0; x < width; x++)
{
Bgra sourcePixel = source[x, y];
// Check if this is the same as the last pixel. If so use that value
// rather than calculating it again. This is an inexpensive optimization.
if (sourcePixel != previousPixel)
{
// Quantize the pixel
pixelValue = QuantizePixel(sourcePixel);
// And setup the previous pointer
previousPixel = sourcePixel;
}
output[i++] = pixelValue;
}
}
}
/// <summary>

1
src/ImageProcessor/ImageProcessor.csproj

@ -52,6 +52,7 @@
<Compile Include="Formats\Bmp\BmpEncoder.cs" />
<Compile Include="Formats\Gif\GifDecoderCore.cs" />
<Compile Include="Formats\Gif\GifEncoder.cs" />
<Compile Include="Formats\Gif\Quantizer\QuantizedImage.cs" />
<Compile Include="Formats\Gif\Quantizer\IQuantizer.cs" />
<Compile Include="Formats\Gif\Quantizer\OctreeQuantizer.cs" />
<Compile Include="Formats\Gif\Quantizer\Quantizer.cs" />

20
tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs

@ -57,5 +57,25 @@
Trace.WriteLine(string.Format("{0} : {1}ms", filename, watch.ElapsedMilliseconds));
}
[Theory]
[InlineData("../../TestImages/Formats/Bmp/Car.bmp")]
public void QuantizedImageShouldPreserveMaximumColorPrecision(string filename)
{
if (!Directory.Exists("Quantized"))
{
Directory.CreateDirectory("Quantized");
}
Image image = new Image(File.OpenRead(filename));
IQuantizer quantizer = new OctreeQuantizer();
QuantizedImage quantizedImage = quantizer.Quantize(image);
using (FileStream output = File.OpenWrite($"Quantized/{ Path.GetFileName(filename) }"))
{
IImageEncoder encoder = Image.Encoders.First(e => e.IsSupportedFileExtension(Path.GetExtension(filename)));
encoder.Encode(quantizedImage.ToImage(), output);
}
}
}
}
Loading…
Cancel
Save