code wiki / (root) / nx_photogrambench.nx

nx_photogrambench.nx source

↩ module page · 536 lines · 26154 B

1// nx_photogrambench.nx -- THE PHOTOGRAMMETRY RULER. Measures where the Nishi stack stands on the FULL 2// photo->metric-3D pipeline vs the July-2026 SOTA bar (COLMAP / RealityCapture / Metashape / OpenMVS / 3// MVSNet / VGGT-Pi3 feed-forward / 3DGS), so the build order is CHOSEN BY MEASUREMENT, not by taste. 4// 5// WHY THIS INSTRUMENT AND NOT THE OLD ONE: nx_cadtwin_census scores a photogrammetry-adjacent slice by 6// HAND-TYPED cell values, and its citations drift from what the files actually do -- it credits classical 7// image features to "nx_descriptor", which is a TEXT descriptor profiler for the writing arc (dx_profile 8// over a term lexicon), with no pixel in it. Presence-scoring plus hand-typed cells is exactly the 9// author-optimism the operator liar-killed. So this ruler is built the other way round: 10// 11// 1. EVERY score is COMPUTED FROM DISK, never typed. us = f(source-file exists, gate/test exists). 12// us=0 ABSENT (no source) us=1 PRESENT-UNPROVEN (source, no gate) us=2 PROVEN (source + gate). 13// "Unproven = absent" is softened to 1 rather than 0 only because the source is a real head start; 14// it can never reach 2 without a gate on disk. No file, no credit. 15// 2. A read-grounded CAP per axis: us = min(computed, cap). The cap is the honest ceiling of what the 16// named organ ACTUALLY does, set only after reading it. This is what stops a file-name from voting. 17// Every cap below 2 carries its reason inline. THIS is where the old census leaked. 18// 3. INSTRUMENT SELF-CHECK: a positive probe (a file that must exist) and a negative probe (a file that 19// must not) run every time. If the prober cannot tell present from absent, the census is VOID and 20// the organ exits RED -- a ruler that cannot discriminate must refuse to publish a number. 21// 4. GAP QUEUE: axes ranked by (2-us) * weight * (1+blocks), where `blocks` counts how many OTHER axes 22// are structurally dead until this one exists. Weight alone is not a build order -- graded on weight 23// only, twelve axes tie at the top and the "queue" orders nothing. The blocking term is what makes 24// it an order: it promotes the axis whose absence sterilizes the most of the pipeline. The top of 25// the queue is the next rung, chosen by the ruler and not by me. 26// 27// Run from the nxc2 root (paths are repo-relative). Emits human rows, a summary, the gap queue, and a 28// final one-line JSON envelope for the MCP/API surface. 29// license_tier: ORIGINAL expect_exit: 0 30import "nx_syscalls.nx" 31import "nx_estate_path.nx" // ep_anchor: the CWD must not decide this organ's verdict 32const PGB_MAGIC_1048576: i64 = 1048576 33const PGB_MAGIC_65536: i64 = 65536 34const PGB_MAGIC_917504: i64 = 917504 35 36const PGB_MAX: i64 = 64 37const PGB_NONE: *u8 = "runtime/_pgb_no_such_file.nx" 38 39static pgb_nm: *i64 40static pgb_bar: *i64 41static pgb_us: *i64 42static pgb_wt: *i64 43static pgb_blk: *i64 44static pgb_n: i64 45 46// ---- MEASURED head-to-head totals, loaded from the benchmark's own output --------------------------- 47// Presence-scoring answers "does a file exist". It cannot answer "is it any good", and a read-grounded 48// cap only replaces a filename's opinion with MINE -- still a hand-typed number, still unfalsifiable. 49// For the axes where an industry baseline exists, the score is instead COMPUTED from a real 50// head-to-head against OpenCV SIFT and ORB on the Oxford/VGG sequences, restricted to the HELD-OUT 51// split so sequences that informed the design cannot vote on it. 52static pgb_bench_ok: i64 53static pgb_m_sift: *i64 // [matches, correct, kp] 54static pgb_m_orb: *i64 55static pgb_m_nishi: *i64 56static pgb_booted: i64 57 58func pw(s: *u8) -> i64 { 59 var n: i64 = 0 60 while s[n] != (0 as u8) { n = n + 1 } 61 sys_write(1, s, n) 62 return 0 63} 64 65func pn(v: i64) -> i64 { 66 let bb: *u8 = sys_mmap(32) 67 let t: *u8 = sys_mmap(32) 68 var m: i64 = v 69 var neg: i64 = 0 70 if m < 0 { neg = 1; m = 0 - m } 71 var k: i64 = 0 72 if m == 0 { t[0] = 48 as u8; k = 1 } 73 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 74 var i: i64 = 0 75 if neg == 1 { bb[0] = 45 as u8; i = 1 } 76 var j: i64 = 0 77 while j < k { bb[i + j] = t[k - 1 - j]; j = j + 1 } 78 sys_write(1, bb, i + k) 79 return 0 80} 81 82// pad a string out to width columns so the table reads as a table 83func ppad(s: *u8, width: i64) -> i64 { 84 var n: i64 = 0 85 while s[n] != (0 as u8) { n = n + 1 } 86 sys_write(1, s, n) 87 let sp: *u8 = sys_mmap(8) 88 sp[0] = 32 as u8 89 var i: i64 = n 90 while i < width { sys_write(1, sp, 1); i = i + 1 } 91 return 0 92} 93 94// THE PROBE: 1 iff the path opens for read. This single primitive is the whole evidence base, which is 95// why it is self-checked against a known-present and a known-absent path before any row is scored. 96// 97// TWO ROOTS, deliberately: the organ runs from the repo root during development (sources under 98// runtime/) and from nishihost when invoked as a registered tool on the NAS (the same tree lives under 99// buildroot/runtime/). Probing only the first would make every axis read ABSENT on the NAS and the 100// positive self-check would fail, so the tool would correctly declare its own census VOID -- correct, 101// but useless. Trying the buildroot prefix as a fallback makes the ruler location-independent without 102// weakening it: a path that exists under neither root still scores 0, and the negative control still 103// fails under both. 104func phave(path: *u8) -> i64 { 105 let fd: i64 = sys_openat_rd(path) 106 if fd >= 0 { 107 sys_close(fd) 108 return 1 109 } 110 let buf: *u8 = sys_mmap(512) 111 let pfx: *u8 = "buildroot/" as *u8 112 var i: i64 = 0 113 while pfx[i] != (0 as u8) { 114 buf[i] = pfx[i] 115 i = i + 1 116 } 117 var j: i64 = 0 118 while path[j] != (0 as u8) { 119 let o: i64 = i + j 120 buf[o] = path[j] 121 j = j + 1 122 } 123 let e: i64 = i + j 124 buf[e] = 0 as u8 125 let fd2: i64 = sys_openat_rd(buf) 126 if fd2 >= 0 { 127 sys_close(fd2) 128 return 1 129 } 130 return 0 131} 132 133// Accumulate one TSV row's numbers into the right method bucket. 134// Columns: seq pair nuisance split method kp matches correct prec_permil ms_permil corner_x100 135func pgb_bench_row(buf: *u8, a: i64, b: i64) -> i64 { 136 // walk the tab-separated fields, recording each field's start offset 137 let fs: *i64 = sys_mmap(32 * 8) as *i64 138 var nf: i64 = 0 139 fs[0] = a 140 nf = 1 141 var i: i64 = a 142 while i < b { 143 if buf[i] == (9 as u8) { 144 if nf < 24 { 145 fs[nf] = i + 1 146 nf = nf + 1 147 } 148 } 149 i = i + 1 150 } 151 if nf < 9 { return 0 } 152 // field 3 = split; only HELD-OUT rows may vote 153 let sp: i64 = fs[3] 154 if buf[sp] != (72 as u8) { return 0 } 155 if buf[sp + 1] != (69 as u8) { return 0 } 156 // field 4 = method; discriminate on first two chars: SIft / ORb / NIshi 157 let mp: i64 = fs[4] 158 var which: i64 = 0 - 1 159 if buf[mp] == (83 as u8) { which = 0 } 160 if buf[mp] == (79 as u8) { which = 1 } 161 if buf[mp] == (78 as u8) { which = 2 } 162 if which < 0 { return 0 } 163 // fields 5,6,7 = kp, matches, correct 164 let vals: *i64 = sys_mmap(8 * 8) as *i64 165 var f: i64 = 5 166 while f < 8 { 167 var p: i64 = fs[f] 168 var v: i64 = 0 169 var reading: i64 = 1 170 while reading == 1 { 171 if p >= b { reading = 0 } else { 172 let d: i64 = buf[p] as i64 173 if d >= 48 { if d <= 57 { v = v * 10 + (d - 48); p = p + 1 } else { reading = 0 } } else { reading = 0 } 174 } 175 } 176 vals[f - 5] = v 177 f = f + 1 178 } 179 var tgt: *i64 = pgb_m_sift 180 if which == 1 { tgt = pgb_m_orb } 181 if which == 2 { tgt = pgb_m_nishi } 182 tgt[0] = tgt[0] + vals[1] // matches 183 tgt[1] = tgt[1] + vals[2] // correct 184 tgt[2] = tgt[2] + vals[0] // kp 185 return 1 186} 187 188func pgb_load_bench() -> i64 { 189 pgb_m_sift = sys_mmap(64) as *i64 190 pgb_m_orb = sys_mmap(64) as *i64 191 pgb_m_nishi = sys_mmap(64) as *i64 192 pgb_bench_ok = 0 193 // Four candidate locations, in preference order. The last two matter operationally: nx_ship packs a 194 // source directory into buildroot/runtime, so shipping the benchmark output ALONGSIDE the sources is 195 // what lets the deployed tool score from the same evidence as the local one. Without it the live 196 // instrument honestly reports UNMEASURED while the local one reports numbers, and two rulers 197 // disagreeing about the same stack is worse than either being wrong. 198 var fd: i64 = sys_openat_rd("knowledge/status/photogram_hbench_results.tsv" as *u8) 199 if fd < 0 { fd = sys_openat_rd("buildroot/knowledge/status/photogram_hbench_results.tsv" as *u8) } 200 if fd < 0 { fd = sys_openat_rd("runtime/photogram_hbench_results.tsv" as *u8) } 201 if fd < 0 { fd = sys_openat_rd("buildroot/runtime/photogram_hbench_results.tsv" as *u8) } 202 if fd < 0 { return 0 } 203 let buf: *u8 = sys_mmap(PGB_MAGIC_1048576) 204 var total: i64 = 0 205 var reading: i64 = 1 206 while reading == 1 { 207 let got: i64 = sys_read(fd, buf + total, PGB_MAGIC_65536) 208 if got <= 0 { reading = 0 } else { 209 total = total + got 210 if total > PGB_MAGIC_917504 { reading = 0 } 211 } 212 } 213 sys_close(fd) 214 if total <= 0 { return 0 } 215 var rows: i64 = 0 216 var ls: i64 = 0 217 var i: i64 = 0 218 while i <= total { 219 var eol: i64 = 0 220 if i == total { eol = 1 } else { if buf[i] == (10 as u8) { eol = 1 } } 221 if eol == 1 { 222 if i > ls { rows = rows + pgb_bench_row(buf, ls, i) } 223 ls = i + 1 224 } 225 i = i + 1 226 } 227 if rows > 0 { pgb_bench_ok = 1 } 228 return rows 229} 230 231func pgb_boot() -> i64 { 232 if pgb_booted == 1 { return 0 } 233 pgb_nm = sys_mmap(PGB_MAX * 8) as *i64 234 pgb_bar = sys_mmap(PGB_MAX * 8) as *i64 235 pgb_us = sys_mmap(PGB_MAX * 8) as *i64 236 pgb_wt = sys_mmap(PGB_MAX * 8) as *i64 237 pgb_blk = sys_mmap(PGB_MAX * 8) as *i64 238 pgb_n = 0 239 pgb_booted = 1 240 return 0 241} 242 243// Score and record ONE axis. name/bar are labels; src1/src2 are candidate implementations (either counts); 244// g1/g2 are candidate gate/test files (either counts); w = weight 1..3 (how much a working pipeline needs 245// it); cap = the read-grounded ceiling; blk = how many other axes are structurally dead without this one 246// (counted from the pipeline dependency chain, not guessed). Returns the scored value. 247func pgb_axis(name: *u8, bar: *u8, src1: *u8, src2: *u8, g1: *u8, g2: *u8, w: i64, cap: i64, blk: i64) -> i64 { 248 pgb_boot() 249 var src: i64 = 0 250 if phave(src1) == 1 { src = 1 } 251 if phave(src2) == 1 { src = 1 } 252 var gt: i64 = 0 253 if phave(g1) == 1 { gt = 1 } 254 if phave(g2) == 1 { gt = 1 } 255 var us: i64 = 0 256 if src == 1 { us = 1 } 257 if src == 1 { if gt == 1 { us = 2 } } 258 if us > cap { us = cap } 259 let idx: i64 = pgb_n 260 pgb_nm[idx] = name as i64 261 pgb_bar[idx] = bar as i64 262 pgb_us[idx] = us 263 pgb_wt[idx] = w 264 pgb_blk[idx] = blk 265 pgb_n = idx + 1 266 ppad(name, 44) 267 pw(" w" as *u8) 268 pn(w) 269 pw(" blk" as *u8) 270 pn(blk) 271 pw(" us=" as *u8) 272 pn(us) 273 pw("/2 " as *u8) 274 if us == 0 { pw("ABSENT " as *u8) } 275 if us == 1 { pw("PRESENT-UNPROVEN " as *u8) } 276 if us == 2 { pw("PROVEN " as *u8) } 277 pw(" bar: " as *u8) 278 pw(bar) 279 pw("\n" as *u8) 280 return us 281} 282 283// MEASURED axis: score from the head-to-head instead of from a hand-typed cap. 284// The bar is deliberately the STRONGER baseline: full marks require matching or beating SIFT's 285// matching score on held-out sequences. Parity with ORB alone earns 1. No benchmark data means 0 -- 286// unmeasured is not "probably fine", it is unproven, and this ruler already refuses to score anything 287// it cannot check. metric: correct matches per keypoint (yield), which unlike bare precision cannot be 288// inflated by returning a handful of confident matches. 289func pgb_axis_measured(name: *u8, bar: *u8, w: i64, blk: i64) -> i64 { 290 pgb_boot() 291 var us: i64 = 0 292 var ours: i64 = 0 293 var sift: i64 = 0 294 var orb: i64 = 0 295 if pgb_bench_ok == 1 { 296 if pgb_m_nishi[2] > 0 { ours = (pgb_m_nishi[1] * 1000) / pgb_m_nishi[2] } 297 if pgb_m_sift[2] > 0 { sift = (pgb_m_sift[1] * 1000) / pgb_m_sift[2] } 298 if pgb_m_orb[2] > 0 { orb = (pgb_m_orb[1] * 1000) / pgb_m_orb[2] } 299 if ours > 0 { us = 1 } 300 if ours >= orb { us = 1 } 301 if ours >= sift { us = 2 } 302 if ours == 0 { us = 0 } 303 } 304 let idx: i64 = pgb_n 305 pgb_nm[idx] = name as i64 306 pgb_bar[idx] = bar as i64 307 pgb_us[idx] = us 308 pgb_wt[idx] = w 309 pgb_blk[idx] = blk 310 pgb_n = idx + 1 311 ppad(name, 44) 312 pw(" w" as *u8) 313 pn(w) 314 pw(" blk" as *u8) 315 pn(blk) 316 pw(" us=" as *u8) 317 pn(us) 318 pw("/2 " as *u8) 319 if us == 0 { pw("UNMEASURED " as *u8) } 320 if us == 1 { pw("BEHIND-SOTA " as *u8) } 321 if us == 2 { pw("MEETS-SOTA " as *u8) } 322 pw(" MEASURED yield/1000: nishi=" as *u8) 323 pn(ours) 324 pw(" sift=" as *u8) 325 pn(sift) 326 pw(" orb=" as *u8) 327 pn(orb) 328 pw(" " as *u8) 329 pw(bar) 330 pw("\n" as *u8) 331 return us 332} 333 334func main() -> i64 { 335 // ANCHOR FIRST (2026-08-04, nx_cwdguard finding): this organ reads a RELATIVE 336 // knowledge/ path, so its answer depended on where it was launched. No-op when 337 // already at the estate root, so the cron/MCP context is unchanged. 338 ep_anchor() 339 pgb_boot() 340 pgb_load_bench() 341 pw("=== nx_photogrambench -- NISHI photogrammetry stack vs the July-2026 SOTA pipeline ===\n" as *u8) 342 pw("scoring: presence axes COMPUTED from disk (source + gate) under a read-grounded cap; front-end axes\n" as *u8) 343 pw(" scored by a MEASURED head-to-head vs OpenCV SIFT/ORB on held-out Oxford/VGG sequences.\n" as *u8) 344 pw(" Full marks on a measured axis require meeting the STRONGER baseline (SIFT), not merely ORB.\n" as *u8) 345 pw("bar: COLMAP/RealityCapture/Metashape (classical) . OpenMVS/MVSNet (dense) . VGGT/Pi3 (feed-forward) . 3DGS\n\n" as *u8) 346 347 // ---- INSTRUMENT SELF-CHECK: the prober must discriminate, or the whole census is void ---- 348 let pos: i64 = phave("runtime/nx_recon3d.nx" as *u8) 349 let neg: i64 = phave("runtime/_pgb_no_such_file.nx" as *u8) 350 pw("-- instrument self-check -- positive-probe(nx_recon3d.nx)=" as *u8) 351 pn(pos) 352 pw(" negative-probe(absent)=" as *u8) 353 pn(neg) 354 var probe_ok: i64 = 0 355 if pos == 1 { if neg == 0 { probe_ok = 1 } } 356 if probe_ok == 1 { pw(" -> DISCRIMINATES (census valid)\n\n" as *u8) } 357 if probe_ok == 0 { pw(" -> BROKEN (census VOID)\n\n" as *u8) } 358 359 pw("-- A. INPUT / CAMERA --\n" as *u8) 360 pgb_axis("input:jpeg-decode-real-photo" as *u8, "read a real camera JPEG" as *u8, "runtime/nx_jpeg_decode_image.nx" as *u8, "runtime/nx_jpeg_decoder.nx" as *u8, "runtime/nx_jpeg_decode_image_test.nx" as *u8, "runtime/nx_jpeg_decode_test.nx" as *u8, 3, 2, 10) 361 pgb_axis("input:png-decode" as *u8, "lossless source frames" as *u8, "runtime/nx_png_decoder.nx" as *u8, "runtime/nx_png.nx" as *u8, "runtime/nx_png_dynamic_huffman_test.nx" as *u8, "runtime/nx_png_real_smoke.nx" as *u8, 1, 2, 0) 362 pgb_axis("input:exif-intrinsics-prior" as *u8, "COLMAP seeds f from EXIF" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 1) 363 pgb_axis("input:lens-distortion-undistort" as *u8, "Brown-Conrady / OPENCV model" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 2) 364 // cap 1: nx_camera_q14 carries a Q14 pinhole basis+focal; no distortion terms, no calibration solve 365 pgb_axis("camera:intrinsics-model-K" as *u8, "full K with principal point + distortion" as *u8, "runtime/nx_camera_q14.nx" as *u8, "runtime/nx_calibrate.nx" as *u8, "runtime/nx_calibrate_test.nx" as *u8, PGB_NONE, 3, 1, 3) 366 367 pw("\n-- B. FRONT END (correspondence) -- the half that decides whether ANY of the back end can run --\n" as *u8) 368 // cap 1: nx_features is Harris/Sobel at ONE scale + Hu moments. No DoG/scale-space pyramid, no 369 // orientation assignment -> not scale-invariant. READ 2026-07-25. 370 // Scale invariance now EXISTS (pf_pyramid) but the honest question is whether it works, and the 371 // head-to-head answers that on the zoom+rotation sequences. Measured, not capped. 372 pgb_axis_measured("front:keypoint-detect-scale-invariant" as *u8, "vs OpenCV SIFT/ORB, Oxford held-out" as *u8, 3, 9) 373 // 2026-07-25: BUILT. nx_photogram_front supplies a 256-bit steered-BRIEF descriptor with 374 // intensity-centroid orientation; gate T4 measures Hamming 0 under a 90-degree rotation against 128 375 // for the unsteered control. (The two files formerly credited here were nx_descriptor = a TEXT 376 // lexicon profiler and nx_image_feature_extract = a 16-element GLOBAL aesthetic signature; neither 377 // is a local patch descriptor. That mis-citation is what the read-grounded cap exists to catch.) 378 // These two axes are no longer scored by file presence under a cap I typed. They are scored by a 379 // real head-to-head against OpenCV SIFT and ORB on held-out Oxford/VGG sequences. 380 pgb_axis_measured("front:local-descriptor-rot-invariant" as *u8, "vs OpenCV SIFT/ORB, Oxford held-out" as *u8, 3, 8) 381 pgb_axis_measured("front:descriptor-match-ratio-test" as *u8, "vs OpenCV SIFT/ORB, Oxford held-out" as *u8, 3, 7) 382 // 2026-07-25: cap RAISED 1 -> 2. nx_imgransac remains capped at 1 on this axis (a real RANSAC, but 383 // over 2-DOF translation / 4-DOF similarity for copy-detection -- it cannot fit E). pf_ransac_e in 384 // nx_photogram_front fits the ESSENTIAL matrix itself, scored by epipolar residual and re-fit on the 385 // full inlier set; gate T5a/T5b measure 5-of-6 planted outliers rejected with the noise control firing. 386 pgb_axis("front:geometric-verify-ransac-epipolar" as *u8, "RANSAC on E/F, inlier epipolar support" as *u8, "runtime/nx_photogram_front.nx" as *u8, PGB_NONE, "runtime/nx_photogram_front_gate.nx" as *u8, PGB_NONE, 3, 2, 4) 387 pgb_axis("front:pair-selection-vocab-tree" as *u8, "vocab tree / retrieval for N^2 blowup" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 2, 2, 0) 388 389 pw("\n-- C. SPARSE SfM (the back end we actually have) --\n" as *u8) 390 pgb_axis("sfm:two-view-essential-8point" as *u8, "normalized 8-point + Hartley norm" as *u8, "runtime/nx_essential3d.nx" as *u8, PGB_NONE, "runtime/nx_essential3d_gate.nx" as *u8, "runtime/_hdl_build/nx_essential3d_gate.nx" as *u8, 3, 2, 5) 391 pgb_axis("sfm:pose-decompose-cheirality" as *u8, "E -> (R,t), 4-way cheirality select" as *u8, "runtime/nx_epose.nx" as *u8, PGB_NONE, "runtime/nx_epose_gate.nx" as *u8, "runtime/_hdl_build/nx_epose_gate.nx" as *u8, 3, 2, 5) 392 pgb_axis("sfm:triangulation-multiview" as *u8, "N-view triangulation" as *u8, "runtime/nx_recon3d.nx" as *u8, PGB_NONE, "runtime/nx_recon3d_gate.nx" as *u8, "runtime/_hdl_build/nx_recon3d_gate.nx" as *u8, 3, 2, 5) 393 pgb_axis("sfm:absolute-pose-pnp-p3p" as *u8, "P3P/EPnP: register view N+1 to cloud" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 3) 394 pgb_axis("sfm:feature-tracks-across-views" as *u8, "union-find tracks over match graph" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 4) 395 pgb_axis("sfm:incremental-reconstruction-loop" as *u8, "seed -> register -> triangulate -> BA -> loop" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 6) 396 // cap 1: nx_bundleadjust is POSE-ONLY 2-view coordinate descent on the epipolar cost. No joint 397 // camera+point refinement, no Jacobians/Schur complement. READ 2026-07-25. 398 pgb_axis("sfm:bundle-adjustment-full" as *u8, "joint cameras+points, Schur (Ceres-class)" as *u8, "runtime/nx_bundleadjust.nx" as *u8, PGB_NONE, "runtime/nx_bundleadjust_gate.nx" as *u8, "runtime/_hdl_build/nx_bundleadjust_gate.nx" as *u8, 3, 1, 2) 399 pgb_axis("sfm:rigid-registration-align" as *u8, "Horn/Kabsch scan-to-scan align" as *u8, "runtime/nx_register3d.nx" as *u8, PGB_NONE, "runtime/nx_register3d_gate.nx" as *u8, "runtime/_hdl_build/nx_register3d_gate.nx" as *u8, 2, 2, 1) 400 pgb_axis("sfm:global-rotation-averaging" as *u8, "drift control on long loops" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 2, 2, 1) 401 402 pw("\n-- D. DENSE / SURFACE (entirely absent: nx_depth_tri is a Z-BUFFER RASTERIZER, not stereo depth) --\n" as *u8) 403 pgb_axis("dense:stereo-match-patchmatch-sgm" as *u8, "PatchMatch / SGM per-pixel depth" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 3) 404 pgb_axis("dense:depth-map-fusion" as *u8, "multi-view fusion + visibility check" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 2) 405 pgb_axis("dense:surface-recon-poisson" as *u8, "Poisson / Delaunay watertight surface" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 1) 406 // cap 1: nx_meshtex exists as mesh texturing, but no multi-view photo-to-UV atlas projection 407 pgb_axis("dense:texture-from-source-photos" as *u8, "per-face view selection + UV atlas" as *u8, "runtime/nx_meshtex.nx" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, 2, 1, 0) 408 pgb_axis("dense:gaussian-splat-radiance" as *u8, "3DGS from posed images" as *u8, "runtime/nx_gsplat.nx" as *u8, PGB_NONE, "runtime/nx_gsplat_gate.nx" as *u8, "runtime/nx_gsplat_photofit_gate.nx" as *u8, 2, 2, 0) 409 410 pw("\n-- E. QUALITY / METROLOGY --\n" as *u8) 411 pgb_axis("qual:chamfer-accuracy-metric" as *u8, "DTU-style Chamfer vs ground truth" as *u8, "runtime/nx_recon3d.nx" as *u8, PGB_NONE, "runtime/nx_recon3d_gate.nx" as *u8, "runtime/_hdl_build/nx_recon3d_gate.nx" as *u8, 3, 2, 2) 412 pgb_axis("qual:reprojection-error-report" as *u8, "per-camera RMS reprojection" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 2, 2, 1) 413 pgb_axis("qual:metric-scale-gcp" as *u8, "scale bar / GCP -> real-world units" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 2, 2, 0) 414 415 pw("\n-- F. PRODUCT SURFACE --\n" as *u8) 416 pgb_axis("prod:end-to-end-cli-organ" as *u8, "one command: images -> reconstruction" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 2) 417 pgb_axis("prod:mcp-tool-live" as *u8, "agent-callable over /mcp" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 3, 2, 0) 418 pgb_axis("prod:http-api-endpoint" as *u8, "sovereign /api route" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, PGB_NONE, 2, 2, 0) 419 // 2026-07-25: filed. Capped at 1 deliberately -- the document exists but nothing MECHANICALLY 420 // enforces it, and a governance doc no gate checks is exactly the kind of unproven artifact this 421 // ruler refuses to score full marks. Raise to 2 when a gate verifies the RACI's cells still resolve. 422 pgb_axis("prod:raci-governance" as *u8, "documented ownership per stage" as *u8, "knowledge/status/photogrammetry_raci_20260725.md" as *u8, PGB_NONE, PGB_NONE, PGB_NONE, 1, 1, 0) 423 424 // ---- SUMMARY ---- 425 var absent: i64 = 0 426 var unproven: i64 = 0 427 var proven: i64 = 0 428 var sum_us: i64 = 0 429 var sum_max: i64 = 0 430 var i: i64 = 0 431 while i < pgb_n { 432 let u: i64 = pgb_us[i] 433 let w: i64 = pgb_wt[i] 434 if u == 0 { absent = absent + 1 } 435 if u == 1 { unproven = unproven + 1 } 436 if u == 2 { proven = proven + 1 } 437 sum_us = sum_us + u * w 438 sum_max = sum_max + 2 * w 439 i = i + 1 440 } 441 var permil: i64 = 0 442 if sum_max > 0 { permil = (sum_us * 1000) / sum_max } 443 444 pw("\n=== SUMMARY ===\n" as *u8) 445 pw("axes graded = " as *u8) 446 pn(pgb_n) 447 pw("\nPROVEN (2/2) = " as *u8) 448 pn(proven) 449 pw("\nUNPROVEN(1/2) = " as *u8) 450 pn(unproven) 451 pw("\nABSENT (0/2) = " as *u8) 452 pn(absent) 453 pw("\nHONEST SCORE = " as *u8) 454 pn(permil) 455 pw(" permil [weighted " as *u8) 456 pn(sum_us) 457 pw("/" as *u8) 458 pn(sum_max) 459 pw("]\n" as *u8) 460 461 // ---- GAP QUEUE: deficit*weight desc = the build order. Selection sort over an index array. ---- 462 pw("\n=== GAP QUEUE prio = (2-us) x weight x (1+blocks), desc = THE BUILD ORDER ===\n" as *u8) 463 let ord: *i64 = sys_mmap(PGB_MAX * 8) as *i64 464 i = 0 465 while i < pgb_n { ord[i] = i; i = i + 1 } 466 var a: i64 = 0 467 while a < pgb_n { 468 var best: i64 = a 469 var b: i64 = a + 1 470 while b < pgb_n { 471 let ib: i64 = ord[b] 472 let ibest: i64 = ord[best] 473 let pb: i64 = (2 - pgb_us[ib]) * pgb_wt[ib] * (1 + pgb_blk[ib]) 474 let pbest: i64 = (2 - pgb_us[ibest]) * pgb_wt[ibest] * (1 + pgb_blk[ibest]) 475 if pb > pbest { best = b } 476 b = b + 1 477 } 478 let tmp: i64 = ord[a] 479 ord[a] = ord[best] 480 ord[best] = tmp 481 a = a + 1 482 } 483 var rank: i64 = 1 484 i = 0 485 while i < pgb_n { 486 let ix: i64 = ord[i] 487 let def: i64 = (2 - pgb_us[ix]) * pgb_wt[ix] * (1 + pgb_blk[ix]) 488 if def > 0 { 489 if rank <= 12 { 490 pw(" " as *u8) 491 pn(rank) 492 pw(". prio=" as *u8) 493 pn(def) 494 pw(" " as *u8) 495 let nmv: i64 = pgb_nm[ix] 496 let nmp: *u8 = nmv as *u8 497 ppad(nmp, 42) 498 pw(" (us=" as *u8) 499 pn(pgb_us[ix]) 500 pw(")\n" as *u8) 501 rank = rank + 1 502 } 503 } 504 i = i + 1 505 } 506 507 // ---- MACHINE ENVELOPE for the MCP / API surface ---- 508 pw("\n{\"tool\":\"nx_photogrambench\",\"v\":1,\"axes\":" as *u8) 509 pn(pgb_n) 510 pw(",\"proven\":" as *u8) 511 pn(proven) 512 pw(",\"unproven\":" as *u8) 513 pn(unproven) 514 pw(",\"absent\":" as *u8) 515 pn(absent) 516 pw(",\"weighted_us\":" as *u8) 517 pn(sum_us) 518 pw(",\"weighted_max\":" as *u8) 519 pn(sum_max) 520 pw(",\"permil\":" as *u8) 521 pn(permil) 522 pw(",\"probe_ok\":" as *u8) 523 pn(probe_ok) 524 pw("}\n" as *u8) 525 526 if probe_ok == 0 { 527 pw("\nRED -- prober cannot discriminate present from absent; census VOID\n" as *u8) 528 return 1 529 } 530 pw("\nGREEN -- census valid (prober discriminates; nothing hand-scored)\n" as *u8) 531 if pgb_bench_ok == 0 { 532 pw("NOTE: no head-to-head results on disk, so the measured axes read UNMEASURED (0), not 'probably fine'.\n" as *u8) 533 pw(" Run the benchmark to populate knowledge/status/photogram_hbench_results.tsv.\n" as *u8) 534 } 535 return 0 536}