image-quantization
image-quantization copied to clipboard
Black/White dithering
Hi, I'm not sure if this is an issue. Probably only my ignorance.
I want to apply a dithering algorithm to an image that come from an input (code as an angular component):
@Component({
selector: "graphics-form",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div>
<button mat-stroked-button color="primary" (click)="fileInput.click()">
Upload
</button>
<input hidden type="file" #fileInput (change)="onFileChange($event)" />
</div>
`
})
export class GraphicsFormComponent {
onFileChange(event) {
if (event.target.files && event.target.files.length) {
var img = new Image();
img.onload = function() {
const canvas: HTMLCanvasElement = document.createElement("canvas");
canvas.height = img.height;
canvas.width = img.width;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(img.src);
console.log(
"the image is drawn",
img.width,
img.height,
canvas.width,
canvas.height
);
const pointContainer = iq.utils.PointContainer.fromHTMLCanvasElement(
canvas
);
const distanceCalculator = new iq.distance.EuclideanBT709();
const paletteQuantizer = new iq.palette.WuQuant(distanceCalculator, 2);
paletteQuantizer.sample(pointContainer);
const palette = paletteQuantizer.quantizeSync();
console.log(palette);
const imageQuantizer = new iq.image.ErrorDiffusionArray(
distanceCalculator,
iq.image.ErrorDiffusionArrayKernel.Jarvis
);
const outPointContainer = imageQuantizer.quantizeSync(
pointContainer,
palette
);
var uint8array = outPointContainer.toUint8Array();
console.log(uint8array);
};
img.src = URL.createObjectURL(event.target.files[0]);
}
}
}
The palette contains 2 points (colors?): _pointArray: Array(2) 0: Point {uint32: 85133568, a: 5, b: 19, g: 9, r: 0, …} 1: Point {uint32: 4104476424, a: 244, b: 165, g: 87, r: 8, …} length: 2
Is the method correct? How should I proceed? I would like to have only black/white pixels. Should I iterate the canvas pixels and substitute with black each time I find (a: 5, b: 19, g: 9, r: 0) and with white each time I find (a: 244, b: 165, g: 87, r: 8)?
Thanks!
I saw that setting the palette manually I get what I was looking for:
palette.add(iq.utils.Point.createByRGBA(0, 0, 0, 255)); // black
palette.add(iq.utils.Point.createByRGBA(255, 255, 255, 0)); // white
but why the Alpha channel of white has to be zero? If I set to 255 the result is wrong (or at least not what I expect).
I'm having an issue that I think is related. When I generate a palette from an image it correctly limits the number of colors to what I specify when creating the palette quantizer. However, if I then add dithering, it completely ignores the target number of colors. Is there a way to limit the total number of colors used including after dithering?
Quantizer: WuQuant Color distance: EuclideanBT709 Dithering: Jarvis
Palette with alpha = 255 for white color:
palette = new iq.utils.Palette();
palette.add(iq.utils.Point.createByRGBA(0, 0, 0, 255))
palette.add(iq.utils.Point.createByRGBA(255, 255, 255, 255))

Palette with alpha = 0 for white color:
palette = new iq.utils.Palette();
palette.add(iq.utils.Point.createByRGBA(0, 0, 0, 255))
palette.add(iq.utils.Point.createByRGBA(255, 255, 255, 0))

Demo app where to put these changes:
https://github.com/ibezkrovnyi/image-quantization/blob/45ca880ec8b8fb8cb96d0e863cdea4adb1df214c/packages/demo/src/controller/usage.ts#L115
How to run:
https://github.com/ibezkrovnyi/image-quantization/blob/45ca880ec8b8fb8cb96d0e863cdea4adb1df214c/packages/demo/package.json#L31
Summary:
Looks like alpha=255 (as expected) for white color gives best results.
Note: for images without alpha channel with alpha=0 for white color in palette the above settings give empty black box. Changing to Manhatten gives better results, however because one of the colors in palette now has alpha 0 - final result now depends on background color.
Please test first in demo app and then please let me know if it is still an issue.