Skip to content

Latest commit

 

History

History
348 lines (288 loc) · 14.8 KB

API-README.md

File metadata and controls

348 lines (288 loc) · 14.8 KB

Image Color Number Reduction with alpha support using RGBQuant/NeuQuant/Xiaolin Wu's algorithms and Euclidean/Manhattan/CIEDE2000 color distance formulas in TypeScript

Table of Contents

Basic API

Basic API Tutorial

Convert 24 bit PNG to indexed 8 bit

import { PNG } from 'pngjs';
import { buildPaletteSync, utils } from 'image-q';

// read file
const { data, width, height } = PNG.sync.read(fs.readFileSync('file.png'));
const inPointContainer = utils.PointContainer.fromUint8Array(data, width, height);

// convert
const palette = buildPaletteSync([inPointContainer]);
const outPointContainer = applyPaletteSync(inPointContainer, palette);

// use outPointContainer.toUint8Array() somehow

Build Palette

This API allows to Build (quantize) palette using Sample Images, returns [[Palette]] instance.

Async

import { buildPalette } from 'image-q'; // or const buildPalette = require('image-q').buildPalette
const palette = await buildPalette([pointContainer], { 
  colorDistanceFormula: 'euclidean', // optional
  paletteQuantization: 'neuquant', // optional
  colors: 128, // optional
  onProgress: progress => console.log('applyPalette', progress), // optional
});

Sync

import { buildPaletteSync } from 'image-q'; // or const buildPaletteSync = require('image-q').buildPaletteSync
const palette = buildPaletteSync([pointContainer], { 
  colorDistanceFormula: 'euclidean', // optional
  paletteQuantization: 'neuquant', // optional
  colors: 128, // optional
});

implementation detail: generator is wrapped with setImmediate (polyfilled)

Apply Palette to Image

This API applies given [[Palette]] to the [[PointContainer]], returns [[PointContainer]] containing new image.

Async (Promise-based)

import { applyPalette } from 'image-q'; // or const applyPalette = require('image-q').applyPalette
const outPointContainer = await applyPalette(pointContainer, palette, {
  colorDistanceFormula: 'euclidean', // optional
  imageQuantization: 'floyd-steinberg', // optional
  onProgress: progress => console.log('applyPalette', progress), // optional
});

Sync

import { applyPaletteSync } from 'image-q'; // or const applyPaletteSync = require('image-q').applyPaletteSync
const outPointContainer = applyPaletteSync(pointContainer, palette, {
  colorDistanceFormula: 'euclidean', // optional
  imageQuantization: 'floyd-steinberg', // optional
});

Optional parameters

See description of string constants in Advanced API Section

export type ColorDistanceFormula =
  | 'cie94-textiles'
  | 'cie94-graphic-arts'
  | 'ciede2000'
  | 'color-metric'
  | 'euclidean'
  | 'euclidean-bt709-noalpha'
  | 'euclidean-bt709'
  | 'manhattan'
  | 'manhattan-bt709'
  | 'manhattan-nommyde'
  | 'pngquant';
export type PaletteQuantization =
  | 'neuquant'
  | 'neuquant-float'
  | 'rgbquant'
  | 'wuquant';
export type ImageQuantization =
  | 'nearest'
  | 'riemersma'
  | 'floyd-steinberg'
  | 'false-floyd-steinberg'
  | 'stucki'
  | 'atkinson'
  | 'jarvis'
  | 'burkes'
  | 'sierra'
  | 'two-sierra'
  | 'sierra-lite';

Apply Palette to Image

Advanced API

Image Import Sources

API Source
Canvas related
[[PointContainer.fromHTMLCanvasElement]] HTMLCanvasElement
[[PointContainer.fromImageData]] ImageData ctx.getImageData()
[[PointContainer.fromUint8Array]] Uint8ClampedArray ctx.getImageData().data
[[PointContainer.fromUint8Array]] deprecated CanvasPixelArray ctx.getImageData().data
Other
[[PointContainer.fromHTMLImageElement]] HTMLImageElement
[[PointContainer.fromImageData]] Array
[[PointContainer.fromUint8Array]] Uint8Array
[[PointContainer.fromUint32Array]] Uint32Array
[[PointContainer.fromBuffer]] Buffer (Node.js)

Usage:

const canvas = document.querySelector('#canvas');
const pointContainer = PointContainer.fromHTMLCanvasElement(canvas);

Color Distance

API Description Originally used by
[[Euclidean]] 1/1/1/1 coefficients WuQuant
[[EuclideanBT709]] BT.709 sRGB coefficients
[[Manhattan]] 1/1/1/1 coefficients NeuQuant
[[ManhattanBT709]] BT.709 sRGB coefficients
[[CIEDE2000]] CIEDE2000 (very slow)
[[CIE94Textiles]] CIE94 implementation for textiles
[[CIE94GraphicArts]] CIE94 implementation for graphic arts
[[CMetric]] see
[[PNGQuant]] used in PNGQuant tools
[[EuclideanBT709NoAlpha]] BT.709 sRGB coefficients RGBQuant
[[ManhattanNommyde]] discussion

Usage:

const distanceCalculator = new EuclideanBT709();

Palette Quantizers

API Description
[[NeuQuant]] original code ported, integer calculations
[[RGBQuant]]
[[WuQuant]]
[[NeuQuantFloat]] floating-point calculations

Usage (sync):

const paletteQuantizer = new WuQuant(distanceCalculator, 256);
paletteQuantizer.sample(pointContainer1);
paletteQuantizer.sample(pointContainer2);
const palette = paletteQuantizer.quantizeSync();

Usage (generator):

// example 1
const paletteQuantizer = new WuQuant(distanceCalculator, 256);
paletteQuantizer.sample(pointContainer1);
paletteQuantizer.sample(pointContainer2);
const generator = paletteQuantizer.quantize();
let palette;
while(true) {
  // calling to generator.next() may be easily wrapped with setTimeout to make it async
  const result = generator.next();
  if (result.done) break;
  if (result.value.palette) palette = result.palette;
  console.log(`${result.value.progress}% done`);
}
// example 2
const paletteQuantizer = new WuQuant(distanceCalculator, 256);
paletteQuantizer.sample(pointContainer1);
paletteQuantizer.sample(pointContainer2);
const palette = Array.from(paletteQuantizer.quantize()).pop().palette;

Image Quantizers

API Description
[[NearestColor]]
[[ErrorDiffusionArray]] 2 modes of error propagation are supported: xnview and gimp
- 1. [[FloydSteinberg]]
- 2. [[FalseFloydSteinberg]]
- 3. [[Stucki]]
- 4. [[Atkinson]]
- 5. [[Jarvis]]
- 6. [[Burkes]]
- 7. [[Sierra]]
- 8. [[TwoSierra]]
- 9. [[SierraLite]]
[[ErrorDiffusionRiemersma]] Hilbert space-filling curve is used

Usage (sync):

const imageQuantizer = new ErrorDiffusionArray(distanceCalculator, ErrorDiffusionArrayKernel.Jarvis);
const outPointContainer = imageQuantizer.quantizeSync(inPointContainer, palette);

Usage (generator):

// example 1
const imageQuantizer = new ErrorDiffusionArray(distanceCalculator, ErrorDiffusionArrayKernel.Jarvis);
const generator = imageQuantizer.quantize(inPointContainer, palette);
let outPointContainer;
while(true) {
  // calling to generator.next() may be easily wrapped with setTimeout to make it async
  const result = generator.next();
  if (result.done) break;
  if (result.value.pointContainer) outPointContainer = result.pointContainer;
  console.log(`${result.value.progress}% done`);
}
// example 2
const imageQuantizer = new ErrorDiffusionArray(distanceCalculator, ErrorDiffusionArrayKernel.Jarvis);
const outPointContainer = Array.from(imageQuantizer.quantize(inPointContainer, palette)).pop().pointContainer;

Output

API Description
[[PointContainer.toUint8Array]] Uint8Array
[[PointContainer.toUint32Array]] Uint32Array

Usage:

// write PNG using pngjs
png.data = outPointContainer.toUint8Array();
fs.writeFileSync('filename.png', PNG.sync.write(png))

Advanced API Usage

Load Image (simple example)

var img = document.createElement("img");
img.onload = function() {
	// image is loaded, here should be all code utilizing image
	...
}
img.src = "http://pixabay.com/static/uploads/photo/2012/04/11/11/32/letter-a-27580_640.png"

Generate Palette

// desired colors number
var targetColors = 256;
   
// create pointContainer and fill it with image
var pointContainer = iq.utils.PointContainer.fromHTMLImageElement(img);

// create chosen distance calculator (see classes inherited from `iq.distance.AbstractDistanceCalculator`)
var distanceCalculator = new iq.distance.Euclidean();

// create chosen palette quantizer (see classes implementing `iq.palette.AbstractPaletteQuantizer`) 
var paletteQuantizer = new iq.palette.RGBQuant(distanceCalculator, targetColors);
		
// feed out pointContainer filled with image to paletteQuantizer
paletteQuantizer.sample(pointContainer);

... (you may sample more than one image to create mutual palette) 

// take generated palette
var palette = paletteQuantizer.quantizeSync();

Apply Palette to Image (Image Dithering)

// create image quantizer (see classes implementing `iq.image.AbstractImageQuantizer`)
var imageQuantizer = new iq.image.NearestColor(distanceCalculator);

// apply palette to image
var resultPointContainer = imageQuantizer.quantizeSync(pointContainer, palette);

You may work with resultPointContainer directly or you may convert it to Uint8Array/Uint32Array

var uint8array = resultPointContainer.toUint8Array();

please also refer to tests

Misc API

Color Conversion

API Description
[[lab2rgb]] CIE L*a*b* => CIE RGB
[[lab2xyz]] CIE L*a*b* => CIE XYZ
[[rgb2hsl]] CIE RGB => HSL
[[rgb2lab]] CIE RGB => CIE L*a*b*
[[rgb2xyz]] CIE RGB => CIE XYZ
[[xyz2lab]] CIE XYZ => CIE L*a*b*
[[xyz2rgb]] CIE XYZ => CIE RGB

https://wolfcrow.com/blog/what-is-the-difference-between-cie-lab-cie-rgb-cie-xyy-and-cie-xyz/

Structural Similarity

Have fun! Any problems or queries let me know!

-- Igor