woo
This commit is contained in:
parent
f437114be0
commit
2c943419ce
1 changed files with 19 additions and 138 deletions
157
dither-image.js
157
dither-image.js
|
@ -98,10 +98,6 @@ class RGB {
|
|||
);
|
||||
}
|
||||
|
||||
add(other) {
|
||||
return RGB.add(this, other);
|
||||
}
|
||||
|
||||
difference(other) {
|
||||
return new RGB(
|
||||
this.r - other.r,
|
||||
|
@ -110,6 +106,14 @@ class RGB {
|
|||
);
|
||||
}
|
||||
|
||||
multiply(scalar) {
|
||||
return new RGB(
|
||||
this.r * scalar,
|
||||
this.g * scalar,
|
||||
this.b * scalar,
|
||||
);
|
||||
}
|
||||
|
||||
divide(scalar) {
|
||||
return new RGB(
|
||||
this.r / scalar,
|
||||
|
@ -472,15 +476,12 @@ function decimate(input, palette, n, inputError, y) {
|
|||
output[x] = pick;
|
||||
popularity[pick]++;
|
||||
|
||||
let shares = 8;
|
||||
let single = nextError.divide(shares);
|
||||
let double = nextError.divide(shares / 2);
|
||||
error.cur[x + 1]?.inc(double);
|
||||
error.cur[x + 2]?.inc(single);
|
||||
let share = (n) => nextError.multiply(n / 16);
|
||||
|
||||
error.next[x - 1]?.inc(single);
|
||||
error.next[x]?.inc(double);
|
||||
error.next[x + 1]?.inc(double);
|
||||
error.cur[x + 1]?.inc(share(7));
|
||||
error.next[x - 1]?.inc(share(3));
|
||||
error.next[x ]?.inc(share(5));
|
||||
error.next[x + 1]?.inc(share(1));
|
||||
|
||||
let mag = nextError.magnitude();
|
||||
fitness[x] = maxDist / mag;
|
||||
|
@ -533,129 +534,6 @@ function decimate(input, palette, n, inputError, y) {
|
|||
keepers[i & 0xfe] = 1; // drop that 0 luminance bit!
|
||||
}
|
||||
|
||||
// this takes the top hues, and uses the brightest of each hue
|
||||
// needs tuning
|
||||
/*
|
||||
// first, dither to the total atari palette
|
||||
while (decimated.length > n) {
|
||||
let {popularity} = dither(decimated);
|
||||
let hues = zeros(16);
|
||||
let lumas = zeros(16);
|
||||
for (let i = 0; i < decimated.length; i++) {
|
||||
let color = decimated[i];
|
||||
let hue = color >> 4;
|
||||
let luma = color & 0xe;
|
||||
hues[hue] += popularity[i];
|
||||
if (luma > lumas[hue]) {
|
||||
lumas[hue] = luma;
|
||||
}
|
||||
}
|
||||
|
||||
let a = hues;
|
||||
a = a.map((count, hue) => { return {count, hue} });
|
||||
a = a.sort((a, b) => b.count - a.count);
|
||||
a = a.map(({hue}) => hue);
|
||||
a = a.filter((color) => !keepers[color]);
|
||||
//a = a.slice(0, n - reserved.length);
|
||||
a = a.slice(0, decimated.length - 1 - reserved.length);
|
||||
a = a.map((hue) => (hue << 4) | lumas[hue]);
|
||||
a = a.sort((a, b) => a - b);
|
||||
|
||||
decimated = reserved.concat(a);
|
||||
}
|
||||
console.log('end', decimated);
|
||||
*/
|
||||
|
||||
// popularity? not really working right
|
||||
// first, dither to the total atari palette
|
||||
/*
|
||||
while (decimated.length > n) {
|
||||
//console.log(y);
|
||||
|
||||
let {popularity, fitness, output} = dither(decimated);
|
||||
let pops = [];
|
||||
let fits = [];
|
||||
pops.fill(0, 0, 256);
|
||||
fits.fill(0, 0, 256);
|
||||
for (let i = 0; i < decimated.length; i++) {
|
||||
let c = decimated[i];
|
||||
pops[c] = popularity[i];
|
||||
for (let x = 0; x < fitness.length; x++) {
|
||||
if (output[x] === i) {
|
||||
fits[c] += fitness[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
let metric = (c) => {
|
||||
let rgb = atariRGB[c];
|
||||
let max = Math.max(rgb.r, rgb.g, rgb.b);
|
||||
let fit = fits[c];
|
||||
let pop = pops[c];
|
||||
return pop;
|
||||
}
|
||||
|
||||
let a = decimated.slice();
|
||||
// temporarily strip the reserved items
|
||||
a = a.filter((color) => !keepers[color]);
|
||||
a = a.filter((color) => popularity[color]);
|
||||
a.sort((a, b) => {
|
||||
return metric(b) - metric(a);
|
||||
});
|
||||
console.log(a);
|
||||
a = reserved.concat(a);
|
||||
decimated = a.slice(0, n);
|
||||
//decimated = a.slice(0, decimated.length - 1);
|
||||
//console.log(decimated);
|
||||
}
|
||||
*/
|
||||
//console.log('end', decimated);
|
||||
|
||||
// old algo
|
||||
/*
|
||||
while (decimated.length > n) {
|
||||
let {popularity, fitness, output} = dither(decimated);
|
||||
|
||||
// Try dropping least used color on each iteration
|
||||
let least = Infinity;
|
||||
let pick = -1;
|
||||
for (let i = 1; i < decimated.length; i++) {
|
||||
|
||||
//let coolFactor = popularity[i];
|
||||
|
||||
let coolFactor = 0;
|
||||
if (popularity[i]) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
if (output[x] == i) {
|
||||
// Scale up the scoring for close matches to prioritize
|
||||
// color accuracy over raw linear usage.
|
||||
//coolFactor += (fitness[x] ** 2);
|
||||
coolFactor += (fitness[x] ** 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (coolFactor < least) {
|
||||
pick = i;
|
||||
least = coolFactor;
|
||||
}
|
||||
decimated = decimated.filter((color, i) => {
|
||||
if (i == 0) {
|
||||
return true;
|
||||
}
|
||||
if (i == pick) {
|
||||
return false;
|
||||
}
|
||||
// Also drop any non-black unused colors to save trouble.
|
||||
// However -- this may change dither results.
|
||||
// Try this with/without later.
|
||||
if (popularity[i] == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Median cut!
|
||||
// https://en.wikipedia.org/wiki/Median_cut
|
||||
//let buckets = [input.slice()];
|
||||
|
@ -694,13 +572,16 @@ function decimate(input, palette, n, inputError, y) {
|
|||
}
|
||||
decimated = buckets.map((bucket) => {
|
||||
// Average the RGB colors in this chunk
|
||||
let rgb = bucket
|
||||
.reduce((acc, rgb) => acc.inc(rgb), new RGB(0, 0, 0))
|
||||
.divide(bucket.length);
|
||||
//let rgb = bucket
|
||||
// .reduce((acc, rgb) => acc.inc(rgb), new RGB(0, 0, 0))
|
||||
// .divide(bucket.length);
|
||||
|
||||
// Take the brightest color in the bucket
|
||||
//let rgb = bucket[bucket.length - 1];
|
||||
|
||||
// Take the median color in the bucket
|
||||
let rgb = bucket[bucket.length >> 1];
|
||||
|
||||
// And map into the Atari palette
|
||||
let dists = palette.map(( i) => rgb.difference(atariRGB[i]).magnitude());
|
||||
let closest = Math.min(...dists);
|
||||
|
|
Loading…
Reference in a new issue