code wiki / (root) / nx_dedither.nx

nx_dedither.nx source

↩ module page · 831 lines · 41866 B

1// nx_dedither.nx -- RECOVER A 1990s DITHERED IMAGE WITHOUT TOUCHING THE ORIGINAL BYTES. 2// 3// WHY THIS EXISTS (measured 2026-08-07, page3.com/13Nov96.gif): a 280x403 GIF weighing 81,953 B is a 4// PHOTOGRAPH crushed into a 256-entry palette. The encoder of 1996 traded colour depth for file size by 5// scattering alternating palette entries across smooth regions -- so a plain studio backdrop is stored as 6// high-frequency speckle. The archive serves those bytes FAITHFULLY, and faithful is exactly why the 7// backdrop reads as white fuzz. The fuzz is not our defect and not decode damage: header valid, trailer 8// 0x3B present, extension preserved 1:1 from the 1996 HTML, no re-encode anywhere in the pipeline. 9// 10// *THE ARCHIVE WAS RIGHT AND THE PICTURE STILL LOOKED WRONG. Those are different problems and only the 11// second one is fixable. Rule 25 says build intelligence, never strip features -- so this NEVER rewrites 12// the stored asset. It emits a SEPARATE enhanced render beside it. The original remains the record. 13// 14// HOW: a sigma filter (Lee 1983) -- average only over neighbours whose colour is already CLOSE to the 15// centre. Dither speckle is by construction a set of near-neighbours straddling the true value, so it 16// averages away; a real edge has far neighbours, which are EXCLUDED from the mean and therefore survive. 17// A plain box blur would also lower the noise metric -- and would destroy the picture. That is precisely 18// why the selftest below refuses to pass on noise reduction alone. 19// 20// RULER: mean absolute local deviation (MALD) vs a 3x3 mean, x1000, integer. Reported BEFORE and AFTER 21// so the claim is a measurement and not an adjective. 22// 23// usage: nx_dedither <in-image> <out.png> [radius=2] [threshold=40] 24// nx_dedither --kat (selftest: 4 teeth, 2 of them anti-vacuity) 25// argv[1]=input (any format nx_img_bytes_to_rgb decodes), argv[2]=output PNG. 26// exit 0 ok | 2 decode/write failure | 3 usage | 1 selftest RED 27// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 28import "nx_syscalls.nx" 29import "nx_img_to_rgb.nx" 30import "nx_png_write.nx" 31import "nx_img_resample.nx" 32 33// SCALE STAGE -- added 2026-08-07 on OCR EVIDENCE, not taste. A neutral OCR instrument 34// (Windows.Media.Ocr, an oracle in the qemu/openssl sense, never a runtime dep) read the archive's 35// prop.gif as "PropertyXET" at native 250x48; ground truth is "PropertyNET", fixed by the 1996 href 36// god.co.uk/propertynet/. At x2 and x3 the SAME instrument reads "PropertyNET" exactly; at x4 and x6 it 37// degrades again to "PropenyXET"/"PropenyNET" as the resampler fuses r+t into n. So the useful band is 38// MEASURED at x2-x3 with a real optimum, not assumed monotonic -- more upscale is NOT more legible. 39// *BICUBIC ON PURPOSE: the evidence above was produced with a bicubic resampler, so shipping bilinear 40// would be quoting a measurement taken with a different instrument than the one that runs. 41const DD_MAXR: i64 = 8 42const DD_MAXSCALE: i64 = 4 43// DITHER THRESHOLD -- CALIBRATED FROM A POPULATION, not from the two examples that inspired the feature. 44// `nx_dedither --census` over 78 real archive assets (JPEG photos, GIF logos, line art, and one known 45// heavily-dithered 1996 photograph) produced: bulk 212-405, then 457, then 549. The known-dithered photo 46// IS the corpus maximum, and the gap between 405 and 457 is the only real gap in the distribution. 47// 450 sits in that gap. This is an EMPIRICAL boundary for this corpus, not a physical constant -- 48// it is quoted with the census that produced it precisely so a future seat can re-derive or move it. 49// CONFIRMATION LOOP (unplanned, and the strongest evidence here): running the filter on that photo drops 50// its altrate 549 -> 388, back into the bulk. The feature detects the property, the filter removes it, 51// and the feature then agrees it is gone -- measured on the same asset, both directions. 52// Erring permissive is CHEAP and deliberate: the sigma filter is measurably INERT on non-dithered art 53// (-22 permille on crisp line art), so a false positive costs disk, while a false negative leaves the 54// fuzz the operator actually complained about. 55const DD_DITHER_MIN: i64 = 450 56const DD_MAXTHR: i64 = 255 57 58func dp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 59func de(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(2,s,n); return 0 } 60func dnum(v: i64) -> i64 { 61 var m: i64 = v 62 if m < 0 { dp("-" as *u8); m = 0 - m } 63 let t: *u8 = sys_mmap(32) 64 var k: i64 = 0 65 if m == 0 { t[0] = 48 as u8; k = 1 } 66 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 67 let b: *u8 = sys_mmap(32) 68 var i: i64 = 0 69 while i < k { b[i] = t[k-1-i]; i = i + 1 } 70 sys_write(1, b, k) 71 return 0 72} 73func dd_atoi(s: *u8) -> i64 { 74 var v: i64 = 0 75 var i: i64 = 0 76 while s[i] != (0 as u8) { 77 let c: i64 = s[i] as i64 78 if c < 48 { return 0 - 1 } 79 if c > 57 { return 0 - 1 } 80 v = v * 10 + (c - 48) 81 i = i + 1 82 } 83 if i == 0 { return 0 - 1 } 84 return v 85} 86 87// MEAN ABSOLUTE LOCAL DEVIATION vs a 3x3 mean, x1000. Border pixels excluded (no clamped mean to bias it). 88// This is the dither ruler: speckle scores high, a smooth gradient scores near zero, and a hard edge also 89// scores high -- which is WHY the selftest cannot rely on this number alone. 90func dd_mald(rgb: *u8, w: i64, h: i64) -> i64 { 91 if w < 3 { return 0 } 92 if h < 3 { return 0 } 93 var acc: i64 = 0 94 var cnt: i64 = 0 95 var y: i64 = 1 96 while y < h - 1 { 97 var x: i64 = 1 98 while x < w - 1 { 99 var c: i64 = 0 100 while c < 3 { 101 var sum: i64 = 0 102 var yy: i64 = y - 1 103 while yy <= y + 1 { 104 var xx: i64 = x - 1 105 while xx <= x + 1 { 106 sum = sum + (rgb[(yy*w + xx)*3 + c] as i64) 107 xx = xx + 1 108 } 109 yy = yy + 1 110 } 111 var d: i64 = (rgb[(y*w + x)*3 + c] as i64) - (sum / 9) 112 if d < 0 { d = 0 - d } 113 acc = acc + d 114 cnt = cnt + 1 115 c = c + 1 116 } 117 x = x + 1 118 } 119 y = y + 1 120 } 121 if cnt == 0 { return 0 } 122 return (acc * 1000) / cnt 123} 124 125// LOCAL MEAN of the 3x3 window at (x,y) for channel c. Callers must keep 1 <= x < w-1, 1 <= y < h-1. 126func dd_localmean(rgb: *u8, w: i64, x: i64, y: i64, c: i64) -> i64 { 127 var sum: i64 = 0 128 var yy: i64 = y - 1 129 while yy <= y + 1 { 130 var xx: i64 = x - 1 131 while xx <= x + 1 { 132 sum = sum + (rgb[(yy*w + xx)*3 + c] as i64) 133 xx = xx + 1 134 } 135 yy = yy + 1 136 } 137 return sum / 9 138} 139 140// MEAN ABSOLUTE GRADIENT OF THE LOCAL-MEAN FIELD, x1000 -- the SECOND ruler, and the one that makes the 141// first one usable. MALD alone cannot identify dither: it reads high for palette speckle AND for genuine 142// text strokes (MEASURED -- the photo scored 6863 and a crisp logo scored 5062, and they need OPPOSITE 143// remedies). The discriminator is what the high frequency is sitting ON: 144// dither = high-frequency noise over a SMOOTH underlying mean -> MALD high, MEANGRAD low 145// edges = high frequency BECAUSE the underlying mean moves -> MALD high, MEANGRAD high 146// So the ratio separates them where either number alone cannot. 147// *A SINGLE STATISTIC THAT TWO OPPOSITE PHENOMENA BOTH MAXIMISE IS NOT A CLASSIFIER, IT IS A COINCIDENCE 148// DETECTOR -- it took a second, independent axis to tell them apart. 149func dd_meangrad(rgb: *u8, w: i64, h: i64) -> i64 { 150 if w < 4 { return 0 } 151 if h < 4 { return 0 } 152 var acc: i64 = 0 153 var cnt: i64 = 0 154 var y: i64 = 1 155 while y < h - 2 { 156 var x: i64 = 1 157 while x < w - 2 { 158 var c: i64 = 0 159 while c < 3 { 160 let m0: i64 = dd_localmean(rgb, w, x, y, c) 161 var dx: i64 = dd_localmean(rgb, w, x+1, y, c) - m0 162 if dx < 0 { dx = 0 - dx } 163 var dy: i64 = dd_localmean(rgb, w, x, y+1, c) - m0 164 if dy < 0 { dy = 0 - dy } 165 acc = acc + dx + dy 166 cnt = cnt + 1 167 c = c + 1 168 } 169 x = x + 1 170 } 171 y = y + 1 172 } 173 if cnt == 0 { return 0 } 174 return (acc * 1000) / cnt 175} 176 177// SIGN-ALTERNATION RATE, permille -- THE discriminator, after two weaker ones were measured and rejected. 178// MALD alone failed: it reads high for dither AND for text strokes (6863 photo vs 5062 logo). 179// MALD/meangrad failed too: only a 1.27x margin, and the photo's mean-gradient came out HIGHER than the 180// logo's because a photograph has real structure everywhere. Both were plausible and both were wrong. 181// *A FEATURE THAT MERELY CORRELATES WITH THE TARGET LOSES TO ONE DERIVED FROM ITS MECHANISM. 182// Dither is not just high-frequency -- it has a specific PHASE. Two palette entries approximate an 183// in-between value by alternating ABOVE and BELOW it on adjacent pixels, so the deviation-from-local-mean 184// FLIPS SIGN nearly every pixel (-> ~1000). Natural photographic texture has no phase discipline (~500 = 185// chance). A hard edge does not alternate at all. That is a property of how dithering WORKS, not a 186// statistic that happens to co-vary with it. 187// Flat regions are EXCLUDED (|deviation| < 2): they have no phase to measure, and counting their rounding 188// noise would drag every image toward chance and destroy the very separation this is for. 189func dd_altrate(rgb: *u8, w: i64, h: i64) -> i64 { 190 if w < 5 { return 0 } 191 if h < 5 { return 0 } 192 var flips: i64 = 0 193 var pairs: i64 = 0 194 var y: i64 = 1 195 while y < h - 1 { 196 var x: i64 = 1 197 while x < w - 2 { 198 var c: i64 = 0 199 while c < 3 { 200 let d0: i64 = (rgb[(y*w+x)*3+c] as i64) - dd_localmean(rgb, w, x, y, c) 201 let d1: i64 = (rgb[(y*w+x+1)*3+c] as i64) - dd_localmean(rgb, w, x+1, y, c) 202 var m0: i64 = d0 203 if m0 < 0 { m0 = 0 - m0 } 204 var m1: i64 = d1 205 if m1 < 0 { m1 = 0 - m1 } 206 var ok: i64 = 1 207 if m0 < 2 { ok = 0 } 208 if m1 < 2 { ok = 0 } 209 if ok == 1 { 210 pairs = pairs + 1 211 var flip: i64 = 0 212 if d0 > 0 { if d1 < 0 { flip = 1 } } 213 if d0 < 0 { if d1 > 0 { flip = 1 } } 214 flips = flips + flip 215 } 216 c = c + 1 217 } 218 x = x + 1 219 } 220 y = y + 1 221 } 222 if pairs == 0 { return 0 } 223 return (flips * 1000) / pairs 224} 225 226// SIGMA FILTER. Window bounds are clamped BEFORE the inner loops so the hot path carries no nested 227// bounds tests -- flat ifs on purpose (a 4-deep nested if is a known nx_cc landmine). 228func dd_sigma(src: *u8, dst: *u8, w: i64, h: i64, r: i64, thr: i64) -> i64 { 229 var y: i64 = 0 230 while y < h { 231 var x: i64 = 0 232 while x < w { 233 let po: i64 = (y*w + x)*3 234 let pr: i64 = src[po] as i64 235 let pg: i64 = src[po+1] as i64 236 let pb: i64 = src[po+2] as i64 237 var y0: i64 = y - r 238 if y0 < 0 { y0 = 0 } 239 var y1: i64 = y + r 240 if y1 > h - 1 { y1 = h - 1 } 241 var x0: i64 = x - r 242 if x0 < 0 { x0 = 0 } 243 var x1: i64 = x + r 244 if x1 > w - 1 { x1 = w - 1 } 245 var sr: i64 = 0 246 var sg: i64 = 0 247 var sb: i64 = 0 248 var n: i64 = 0 249 var yy: i64 = y0 250 while yy <= y1 { 251 var xx: i64 = x0 252 while xx <= x1 { 253 let qo: i64 = (yy*w + xx)*3 254 let qr: i64 = src[qo] as i64 255 let qg: i64 = src[qo+1] as i64 256 let qb: i64 = src[qo+2] as i64 257 var da: i64 = qr - pr 258 if da < 0 { da = 0 - da } 259 var db: i64 = qg - pg 260 if db < 0 { db = 0 - db } 261 var dc: i64 = qb - pb 262 if dc < 0 { dc = 0 - dc } 263 // THRESHOLD IS THE PER-CHANNEL MEAN, NOT THE 3-CHANNEL SUM. The sum form shipped first 264 // and T2 caught it immediately: a grey dither of 120 vs 136 is 16 PER CHANNEL but 48 265 // SUMMED, so a threshold of 40 excluded the very speckle the filter exists to average -- 266 // the organ ran, reported success, and changed nothing. Comparing against thr*3 keeps the 267 // arithmetic exact (no divide, no rounding) while letting the operator think in 0-255 268 // levels, which is the only unit a threshold on 8-bit colour can honestly be quoted in. 269 if da + db + dc <= thr * 3 { sr = sr + qr; sg = sg + qg; sb = sb + qb; n = n + 1 } 270 xx = xx + 1 271 } 272 yy = yy + 1 273 } 274 if n == 0 { sr = pr; sg = pg; sb = pb; n = 1 } 275 dst[po] = (sr/n) as u8 276 dst[po+1] = (sg/n) as u8 277 dst[po+2] = (sb/n) as u8 278 x = x + 1 279 } 280 y = y + 1 281 } 282 return 0 283} 284 285// mean absolute difference between column xa and column xb (edge-contrast probe) 286func dd_coldiff(rgb: *u8, w: i64, h: i64, xa: i64, xb: i64) -> i64 { 287 var acc: i64 = 0 288 var cnt: i64 = 0 289 var y: i64 = 0 290 while y < h { 291 var c: i64 = 0 292 while c < 3 { 293 var d: i64 = (rgb[(y*w + xa)*3 + c] as i64) - (rgb[(y*w + xb)*3 + c] as i64) 294 if d < 0 { d = 0 - d } 295 acc = acc + d 296 cnt = cnt + 1 297 c = c + 1 298 } 299 y = y + 1 300 } 301 if cnt == 0 { return 0 } 302 return acc / cnt 303} 304 305func dd_kat() -> i64 { 306 let w: i64 = 64 307 let h: i64 = 64 308 let a: *u8 = sys_mmap(w*h*3 + 16) 309 let b: *u8 = sys_mmap(w*h*3 + 16) 310 var pass: i64 = 0 311 var total: i64 = 0 312 313 // ---- fixture 1: a DITHERED flat field. Two palette entries (120 and 136) alternating on a 314 // checkerboard -- the exact structure a 1996 encoder produces for a smooth mid-grey backdrop. 315 var y: i64 = 0 316 while y < h { 317 var x: i64 = 0 318 while x < w { 319 var v: i64 = 120 320 if (x + y) % 2 == 0 { v = 136 } 321 let o: i64 = (y*w + x)*3 322 a[o] = v as u8; a[o+1] = v as u8; a[o+2] = v as u8 323 x = x + 1 324 } 325 y = y + 1 326 } 327 let m0: i64 = dd_mald(a, w, h) 328 dd_sigma(a, b, w, h, 2, 40) 329 let m1: i64 = dd_mald(b, w, h) 330 total = total + 1 331 if m0 > 3000 { pass = pass + 1; dp("T1 GREEN dither fixture registers on the ruler (mald=" as *u8); dnum(m0); dp(")\n" as *u8) } else { dp("T1 RED fixture did not register (mald=" as *u8); dnum(m0); dp(")\n" as *u8) } 332 total = total + 1 333 if m1 * 2 < m0 { pass = pass + 1; dp("T2 GREEN dither removed >50pct (" as *u8); dnum(m0); dp(" -> " as *u8); dnum(m1); dp(")\n" as *u8) } else { dp("T2 RED dither NOT removed (" as *u8); dnum(m0); dp(" -> " as *u8); dnum(m1); dp(")\n" as *u8) } 334 335 // ---- fixture 2: ANTI-VACUITY. A hard vertical edge, black|white at x=32. A box blur would smear 336 // this and still score well on T2, so T3 is the tooth that a mere blur CANNOT pass. 337 var y2: i64 = 0 338 while y2 < h { 339 var x2: i64 = 0 340 while x2 < w { 341 var v2: i64 = 0 342 if x2 >= 32 { v2 = 255 } 343 let o2: i64 = (y2*w + x2)*3 344 a[o2] = v2 as u8; a[o2+1] = v2 as u8; a[o2+2] = v2 as u8 345 x2 = x2 + 1 346 } 347 y2 = y2 + 1 348 } 349 let e0: i64 = dd_coldiff(a, w, h, 31, 32) 350 dd_sigma(a, b, w, h, 2, 40) 351 let e1: i64 = dd_coldiff(b, w, h, 31, 32) 352 total = total + 1 353 if e1 * 10 >= e0 * 9 { pass = pass + 1; dp("T3 GREEN edge PRESERVED (" as *u8); dnum(e0); dp(" -> " as *u8); dnum(e1); dp(") -- a box blur cannot pass this\n" as *u8) } else { dp("T3 RED edge was smeared (" as *u8); dnum(e0); dp(" -> " as *u8); dnum(e1); dp(")\n" as *u8) } 354 355 // ---- fixture 3: ANTI-VACUITY. An ALREADY-CLEAN flat field must come out unchanged: a filter that 356 // invents texture on smooth input would be worse than the disease. 357 var y3: i64 = 0 358 while y3 < h { 359 var x3: i64 = 0 360 while x3 < w { 361 let o3: i64 = (y3*w + x3)*3 362 a[o3] = 128 as u8; a[o3+1] = 128 as u8; a[o3+2] = 128 as u8 363 x3 = x3 + 1 364 } 365 y3 = y3 + 1 366 } 367 dd_sigma(a, b, w, h, 2, 40) 368 var flat: i64 = 1 369 var i3: i64 = 0 370 while i3 < w*h*3 { if b[i3] != (128 as u8) { flat = 0; i3 = w*h*3 } else { i3 = i3 + 1 } } 371 total = total + 1 372 if flat == 1 { pass = pass + 1; dp("T4 GREEN clean input unchanged (no invented texture)\n" as *u8) } else { dp("T4 RED filter altered an already-clean field\n" as *u8) } 373 374 // ---- T5/T6: the PHASE discriminator, with a control that isolates phase and NOTHING else. 375 // Both fixtures use the SAME two palette entries (120/136) and therefore the same amplitude and 376 // nearly the same MALD. They differ ONLY in phase: T5 alternates every pixel (real dither), T6 uses 377 // 2x2 blocks (same colours, same contrast, no per-pixel alternation). If altrate could be fooled by 378 // amplitude or palette size it would score these two alike -- so T6 is what makes T5 mean something. 379 var y5: i64 = 0 380 while y5 < h { 381 var x5: i64 = 0 382 while x5 < w { 383 var v5: i64 = 120 384 if (x5 + y5) % 2 == 0 { v5 = 136 } 385 let o5: i64 = (y5*w + x5)*3 386 a[o5] = v5 as u8; a[o5+1] = v5 as u8; a[o5+2] = v5 as u8 387 var v6: i64 = 120 388 if ((x5/2) + (y5/2)) % 2 == 0 { v6 = 136 } 389 let o6: i64 = (y5*w + x5)*3 390 b[o6] = v6 as u8; b[o6+1] = v6 as u8; b[o6+2] = v6 as u8 391 x5 = x5 + 1 392 } 393 y5 = y5 + 1 394 } 395 let ar_dith: i64 = dd_altrate(a, w, h) 396 let ar_blok: i64 = dd_altrate(b, w, h) 397 total = total + 1 398 if ar_dith >= 900 { pass = pass + 1; dp("T5 GREEN per-pixel dither has dither PHASE (altrate=" as *u8); dnum(ar_dith); dp("/1000)\n" as *u8) } else { dp("T5 RED dither phase not detected (altrate=" as *u8); dnum(ar_dith); dp(")\n" as *u8) } 399 total = total + 1 400 if ar_blok + 200 < ar_dith { pass = pass + 1; dp("T6 GREEN 2x2 blocks SAME colours REJECTED on phase (" as *u8); dnum(ar_blok); dp(" vs " as *u8); dnum(ar_dith); dp(") -- amplitude alone cannot pass\n" as *u8) } else { dp("T6 RED phase control not separated (" as *u8); dnum(ar_blok); dp(" vs " as *u8); dnum(ar_dith); dp(")\n" as *u8) } 401 402 // ---- T7: SELECTIVITY WITH BOTH SIGNALS PRESENT AT ONCE. 403 // T2 proves noise falls on a pure-dither fixture; T3 proves an edge survives on a pure-edge fixture. 404 // Neither proves the filter can tell them apart WHEN THEY OVERLAP, which is the only case a real 405 // photograph ever presents. This fixture is a hard edge (60|200) with per-pixel dither laid over 406 // BOTH sides, and it demands the two outcomes SIMULTANEOUSLY: the speckle goes and the edge stays. 407 // A filter that passed T2 and T3 separately can still fail here by smearing the edge wherever noise 408 // sits on it -- and an image is nothing but edges with noise sitting on them. 409 var y7: i64 = 0 410 while y7 < h { 411 var x7: i64 = 0 412 while x7 < w { 413 var base: i64 = 60 414 if x7 >= 32 { base = 200 } 415 var v7: i64 = base - 8 416 if (x7 + y7) % 2 == 0 { v7 = base + 8 } 417 let o7: i64 = (y7*w + x7)*3 418 a[o7] = v7 as u8; a[o7+1] = v7 as u8; a[o7+2] = v7 as u8 419 x7 = x7 + 1 420 } 421 y7 = y7 + 1 422 } 423 let n7a: i64 = dd_mald(a, w, h) 424 let e7a: i64 = dd_coldiff(a, w, h, 20, 44) 425 dd_sigma(a, b, w, h, 2, 40) 426 let n7b: i64 = dd_mald(b, w, h) 427 let e7b: i64 = dd_coldiff(b, w, h, 20, 44) 428 total = total + 1 429 var sel: i64 = 0 430 if n7b * 2 < n7a { if e7b * 10 >= e7a * 9 { sel = 1 } } 431 if sel == 1 { pass = pass + 1; dp("T7 GREEN SELECTIVE with both present: noise " as *u8); dnum(n7a); dp("->" as *u8); dnum(n7b); dp(" while edge " as *u8); dnum(e7a); dp("->" as *u8); dnum(e7b); dp(" held\n" as *u8) } else { dp("T7 RED not selective: noise " as *u8); dnum(n7a); dp("->" as *u8); dnum(n7b); dp(" edge " as *u8); dnum(e7a); dp("->" as *u8); dnum(e7b); dp("\n" as *u8) } 432 433 dp("nx_dedither KAT " as *u8); dnum(pass); dp("/" as *u8); dnum(total); dp("\n" as *u8) 434 if pass == total { return 0 } 435 return 1 436} 437 438// CLASSIFY: report both rulers + their ratio for one image. NO VERDICT IS PRINTED YET, ON PURPOSE. 439// A threshold separating DITHERED from CLEAN has not been MEASURED, and a constant invented at authoring 440// time would be indistinguishable in the output from one derived from evidence -- which is precisely the 441// failure this organ already committed once (a threshold whose unit nobody had stated). 442// *AN UNCALIBRATED CLASSIFIER MUST REPORT NUMBERS, NEVER VERDICTS: a verdict claims a decision boundary 443// exists; numbers only claim what was observed. Calibrate against known assets, THEN earn the verdict. 444func dd_classify(path: *u8) -> i64 { 445 let wh: *i64 = sys_mmap(32) as *i64 446 let rgb: *u8 = nx_img_to_rgb(path, wh) 447 if rgb == (0 as *u8) { de("nx_dedither: UNDECODABLE\n" as *u8); return 2 } 448 let w: i64 = wh[0] 449 let h: i64 = wh[1] 450 if w < 4 { de("nx_dedither: image too small to classify\n" as *u8); return 2 } 451 if h < 4 { de("nx_dedither: image too small to classify\n" as *u8); return 2 } 452 let mald: i64 = dd_mald(rgb, w, h) 453 let mg: i64 = dd_meangrad(rgb, w, h) 454 let ratio: i64 = (mald * 1000) / (mg + 1) 455 dp("{\"organ\":\"nx_dedither\",\"mode\":\"classify\",\"w\":" as *u8); dnum(w) 456 dp(",\"h\":" as *u8); dnum(h) 457 dp(",\"mald_x1000\":" as *u8); dnum(mald) 458 dp(",\"meangrad_x1000\":" as *u8); dnum(mg) 459 dp(",\"dither_ratio_x1000\":" as *u8); dnum(ratio) 460 let ar: i64 = dd_altrate(rgb, w, h) 461 let pal: i64 = dd_is_palette(path) 462 dp(",\"altrate_permil\":" as *u8); dnum(ar) 463 dp(",\"palette_format\":" as *u8); dnum(pal) 464 dp(",\"threshold\":" as *u8); dnum(DD_DITHER_MIN) 465 // The verdict is now EARNED: the boundary came from an 78-asset census, and it is printed alongside 466 // the number and the threshold so a reader can always re-judge it rather than take the word for it. 467 var vd: i64 = 1 468 if ar < DD_DITHER_MIN { vd = 0 } 469 if pal == 0 { vd = 0 } 470 if vd == 1 { dp(",\"verdict\":\"DITHERED\"}\n" as *u8) } 471 if vd == 0 { if pal == 0 { dp(",\"verdict\":\"NON-PALETTE\"}\n" as *u8) } else { dp(",\"verdict\":\"CLEAN\"}\n" as *u8) } } 472 return 0 473} 474 475// ---- CENSUS ---------------------------------------------------------------------------------- 476// WHY THIS EXISTS: two real samples is not a calibration. Classifying the archive one file per 477// invocation cannot produce a DISTRIBUTION, and without a distribution any threshold is an invented 478// constant wearing a measurement's clothes -- the exact failure this organ already committed once. 479// *A CLASSIFIER IS CALIBRATED BY A POPULATION, NOT BY THE TWO EXAMPLES THAT INSPIRED IT. 480// BOUNDED BY CONSTRUCTION (the nx_gate_build_sweep idiom): maxn is REQUIRED and must be > 0, so a 481// census can never run away over a corpus of unknown size. 482// Undecodable entries print SKIP with a reason rather than vanishing -- a census that silently drops 483// what it could not read reports a cleaner corpus than exists. 484const DD_DIRBUF: i64 = 262144 485const DD_RECLEN_OFF: i64 = 16 486const DD_NAME_OFF: i64 = 19 487 488func dd_cat(dst: *u8, off: i64, src: *u8) -> i64 { var o: i64=off; var j: i64=0; while src[j]!=(0 as u8) { dst[o]=src[j]; o=o+1; j=j+1 } return o } 489 490func dd_census(dir: *u8, maxn: i64) -> i64 { 491 if maxn <= 0 { de("nx_dedither: --census REFUSES an unbounded run; pass maxfiles > 0\n" as *u8); return 3 } 492 let fd: i64 = sys_openat_rd(dir) 493 if fd < 0 { de("nx_dedither: cannot open directory\n" as *u8); return 2 } 494 let db: *u8 = sys_mmap(DD_DIRBUF) 495 dp("# altrate mald meangrad w h name\n" as *u8) 496 let path: *u8 = sys_mmap(1024) 497 let wh: *i64 = sys_mmap(32) as *i64 498 var shown: i64 = 0 499 // Same getdents64 batching contract as dd_sweep -- see the note there. Fixed in BOTH, because a 500 // census and a sweep that disagree about how much of a directory exists is worse than either bug alone. 501 var batches: i64 = 0 502 var go: i64 = 1 503 while go == 1 { 504 let n: i64 = sys_getdents64(fd, db, DD_DIRBUF) 505 if n <= 0 { go = 0 } else { 506 batches = batches + 1 507 var p: i64 = 0 508 while p < n { 509 let reclen: i64 = (db[p+DD_RECLEN_OFF] as i64) | ((db[p+DD_RECLEN_OFF+1] as i64) << 8) 510 if reclen <= 0 { p = n } else { 511 let nm: i64 = p + DD_NAME_OFF 512 var skip: i64 = 0 513 if (db[nm] as i64) == 46 { skip = 1 } 514 if shown >= maxn { skip = 1 } 515 if skip == 0 { 516 var o: i64 = dd_cat(path, 0, dir) 517 path[o] = 47 as u8; o = o + 1 518 var w2: i64 = nm 519 while db[w2] != (0 as u8) { path[o]=db[w2]; o=o+1; w2=w2+1 } 520 path[o] = 0 as u8 521 let rgb: *u8 = nx_img_to_rgb(path, wh) 522 if rgb == (0 as *u8) { 523 dp("SKIP undecodable " as *u8) 524 var w3: i64 = nm 525 while db[w3] != (0 as u8) { sys_write(1, ((db as i64)+w3) as *u8, 1); w3=w3+1 } 526 dp("\n" as *u8) 527 } else { 528 let iw: i64 = wh[0] 529 let ih: i64 = wh[1] 530 dnum(dd_altrate(rgb, iw, ih)); dp(" " as *u8) 531 dnum(dd_mald(rgb, iw, ih)); dp(" " as *u8) 532 dnum(dd_meangrad(rgb, iw, ih)); dp(" " as *u8) 533 dnum(iw); dp(" " as *u8); dnum(ih); dp(" " as *u8) 534 var w4: i64 = nm 535 while db[w4] != (0 as u8) { sys_write(1, ((db as i64)+w4) as *u8, 1); w4=w4+1 } 536 dp("\n" as *u8) 537 } 538 shown = shown + 1 539 } 540 p = p + reclen 541 } 542 } 543 } 544 } 545 sys_close(fd) 546 if batches == 0 { de("nx_dedither: not a directory (getdents64 refused)\n" as *u8); return 2 } 547 dp("# census rows=" as *u8); dnum(shown); dp("\n" as *u8) 548 return 0 549} 550 551// ---- FORMAT PRIOR ------------------------------------------------------------------------------ 552// MEASURED REFUTATION (2026-08-07): the 450 threshold was calibrated on page3.com (78 assets, bulk 553// topped out at 405, the two true positives BOTH .gif). Run against megastar.co.uk it collapsed -- 554// small heavily-compressed JPEGs score 447/468/476/483/503/509/511 routinely. A threshold that 555// separates one corpus and misfires on the next is not a classifier, it is a fitted constant. 556// *A BOUNDARY CALIBRATED ON ONE POPULATION MUST BE RE-TESTED ON A SECOND BEFORE IT IS BELIEVED -- 557// the second corpus is where a fit stops being evidence and starts being a coincidence. 558// The fix is a PRIOR FROM THE MECHANISM, not a retuned number: palette dithering is something an 559// INDEXED format does -- approximating an unavailable colour by alternating two palette entries. 560// JPEG has no palette at all (DCT coefficients), so whatever raises its alternation score is ringing 561// and blocking, which a sigma filter is the wrong remedy for anyway. Gating on the container removes 562// every observed false positive by construction and cannot be tuned away by a future corpus. 563// Sniffed from MAGIC BYTES, never the extension -- .gif files that are secretly JPEG are exactly the 564// kind of thing an archive is full of, and the extension is a claim while the header is evidence. 565func dd_is_palette(path: *u8) -> i64 { 566 let fd: i64 = sys_openat_rd(path) 567 if fd < 0 { return 0 } 568 let b: *u8 = sys_mmap(16) 569 let n: i64 = sys_read(fd, b, 8) 570 sys_close(fd) 571 if n < 4 { return 0 } 572 // GIF87a / GIF89a -- the dominant 1990s palette container 573 var g: i64 = 1 574 if (b[0] as i64) != 71 { g = 0 } 575 if (b[1] as i64) != 73 { g = 0 } 576 if (b[2] as i64) != 70 { g = 0 } 577 if g == 1 { return 1 } 578 // BMP ("BM") -- indexed at 1/4/8bpp; truecolour BMPs simply will not score as dithered 579 var m: i64 = 1 580 if (b[0] as i64) != 66 { m = 0 } 581 if (b[1] as i64) != 77 { m = 0 } 582 if m == 1 { return 1 } 583 // PCX (0x0A) -- era palette format 584 if (b[0] as i64) == 10 { return 1 } 585 return 0 586} 587 588// does name end with sfx? 589func dd_endswith(nm: *u8, sfx: *u8) -> i64 { 590 var ln: i64 = 0 591 while nm[ln] != (0 as u8) { ln = ln + 1 } 592 var ls: i64 = 0 593 while sfx[ls] != (0 as u8) { ls = ls + 1 } 594 if ln < ls { return 0 } 595 var i: i64 = 0 596 while i < ls { 597 if nm[ln-ls+i] != sfx[i] { return 0 } 598 i = i + 1 599 } 600 return 1 601} 602 603// ---- SWEEP ------------------------------------------------------------------------------------- 604// Enhance ONLY what the census says needs it. Blanket enhancement was measured to be waste: the sigma 605// filter moves crisp line art by -22 permille, so re-encoding 78 logos to "fix" them buys nothing and 606// costs disk. The classifier decides per asset, and the DECISION IS PRINTED for every file either way -- 607// a sweep that logs only what it changed cannot be audited for what it silently skipped. 608// IDEMPOTENT (rule 10): outputs are skipped by suffix, so a second run over the same directory is a 609// no-op rather than a cascade of .enh.enh.png. THE ORIGINAL IS NEVER WRITTEN. 610func dd_sweep(dir: *u8, maxn: i64, r: i64, thr: i64, scale: i64) -> i64 { 611 if maxn <= 0 { de("nx_dedither: --sweep REFUSES an unbounded run; pass maxfiles > 0\n" as *u8); return 3 } 612 let fd: i64 = sys_openat_rd(dir) 613 if fd < 0 { de("nx_dedither: cannot open directory\n" as *u8); return 2 } 614 let db: *u8 = sys_mmap(DD_DIRBUF) 615 let inp: *u8 = sys_mmap(1024) 616 let outp: *u8 = sys_mmap(1024) 617 let wh: *i64 = sys_mmap(32) as *i64 618 var seen: i64 = 0 619 var enh: i64 = 0 620 var clean: i64 = 0 621 var nonpal: i64 = 0 622 var bad: i64 = 0 623 // *ONE getdents64 CALL IS NOT A DIRECTORY LISTING. The kernel returns as many entries as fit the 624 // buffer and expects to be called again until it answers 0. A single call happened to cover both 625 // archive dirs (69 and 77 entries) ONLY because they are small -- on a corpus large enough to matter 626 // this would have processed a PREFIX and then printed a confident total for it. That is precisely the 627 // failure this organ warns about two functions up: a census that drops what it did not read reports a 628 // cleaner corpus than exists. It passed its first two real runs by luck, which is the worst way to 629 // pass. Loop until the kernel says stop, and keep the fd open across calls. 630 var batches: i64 = 0 631 var go: i64 = 1 632 while go == 1 { 633 let n: i64 = sys_getdents64(fd, db, DD_DIRBUF) 634 if n <= 0 { go = 0 } else { 635 batches = batches + 1 636 var p: i64 = 0 637 while p < n { 638 let reclen: i64 = (db[p+DD_RECLEN_OFF] as i64) | ((db[p+DD_RECLEN_OFF+1] as i64) << 8) 639 if reclen <= 0 { p = n } else { 640 let nmo: i64 = p + DD_NAME_OFF 641 let nm: *u8 = ((db as i64) + nmo) as *u8 642 var skip: i64 = 0 643 if (nm[0] as i64) == 46 { skip = 1 } 644 if dd_endswith(nm, ".enh.png" as *u8) == 1 { skip = 1 } 645 if seen >= maxn { skip = 1 } 646 if skip == 0 { 647 seen = seen + 1 648 var io: i64 = dd_cat(inp, 0, dir) 649 inp[io] = 47 as u8; io = io + 1 650 io = dd_cat(inp, io, nm) 651 inp[io] = 0 as u8 652 let rgb: *u8 = nx_img_to_rgb(inp, wh) 653 if rgb == (0 as *u8) { 654 bad = bad + 1 655 dp("SKIP-UNDECODABLE " as *u8); dp(nm); dp("\n" as *u8) 656 } else { 657 let iw: i64 = wh[0] 658 let ih: i64 = wh[1] 659 let ar: i64 = dd_altrate(rgb, iw, ih) 660 let pal: i64 = dd_is_palette(inp) 661 var doit: i64 = 1 662 if ar < DD_DITHER_MIN { doit = 0 } 663 if pal == 0 { doit = 0 } 664 if doit == 0 { 665 // COUNT THE TWO CAUSES SEPARATELY. The per-line log already names which reason 666 // applied, but the SUMMARY collapsed both into "skipped_clean" -- so a run that 667 // rejected 60 JPEGs on format read as though it had inspected 60 clean assets and 668 // found them fine. A total that merges two causes is a total that cannot be acted on. 669 if pal == 0 { nonpal = nonpal + 1 } else { clean = clean + 1 } 670 // NAME WHICH REASON. "Skipped" without a cause is unauditable, and these two have 671 // opposite remedies: a low score means the asset is genuinely fine, a non-palette 672 // container means this organ is simply the wrong tool for whatever it does have. 673 if pal == 0 { dp("SKIP-NONPALETTE altrate=" as *u8) } else { dp("SKIP-CLEAN altrate=" as *u8) } 674 dnum(ar); dp(" " as *u8); dp(nm); dp("\n" as *u8) 675 } else { 676 let before: i64 = dd_mald(rgb, iw, ih) 677 let dst: *u8 = sys_mmap(iw*ih*3 + 16) 678 dd_sigma(rgb, dst, iw, ih, r, thr) 679 let after: i64 = dd_mald(dst, iw, ih) 680 var ow: i64 = iw 681 var oh: i64 = ih 682 var fin: *u8 = dst 683 if scale > 1 { 684 ow = iw * scale 685 oh = ih * scale 686 fin = sys_mmap(ow*oh*3 + 16) 687 nx_img_scale_bicubic(dst, iw, ih, 3, fin, ow, oh) 688 } 689 var oo: i64 = dd_cat(outp, 0, dir) 690 outp[oo] = 47 as u8; oo = oo + 1 691 oo = dd_cat(outp, oo, nm) 692 oo = dd_cat(outp, oo, ".enh.png" as *u8) 693 outp[oo] = 0 as u8 694 if nx_png_write_rgb(outp, fin, ow, oh) != 0 { 695 bad = bad + 1 696 dp("WRITE-FAILED " as *u8); dp(nm); dp("\n" as *u8) 697 } else { 698 enh = enh + 1 699 dp("ENHANCED altrate=" as *u8); dnum(ar) 700 dp(" mald " as *u8); dnum(before); dp("->" as *u8); dnum(after) 701 dp(" out " as *u8); dnum(ow); dp("x" as *u8); dnum(oh) 702 dp(" " as *u8); dp(nm); dp(".enh.png\n" as *u8) 703 } 704 } 705 } 706 } 707 p = p + reclen 708 } 709 } 710 } 711 } 712 sys_close(fd) 713 if batches == 0 { de("nx_dedither: not a directory (getdents64 refused)\n" as *u8); return 2 } 714 dp("# sweep seen=" as *u8); dnum(seen) 715 dp(" enhanced=" as *u8); dnum(enh) 716 dp(" skipped_clean=" as *u8); dnum(clean) 717 dp(" skipped_nonpalette=" as *u8); dnum(nonpal) 718 dp(" undecodable=" as *u8); dnum(bad) 719 dp(" threshold=" as *u8); dnum(DD_DITHER_MIN) 720 dp("\n" as *u8) 721 return 0 722} 723 724func main(argc: i64, argv: *i64) -> i64 { 725 if argc < 2 { de("usage: nx_dedither <in-image> <out.png> [radius=2] [threshold=40] | --kat\n" as *u8); sys_exit(3); return 3 } 726 let a1: *u8 = argv[1] as *u8 727 if a1[0] == (45 as u8) { 728 if a1[2] == (107 as u8) { let rc: i64 = dd_kat(); sys_exit(rc); return rc } 729 if a1[2] == (99 as u8) { 730 if argc < 3 { de("usage: nx_dedither --classify <image> | --census <dir> <maxfiles>\n" as *u8); sys_exit(3); return 3 } 731 // --classify and --census both begin "--c"; the 4th char disambiguates ('l' vs 'e'). 732 if a1[3] == (101 as u8) { 733 var mx: i64 = 0 734 if argc >= 4 { mx = dd_atoi(argv[3] as *u8) } 735 let rc3: i64 = dd_census(argv[2] as *u8, mx) 736 sys_exit(rc3); return rc3 737 } 738 let rc2: i64 = dd_classify(argv[2] as *u8) 739 sys_exit(rc2); return rc2 740 } 741 if a1[2] == (115 as u8) { 742 if argc < 4 { de("usage: nx_dedither --sweep <dir> <maxfiles> [radius=2] [threshold=40] [scale=1]\n" as *u8); sys_exit(3); return 3 } 743 var smx: i64 = dd_atoi(argv[3] as *u8) 744 var sr: i64 = 2 745 var sthr: i64 = 40 746 var ssc: i64 = 1 747 if argc >= 5 { sr = dd_atoi(argv[4] as *u8) } 748 if argc >= 6 { sthr = dd_atoi(argv[5] as *u8) } 749 if argc >= 7 { ssc = dd_atoi(argv[6] as *u8) } 750 if sr < 0 { de("nx_dedither: REFUSED bad radius\n" as *u8); sys_exit(3); return 3 } 751 if sr > DD_MAXR { de("nx_dedither: REFUSED radius > 8\n" as *u8); sys_exit(3); return 3 } 752 if ssc < 1 { de("nx_dedither: REFUSED scale < 1\n" as *u8); sys_exit(3); return 3 } 753 if ssc > DD_MAXSCALE { de("nx_dedither: REFUSED scale > 4 (OCR-measured optimum is x2-x3)\n" as *u8); sys_exit(3); return 3 } 754 let rc4: i64 = dd_sweep(argv[2] as *u8, smx, sr, sthr, ssc) 755 sys_exit(rc4); return rc4 756 } 757 de("nx_dedither: unknown flag (--kat | --classify <image> | --census <dir> <n> | --sweep <dir> <n>)\n" as *u8); sys_exit(3); return 3 758 } 759 if argc < 3 { de("usage: nx_dedither <in-image> <out.png> [radius=2] [threshold=40]\n" as *u8); sys_exit(3); return 3 } 760 let outp: *u8 = argv[2] as *u8 761 var r: i64 = 2 762 var thr: i64 = 40 763 if argc >= 4 { r = dd_atoi(argv[3] as *u8) } 764 if argc >= 5 { thr = dd_atoi(argv[4] as *u8) } 765 // REFUSE nonsense rather than clamp silently: a caller who typed a bad radius wants to know. 766 if r < 0 { de("nx_dedither: REFUSED bad radius\n" as *u8); sys_exit(3); return 3 } 767 if r > DD_MAXR { de("nx_dedither: REFUSED radius > 8\n" as *u8); sys_exit(3); return 3 } 768 if thr < 0 { de("nx_dedither: REFUSED bad threshold\n" as *u8); sys_exit(3); return 3 } 769 if thr > DD_MAXTHR { de("nx_dedither: REFUSED threshold > 255 (it is a per-channel level, 0-255)\n" as *u8); sys_exit(3); return 3 } 770 var scale: i64 = 1 771 if argc >= 6 { scale = dd_atoi(argv[5] as *u8) } 772 if scale < 1 { de("nx_dedither: REFUSED scale < 1\n" as *u8); sys_exit(3); return 3 } 773 // REFUSE past the MEASURED band rather than silently obeying. x4 and x6 were both tested and both 774 // read WORSE than x3 on the OCR instrument, so a caller asking for x8 is asking for a degradation 775 // this organ has already measured. The ceiling is evidence, not caution. 776 if scale > DD_MAXSCALE { de("nx_dedither: REFUSED scale > 4 (OCR-measured optimum is x2-x3; x4+ degrades)\n" as *u8); sys_exit(3); return 3 } 777 778 let wh: *i64 = sys_mmap(32) as *i64 779 let src: *u8 = nx_img_to_rgb(a1, wh) 780 if src == (0 as *u8) { de("nx_dedither: UNDECODABLE (format unsupported or file unreadable)\n" as *u8); sys_exit(2); return 2 } 781 let w: i64 = wh[0] 782 let h: i64 = wh[1] 783 if w <= 0 { de("nx_dedither: bad width\n" as *u8); sys_exit(2); return 2 } 784 if h <= 0 { de("nx_dedither: bad height\n" as *u8); sys_exit(2); return 2 } 785 786 let before: i64 = dd_mald(src, w, h) 787 let dst: *u8 = sys_mmap(w*h*3 + 16) 788 dd_sigma(src, dst, w, h, r, thr) 789 // MALD is measured on the FILTERED pixels at NATIVE resolution, before any upscale. Measuring it 790 // after resampling would score the resampler's own smoothing as if the filter had earned it. 791 let after: i64 = dd_mald(dst, w, h) 792 // *NOISE REDUCTION ALONE IS NOT A QUALITY MEASURE -- A FILTER THAT DESTROYS THE PICTURE SCORES 793 // PERFECTLY ON IT. Reporting one number invites exactly the conclusion the number cannot support. 794 // The second axis is the mean-gradient of the local-mean field: it tracks real STRUCTURE (edges, 795 // features), which a selective filter must LEAVE ALONE while it removes speckle. Measured on the 796 // 1996 photo the two move at very different rates -- noise falls to 38 percent while structure holds 797 // at 82 -- and that GAP, not the reduction, is what says the filter is selective rather than a blur. 798 // Quoted together so the trade-off is visible at the point of decision instead of inferred later. 799 let mg_before: i64 = dd_meangrad(src, w, h) 800 let mg_after: i64 = dd_meangrad(dst, w, h) 801 var ow: i64 = w 802 var oh: i64 = h 803 var final: *u8 = dst 804 if scale > 1 { 805 ow = w * scale 806 oh = h * scale 807 final = sys_mmap(ow*oh*3 + 16) 808 if nx_img_scale_bicubic(dst, w, h, 3, final, ow, oh) != 0 { de("nx_dedither: SCALE FAILED\n" as *u8); sys_exit(2); return 2 } 809 } 810 if nx_png_write_rgb(outp, final, ow, oh) != 0 { de("nx_dedither: PNG WRITE FAILED\n" as *u8); sys_exit(2); return 2 } 811 812 var red: i64 = 0 813 if before > 0 { red = ((before - after) * 1000) / before } 814 dp("{\"organ\":\"nx_dedither\",\"w\":" as *u8); dnum(w) 815 dp(",\"h\":" as *u8); dnum(h) 816 dp(",\"radius\":" as *u8); dnum(r) 817 dp(",\"threshold\":" as *u8); dnum(thr) 818 dp(",\"mald_before_x1000\":" as *u8); dnum(before) 819 dp(",\"mald_after_x1000\":" as *u8); dnum(after) 820 dp(",\"noise_reduction_permil\":" as *u8); dnum(red) 821 var keep: i64 = 1000 822 if mg_before > 0 { keep = (mg_after * 1000) / mg_before } 823 dp(",\"meangrad_before_x1000\":" as *u8); dnum(mg_before) 824 dp(",\"meangrad_after_x1000\":" as *u8); dnum(mg_after) 825 dp(",\"structure_retained_permil\":" as *u8); dnum(keep) 826 dp(",\"scale\":" as *u8); dnum(scale) 827 dp(",\"out_w\":" as *u8); dnum(ow) 828 dp(",\"out_h\":" as *u8); dnum(oh) 829 dp(",\"original_untouched\":1}\n" as *u8) 830 return 0 831}