nx_contactsheet.nx source
↩ module page · 196 lines · 6859 B
1// nx_contactsheet.nx -- VISUAL VALIDATION: render a whole gathered corpus as ONE tiled PNG.
2//
3// Numbers prove a pipeline ran; pixels prove it ran CORRECTLY. Every tile here is decoded through OUR
4// decoder (nx_img_to_rgb -> the same path the fingerprinter uses), box-average downscaled and composited.
5// So the sheet is simultaneously (a) a look at the corpus and (b) proof the decode path yields real pixels:
6// a broken decoder produces noise or a hole, not a recognisable thumbnail.
7//
8// usage: nx_contactsheet <dir> <out.png> [tile_px] [cols]
9
10import "nx_str.nx"
11import "nx_syscalls.nx"
12import "nx_dir.nx"
13import "nx_img_to_rgb.nx"
14import "nx_png_write.nx"
15
16const CS_MAXROWS: i64 = 4096
17const CS_ARENA: i64 = 1048576
18const CS_PATH: i64 = 4096
19const CS_PAD: i64 = 4
20
21func cs_puts(s: *u8) -> i64 { sys_write(1, s, nx_str_len(s)); return 0 }
22
23func cs_pi(v: i64) -> i64 {
24 let t: *u8 = sys_mmap(32)
25 let o: *u8 = sys_mmap(32)
26 var m: i64 = v
27 var k: i64 = 0
28 if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) }
29 if m == 0 { t[0] = 48; k = 1 }
30 while m > 0 { t[k] = 48 + (m % 10); m = m / 10; k = k + 1 }
31 var i: i64 = 0
32 while i < k { o[i] = t[k - 1 - i]; i = i + 1 }
33 sys_write(1, o, k)
34 return 0
35}
36
37func cs_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i }
38
39func cs_atoi(s: *u8) -> i64 {
40 var i: i64 = 0
41 var v: i64 = 0
42 while s[i] != (0 as u8) {
43 let c: i64 = s[i] as i64
44 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } }
45 i = i + 1
46 }
47 return v
48}
49
50// box-average blit of src (sw x sh RGB) into sheet at (ox,oy) scaled to fw x fh
51func cs_draw(sheet: *u8, SW: i64, ox: i64, oy: i64, fw: i64, fh: i64, src: *u8, sw: i64, sh: i64) -> i64 {
52 var y: i64 = 0
53 while y < fh {
54 var x: i64 = 0
55 while x < fw {
56 let sx0: i64 = (x * sw) / fw
57 let sy0: i64 = (y * sh) / fh
58 var sx1: i64 = ((x + 1) * sw) / fw
59 var sy1: i64 = ((y + 1) * sh) / fh
60 if sx1 <= sx0 { sx1 = sx0 + 1 }
61 if sy1 <= sy0 { sy1 = sy0 + 1 }
62 if sx1 > sw { sx1 = sw }
63 if sy1 > sh { sy1 = sh }
64 var r: i64 = 0
65 var g: i64 = 0
66 var b: i64 = 0
67 var n: i64 = 0
68 var yy: i64 = sy0
69 while yy < sy1 {
70 var xx: i64 = sx0
71 while xx < sx1 {
72 let o: i64 = (yy * sw + xx) * 3
73 r = r + (src[o] as i64)
74 g = g + (src[o+1] as i64)
75 b = b + (src[o+2] as i64)
76 n = n + 1
77 xx = xx + 1
78 }
79 yy = yy + 1
80 }
81 if n > 0 {
82 let d: i64 = ((oy + y) * SW + (ox + x)) * 3
83 sheet[d] = (r / n) as u8
84 sheet[d+1] = (g / n) as u8
85 sheet[d+2] = (b / n) as u8
86 }
87 x = x + 1
88 }
89 y = y + 1
90 }
91 return 0
92}
93
94func main(argc: i64, argv: *i64) -> i64 {
95 var dir: *u8 = "knowledge/media/barack_obama" as *u8
96 var outp: *u8 = "knowledge/status/contactsheet.png" as *u8
97 var tile: i64 = 96
98 var cols: i64 = 10
99 if argc >= 2 { dir = argv[1] as *u8 }
100 if argc >= 3 { outp = argv[2] as *u8 }
101 if argc >= 4 { tile = cs_atoi(argv[3] as *u8) }
102 if argc >= 5 { cols = cs_atoi(argv[4] as *u8) }
103 if tile < 16 { tile = 16 }
104 if tile > 512 { tile = 512 }
105 if cols < 1 { cols = 1 }
106 if cols > 64 { cols = 64 }
107
108 let rows_a: *NxDirRow = sys_mmap(NX_DIR_ROW_BYTES * CS_MAXROWS) as *NxDirRow
109 let arena: *u8 = sys_mmap(CS_ARENA)
110 let res: *NxDirResult = sys_mmap(NX_DIR_RESULT_BYTES) as *NxDirResult
111 let lrc: i64 = nx_dir_list(dir, rows_a, CS_MAXROWS, arena, CS_ARENA, 0, res)
112 cs_puts("=== nx_contactsheet dir=" as *u8); cs_puts(dir)
113 cs_puts(" list_rc=" as *u8); cs_pi(lrc)
114 cs_puts(" entries=" as *u8); cs_pi(res.n_filled); cs_puts("\n" as *u8)
115 if res.n_filled <= 0 { cs_puts("CONTACTSHEET-FAIL empty-dir\n" as *u8); sys_exit(1); return 1 }
116
117 let nfiles: i64 = res.n_filled
118 var nrows: i64 = (nfiles + cols - 1) / cols
119 if nrows < 1 { nrows = 1 }
120 let SW: i64 = cols * (tile + CS_PAD) + CS_PAD
121 let SH: i64 = nrows * (tile + CS_PAD) + CS_PAD
122 let sheet: *u8 = sys_mmap(SW * SH * 3 + 64)
123
124 // background
125 var p: i64 = 0
126 while p < SW * SH {
127 sheet[p*3] = 18 as u8
128 sheet[p*3+1] = 20 as u8
129 sheet[p*3+2] = 26 as u8
130 p = p + 1
131 }
132
133 let path: *u8 = sys_mmap(CS_PATH)
134 let wh: *i64 = sys_mmap(32) as *i64
135 var placed: i64 = 0
136 var undec: i64 = 0
137
138 var i: i64 = 0
139 while i < nfiles {
140 let row: *NxDirRow = nx_dir_row_at(rows_a, i)
141 var st: i64 = 0
142 if nx_dir_row_is_regular_file(row) != 1 { st = 1 }
143
144 if st == 0 {
145 var po: i64 = cs_cat(path, 0, dir)
146 po = cs_cat(path, po, "/" as *u8)
147 var c: i64 = 0
148 while c < row.name_len { path[po + c] = row.name_ptr[c]; c = c + 1 }
149 path[po + row.name_len] = 0 as u8
150 }
151
152 var rgb: *u8 = 0 as *u8
153 if st == 0 {
154 wh[0] = 0
155 wh[1] = 0
156 rgb = nx_img_to_rgb(path, wh)
157 if rgb == 0 as *u8 { st = 1; undec = undec + 1 }
158 }
159 if st == 0 { if wh[0] <= 0 { st = 1; undec = undec + 1 } }
160 if st == 0 { if wh[1] <= 0 { st = 1; undec = undec + 1 } }
161
162 if st == 0 {
163 let sw: i64 = wh[0]
164 let sh: i64 = wh[1]
165 // aspect-fit inside the tile
166 var fw: i64 = tile
167 var fh: i64 = tile
168 if sw >= sh { fh = (tile * sh) / sw } else { fw = (tile * sw) / sh }
169 if fw < 1 { fw = 1 }
170 if fh < 1 { fh = 1 }
171 let cx: i64 = placed % cols
172 let cy: i64 = placed / cols
173 let ox: i64 = CS_PAD + cx * (tile + CS_PAD) + (tile - fw) / 2
174 let oy: i64 = CS_PAD + cy * (tile + CS_PAD) + (tile - fh) / 2
175 cs_draw(sheet, SW, ox, oy, fw, fh, rgb, sw, sh)
176 placed = placed + 1
177 }
178
179 i = i + 1
180 }
181
182 let wrc: i64 = nx_png_write_rgb(outp, sheet, SW, SH)
183 cs_puts("{\"tool\":\"nx_contactsheet\",\"dir\":\"" as *u8); cs_puts(dir)
184 cs_puts("\",\"files\":" as *u8); cs_pi(nfiles)
185 cs_puts(",\"placed\":" as *u8); cs_pi(placed)
186 cs_puts(",\"undecodable\":" as *u8); cs_pi(undec)
187 cs_puts(",\"sheet_w\":" as *u8); cs_pi(SW)
188 cs_puts(",\"sheet_h\":" as *u8); cs_pi(SH)
189 cs_puts(",\"tile\":" as *u8); cs_pi(tile)
190 cs_puts(",\"cols\":" as *u8); cs_pi(cols)
191 cs_puts(",\"png_rc\":" as *u8); cs_pi(wrc)
192 cs_puts(",\"out\":\"" as *u8); cs_puts(outp)
193 cs_puts("\"}\nCONTACTSHEET-OK\n" as *u8)
194 sys_exit(0)
195 return 0
196}