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}