code wiki / (root) / nx_imgcorpus.nx

nx_imgcorpus.nx source

↩ module page · 89 lines · 4067 B

1// nx_imgcorpus.nx -- DETERMINISTIC SYNTHETIC IMAGE CORPUS for reverse-image measurement. 2// 3// Every organ that measures the reverse-image stack (the ruler, the engine gate, the CLI self-test) 4// must agree on what "an image" is, or their numbers are not comparable. One generator, imported 5// everywhere. 6// 7// WHY SYNTHETIC: a fixture corpus on disk makes a gate environment-dependent (missing file -> RED for 8// a reason unrelated to the code) and makes results irreproducible across machines. A procedural 9// corpus is byte-identical on the laptop, in the buildroot, and on the NAS, forever. 10// 11// WHY *THIS* CORPUS: a perceptual-hash measurement is only meaningful on images with MULTI-SCALE 12// structure. Flat images would flatter every hash (nothing to lose under transformation); pure noise 13// would destroy every hash (nothing to preserve). Real photographs have a large-scale layout, a 14// mid-scale structure, and fine detail, so each image here is the sum of exactly those three: 15// coarse -- an 8x8 random control grid, nearest-upscaled: the large-scale light/dark layout 16// struct -- stripes, checkerboard, concentric rings, or a diagonal ramp, at a per-image period 17// fine -- low-amplitude deterministic detail 18// The measured mean pairwise dHash Hamming over the resulting corpus is ~32 of 64 -- exactly the 19// "unrelated images" separation the shipped nx_phash_test observes on real photographs (33), which is 20// the evidence that this corpus is a fair stand-in rather than a convenient one. 21// license_tier: ORIGINAL 22import "syscalls.nx" 23 24func ic_rng(s: *i64) -> i64 { let x: i64 = s[0] * 0x5851F42D4C957F2D + 0x14057B7EF767814F; s[0] = x; return x } 25func ic_pos(s: *i64) -> i64 { var r: i64 = ic_rng(s) >> 13; if r < 0 { r = 0 - r } return r } 26 27// Generate corpus image `idx` (w x h grayscale) into out. Same idx -> byte-identical image, always. 28func nx_imgcorpus_gen(idx: i64, w: i64, h: i64, out: *u8) -> i64 { 29 let s: *i64 = sys_mmap(8) as *i64 30 s[0] = 0x9E3779B97F4A7C15 + idx * 0x100000001B3 31 let grid: *i64 = sys_mmap(8 * 64) as *i64 32 var g: i64 = 0 33 while g < 64 { grid[g] = ic_pos(s) % 256; g = g + 1 } 34 35 let fam: i64 = idx % 4 36 let per: i64 = 4 + (idx % 13) 37 var y: i64 = 0 38 while y < h { 39 var x: i64 = 0 40 while x < w { 41 let gx: i64 = x * 8 / w 42 let gy: i64 = y * 8 / h 43 let coarse: i64 = grid[gy * 8 + gx] 44 45 var st: i64 = 0 46 if fam == 0 { if ((x / per) % 2) == 0 { st = 200 } else { st = 40 } } 47 if fam == 1 { if (((x / per) + (y / per)) % 2) == 0 { st = 210 } else { st = 30 } } 48 if fam == 2 { 49 let dx: i64 = x - w / 2 50 let dy: i64 = y - h / 2 51 st = ((dx * dx + dy * dy) / (per * 2)) % 256 52 } 53 if fam == 3 { st = ((x + y) * 256 / (w + h) + idx * 7) % 256 } 54 55 let fine: i64 = (ic_pos(s) >> 3) % 24 56 var v: i64 = coarse * 5 / 8 + st * 3 / 8 + fine - 12 57 if v < 0 { v = 0 } 58 if v > 255 { v = 255 } 59 out[y * w + x] = v as u8 60 x = x + 1 61 } 62 y = y + 1 63 } 64 return 0 65} 66 67// Allocate and generate in one call (the common case for corpus loops). 68func nx_imgcorpus_new(idx: i64, w: i64, h: i64) -> *u8 { 69 let b: *u8 = sys_mmap(w * h) 70 nx_imgcorpus_gen(idx, w, h, b) 71 return b 72} 73 74// Naive RGB companion for tiers that need chroma (colour descriptors): three deterministic channel 75// rotations of the same luminance field, so an RGB-consuming tier has real per-channel variation 76// without the corpus needing a second generator to keep in sync. 77func nx_imgcorpus_rgb(idx: i64, w: i64, h: i64, gray: *u8) -> *u8 { 78 let rgb: *u8 = sys_mmap(w * h * 3) 79 let n: i64 = w * h 80 var i: i64 = 0 81 while i < n { 82 let v: i64 = gray[i] as i64 83 rgb[i * 3] = v as u8 84 rgb[i * 3 + 1] = ((v * 3 / 4 + (idx % 7) * 16) % 256) as u8 85 rgb[i * 3 + 2] = ((v * 5 / 8 + (idx % 11) * 20) % 256) as u8 86 i = i + 1 87 } 88 return rgb 89}