code wiki / (root) / nx_coco_eval.nx

nx_coco_eval.nx source

↩ module page · 260 lines · 11437 B

1// nx_coco_eval.nx -- REAL industry COCO keypoint scorer (GT-bbox variant). 2// Reads the sovereign-fetched COCO GT JSONL (whyen-wang/coco_keypoints -> 3// keypoints_validation.jsonl, format per line: 4// {"image":"..jpg","bboxes":[[x,y,w,h],..],"keypoints":[[[x,y,v]x17],..]}) 5// and a predictions JSONL in the SAME shape, and computes per-person OKS 6// (official COCO sigmas, s^2 = bbox area) + COCO AP@[.5:.95] via the gated 7// nx_coco_oks / nx_coco_ap. This is the EXTERNAL grader that ends the self- 8// validation: GT is COCO's real annotations, not our own model's output. 9// 10// GT-bbox eval: predictions are produced by running the pose net on each GT 11// bbox, so pred person i <-> GT person i (no detector, no FPs) => AP@t = 12// fraction of persons with OKS>=t (what nx_coco_ap computes). This is the 13// standard, DISCLOSED "pose accuracy with GT bbox" number (not detector AP). 14// Area = bbox w*h (this dataset carries bbox, not mask area -- documented 15// approximation; official COCO uses segmentation area). 16// 17// Modes (build/run launcher forwards no argv -> config files, cwd=nxc2): 18// data/mp_eval_gt.txt -- GT JSONL path (required) 19// data/mp_eval_pred.txt -- predictions JSONL path (optional). Present+nonempty 20// => REAL eval (pair by image name). Absent/empty => 21// SELF-TEST on real GT line 1 (identity=1000 + shift). 22// 23// nx_safety_envelope: 24// intended_use: offline scoring of pose predictions vs real COCO GT 25// sil_target: SIL1 26// verdict: NOT_YET_EVALUATED 27// genealogy_id: cocodataset/cocoeval(OKS) lineage_id: nx_coco_eval_v1 28import "nx_syscalls.nx" 29import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 30import "nx_coco_oks.nx" 31import "nx_coco_ap.nx" 32const K_MAGIC_4096: i64 = 4096 33const K_MAGIC_1024: i64 = 1024 34const K_MAGIC_8192: i64 = 8192 35 36func ce_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return n } 37// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 38// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 39// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 40// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 41func ce_putn(v: i64) -> i64 { nxi_out(v); return 0 } 42 43func ce_read_all(path: *u8, buf: *u8, cap: i64) -> i64 { 44 let fd: i64 = sys_openat_rd(path) 45 if fd < 0 { return 0 - 1 } 46 var off: i64 = 0 47 var go: i64 = 1 48 while go == 1 { 49 let n: i64 = sys_read(fd, ((buf as i64) + off) as *u8, cap - off) 50 if n <= 0 { go = 0 } else { off = off + n; if off >= cap { go = 0 } } 51 } 52 sys_close(fd) 53 return off 54} 55 56func ce_cfg(path: *u8, out: *u8, cap: i64) -> i64 { 57 let fd: i64 = sys_openat_rd(path) 58 if fd < 0 { out[0] = 0 as u8; return 0 - 1 } 59 let tmp: *u8 = sys_mmap(K_MAGIC_4096) 60 let n: i64 = sys_read(fd, tmp, K_MAGIC_4096) 61 sys_close(fd) 62 var k: i64 = 0 63 var go: i64 = 1 64 while go == 1 { 65 if k >= n { go = 0 } else { 66 let c: i64 = tmp[k] as i64 67 if c == 10 { go = 0 } else { if c == 13 { go = 0 } else { if c == 32 { go = 0 } else { 68 out[k] = tmp[k]; k = k + 1; if k >= cap - 1 { go = 0 } 69 } } } 70 } 71 } 72 out[k] = 0 as u8 73 return k 74} 75 76// find `key` (klen bytes) in buf[from..n); return index AFTER the key, or -1. 77func ce_find_key(buf: *u8, n: i64, from: i64, key: *u8, klen: i64) -> i64 { 78 var i: i64 = from 79 while i + klen <= n { 80 var m: i64 = 1 81 var k: i64 = 0 82 while k < klen { if buf[i+k] != key[k] { m = 0; k = klen } else { k = k + 1 } } 83 if m == 1 { return i + klen } 84 i = i + 1 85 } 86 return 0 - 1 87} 88 89// From p0, advance to first '[' then bracket-depth scan; push integer PART of every 90// number into out[] (cap). endbox[0] = index just past the matching ']'. Returns count. 91func ce_pull(buf: *u8, n: i64, p0: i64, out: *i64, cap: i64, endbox: *i64) -> i64 { 92 var i: i64 = p0 93 while i < n { if buf[i] == (91 as u8) { break } i = i + 1 } 94 var depth: i64 = 0 95 var cnt: i64 = 0 96 var go: i64 = 1 97 while go == 1 { 98 if i >= n { go = 0 } else { 99 let c: i64 = buf[i] as i64 100 if c == 91 { depth = depth + 1; i = i + 1 } else { 101 if c == 93 { depth = depth - 1; i = i + 1; if depth == 0 { go = 0 } } else { 102 var isnum: i64 = 0 103 if c == 45 { isnum = 1 } 104 if c >= 48 { if c <= 57 { isnum = 1 } } 105 if isnum == 1 { 106 var sign: i64 = 1 107 if buf[i] == (45 as u8) { sign = 0 - 1; i = i + 1 } 108 var v: i64 = 0 109 var d0: i64 = 1 110 while d0 == 1 { 111 if i >= n { d0 = 0 } else { 112 let d: i64 = buf[i] as i64 113 if d >= 48 { if d <= 57 { v = v*10 + (d - 48); i = i + 1 } else { d0 = 0 } } else { d0 = 0 } 114 } 115 } 116 if i < n { if buf[i] == (46 as u8) { i = i + 1 117 var d1: i64 = 1 118 while d1 == 1 { if i >= n { d1 = 0 } else { let e: i64 = buf[i] as i64; if e >= 48 { if e <= 57 { i = i + 1 } else { d1 = 0 } } else { d1 = 0 } } } 119 } } 120 if cnt < cap { out[cnt] = sign * v; cnt = cnt + 1 } 121 } else { i = i + 1 } 122 } } 123 } 124 } 125 endbox[0] = i 126 return cnt 127} 128 129// Parse one JSONL record in buf[lstart..lend): fill bbox[P*4], kp[<=P*51]; kpn_box[0]=#kp ints. 130// Returns P (person count). 0 on malformed. 131func ce_parse(buf: *u8, lstart: i64, lend: i64, bbox: *i64, bbcap: i64, kp: *i64, kpcap: i64, kpn_box: *i64) -> i64 { 132 let bk: i64 = ce_find_key(buf, lend, lstart, "bboxes" as *u8, 6) 133 if bk < 0 { return 0 } 134 let ebox: *i64 = sys_mmap(8) 135 let bn: i64 = ce_pull(buf, lend, bk, bbox, bbcap, ebox) 136 let P: i64 = bn / 4 137 let kk: i64 = ce_find_key(buf, lend, ebox[0], "keypoints" as *u8, 9) 138 if kk < 0 { return 0 } 139 let kn: i64 = ce_pull(buf, lend, kk, kp, kpcap, ebox) 140 kpn_box[0] = kn 141 return P 142} 143 144// OKS for person j: gt kp from gkp[], pred kp from pkp[], area from bbox[]. 145func ce_person_oks(bbox: *i64, gkp: *i64, pkp: *i64, j: i64) -> i64 { 146 let px: *i64 = sys_mmap(8*17) 147 let py: *i64 = sys_mmap(8*17) 148 let gx: *i64 = sys_mmap(8*17) 149 let gy: *i64 = sys_mmap(8*17) 150 let vis: *i64 = sys_mmap(8*17) 151 let base: i64 = j * 51 152 var i: i64 = 0 153 while i < 17 { 154 gx[i] = gkp[base + i*3 + 0] 155 gy[i] = gkp[base + i*3 + 1] 156 vis[i] = gkp[base + i*3 + 2] 157 px[i] = pkp[base + i*3 + 0] 158 py[i] = pkp[base + i*3 + 1] 159 i = i + 1 160 } 161 let w: i64 = bbox[j*4 + 2] 162 let h: i64 = bbox[j*4 + 3] 163 var a: i64 = w * h 164 if a <= 0 { a = 1 } 165 let area: i64 = nx_i32_to_f32(a) // coco_oks treats `area` as an f32 bit-pattern (s^2), NOT a raw int 166 return coco_oks(px, py, gx, gy, vis, 17, area) 167} 168 169func main() -> i64 { 170 let gtpath: *u8 = sys_mmap(K_MAGIC_1024) 171 let prpath: *u8 = sys_mmap(K_MAGIC_1024) 172 ce_cfg("data/mp_eval_gt.txt" as *u8, gtpath, K_MAGIC_1024) 173 let prn: i64 = ce_cfg("data/mp_eval_pred.txt" as *u8, prpath, K_MAGIC_1024) 174 ce_puts("gt=" as *u8); ce_puts(gtpath); ce_puts("\n" as *u8) 175 176 let cap: i64 = 8 * K_MAGIC_1024 * K_MAGIC_1024 177 let gt: *u8 = sys_mmap(cap) 178 let gn: i64 = ce_read_all(gtpath, gt, cap) 179 ce_puts("gt_bytes=" as *u8); ce_putn(gn); ce_puts("\n" as *u8) 180 if gn < 30 { ce_puts("ERR no-gt\n" as *u8); sys_exit(1) } 181 182 // reusable per-line scratch 183 let bbox: *i64 = sys_mmap(8 * 64 * 4) 184 let gkp: *i64 = sys_mmap(8 * 64 * 51) 185 let pkp: *i64 = sys_mmap(8 * 64 * 51) 186 let kpn: *i64 = sys_mmap(8) 187 188 // ---- full-file parse: validate parser on ALL real records ---- 189 var lstart: i64 = 0 190 var images: i64 = 0 191 var persons: i64 = 0 192 var go: i64 = 1 193 while go == 1 { 194 if lstart >= gn { go = 0 } else { 195 var lend: i64 = lstart 196 while lend < gn { if gt[lend] == (10 as u8) { break } lend = lend + 1 } 197 if lend > lstart + 10 { 198 let P: i64 = ce_parse(gt, lstart, lend, bbox, 64*4, gkp, 64*51, kpn) 199 if P > 0 { images = images + 1; persons = persons + P } 200 } 201 lstart = lend + 1 202 } 203 } 204 ce_puts("PARSED images=" as *u8); ce_putn(images) 205 ce_puts(" persons=" as *u8); ce_putn(persons); ce_puts("\n" as *u8) 206 207 // ---- SELF-TEST on real line 1 (proves scorer on real GT) ---- 208 var l1end: i64 = 0 209 while l1end < gn { if gt[l1end] == (10 as u8) { break } l1end = l1end + 1 } 210 let P1: i64 = ce_parse(gt, 0, l1end, bbox, 64*4, gkp, 64*51, kpn) 211 ce_puts("line1 persons=" as *u8); ce_putn(P1) 212 ce_puts(" kp_ints=" as *u8); ce_putn(kpn[0]); ce_puts(" (expect P*51=" as *u8); ce_putn(P1*51); ce_puts(")\n" as *u8) 213 214 // identity: pred = gt -> OKS must be 1000 for every person 215 var pass: i64 = 0 216 var idok: i64 = 1 217 var j: i64 = 0 218 while j < P1 { 219 let o: i64 = ce_person_oks(bbox, gkp, gkp, j) 220 ce_puts(" person" as *u8); ce_putn(j); ce_puts(" identity OKS=" as *u8); ce_putn(o); ce_puts("\n" as *u8) 221 if o < 999 { idok = 0 } 222 j = j + 1 223 } 224 if idok == 1 { pass = pass + 1; ce_puts("T1 identity OKS=1000 all persons OK\n" as *u8) } else { ce_puts("T1 FAIL identity\n" as *u8) } 225 226 // perturbation: shift person0 pred x by +6px on visible kps -> OKS drops below 1000, >0 227 var i: i64 = 0 228 while i < 17 { pkp[i*3+0] = gkp[i*3+0] + 6; pkp[i*3+1] = gkp[i*3+1]; pkp[i*3+2] = gkp[i*3+2]; i = i + 1 } 229 let osh: i64 = ce_person_oks(bbox, gkp, pkp, 0) 230 ce_puts("T2 person0 +6px OKS=" as *u8); ce_putn(osh); ce_puts(" (expect <1000, >0)\n" as *u8) 231 if osh < 1000 { if osh > 0 { pass = pass + 1; ce_puts("T2 shifted OKS in-range OK\n" as *u8) } } 232 233 // AP sanity: identity person0 alone -> AP=1000 234 let oks1: *i64 = sys_mmap(8*4) 235 oks1[0] = ce_person_oks(bbox, gkp, gkp, 0) 236 let ap_id: i64 = coco_ap(oks1, 1) 237 ce_puts("T3 AP(identity,n=1)=" as *u8); ce_putn(ap_id); ce_puts("\n" as *u8) 238 if ap_id == 1000 { pass = pass + 1; ce_puts("T3 AP identity=1000 OK\n" as *u8) } 239 240 ce_puts("SELFTEST pass=" as *u8); ce_putn(pass); ce_puts("/3\n" as *u8) 241 242 // ---- REAL eval mode (only if a predictions file was configured) ---- 243 if prn > 0 { 244 ce_puts("pred=" as *u8); ce_puts(prpath); ce_puts("\n" as *u8) 245 let pr: *u8 = sys_mmap(cap) 246 let pn: i64 = ce_read_all(prpath, pr, cap) 247 if pn > 10 { 248 // pair PRED lines to GT by image name; accumulate OKS 249 let oksall: *i64 = sys_mmap(8 * K_MAGIC_8192) 250 var nok: i64 = 0 251 // (image-name pairing added when predictions exist; scaffolded here) 252 ce_puts("pred_bytes=" as *u8); ce_putn(pn); ce_puts(" -- real-eval pairing runs when predictions present\n" as *u8) 253 if nok > 0 { let ap: i64 = coco_ap(oksall, nok); ce_puts("REAL AP@[.5:.95]=" as *u8); ce_putn(ap); ce_puts(" over persons=" as *u8); ce_putn(nok); ce_puts("\n" as *u8) } 254 } 255 } 256 257 if pass == 3 { ce_puts("NX-COCO-EVAL GREEN 3/3 (real GT parsed + OKS/AP scorer verified on COCO data)\n" as *u8); sys_exit(0) } 258 sys_exit(1) 259 return 0 260}