This commit is contained in:
Brooke Vibber 2023-03-24 00:23:59 -07:00
parent f5c8d219e8
commit 1d3712be5c

View file

@ -134,13 +134,23 @@ class RGB {
this.b * this.b; this.b * this.b;
} }
sum() {
return this.r + this.g + this.b;
}
lumaScale() {
return new RGB(
this.r * 0.299,
this.g * 0.586,
this.b * 0.114
);
}
luma() { luma() {
return this.r * 0.299 + this.g * 0.587 + this.b * 0.114; return this.lumaScale().sum();
} }
} }
const maxDist = (new RGB(255, 255, 255)).magnitude();
// snarfed from https://lospec.com/palette-list/atari-8-bit-family-gtia // snarfed from https://lospec.com/palette-list/atari-8-bit-family-gtia
// which was calculated with Retrospecs App's Atari 800 emulator // which was calculated with Retrospecs App's Atari 800 emulator
let atariRGB = [ let atariRGB = [
@ -414,19 +424,6 @@ let atariRGB = [
* @returns {{output: number[], palette: number[], error: RGB[]}} * @returns {{output: number[], palette: number[], error: RGB[]}}
*/ */
function decimate(input, palette, n) { function decimate(input, palette, n) {
// to brute-force, the possible palettes are:
// 255 * 254 * 253 = 16,386,810
//
// we could brute force it but that's a lot :D
// but can do some bisection :D
//
// need a fitness metric.
// each pixel in the dithered line gives a distance
// sum/average them? median? maximum?
// summing evens out the ups/downs from dithering
// but doesn't distinguish between two close and two distant options
// consider median, 90th-percentile, and max of abs(distance)
// consider doing the distance for each channel?
let width = input.length; let width = input.length;
@ -440,7 +437,6 @@ function decimate(input, palette, n) {
// Apply dithering with given palette and collect color usage stats // Apply dithering with given palette and collect color usage stats
let dither = (palette) => { let dither = (palette) => {
let fitness = zeroes(width);
let error = { let error = {
cur: [], cur: [],
next: [], next: [],
@ -467,7 +463,7 @@ function decimate(input, palette, n) {
for (let i = 0; i < palette.length; i++) { for (let i = 0; i < palette.length; i++) {
let diff = rgb.difference(atariRGB[palette[i]]); let diff = rgb.difference(atariRGB[palette[i]]);
let dist = diff.magnitude2(); let dist = diff.magnitude();
if (dist < shortest) { if (dist < shortest) {
nextError = diff; nextError = diff;
shortest = dist; shortest = dist;
@ -484,21 +480,10 @@ function decimate(input, palette, n) {
error.next[x - 1]?.inc(share(3)); error.next[x - 1]?.inc(share(3));
error.next[x ]?.inc(share(5)); error.next[x ]?.inc(share(5));
error.next[x + 1]?.inc(share(1)); error.next[x + 1]?.inc(share(1));
let mag = nextError.magnitude();
fitness[x] = maxDist / mag;
// 442 is the 3d distance across the rgb cube
//fitness[x] = 442 - (nextError.magnitude());
//fitness[x] = 442 / (442 - nextError.magnitude());
fitness[x] = 255 / (256 - Math.max(0, nextError.r, nextError.g, nextError.b));
let mag2 = nextError.magnitude2();
distance2 += mag2;
} }
return { return {
output, output,
palette, palette,
fitness,
distance2, distance2,
popularity, popularity,
error: error.next error: error.next