- commit
- b5fe756e51e6458241fd0bac1351efc472041b15
- parent
- 40b91c9ff42f398f53e5d1148fea0a9d6449eb73
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2023-02-13 09:04
more sophisticated color clustering
Diffstat
| A | js/color.js | 62 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | js/loader.js | 80 | +++++++++++++++++++++++++++++++++++++------------------------ |
2 files changed, 111 insertions, 31 deletions
diff --git a/js/color.js b/js/color.js
@@ -0,0 +1,62 @@
-1 1 var srgbToRgb = function(c) {
-1 2 var x = c / 255;
-1 3 if (x < 0.04045) {
-1 4 return x / 12.92;
-1 5 } else {
-1 6 return Math.pow((x + 0.055) / 1.055, 2.4);
-1 7 }
-1 8 };
-1 9
-1 10 var rgbToSrgb = function(c) {
-1 11 var x;
-1 12 if (c < 0.04045 / 12.92) {
-1 13 x = c * 12.92;
-1 14 } else {
-1 15 x = Math.pow(c, 1 / 2.4) * 1.055 - 0.055;
-1 16 }
-1 17 return x * 255;
-1 18 };
-1 19
-1 20 export var rgbToLab = function(srgb) {
-1 21 var [r, g, b] = srgb.map(srgbToRgb);
-1 22
-1 23 var l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
-1 24 var m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
-1 25 var s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
-1 26
-1 27 l = Math.cbrt(l);
-1 28 m = Math.cbrt(m);
-1 29 s = Math.cbrt(s);
-1 30
-1 31 return [
-1 32 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
-1 33 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
-1 34 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s,
-1 35 ];
-1 36 };
-1 37
-1 38 export var labToRgb = function(lab) {
-1 39 var l = lab[0] + 0.3963377774 * lab[1] + 0.2158037573 * lab[2];
-1 40 var m = lab[0] - 0.1055613458 * lab[1] - 0.0638541728 * lab[2];
-1 41 var s = lab[0] - 0.0894841775 * lab[1] - 1.2914855480 * lab[2];
-1 42
-1 43 l = l * l * l;
-1 44 m = m * m * m;
-1 45 s = s * s * s;
-1 46
-1 47 var rgb = [
-1 48 +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
-1 49 -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
-1 50 -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s,
-1 51 ];
-1 52
-1 53 return rgb.map(rgbToSrgb);
-1 54 };
-1 55
-1 56 export var hex = function(rgb) {
-1 57 return '#'
-1 58 + Math.floor(rgb[0] / 16).toString(16)
-1 59 + Math.floor(rgb[1] / 16).toString(16)
-1 60 + Math.floor(rgb[2] / 16).toString(16);
-1 61 };
-1 62
diff --git a/js/loader.js b/js/loader.js
@@ -1,3 +1,5 @@
-1 1 import { rgbToLab, labToRgb, hex } from './color.js';
-1 2
1 3 var file2img = function(file) {
2 4 // FIXME: uses unsafe inline image
3 5 return new Promise((resolve, reject) => {
@@ -26,46 +28,62 @@ var img2data = function(img, width) {
26 28 return _ctx.getImageData(0, 0, _canvas.width, _canvas.height);
27 29 };
28 30
29 -1 var round = function(c) {
30 -1 return Math.floor(c / 51) * 3;
31 -1 };
32 31
33 -1 var makecolor = function(a) {
34 -1 return '#'
35 -1 + round(a[0]).toString(16)
36 -1 + round(a[1]).toString(16)
37 -1 + round(a[2]).toString(16);
38 -1 };
-1 32 class Cluster {
-1 33 constructor(center) {
-1 34 this.center = center;
-1 35 this.count = 1;
-1 36 }
39 37
40 -1 var sRGB = function(c) {
41 -1 var x = round(c) / 15;
42 -1 if (x < 0.04045) {
43 -1 return x / 12.92;
44 -1 } else {
45 -1 return Math.pow((x + 0.055) / 1.055, 2.4);
-1 38 distance(color) {
-1 39 return Math.sqrt(
-1 40 Math.pow(this.center[0] - color[0], 2)
-1 41 + Math.pow(this.center[1] - color[1], 2)
-1 42 + Math.pow(this.center[2] - color[2], 2)
-1 43 );
46 44 }
47 -1 };
48 45
49 -1 var makeContrast = function(a) {
50 -1 var l = 0.2126 * sRGB(a[0]) + 0.7152 * sRGB(a[1]) + 0.0722 * sRGB(a[2]);
51 -1 return l > 0.18 ? '#000' : '#fff';
52 -1 };
-1 46 add(color) {
-1 47 this.center = [
-1 48 (this.center[0] * this.count + color[0]) / (this.count + 1),
-1 49 (this.center[1] * this.count + color[1]) / (this.count + 1),
-1 50 (this.center[2] * this.count + color[2]) / (this.count + 1),
-1 51 ];
-1 52 this.count += 1;
-1 53 }
-1 54 }
53 55
54 56 var analyze = function(img) {
55 -1 var c, i, j;
56 -1 var colors = ['white'];
57 -1 var contrasts = ['black'];
-1 57 var j;
-1 58 var clusters = [];
58 59 var out = [];
59 -1 for (i = 0; i < img.data.length; i += 4) {
60 -1 c = makecolor(img.data.slice(i, i + 3));
61 -1 j = colors.indexOf(c);
62 -1 if (j === -1) {
63 -1 j = colors.length;
64 -1 colors.push(c);
65 -1 contrasts.push(makeContrast(img.data.slice(i, i + 3)));
-1 60 for (var i = 0; i < img.data.length; i += 4) {
-1 61 var lab = rgbToLab([
-1 62 img.data[i],
-1 63 img.data[i + 1],
-1 64 img.data[i + 2],
-1 65 ]);
-1 66
-1 67 for (j = 0; j < clusters.length; j++) {
-1 68 if (clusters[j].distance(lab) < 0.05) {
-1 69 clusters[j].add(lab);
-1 70 out.push(j + 1);
-1 71 break;
-1 72 }
66 73 }
67 -1 out.push(j);
-1 74 if (j === clusters.length) {
-1 75 clusters.push(new Cluster(lab));
-1 76 out.push(j + 1);
-1 77 }
-1 78 }
-1 79
-1 80 var colors = ['white'];
-1 81 var contrasts = ['black'];
-1 82 for (j = 0; j < clusters.length; j++) {
-1 83 colors.push(hex(labToRgb(clusters[j].center)));
-1 84 contrasts.push(clusters[j].center[0] < 0.5 ? 'white' : 'black');
68 85 }
-1 86
69 87 return {
70 88 width: img.width,
71 89 height: img.height,