code wiki / (root) / nx_mediajudge_lib.nx

nx_mediajudge_lib.nx source

↩ module page · 463 lines · 25714 B

1// nx_mediajudge_lib.nx -- aesthetictwin AT36 (2026-09-17): SUBMITTED MEDIA THROUGH THE BEAUTY RULERS WITH A REPORT THE 2// OPERATOR CAN CHECK. Operator: "submit media to be stored in the vault and reaccessible to go through the beauty evaluator 3// and have output so i can confirm and triangulate the correct analysis is happening ... and not just photos but gif and 4// video". One library, every leg an incumbent: the bytes-to-RGB chokepoint decodes the still (JPEG, PNG, GIF first frame, 5// WebP lossless, BMP, TIFF, TGA, PCX, ICO, PNM), the APNG decoder yields frames, the photo ruler (nx_photomark_lib) places 6// the face, canthi and mouth and derives the card axes, the skin ruler (nx_skinsample_lib) reads tone, the symmetry judge 7// (nx_image_symmetry_lib) scores bilateral symmetry on the ORIGINAL pixels, the estate's PNG writer draws the OVERLAY of 8// what the rulers saw (face box, eye bands, canthus crosses, mouth box, stomion row), and the referee (pm_referee_sum) 9// scores the placement against an outside oracle row when one exists. The output is DATA: a judge file (pipe rows), a 10// manifest in nx_asset_page's row shape, and the same rows keyed by slug as a plane staging file (<slug>.plane.tsv) for 11// nx_store_seed, so nx_asset_page emit publishes the original beside the overlay and the numbers from the asset plane. 12// Consumers: nx_mediajudge (CLI), nx_mediajudge_gate (planted controls). Video (MP4/H.264, VP8) is the next rung: the 13// decoders exist (nx_mp4_demux, nx_h264_decode_frame, nx_vp8_kf) and this library's frame path takes their frames as data. 14// license_tier: ORIGINAL No hw writes (Rule 26). 15import "nx_syscalls.nx" 16import "nx_photomark_lib.nx" 17import "nx_img_bytes_to_rgb.nx" 18import "nx_apng_decode.nx" 19import "nx_png.nx" 20import "nx_image_symmetry_lib.nx" 21 22const MJ_I64: i64 = 8 23const MJ_RGB_BPP: i64 = 3 24const MJ_BYTE: i64 = 255 25const MJ_SHIFT_G: i64 = 8 26const MJ_SHIFT_B: i64 = 16 27const MJ_C_FACE: i64 = 65280 // green: the face box 28const MJ_C_EYE: i64 = 16776960 // cyan: the eye bands 29const MJ_C_CANTHUS: i64 = 255 // red: the four canthi 30const MJ_C_MOUTH: i64 = 16711935 // magenta: the mouth box 31const MJ_C_STOMION: i64 = 65535 // yellow: the stomion row 32const MJ_CROSS_R: i64 = 4 33const MJ_E_READ: i64 = 0 - 1 34const MJ_E_DECODE: i64 = 0 - 2 35const MJ_E_WRITE: i64 = 0 - 3 36const MJ_MODE644: i64 = 420 37const MJ_PATH_CAP: i64 = 1024 38const MJ_MANIFEST_CAP: i64 = 16384 // a named reserve for the manifest and judge text; exceeding it is a refusal, never a truncation 39const MJ_MAX_FRAMES: i64 = 16 // the APNG frame budget (frames of the decoded size are reserved for the count), announced in the receipt when hit 40const MJ_TAB: i64 = 9 41const MJ_NL: i64 = 10 42const MJ_DOT: i64 = 46 43const MJ_SLASH: i64 = 47 44const MJ_PNG_SIG0: i64 = 137 45const MJ_APNG_ACTL_A: i64 = 97 // "acTL" chunk letters, the APNG marker 46const MJ_APNG_ACTL_C: i64 = 99 47const MJ_APNG_ACTL_T: i64 = 84 48const MJ_APNG_ACTL_L: i64 = 76 49const MJ_APNG_SCAN: i64 = 4096 // the acTL chunk sits in the first chunks; scanning this far names an APNG 50const MJ_CODEC_STILL: i64 = 1 51const MJ_CODEC_APNG: i64 = 2 52// result slots 53const MJ_R_W: i64 = 0 54const MJ_R_H: i64 = 1 55const MJ_R_CODEC: i64 = 2 56const MJ_R_FRAMES: i64 = 3 57const MJ_R_FRAMES_CAPPED: i64 = 4 58const MJ_R_SYMMETRY: i64 = 5 59const MJ_R_OVERLAY_BYTES: i64 = 6 60const MJ_R_MANIFEST_BYTES: i64 = 7 61const MJ_R_JUDGE_ROWS: i64 = 8 62const MJ_R_RULER_RC: i64 = 9 63const MJ_R_ORIGINAL_BYTES: i64 = 10 64const MJ_R_REFEREE_FOUND: i64 = 11 65const MJ_R_REFEREE_NME_MAX: i64 = 12 66const MJ_R_REFEREE_JUDGED: i64 = 13 67const MJ_R_PLANE_BYTES: i64 = 14 68const MJ_R_GAP_ROWS: i64 = 15 69const MJ_R_N: i64 = 16 70const MJ_ROWS_FIXED: i64 = 8 // meta 1 + tasset 2 + tier 3 + lic 2: the manifest rows that are neither judge nor gap rows 71const MJ_OF_LIP: i64 = 25 // the oracle row's own upper-over-lower lip ratio in permil (column 26 is exo_px) 72const MJ_HASH: i64 = 35 73const MJ_GAP_HOLDER: *u8 = "outside oracle: MediaPipe FaceLandmarker landmarks through the ruler's own axis arithmetic" 74const MJ_GAP_HOLDER_LIP: *u8 = "outside oracle: the oracle row's own lip ratio" 75const MJ_LIC_STATUS: *u8 = "no licence row was submitted with this media: beta review only, production publish is gated on a licence row" 76 77func mj_pack(rgb: *u8, n: i64, fb: *i64) -> i64 { 78 var i: i64 = 0 79 while i < n { 80 fb[i] = (rgb[i * MJ_RGB_BPP] as i64) + ((rgb[i * MJ_RGB_BPP + 1] as i64) << MJ_SHIFT_G) + ((rgb[i * MJ_RGB_BPP + 2] as i64) << MJ_SHIFT_B) 81 i = i + 1 82 } 83 return 0 84} 85func mj_unpack(fb: *i64, n: i64, rgb: *u8) -> i64 { 86 var i: i64 = 0 87 while i < n { 88 let c: i64 = fb[i] 89 rgb[i * MJ_RGB_BPP] = (c & MJ_BYTE) as u8 90 rgb[i * MJ_RGB_BPP + 1] = ((c >> MJ_SHIFT_G) & MJ_BYTE) as u8 91 rgb[i * MJ_RGB_BPP + 2] = ((c >> MJ_SHIFT_B) & MJ_BYTE) as u8 92 i = i + 1 93 } 94 return 0 95} 96func mj_px(fb: *i64, w: i64, h: i64, x: i64, y: i64, c: i64) -> i64 { 97 if x < 0 { return 0 } 98 if y < 0 { return 0 } 99 if x >= w { return 0 } 100 if y >= h { return 0 } 101 fb[y * w + x] = c 102 return 1 103} 104func mj_hline(fb: *i64, w: i64, h: i64, x0: i64, x1: i64, y: i64, c: i64) -> i64 { 105 var x: i64 = x0 106 while x <= x1 { mj_px(fb, w, h, x, y, c); x = x + 1 } 107 return 0 108} 109func mj_vline(fb: *i64, w: i64, h: i64, x: i64, y0: i64, y1: i64, c: i64) -> i64 { 110 var y: i64 = y0 111 while y <= y1 { mj_px(fb, w, h, x, y, c); y = y + 1 } 112 return 0 113} 114func mj_rect(fb: *i64, w: i64, h: i64, x0: i64, y0: i64, x1: i64, y1: i64, c: i64) -> i64 { 115 mj_hline(fb, w, h, x0, x1, y0, c); mj_hline(fb, w, h, x0, x1, y1, c) 116 mj_vline(fb, w, h, x0, y0, y1, c); mj_vline(fb, w, h, x1, y0, y1, c) 117 return 0 118} 119func mj_cross(fb: *i64, w: i64, h: i64, x: i64, y: i64, c: i64) -> i64 { 120 mj_hline(fb, w, h, x - MJ_CROSS_R, x + MJ_CROSS_R, y, c) 121 mj_vline(fb, w, h, x, y - MJ_CROSS_R, y + MJ_CROSS_R, c) 122 return 0 123} 124// draw what the ruler saw onto a packed copy of the frame 125func mj_draw(fb: *i64, w: i64, h: i64, res: *i64) -> i64 { 126 if res[PM_R_FACE] == 1 { mj_rect(fb, w, h, res[PM_R_FX0], res[PM_R_FY0], res[PM_R_FX1], res[PM_R_FY1], MJ_C_FACE) } 127 if res[PM_R_EYES] == 2 { 128 mj_rect(fb, w, h, res[PM_R_EX0], res[PM_R_EY0], res[PM_R_EX1], res[PM_R_EY1], MJ_C_EYE) 129 mj_rect(fb, w, h, res[PM_R_LX0], res[PM_R_LY0], res[PM_R_LX1], res[PM_R_LY1], MJ_C_EYE) 130 mj_cross(fb, w, h, res[PM_R_RLAT_X], res[PM_R_RLAT_Y], MJ_C_CANTHUS) 131 mj_cross(fb, w, h, res[PM_R_RMED_X], res[PM_R_RMED_Y], MJ_C_CANTHUS) 132 mj_cross(fb, w, h, res[PM_R_LMED_X], res[PM_R_LMED_Y], MJ_C_CANTHUS) 133 mj_cross(fb, w, h, res[PM_R_LLAT_X], res[PM_R_LLAT_Y], MJ_C_CANTHUS) 134 } 135 if res[PM_R_MOUTH] == 1 { 136 mj_rect(fb, w, h, res[PM_R_MX0], res[PM_R_MY0], res[PM_R_MX1], res[PM_R_MY1], MJ_C_MOUTH) 137 mj_hline(fb, w, h, res[PM_R_MX0], res[PM_R_MX1], res[PM_R_STOMION_Y], MJ_C_STOMION) 138 } 139 return 0 140} 141// the overlay PNG on disk; returns the bytes on disk (png_publish's answer) 142func mj_overlay(rgb: *u8, w: i64, h: i64, res: *i64, path: *u8) -> i64 { 143 let n: i64 = w * h 144 let fb: *i64 = sys_mmap(n * MJ_I64) as *i64 145 mj_pack(rgb, n, fb) 146 mj_draw(fb, w, h, res) 147 return png_publish(fb, w, h, path, 0) 148} 149// bilateral symmetry on the ORIGINAL pixels: the judge reads PNG bytes, so the frame is encoded losslessly in memory first 150func mj_symmetry(rgb: *u8, w: i64, h: i64) -> i64 { 151 let n: i64 = w * h 152 let fb: *i64 = sys_mmap(n * MJ_I64) as *i64 153 mj_pack(rgb, n, fb) 154 let cap: i64 = png_buf_cap(w, h) 155 if cap <= 0 { return 0 - 1 } 156 let buf: *u8 = sys_mmap(cap) 157 let total: i64 = write_png_buf(fb, w, h, buf) 158 if total <= 0 { return 0 - 1 } 159 return go_image_symmetry(buf, total) 160} 161func mj_write(path: *u8, buf: *u8, n: i64) -> i64 { 162 let fd: i64 = sys_openat_wr(path, MJ_MODE644) 163 if fd < 0 { return MJ_E_WRITE } 164 var off: i64 = 0 165 var going: i64 = 1 166 while going == 1 { 167 if off >= n { going = 0 } else { 168 let k: i64 = sys_write(fd, (buf as i64 + off) as *u8, n - off) 169 if k <= 0 { going = 0 } else { off = off + k } 170 } 171 } 172 sys_close(fd) 173 if off != n { return MJ_E_WRITE } 174 return n 175} 176// the extension of a path (bytes after the last dot after the last slash), or an empty string 177func mj_ext(path: *u8) -> *u8 { 178 var i: i64 = 0 179 var dot: i64 = 0 - 1 180 while path[i] != (0 as u8) { 181 if (path[i] as i64) == MJ_SLASH { dot = 0 - 1 } 182 if (path[i] as i64) == MJ_DOT { dot = i } 183 i = i + 1 184 } 185 if dot < 0 { return "" as *u8 } 186 return (path as i64 + dot + 1) as *u8 187} 188func mj_join3(out: *u8, a: *u8, b: *u8, c: *u8) -> i64 { 189 var o: i64 = ri_cat(out, 0, a) 190 o = ri_cat(out, o, b) 191 o = ri_cat(out, o, c) 192 out[o] = 0 as u8 193 return o 194} 195// is this PNG an APNG: the acTL chunk within the first bytes 196func mj_is_apng(raw: *u8, n: i64) -> i64 { 197 if n < MJ_APNG_SCAN { return 0 } 198 if (raw[0] as i64) != MJ_PNG_SIG0 { return 0 } 199 var i: i64 = 0 200 while i + 3 < MJ_APNG_SCAN { 201 if (raw[i] as i64) == MJ_APNG_ACTL_A { if (raw[i + 1] as i64) == MJ_APNG_ACTL_C { if (raw[i + 2] as i64) == MJ_APNG_ACTL_T { if (raw[i + 3] as i64) == MJ_APNG_ACTL_L { return 1 } } } } 202 i = i + 1 203 } 204 return 0 205} 206// a tab-separated manifest row: judge <organ> <axis> <value> <unit> <evidence> 207func mj_judge_row(out: *u8, o: i64, organ: *u8, axis: *u8, value: i64, unit: *u8, evidence: *u8) -> i64 { 208 var p: i64 = ri_cat(out, o, "judge\t" as *u8) 209 p = ri_cat(out, p, organ); out[p] = MJ_TAB as u8; p = p + 1 210 p = ri_cat(out, p, axis); out[p] = MJ_TAB as u8; p = p + 1 211 p = ri_catn(out, p, value); out[p] = MJ_TAB as u8; p = p + 1 212 p = ri_cat(out, p, unit); out[p] = MJ_TAB as u8; p = p + 1 213 p = ri_cat(out, p, evidence); out[p] = MJ_NL as u8; p = p + 1 214 return p 215} 216// a tab-separated gap row: gap <axis> <ours> <best> <holder> -- ours beside the outside oracle, the triangulation row 217func mj_gap_row(out: *u8, o: i64, axis: *u8, ours: i64, best: i64, holder: *u8) -> i64 { 218 var p: i64 = ri_cat(out, o, "gap\t" as *u8) 219 p = ri_cat(out, p, axis); out[p] = MJ_TAB as u8; p = p + 1 220 p = ri_catn(out, p, ours); out[p] = MJ_TAB as u8; p = p + 1 221 p = ri_catn(out, p, best); out[p] = MJ_TAB as u8; p = p + 1 222 p = ri_cat(out, p, holder); out[p] = MJ_NL as u8; p = p + 1 223 return p 224} 225// the outside oracle's axes for the FIRST judged row of an oracle file, through the ruler's own axis arithmetic (the 226// referee's method): the four canthi become a result record and pm_axes derives tilts and indices from them, so the gap 227// table compares roll-corrected to roll-corrected; the lip ratio is the oracle row's own column. 1 when a row was read. 228func mj_oracle_axes(oracle_path: *u8, oref: *i64) -> i64 { 229 let fl: *i64 = sys_mmap(16) as *i64 230 fl[0] = 0 231 let buf: *u8 = sys_read_file(oracle_path, fl) 232 if (buf as i64) == 0 { return 0 } 233 let n: i64 = fl[0] 234 if n <= 0 { return 0 } 235 let fb: *i64 = sys_mmap(2 * MJ_I64) as *i64 236 let xy: *i64 = sys_mmap(2 * MJ_I64) as *i64 237 var ls: i64 = 0 238 while ls < n { 239 var le: i64 = ls 240 var scanning: i64 = 1 241 while scanning == 1 { 242 if le >= n { scanning = 0 } else { 243 if (buf[le] as i64) == MJ_NL { scanning = 0 } else { le = le + 1 } 244 } 245 } 246 if le > ls { 247 if (buf[ls] as i64) != MJ_HASH { 248 if pm_field(buf, ls, le, PM_OF_FILE, fb) == 1 { 249 if pm_field_int(buf, ls, le, PM_OF_FACES) == 1 { 250 var k: i64 = 0 251 while k < PM_R_N { oref[k] = 0; k = k + 1 } 252 if pm_field_xy(buf, ls, le, PM_OF_RLAT, xy) == 0 { return 0 } 253 oref[PM_R_RLAT_X] = xy[0]; oref[PM_R_RLAT_Y] = xy[1] 254 if pm_field_xy(buf, ls, le, PM_OF_RMED, xy) == 0 { return 0 } 255 oref[PM_R_RMED_X] = xy[0]; oref[PM_R_RMED_Y] = xy[1] 256 if pm_field_xy(buf, ls, le, PM_OF_LMED, xy) == 0 { return 0 } 257 oref[PM_R_LMED_X] = xy[0]; oref[PM_R_LMED_Y] = xy[1] 258 if pm_field_xy(buf, ls, le, PM_OF_LLAT, xy) == 0 { return 0 } 259 oref[PM_R_LLAT_X] = xy[0]; oref[PM_R_LLAT_Y] = xy[1] 260 pm_axes(oref) 261 oref[PM_R_LIP] = pm_field_int(buf, ls, le, MJ_OF_LIP) 262 return 1 263 } 264 } 265 } 266 } 267 ls = le + 1 268 } 269 return 0 270} 271// the manifest in nx_asset_page's shape; returns the byte count, counts judge rows into out[MJ_R_JUDGE_ROWS] and gap 272// rows into out[MJ_R_GAP_ROWS]; oref holds the oracle-side axes when have_oracle is 1 273func mj_manifest(buf: *u8, slug: *u8, src_path: *u8, orig_href: *u8, overlay_href: *u8, res: *i64, sres: *i64, oref: *i64, have_oracle: i64, out: *i64) -> i64 { 274 var o: i64 = ri_cat(buf, 0, "meta\t" as *u8) 275 o = ri_cat(buf, o, slug); o = ri_cat(buf, o, "\timg\tMedia judged by the beauty rulers: " as *u8); o = ri_cat(buf, o, slug); buf[o] = MJ_NL as u8; o = o + 1 276 o = ri_cat(buf, o, "tasset\t1\timg\t" as *u8); o = ri_cat(buf, o, orig_href); o = ri_cat(buf, o, "\tORIGINAL: the submitted media as stored\n" as *u8) 277 o = ri_cat(buf, o, "tasset\t2\timg\t" as *u8); o = ri_cat(buf, o, overlay_href); o = ri_cat(buf, o, "\tMEASURED: what the rulers saw (face box green, eye bands cyan, canthi red, mouth magenta, stomion yellow)\n" as *u8) 278 o = ri_cat(buf, o, "tier\t1\tLIVE\tthe submitted media, content-addressed in the vault\n" as *u8) 279 o = ri_cat(buf, o, "tier\t2\tLIVE\tthe rulers' overlay and axes, the same instrument that reads the reference\n" as *u8) 280 o = ri_cat(buf, o, "tier\t3\tOPEN\tthe twin render fitted to these axes (aesthetictwin AT34)\n" as *u8) 281 // the licence beside the media: a submission carries no licence row, and the page says so rather than showing a blank 282 o = ri_cat(buf, o, "lic\tstatus\t" as *u8); o = ri_cat(buf, o, MJ_LIC_STATUS); buf[o] = MJ_NL as u8; o = o + 1 283 o = ri_cat(buf, o, "lic\tsubmitted_path\t" as *u8); o = ri_cat(buf, o, src_path); buf[o] = MJ_NL as u8; o = o + 1 284 var rows: i64 = 0 285 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "face_found" as *u8, res[PM_R_FACE], "flag" as *u8, "cascade or skin route, the face box on the overlay" as *u8); rows = rows + 1 286 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "eyes_placed" as *u8, res[PM_R_EYES], "count" as *u8, "both canthi of each eye placed and the pair check passed" as *u8); rows = rows + 1 287 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "mouth_found" as *u8, res[PM_R_MOUTH], "flag" as *u8, "red-chroma mouth band and stomion row" as *u8); rows = rows + 1 288 if res[PM_R_EYES] == 2 { 289 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "canthal_tilt_R" as *u8, res[PM_R_TILT_R], "deg10" as *u8, "roll-corrected, lateral above medial positive, error bar in tilt_err" as *u8); rows = rows + 1 290 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "canthal_tilt_L" as *u8, res[PM_R_TILT_L], "deg10" as *u8, "roll-corrected, lateral above medial positive" as *u8); rows = rows + 1 291 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "tilt_err" as *u8, res[PM_R_TILT_ERR], "deg10" as *u8, "two pixels of canthus error over the shorter fissure" as *u8); rows = rows + 1 292 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "intercanthal_index" as *u8, res[PM_R_ICI], "permil" as *u8, "inner over outer canthal span" as *u8); rows = rows + 1 293 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "intercanthal_eq_fissure" as *u8, res[PM_R_ICF], "permil" as *u8, "inner span over the mean fissure" as *u8); rows = rows + 1 294 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "fissure_R" as *u8, res[PM_R_FISSURE_R], "px" as *u8, "right eye lateral to medial canthus" as *u8); rows = rows + 1 295 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "fissure_L" as *u8, res[PM_R_FISSURE_L], "px" as *u8, "left eye medial to lateral canthus" as *u8); rows = rows + 1 296 } 297 if res[PM_R_MOUTH] == 1 { 298 o = mj_judge_row(buf, o, "nx_photomark" as *u8, "upper_over_lower_lip" as *u8, res[PM_R_LIP], "permil" as *u8, "upper lip height over lower lip height at the stomion" as *u8); rows = rows + 1 299 } 300 o = mj_judge_row(buf, o, "nx_skinsample" as *u8, "skin_L" as *u8, sres[SKS_R_L], "Lab_L_e3" as *u8, "mean Lab lightness of skin-classified pixels" as *u8); rows = rows + 1 301 o = mj_judge_row(buf, o, "nx_skinsample" as *u8, "skin_ITA" as *u8, sres[SKS_R_ITA], "deg10" as *u8, "individual typology angle of the skin pixels" as *u8); rows = rows + 1 302 o = mj_judge_row(buf, o, "nx_skinsample" as *u8, "skin_pixels" as *u8, sres[SKS_R_COUNT], "count" as *u8, "pixels the skin classifier accepted" as *u8); rows = rows + 1 303 o = mj_judge_row(buf, o, "nx_image_beauty" as *u8, "bilateral_symmetry" as *u8, out[MJ_R_SYMMETRY], "q1000" as *u8, "bilateral symmetry on the original pixels" as *u8); rows = rows + 1 304 if out[MJ_R_REFEREE_JUDGED] > 0 { 305 o = mj_judge_row(buf, o, "nx_photomark referee" as *u8, "oracle_nme_max" as *u8, out[MJ_R_REFEREE_NME_MAX], "permil" as *u8, "worst canthus error against the outside oracle row, permil of the oracle's outer-canthal span" as *u8); rows = rows + 1 306 o = mj_judge_row(buf, o, "nx_photomark referee" as *u8, "oracle_found" as *u8, out[MJ_R_REFEREE_FOUND], "flag" as *u8, "1 when every canthus is within the found bar of the oracle" as *u8); rows = rows + 1 307 } 308 // the triangulation rows: every axis both sides measured, ours beside the outside oracle's 309 var gaps: i64 = 0 310 if have_oracle == 1 { 311 if res[PM_R_EYES] == 2 { 312 o = mj_gap_row(buf, o, "canthal_tilt_R_deg10" as *u8, res[PM_R_TILT_R], oref[PM_R_TILT_R], MJ_GAP_HOLDER); gaps = gaps + 1 313 o = mj_gap_row(buf, o, "canthal_tilt_L_deg10" as *u8, res[PM_R_TILT_L], oref[PM_R_TILT_L], MJ_GAP_HOLDER); gaps = gaps + 1 314 o = mj_gap_row(buf, o, "intercanthal_index_permil" as *u8, res[PM_R_ICI], oref[PM_R_ICI], MJ_GAP_HOLDER); gaps = gaps + 1 315 o = mj_gap_row(buf, o, "intercanthal_eq_fissure_permil" as *u8, res[PM_R_ICF], oref[PM_R_ICF], MJ_GAP_HOLDER); gaps = gaps + 1 316 } 317 if res[PM_R_MOUTH] == 1 { 318 o = mj_gap_row(buf, o, "upper_over_lower_lip_permil" as *u8, res[PM_R_LIP], oref[PM_R_LIP], MJ_GAP_HOLDER_LIP); gaps = gaps + 1 319 } 320 } 321 buf[o] = 0 as u8 322 out[MJ_R_JUDGE_ROWS] = rows 323 out[MJ_R_GAP_ROWS] = gaps 324 return o 325} 326// JUDGE ONE FILE: decode, measure, skin, symmetry, overlay, original copy, manifest and judge file into outdir. 327// oracle_path may be an empty string; when given, its rows name the file by the path the referee opens (dir is empty). 328// hrefpfx is the site-absolute prefix the manifest's tasset hrefs carry (the page law: a relative ref resolves against the 329// PARENT behind the edge's slash-stripping 301, so a published report names /compare/<dom>/media/<slug>/); empty = bare names. 330// Returns MJ_E_READ (file unreadable), MJ_E_DECODE (no decoder took it), MJ_E_WRITE (an output refused), else 0. 331func mj_judge_file_at(path: *u8, slug: *u8, outdir: *u8, oracle_path: *u8, hrefpfx: *u8, face_c: *HaarCascade, eye_c: *HaarCascade, res: *i64, sres: *i64, out: *i64) -> i64 { 332 var k: i64 = 0 333 while k < MJ_R_N { out[k] = 0; k = k + 1 } 334 let fl: *i64 = sys_mmap(16) as *i64 335 fl[0] = 0 336 let raw: *u8 = sys_read_file(path, fl) 337 if (raw as i64) == 0 { return MJ_E_READ } 338 if fl[0] <= 0 { return MJ_E_READ } 339 out[MJ_R_ORIGINAL_BYTES] = fl[0] 340 let wh: *i64 = sys_mmap(4 * MJ_I64) as *i64 341 var rgbp: i64 = 0 342 var w: i64 = 0 343 var h: i64 = 0 344 var codec: i64 = MJ_CODEC_STILL 345 let rgb0: *u8 = nx_img_bytes_to_rgb(raw, fl[0], wh) 346 if (rgb0 as i64) == 0 { return MJ_E_DECODE } 347 rgbp = rgb0 as i64 348 w = wh[0] 349 h = wh[1] 350 if w <= 0 { return MJ_E_DECODE } 351 if h <= 0 { return MJ_E_DECODE } 352 out[MJ_R_FRAMES] = 1 353 if mj_is_apng(raw, fl[0]) == 1 { 354 // frames: the FIRST frame is judged (the still contract); the count is announced, capped at the frame budget, 355 // the frame stack sized from the decoded frame so no frame is ever written past what was allocated 356 let cap: i64 = MJ_MAX_FRAMES 357 let probe: *i64 = sys_mmap(4 * MJ_I64) as *i64 358 let stack: *i64 = sys_mmap(w * h * cap * MJ_I64) as *i64 359 let nf: i64 = apng_decode(raw, fl[0], stack, probe, cap) 360 if nf > 0 { 361 codec = MJ_CODEC_APNG 362 out[MJ_R_FRAMES] = nf 363 if nf >= cap { out[MJ_R_FRAMES_CAPPED] = 1 } 364 } 365 } 366 out[MJ_R_W] = w; out[MJ_R_H] = h; out[MJ_R_CODEC] = codec 367 let rgb: *u8 = rgbp as *u8 368 out[MJ_R_RULER_RC] = pm_landmarks_auto(rgb, w, h, res, face_c, eye_c) 369 sks_measure_rgb(rgb, w, h, sres) 370 out[MJ_R_SYMMETRY] = mj_symmetry(rgb, w, h) 371 // the referee against an oracle row, when one is given 372 if oracle_path[0] != (0 as u8) { 373 let sum: *i64 = sys_mmap(PM_RS_N * MJ_I64) as *i64 374 let judged: i64 = pm_referee_sum(oracle_path, "" as *u8, face_c, eye_c, sum) 375 if judged > 0 { 376 out[MJ_R_REFEREE_JUDGED] = judged 377 out[MJ_R_REFEREE_FOUND] = sum[PM_RS_FOUND] 378 out[MJ_R_REFEREE_NME_MAX] = sum[PM_RS_LAST_NME_MAX] 379 } 380 } 381 // the outputs beside each other in outdir 382 let p1: *u8 = sys_mmap(MJ_PATH_CAP) 383 let p2: *u8 = sys_mmap(MJ_PATH_CAP) 384 let p3: *u8 = sys_mmap(MJ_PATH_CAP) 385 let p4: *u8 = sys_mmap(MJ_PATH_CAP) 386 let h1: *u8 = sys_mmap(MJ_PATH_CAP) 387 let h2: *u8 = sys_mmap(MJ_PATH_CAP) 388 let ext: *u8 = mj_ext(path) 389 var o: i64 = ri_cat(h1, 0, slug); o = ri_cat(h1, o, ".original." as *u8); o = ri_cat(h1, o, ext); h1[o] = 0 as u8 390 o = ri_cat(h2, 0, slug); o = ri_cat(h2, o, ".overlay.png" as *u8); h2[o] = 0 as u8 391 mj_join3(p1, outdir, "/" as *u8, h1) 392 mj_join3(p2, outdir, "/" as *u8, h2) 393 // the hrefs the page carries: the prefix then the file name (site-absolute when a prefix is given) 394 let a1: *u8 = sys_mmap(MJ_PATH_CAP) 395 let a2: *u8 = sys_mmap(MJ_PATH_CAP) 396 o = ri_cat(a1, 0, hrefpfx); o = ri_cat(a1, o, h1); a1[o] = 0 as u8 397 o = ri_cat(a2, 0, hrefpfx); o = ri_cat(a2, o, h2); a2[o] = 0 as u8 398 o = ri_cat(p3, 0, outdir); o = ri_cat(p3, o, "/" as *u8); o = ri_cat(p3, o, slug); o = ri_cat(p3, o, ".manifest.tsv" as *u8); p3[o] = 0 as u8 399 o = ri_cat(p4, 0, outdir); o = ri_cat(p4, o, "/" as *u8); o = ri_cat(p4, o, slug); o = ri_cat(p4, o, ".judge" as *u8); p4[o] = 0 as u8 400 if mj_write(p1, raw, fl[0]) < 0 { return MJ_E_WRITE } 401 out[MJ_R_OVERLAY_BYTES] = mj_overlay(rgb, w, h, res, p2) 402 if out[MJ_R_OVERLAY_BYTES] <= 0 { return MJ_E_WRITE } 403 let mbuf: *u8 = sys_mmap(MJ_MANIFEST_CAP) 404 let oref: *i64 = sys_mmap(PM_R_N * MJ_I64) as *i64 405 var have_oracle: i64 = 0 406 if oracle_path[0] != (0 as u8) { have_oracle = mj_oracle_axes(oracle_path, oref) } 407 let mn: i64 = mj_manifest(mbuf, slug, path, a1, a2, res, sres, oref, have_oracle, out) 408 if mn >= MJ_MANIFEST_CAP { return MJ_E_WRITE } 409 if mj_write(p3, mbuf, mn) < 0 { return MJ_E_WRITE } 410 out[MJ_R_MANIFEST_BYTES] = mn 411 // the judge file: the same rows in the estate's pipe grammar (judge|organ|axis|value|unit|evidence) 412 let jbuf: *u8 = sys_mmap(MJ_MANIFEST_CAP) 413 var j: i64 = 0 414 var i: i64 = 0 415 while i < mn { 416 let c: i64 = mbuf[i] as i64 417 if c == MJ_TAB { jbuf[j] = PM_PIPE as u8 } else { jbuf[j] = c as u8 } 418 j = j + 1 419 i = i + 1 420 } 421 if mj_write(p4, jbuf, j) < 0 { return MJ_E_WRITE } 422 // the plane staging file: the same rows keyed by slug, because the asset plane is slug-keyed and 423 // nx_asset_page emit selects one slug's rows from the plane (seed this file, then emit from the plane); 424 // sized from the manifest and the row count, so no reserve is guessed 425 let p5: *u8 = sys_mmap(MJ_PATH_CAP) 426 o = ri_cat(p5, 0, outdir); o = ri_cat(p5, o, "/" as *u8); o = ri_cat(p5, o, slug); o = ri_cat(p5, o, ".plane.tsv" as *u8); p5[o] = 0 as u8 427 let sl: i64 = ri_slen(slug) 428 let rows_total: i64 = MJ_ROWS_FIXED + out[MJ_R_JUDGE_ROWS] + out[MJ_R_GAP_ROWS] 429 let pcap: i64 = mn + (rows_total + 1) * (sl + 1) + 1 430 let pbuf: *u8 = sys_mmap(pcap) 431 var q: i64 = 0 432 var at_line: i64 = 1 433 i = 0 434 while i < mn { 435 if at_line == 1 { 436 if q + sl + 1 > pcap { return MJ_E_WRITE } 437 q = ri_cat(pbuf, q, slug); pbuf[q] = MJ_TAB as u8; q = q + 1; at_line = 0 438 } 439 let c2: i64 = mbuf[i] as i64 440 pbuf[q] = c2 as u8 441 q = q + 1 442 if c2 == MJ_NL { at_line = 1 } 443 i = i + 1 444 } 445 if mj_write(p5, pbuf, q) < 0 { return MJ_E_WRITE } 446 out[MJ_R_PLANE_BYTES] = q 447 return 0 448} 449// the bare-filename form (the board's contract symbol): hrefs are the file names beside the page, for a report read from 450// its own directory or a gate fixture; the published form passes the site-absolute prefix through mj_judge_file_at 451func mj_judge_file(path: *u8, slug: *u8, outdir: *u8, oracle_path: *u8, face_c: *HaarCascade, eye_c: *HaarCascade, res: *i64, sres: *i64, out: *i64) -> i64 { 452 return mj_judge_file_at(path, slug, outdir, oracle_path, "" as *u8, face_c, eye_c, res, sres, out) 453} 454func mj_load_cascades(out: *i64) -> i64 { 455 let rc: *i64 = sys_mmap(MJ_I64) as *i64 456 let face_c: *HaarCascade = ff_load_face(rc) 457 let eye_c: *HaarCascade = ff_load_eye(rc) 458 out[0] = face_c as i64 459 out[1] = eye_c as i64 460 if (face_c as i64) == 0 { return 0 } 461 if (eye_c as i64) == 0 { return 0 } 462 return 1 463}