code wiki / (root) / nx_charjudge_lib.nx

nx_charjudge_lib.nx source

↩ module page · 432 lines · 15879 B

1// nx_charjudge_lib.nx -- CHARACTER-IMAGE JUDGE v2 core (operator 2026-08-04: "eat the debt and build to 2// actually get to sota"). v1 (composition-bimodality x palette) reproduced the human ordering and killed the 3// patch-scramble that fooled ns_assess -- but had a NAMED gameable vector: a designed smooth+noise+palette 4// collage. v2 adds the axes that close it: 5// CONTOUR COHERENCE -- permil of edge pixels on runs >= run_min (row+col propagation). A scramble cannot 6// carry a contour past its patch size; per-pixel noise has edges but no runs. 7// FACE PRESENCE -- multi-scale center-surround dark-blob (iris/pupil class, anime AND photo) + PAIR geometry 8// (two compact blobs, level, spaced). No face = capability floor 0 (a blob with no face is not 40pc of a 9// character). Ring compactness (all 8 ring points lighter) rejects line-art edges. 10// HEADLINE = MIN(composition, palette, contour, face). Thresholds = CONF ROWS (knowledge/charjudge.conf, 11// key=value, # comments; code defaults = the 2026-08-04 battery calibration) per rules 11+17. 12// All integer, zero third-party. Plain-if only in this lib (imported-else parser landmine, debt 1784673261). 13// Buffers are mmap-per-call, bounded by call count (CLI=1, gate<16) -- not a daemon lib. 14import "nx_syscalls.nx" 15 16const CHJ_CELL: i64 = 16 17const CHJ_BUCKETS: i64 = 4096 18const CHJ_CAND_MAX: i64 = 128 19const CHJ_MAGIC_65536: i64 = 65536 20const CHJ_C_SMOOTH_T: i64 = 0 21const CHJ_C_DETAIL_T: i64 = 1 22const CHJ_C_SMOOTH_DIV: i64 = 2 23const CHJ_C_DETAIL_DIV: i64 = 3 24const CHJ_C_PAL_DIV: i64 = 4 25const CHJ_C_EDGE_T: i64 = 5 26const CHJ_C_RUN_MIN: i64 = 6 27const CHJ_C_COH_DIV: i64 = 7 28const CHJ_C_EYE_T: i64 = 8 29const CHJ_C_FACE_REQ: i64 = 9 30const CHJ_NCONF: i64 = 10 31 32func chj_conf_defaults(cf: *i64) -> i64 { 33 cf[CHJ_C_SMOOTH_T] = 2 34 cf[CHJ_C_DETAIL_T] = 10 35 cf[CHJ_C_SMOOTH_DIV] = 150 36 cf[CHJ_C_DETAIL_DIV] = 300 37 cf[CHJ_C_PAL_DIV] = 512 38 cf[CHJ_C_EDGE_T] = 12 39 cf[CHJ_C_RUN_MIN] = 24 40 cf[CHJ_C_COH_DIV] = 60 41 cf[CHJ_C_EYE_T] = 45 42 cf[CHJ_C_FACE_REQ] = 1 43 return 0 44} 45func chj_keyeq(buf: *u8, at: i64, end: i64, key: *u8) -> i64 { 46 var k: i64 = 0 47 var i: i64 = at 48 while key[k] != (0 as u8) { 49 if i >= end { return 0 } 50 if buf[i] != key[k] { return 0 } 51 i = i + 1 52 k = k + 1 53 } 54 if i >= end { return 0 } 55 if buf[i] != (61 as u8) { return 0 } 56 return 1 57} 58func chj_slot_for(buf: *u8, at: i64, end: i64) -> i64 { 59 if chj_keyeq(buf, at, end, "smooth_t" as *u8) == 1 { return CHJ_C_SMOOTH_T } 60 if chj_keyeq(buf, at, end, "detail_t" as *u8) == 1 { return CHJ_C_DETAIL_T } 61 if chj_keyeq(buf, at, end, "smooth_div" as *u8) == 1 { return CHJ_C_SMOOTH_DIV } 62 if chj_keyeq(buf, at, end, "detail_div" as *u8) == 1 { return CHJ_C_DETAIL_DIV } 63 if chj_keyeq(buf, at, end, "pal_div" as *u8) == 1 { return CHJ_C_PAL_DIV } 64 if chj_keyeq(buf, at, end, "edge_t" as *u8) == 1 { return CHJ_C_EDGE_T } 65 if chj_keyeq(buf, at, end, "run_min" as *u8) == 1 { return CHJ_C_RUN_MIN } 66 if chj_keyeq(buf, at, end, "coh_div" as *u8) == 1 { return CHJ_C_COH_DIV } 67 if chj_keyeq(buf, at, end, "eye_t" as *u8) == 1 { return CHJ_C_EYE_T } 68 if chj_keyeq(buf, at, end, "face_required" as *u8) == 1 { return CHJ_C_FACE_REQ } 69 return 0-1 70} 71func chj_conf_load(path: *u8, cf: *i64) -> i64 { 72 chj_conf_defaults(cf) 73 if (path as i64) == 0 { return 0 } 74 let lenp: *i64 = sys_mmap(16) as *i64 75 let buf: *u8 = sys_read_file(path, lenp) 76 if (buf as i64) == 0 { return 0 } 77 let n: i64 = lenp[0] 78 var i: i64 = 0 79 while i < n { 80 var e: i64 = i 81 var go: i64 = 1 82 while go == 1 { 83 if e >= n { go = 0 } 84 if go == 1 { if buf[e] == (10 as u8) { go = 0 } } 85 if go == 1 { e = e + 1 } 86 } 87 if e > i { if buf[i] != (35 as u8) { 88 let slot: i64 = chj_slot_for(buf, i, e) 89 if slot >= 0 { 90 var p: i64 = i 91 var col: i64 = 0-1 92 var g2: i64 = 1 93 while g2 == 1 { 94 if p >= e { g2 = 0 } 95 if g2 == 1 { if buf[p] == (61 as u8) { col = p; g2 = 0 } } 96 if g2 == 1 { p = p + 1 } 97 } 98 if col >= 0 { 99 var v: i64 = 0 100 var neg: i64 = 0 101 var q: i64 = col + 1 102 if q < e { if buf[q] == (45 as u8) { neg = 1; q = q + 1 } } 103 while q < e { 104 let c: i64 = buf[q] as i64 105 if c >= 48 { if c <= 57 { v = v*10 + (c-48) } } 106 q = q + 1 107 } 108 if neg == 1 { v = 0-v } 109 cf[slot] = v 110 } 111 } 112 } } 113 i = e + 1 114 } 115 return 0 116} 117 118func chj_luma(fb: *i64, w: i64, h: i64, luma: *i64) -> i64 { 119 var q: i64 = 0 120 while q < w*h { 121 let px: i64 = fb[q] 122 let r: i64 = px&255 123 let g: i64 = (px>>8)&255 124 let b: i64 = (px>>16)&255 125 luma[q] = (r*299 + g*587 + b*114)/1000 126 q = q + 1 127 } 128 return 0 129} 130 131// composition bimodality: 16px cells classed smooth/detail by mean |grad| per pixel. out[0]=smooth_permil out[1]=detail_permil 132func chj_cells(luma: *i64, w: i64, h: i64, cf: *i64, outp: *i64) -> i64 { 133 let cw: i64 = w/CHJ_CELL 134 let chh: i64 = h/CHJ_CELL 135 var smooth: i64 = 0 136 var detail: i64 = 0 137 var cy: i64 = 0 138 while cy < chh { 139 var cx: i64 = 0 140 while cx < cw { 141 var e: i64 = 0 142 var yy: i64 = 0 143 while yy < CHJ_CELL { 144 var xx: i64 = 0 145 while xx < CHJ_CELL { 146 let ix: i64 = cx*CHJ_CELL + xx 147 let iy: i64 = cy*CHJ_CELL + yy 148 if ix < w-1 { if iy < h-1 { 149 let l0: i64 = luma[iy*w + ix] 150 var gx: i64 = luma[iy*w + ix + 1] - l0 151 var gy: i64 = luma[(iy+1)*w + ix] - l0 152 if gx < 0 { gx = 0-gx } 153 if gy < 0 { gy = 0-gy } 154 e = e + gx + gy 155 } } 156 xx = xx + 1 157 } 158 yy = yy + 1 159 } 160 let me: i64 = e/(CHJ_CELL*CHJ_CELL) 161 if me < cf[CHJ_C_SMOOTH_T] { smooth = smooth + 1 } 162 if me > cf[CHJ_C_DETAIL_T] { detail = detail + 1 } 163 cx = cx + 1 164 } 165 cy = cy + 1 166 } 167 let ncell: i64 = cw*chh 168 if ncell < 1 { outp[0] = 0; outp[1] = 0; return 0 } 169 outp[0] = smooth*1000/ncell 170 outp[1] = detail*1000/ncell 171 return 0 172} 173 174func chj_palette(fb: *i64, w: i64, h: i64) -> i64 { 175 let seen: *u8 = sys_mmap(CHJ_BUCKETS) 176 var q: i64 = 0 177 while q < w*h { 178 let px: i64 = fb[q] 179 let r: i64 = px&255 180 let g: i64 = (px>>8)&255 181 let b: i64 = (px>>16)&255 182 seen[(r/16)*256 + (g/16)*16 + b/16] = 1 as u8 183 q = q + 1 184 } 185 var pal: i64 = 0 186 q = 0 187 while q < CHJ_BUCKETS { if seen[q] == (1 as u8) { pal = pal + 1 } q = q + 1 } 188 return pal 189} 190 191// contour coherence: permil of edge pixels on ORIENTATION-STABLE runs >= run_min. Plain run-length was 192// designed-out at gate time: dense noise is fully CONNECTED (runs grow trivially) and a patch-scramble's 193// aligned seams are long lines -- so a run only extends between neighbors in the SAME quantized gradient 194// orientation bin (4 bins). Noise dies at ~1/4 per step; real contours hold a bin over long arcs. 195// outp[0]=coherent_permil outp[1]=edge_total. returns the banded axis. 196func chj_contour(luma: *i64, w: i64, h: i64, cf: *i64, outp: *i64) -> i64 { 197 let et: i64 = cf[CHJ_C_EDGE_T] 198 let rm: i64 = cf[CHJ_C_RUN_MIN] 199 let emap: *u8 = sys_mmap(w*h) 200 let bmap: *u8 = sys_mmap(w*h) 201 var etotal: i64 = 0 202 var y: i64 = 0 203 while y < h-1 { 204 var x: i64 = 0 205 while x < w-1 { 206 let l0: i64 = luma[y*w+x] 207 let sgx: i64 = luma[y*w+x+1] - l0 208 let sgy: i64 = luma[(y+1)*w+x] - l0 209 var agx: i64 = sgx 210 if agx < 0 { agx = 0-agx } 211 var agy: i64 = sgy 212 if agy < 0 { agy = 0-agy } 213 if agx+agy > et { 214 emap[y*w+x] = 1 as u8 215 etotal = etotal + 1 216 // 8 orientation bins (sign gx, sign gy, |gx|>|gy|): 4 bins percolate through dense 217 // noise (58 pct continuation over 3 neighbors, supercritical -- gate T4 caught it); 218 // 8 bins = ~30 pct, subcritical, noise runs die exponentially, straight contours hold. 219 var bin: i64 = 0 220 if sgx < 0 { bin = bin + 4 } 221 if sgy < 0 { bin = bin + 2 } 222 if agx > agy { bin = bin + 1 } 223 bmap[y*w+x] = bin as u8 224 } 225 x = x + 1 226 } 227 y = y + 1 228 } 229 var coh: i64 = 0 230 let prev: *i64 = sys_mmap(w*8) as *i64 231 let curr: *i64 = sys_mmap(w*8) as *i64 232 let prevb: *i64 = sys_mmap(w*8) as *i64 233 let currb: *i64 = sys_mmap(w*8) as *i64 234 var i: i64 = 0 235 while i < w { prev[i] = 0; prevb[i] = 0-1; i = i + 1 } 236 y = 0 237 while y < h { 238 var x2: i64 = 0 239 while x2 < w { 240 var r: i64 = 0 241 var b: i64 = 0-1 242 if emap[y*w+x2] == (1 as u8) { 243 b = bmap[y*w+x2] as i64 244 var m: i64 = 0 245 if prevb[x2] == b { m = prev[x2] } 246 if x2 > 0 { if prevb[x2-1] == b { if prev[x2-1] > m { m = prev[x2-1] } } } 247 if x2 < w-1 { if prevb[x2+1] == b { if prev[x2+1] > m { m = prev[x2+1] } } } 248 r = m + 1 249 if r >= rm { coh = coh + 1 } 250 } 251 curr[x2] = r 252 currb[x2] = b 253 x2 = x2 + 1 254 } 255 var x3: i64 = 0 256 while x3 < w { prev[x3] = curr[x3]; prevb[x3] = currb[x3]; x3 = x3 + 1 } 257 y = y + 1 258 } 259 let prevh: *i64 = sys_mmap(h*8) as *i64 260 let currh: *i64 = sys_mmap(h*8) as *i64 261 let prevhb: *i64 = sys_mmap(h*8) as *i64 262 let currhb: *i64 = sys_mmap(h*8) as *i64 263 i = 0 264 while i < h { prevh[i] = 0; prevhb[i] = 0-1; i = i + 1 } 265 var x4: i64 = 0 266 while x4 < w { 267 var y2: i64 = 0 268 while y2 < h { 269 var r2: i64 = 0 270 var b2: i64 = 0-1 271 if emap[y2*w+x4] == (1 as u8) { 272 b2 = bmap[y2*w+x4] as i64 273 var m2: i64 = 0 274 if prevhb[y2] == b2 { m2 = prevh[y2] } 275 if y2 > 0 { if prevhb[y2-1] == b2 { if prevh[y2-1] > m2 { m2 = prevh[y2-1] } } } 276 if y2 < h-1 { if prevhb[y2+1] == b2 { if prevh[y2+1] > m2 { m2 = prevh[y2+1] } } } 277 r2 = m2 + 1 278 if r2 >= rm { coh = coh + 1 } 279 } 280 currh[y2] = r2 281 currhb[y2] = b2 282 y2 = y2 + 1 283 } 284 var y3: i64 = 0 285 while y3 < h { prevh[y3] = currh[y3]; prevhb[y3] = currhb[y3]; y3 = y3 + 1 } 286 x4 = x4 + 1 287 } 288 var perm: i64 = 0 289 if etotal > 0 { perm = coh*1000/etotal } 290 if etotal < w*h/400 { perm = 0 } 291 outp[0] = perm 292 outp[1] = etotal 293 var axis: i64 = perm*1000/cf[CHJ_C_COH_DIV] 294 if axis > 1000 { axis = 1000 } 295 return axis 296} 297 298// one face scan at the current scale: center-surround compact dark blobs -> level spaced pair 299func chj_face_scan(l: *i64, w: i64, h: i64, cf: *i64) -> i64 { 300 let eyet: i64 = cf[CHJ_C_EYE_T] 301 let cx: *i64 = sys_mmap(CHJ_CAND_MAX*8) as *i64 302 let cy: *i64 = sys_mmap(CHJ_CAND_MAX*8) as *i64 303 var nc: i64 = 0 304 var y: i64 = 5 305 while y < h-5 { 306 var x: i64 = 5 307 while x < w-5 { 308 let c: i64 = l[y*w+x] 309 var okc: i64 = 1 310 let p1: i64 = l[y*w + x+4] 311 let p2: i64 = l[y*w + x-4] 312 let p3: i64 = l[(y+4)*w + x] 313 let p4: i64 = l[(y-4)*w + x] 314 let p5: i64 = l[(y+3)*w + x+3] 315 let p6: i64 = l[(y+3)*w + x-3] 316 let p7: i64 = l[(y-3)*w + x+3] 317 let p8: i64 = l[(y-3)*w + x-3] 318 if p1 <= c + eyet/2 { okc = 0 } 319 if p2 <= c + eyet/2 { okc = 0 } 320 if p3 <= c + eyet/2 { okc = 0 } 321 if p4 <= c + eyet/2 { okc = 0 } 322 if p5 <= c + eyet/2 { okc = 0 } 323 if p6 <= c + eyet/2 { okc = 0 } 324 if p7 <= c + eyet/2 { okc = 0 } 325 if p8 <= c + eyet/2 { okc = 0 } 326 if okc == 1 { if (p1+p2+p3+p4+p5+p6+p7+p8)/8 - c > eyet { 327 var near: i64 = 0 328 var k: i64 = 0 329 while k < nc { 330 var ddx: i64 = cx[k]-x 331 if ddx < 0 { ddx = 0-ddx } 332 var ddy: i64 = cy[k]-y 333 if ddy < 0 { ddy = 0-ddy } 334 if ddx < 6 { if ddy < 6 { near = 1 } } 335 k = k + 1 336 } 337 if near == 0 { if nc < CHJ_CAND_MAX { cx[nc] = x; cy[nc] = y; nc = nc + 1 } } 338 } } 339 x = x + 2 340 } 341 y = y + 2 342 } 343 var i: i64 = 0 344 while i < nc { 345 var j: i64 = i+1 346 while j < nc { 347 var dyv: i64 = cy[i]-cy[j] 348 if dyv < 0 { dyv = 0-dyv } 349 var dxv: i64 = cx[i]-cx[j] 350 if dxv < 0 { dxv = 0-dxv } 351 if dyv <= 5 { if dxv >= 8 { if dxv <= 44 { return 1 } } } 352 j = j + 1 353 } 354 i = i + 1 355 } 356 return 0 357} 358 359func chj_face(luma: *i64, w: i64, h: i64, cf: *i64) -> i64 { 360 var cw: i64 = w 361 var chh: i64 = h 362 var cur: *i64 = sys_mmap(w*h*8) as *i64 363 var i: i64 = 0 364 while i < w*h { cur[i] = luma[i]; i = i + 1 } 365 var done: i64 = 0 366 var sc: i64 = 0 367 while sc < 3 { 368 if done == 0 { if chj_face_scan(cur, cw, chh, cf) == 1 { return 1000 } } 369 let nw: i64 = cw/2 370 let nh: i64 = chh/2 371 if nw < 24 { done = 1 } 372 if nh < 24 { done = 1 } 373 if done == 0 { 374 let nxt: *i64 = sys_mmap(nw*nh*8) as *i64 375 var y: i64 = 0 376 while y < nh { 377 var x: i64 = 0 378 while x < nw { 379 nxt[y*nw+x] = (cur[(2*y)*cw + 2*x] + cur[(2*y)*cw + 2*x+1] + cur[(2*y+1)*cw + 2*x] + cur[(2*y+1)*cw + 2*x+1])/4 380 x = x + 1 381 } 382 y = y + 1 383 } 384 cur = nxt 385 cw = nw 386 chh = nh 387 } 388 sc = sc + 1 389 } 390 return 0 391} 392 393// the judge. out: 0 composition 1 palette_axis 2 contour_axis 3 face_axis 4 HEADLINE 394// 5 smooth_permil 6 detail_permil 7 pal_buckets 8 coherent_permil 9 edge_total 395func chj_judge(fb: *i64, w: i64, h: i64, confpath: *u8, out: *i64) -> i64 { 396 let cf: *i64 = sys_mmap(CHJ_NCONF*8) as *i64 397 chj_conf_load(confpath, cf) 398 let luma: *i64 = sys_mmap(w*h*8) as *i64 399 chj_luma(fb, w, h, luma) 400 let cellout: *i64 = sys_mmap(16) as *i64 401 chj_cells(luma, w, h, cf, cellout) 402 let sp: i64 = cellout[0] 403 let dp: i64 = cellout[1] 404 var sb: i64 = sp*1000/cf[CHJ_C_SMOOTH_DIV] 405 if sb > 1000 { sb = 1000 } 406 var db: i64 = dp*1000/cf[CHJ_C_DETAIL_DIV] 407 if db > 1000 { db = 1000 } 408 var comp: i64 = sb 409 if db < comp { comp = db } 410 let pal: i64 = chj_palette(fb, w, h) 411 var palax: i64 = pal*1000/cf[CHJ_C_PAL_DIV] 412 if palax > 1000 { palax = 1000 } 413 let conout: *i64 = sys_mmap(16) as *i64 414 let conax: i64 = chj_contour(luma, w, h, cf, conout) 415 var faceax: i64 = 1000 416 if cf[CHJ_C_FACE_REQ] == 1 { faceax = chj_face(luma, w, h, cf) } 417 var head: i64 = comp 418 if palax < head { head = palax } 419 if conax < head { head = conax } 420 if faceax < head { head = faceax } 421 out[0] = comp 422 out[1] = palax 423 out[2] = conax 424 out[3] = faceax 425 out[4] = head 426 out[5] = sp 427 out[6] = dp 428 out[7] = pal 429 out[8] = conout[0] 430 out[9] = conout[1] 431 return head 432}