-
Notifications
You must be signed in to change notification settings - Fork 0
/
quad-owl.qmd
86 lines (77 loc) · 2.45 KB
/
quad-owl.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
```{ojs}
graphic = {
const quads = new TinyQueue([new Quad(0, 0, width, width)], (a, b) => b.score - a.score);
const context = DOM.context2d(width, width);
context.canvas.style.width = "50%";
for (let i = 0; true; ++i) {
const q = quads.pop();
if (q === undefined || q.score < 50) break;
mutable score = q.score;
for (const s of q.split()) {
if (q.w >= 4) quads.push(s);
context.fillStyle = s.color;
context.fillRect(s.x, s.y, s.w, s.h);
context.strokeRect(s.x, s.y, s.w, s.h);
}
if (i % 15 === 0) yield context.canvas;
}
}
mutable score = null
width = 1024
area_power = 0.25
class Quad {
constructor(x, y, w, h) {
const [r, g, b, error] = colorFromHistogram(computeHistogram(x, y, w, h));
this.x = x, this.y = y, this.w = w, this.h = h;
this.color = `#${(0x1000000 + (r << 16) + (g << 8) + b).toString(16).substring(1)}`;
this.score = error * Math.pow(w * h, area_power);
}
split() {
const dx = this.w / 2, x1 = this.x, x2 = this.x + dx;
const dy = this.h / 2, y1 = this.y, y2 = this.y + dy;
return [
new Quad(x1, y1, dx, dy),
new Quad(x2, y1, dx, dy),
new Quad(x1, y2, dx, dy),
new Quad(x2, y2, dx, dy)
];
}
}
function computeHistogram(x, y, w, h) {
const {data} = imageContext.getImageData(x, y, w, h);
const histogram = new Uint32Array(1024);
for (let i = 0, n = data.length; i < n; i += 4) {
++histogram[0 * 256 + data[i + 0]];
++histogram[1 * 256 + data[i + 1]];
++histogram[2 * 256 + data[i + 2]];
++histogram[3 * 256 + data[i + 3]];
}
return histogram;
}
function weightedAverage(histogram) {
let total = 0;
let value = 0;
for (let i = 0; i < 256; ++i) total += histogram[i], value += histogram[i] * i;
value /= total;
let error = 0;
for (let i = 0; i < 256; ++i) error += (value - i) ** 2 * histogram[i];
return [value, Math.sqrt(error / total)];
}
function colorFromHistogram(histogram) {
const [r, re] = weightedAverage(histogram.subarray(0, 256));
const [g, ge] = weightedAverage(histogram.subarray(256, 512));
const [b, be] = weightedAverage(histogram.subarray(512, 768));
return [
Math.round(r),
Math.round(g),
Math.round(b),
re * 0.2989 + ge * 0.5870 + be * 0.1140
];
}
imageContext = FileAttachment("owl.jpg").image().then(image => {
const context = DOM.context2d(width, width, 1);
context.drawImage(image, 0, 0, width, width);
return context;
})
TinyQueue = require("tinyqueue@2")
```