nx_photomark_lib.nx source
↩ module page · 1927 lines · 104806 B
1// nx_photomark_lib.nx -- THE 2D PHOTO LANDMARKER, FIRST BYTE (aesthetictwin AT4 pm_canthal_tilt, AT23 rulers pm_landmarks
2// pm_eyes pm_lips, AT30 cascade route, AT31 pm_referee; 2026-09-16). The estate's real input is a photograph, not a scan:
3// this ruler places the canon landmarks a frontal still can carry -- both canthi of each eye and the vermilion split of the
4// mouth -- and measures the axes that need NO absolute scale: canthal tilt per side (deg10, lateral canthus above the medial
5// is positive, corrected for the head's roll read off the medial canthi), intercanthal_index (endocanthion span over
6// exocanthion span, permil), intercanthal_eq_fissure (endocanthion span over the mean fissure width, permil) and
7// upper_over_lower_lip (permil). Every value is MEASURED: it comes with the counts it rests on printed beside it, and a face
8// the ruler cannot find, an eye pair it cannot pair or a mouth it cannot see is UNMEASURED by name, never a default.
9// TWO ROUTES, one ruler. The CASCADE route (nx_facefind_lib over the loaded cascade data, beta-only bootstrap arm, AT30)
10// gives the face box and an eye box per side; the SKIN route (a coarse skin grid, the largest skin component, a head box,
11// enclosed dark blobs paired by consistency) stands when no cascade is loaded or no face is found; on real stills it read
12// blonde hair and sand as skin and light irises as not dark, which is why the cascade route is primary. Either route only
13// SEEDS each eye: since AT44 (2026-09-18) the canthi are the extremes of the fissure GROWN from the seed's pupil by local
14// darkness out to the face's side and the other eye (pm_eye_fissure), and a fissure that reaches that window's side is
15// refused by name (PM_PF_PINNED) -- the first ruler's canthi were a band's edges on every render.
16// Sides follow the IMAGE: the eye on the image left is the subject's RIGHT eye; a mirrored still swaps them (gate-proven).
17// THE MOUTH (AT44) is five landmarks -- labrale superius, stomion, labrale inferius, both cheilions -- found from the SEAM (a
18// walk from the midline's deepest valley and the window's strongest valley path, each verified by the vermilion's hue on both
19// sides) with every refusal named (PM_MW_*); pm_mouth_anchored runs it on canthi supplied from outside.
20// THE REFEREE (AT31): pm_referee reads the OUTSIDE oracle rows (knowledge/refintake/<slug>.oracle: MediaPipe run outside
21// the estate over the mirrored bytes, pixels x10) and scores every measured canthus as permil of the oracle's outer-canthal
22// span (the field's NME shape), printing per still FOUND | PLACED-OFF | UNMEASURED | UNJUDGED with a partition that sums;
23// since AT44 it scores the mouth too, both as the ruler places it and anchored on the oracle's canthi, each with its partition.
24// LIB, no main. Composes nx_skinsample_lib (decoder, skin mask, colour helpers, fixed-point angle), nx_facefind_lib (boxes),
25// nx_twincard_lib (card). license_tier: ORIGINAL No hw writes (Rule 26).
26import "nx_syscalls.nx"
27import "nx_skinsample_lib.nx"
28import "nx_facefind_lib.nx"
29import "nx_png_decoder.nx"
30
31const PM_GRID: i64 = 96
32const PM_MIN_CELL: i64 = 2
33const PM_EYE_FINE_DIV: i64 = 3
34const PM_EYE_ZONE_TOP_PERMIL: i64 = 180
35const PM_EYE_ZONE_BOT_PERMIL: i64 = 580
36const PM_PAIR_TRIES: i64 = 4
37const PM_FISSURE_ASYM_PERMIL: i64 = 350
38const PM_TILT_MAX_RISE_PERMIL: i64 = 450
39const PM_ENDO_MIN_PERMIL: i64 = 600
40const PM_ENDO_MAX_X10: i64 = 25
41const PM_HEAD_TOP_PERMIL: i64 = 200
42const PM_HEAD_HEIGHT_X10: i64 = 15
43const PM_HEAD_HALFW_PERMIL: i64 = 750
44const PM_TOP_BLOBS: i64 = 6
45const PM_TB_FIELDS: i64 = 5
46const PM_PERMIL: i64 = 1000
47const PM_SKIN_CELL_PERMIL: i64 = 500
48const PM_MIN_FACE_CELLS: i64 = 16
49const PM_DARK_PERMIL: i64 = 600
50const PM_EYE_ZONE_PERMIL: i64 = 620
51const PM_PAIR_DY_PERMIL: i64 = 150
52const PM_PAIR_DX_LO_PERMIL: i64 = 180
53const PM_PAIR_DX_HI_PERMIL: i64 = 700
54const PM_EYE_MAXW_PERMIL: i64 = 450
55const PM_BROW_ASPECT_X10: i64 = 32
56const PM_SIZE_RATIO_MAX: i64 = 4
57const PM_MOUTH_Y_LO_PERMIL: i64 = 900
58const PM_MOUTH_Y_HI_PERMIL: i64 = 2000
59const PM_MOUTH_X_PAD_PERMIL: i64 = 400
60// AT44: the mouth is found from its SEAM, the one feature a closed mouth always shows whatever the lighting does to the vermilion.
61// Both reaches are anatomical proportions of the eye-centre span the ruler already measured (about 63 mm in an adult): the seam
62// probe looks one seam thickness (about 2 mm) past the line on either side, and a vermilion border is sought within one lip reach
63// (about 19 mm) of the seam -- beyond the tallest lower vermilion, short of the nose base above and the chin fold below
64const PM_SEAM_PROBE_PERMIL: i64 = 32
65const PM_LIP_REACH_PERMIL: i64 = 300
66// below the seam the reach is longer: the lower vermilion is the taller (up to about 21 mm on a full lip -- MEASURED 0.30 of the eye
67// span on the media judge's frozen seed-42 still) and the chin fold lies further down than the nose base lies up; one reach of 0.4 for
68// both sides was MEASURED to carry the upper search onto the nose base on the default-tone renders (lip truth found 23 of 29 -> 5)
69const PM_LIP_REACH_LOWER_PERMIL: i64 = 400
70// a mouth is wider than a lip is tall (about 50 mm against at most 15), so a seam shorter than this share of the eye span is a nostril's
71// edge or a pore, not a mouth
72const PM_SEAM_MIN_PERMIL: i64 = 300
73const PM_ROUND: i64 = 500
74const PM_DP_FROM_UP: i64 = 1 // the seam path's predecessor: the row above, the same row, the row below; 0 = the path starts here
75const PM_DP_FROM_SAME: i64 = 2
76const PM_DP_FROM_DOWN: i64 = 3
77// the seam's seed is sought on the face's midline band, the middle quarter of the eye span (a mouth's centre lies between the pupils)
78const PM_SEAM_SEED_PERMIL: i64 = 125
79// the vermilion border's model choice: the Bayesian information criterion's parameter counts (k constant segments carry k means and
80// k - 1 borders), the median of a one-degree chi-square (the noise estimator's scale) and ln 2 (the penalty's log)
81const PM_BIC_PARAMS_2: i64 = 3
82const PM_BIC_PARAMS_3: i64 = 5
83const PM_CHI1_MEDIAN_PERMIL: i64 = 455
84const PM_LN2_PERMIL: i64 = 693
85const PM_MAX_BLOBS: i64 = 64
86const PM_BLOB_FIELDS: i64 = 8
87const PM_B_CELLS: i64 = 0
88const PM_B_X0: i64 = 1
89const PM_B_Y0: i64 = 2
90const PM_B_X1: i64 = 3
91const PM_B_Y1: i64 = 4
92const PM_B_SUMX: i64 = 5
93const PM_B_SUMY: i64 = 6
94const PM_B_TOUCH: i64 = 7
95const PM_TEN: i64 = 10
96const PM_I64: i64 = 8
97const PM_BPP: i64 = 3
98const PM_BYTE: i64 = 255
99const PM_E5: i64 = 100000
100const PM_DP_UNREACHED: i64 = 0 - PM_E5 * PM_E5 // a seam path sum no pixel can reach: its depth per column is at most one byte
101// the cascade route: the eye box's lower band (the brow rides the top of the box) is where the pupil is sought, and the box widened
102// sideways is the band the overlay draws; the fissure itself grows over the whole box's rows out to the face's side (pm_eye_fissure)
103const PM_EYE_WIDEN_PERMIL: i64 = 500
104const PM_EYE_BAND_TOP_PERMIL: i64 = 300
105const PM_EYE_BAND_BOT_PERMIL: i64 = 850
106const PM_ROUTE_NONE: i64 = 0
107const PM_ROUTE_SKIN: i64 = 1
108const PM_ROUTE_CASCADE: i64 = 2
109const PM_NME_FOUND_PERMIL: i64 = 100
110// the referee's summary, handed back to a caller so a gate can hold it to a planted row (it was print-only until 2026-09-17,
111// which is how a wrong span column survived every run: a number nobody can read back is a number nothing can contradict)
112const PM_RS_ROWS: i64 = 0
113const PM_RS_JUDGED: i64 = 1
114const PM_RS_UNJUDGED: i64 = 2
115const PM_RS_FOUND: i64 = 3
116const PM_RS_PLACED_OFF: i64 = 4
117const PM_RS_UNMEASURED: i64 = 5
118const PM_RS_UNREADABLE: i64 = 6
119const PM_RS_NME_MEAN_FOUND: i64 = 7
120const PM_RS_TILT_HITS: i64 = 8
121const PM_RS_LAST_NME_MAX: i64 = 9
122const PM_RS_LAST_NME_MEAN: i64 = 10
123// AT44: the mouth scored beside the canthi (its own partition of the judged rows), and each canthus's mean NME over EVERY measured
124// still rather than over the found ones only -- a mean taken over the passes cannot show which landmark keeps a still from passing
125const PM_RS_LIP_FOUND: i64 = 11
126const PM_RS_LIP_PLACED_OFF: i64 = 12
127const PM_RS_LIP_UNMEASURED: i64 = 13
128const PM_RS_LIP_NME_MEAN_MEASURED: i64 = 14
129const PM_RS_LAST_LIP_NME_MAX: i64 = 15
130const PM_RS_NME_RLAT_MEASURED: i64 = 16
131const PM_RS_NME_RMED_MEASURED: i64 = 17
132const PM_RS_NME_LMED_MEASURED: i64 = 18
133const PM_RS_NME_LLAT_MEASURED: i64 = 19
134const PM_RS_MEASURED: i64 = 20
135const PM_RS_LIP_MEASURED: i64 = 21
136const PM_RS_LIP_RATIO_ERR_MEAN: i64 = 22
137const PM_RS_LAST_LIP_RATIO: i64 = 23
138// the mouth anchored on the oracle's canthi, its own partition of the judged rows
139const PM_RS_ANCH_FOUND: i64 = 24
140const PM_RS_ANCH_OFF: i64 = 25
141const PM_RS_ANCH_UNMEASURED: i64 = 26
142const PM_RS_ANCH_NME_MEAN: i64 = 27
143const PM_RS_ANCH_RATIO_ERR: i64 = 28
144const PM_RS_LAST_ANCH_NME_MAX: i64 = 29
145const PM_RS_N: i64 = 30
146// one mouth's scoring accumulator (pm_lip_score) and its verdicts
147const PM_LA_NME_SUM: i64 = 0
148const PM_LA_RATIO_ERR_SUM: i64 = 1
149const PM_LA_FOUND: i64 = 2
150const PM_LA_OFF: i64 = 3
151const PM_LA_UNMEASURED: i64 = 4
152const PM_LA_N: i64 = 5
153const PM_LIPV_UNMEASURED: i64 = 0
154const PM_LIPV_FOUND: i64 = 1
155const PM_LIPV_OFF: i64 = 2
156const PM_TILT_TOL_DEG10: i64 = 20
157// a canthus placed within PM_Y_ERR_PX rows tilts a fissure of f px by atan(PM_Y_ERR_PX / f): the tilt's own error bar, printed
158// beside it, and a tilt row reaches the card only when that bar is within PM_TILT_ERR_MAX_DEG10 (3 deg needs f >= 38 px)
159const PM_Y_ERR_PX: i64 = 2
160const PM_TILT_ERR_MAX_DEG10: i64 = 30
161const PM_ORACLE_X10: i64 = 10
162const PM_ORACLE_FIELDS: i64 = 27
163const PM_OF_FILE: i64 = 1
164const PM_OF_FACES: i64 = 5
165const PM_OF_RLAT: i64 = 6
166const PM_OF_RMED: i64 = 7
167const PM_OF_LMED: i64 = 8
168const PM_OF_LLAT: i64 = 9
169const PM_OF_ULIP_TOP: i64 = 10
170const PM_OF_ULIP_IN: i64 = 11
171const PM_OF_LLIP_IN: i64 = 12
172const PM_OF_LLIP_BOT: i64 = 13
173const PM_OF_MOUTH_R: i64 = 14 // the image-left commissure
174const PM_OF_MOUTH_L: i64 = 15
175const PM_OF_LIP: i64 = 25 // lip_permil: (ulip_in - ulip_top) over (llip_bot - llip_in), each vermilion to its OWN inner edge
176const PM_OF_EXO: i64 = 26 // exo_px; 25 is lip_permil -- the referee normalised every NME by the LIP RATIO until 2026-09-17
177const PM_PIPE: i64 = 124
178const PM_COMMA: i64 = 44
179const PM_NL: i64 = 10
180const PM_HASH: i64 = 35
181const PM_MINUS: i64 = 45
182const PM_D0: i64 = 48
183const PM_D9: i64 = 57
184const PM_PATHB: i64 = 1024
185const PM_ROWB: i64 = 4096
186const PM_DEFAULT_DIR: *u8 = "knowledge/fetched/"
187// result slots
188const PM_R_FACE: i64 = 0
189const PM_R_FX0: i64 = 1
190const PM_R_FY0: i64 = 2
191const PM_R_FX1: i64 = 3
192const PM_R_FY1: i64 = 4
193const PM_R_FACE_CELLS: i64 = 5
194const PM_R_EYES: i64 = 6
195const PM_R_RLAT_X: i64 = 7
196const PM_R_RLAT_Y: i64 = 8
197const PM_R_RMED_X: i64 = 9
198const PM_R_RMED_Y: i64 = 10
199const PM_R_LMED_X: i64 = 11
200const PM_R_LMED_Y: i64 = 12
201const PM_R_LLAT_X: i64 = 13
202const PM_R_LLAT_Y: i64 = 14
203const PM_R_TILT_R: i64 = 15
204const PM_R_TILT_L: i64 = 16
205const PM_R_ICI: i64 = 17
206const PM_R_ICF: i64 = 18
207const PM_R_EYE_PIX_R: i64 = 19
208const PM_R_EYE_PIX_L: i64 = 20
209const PM_R_MOUTH: i64 = 21
210const PM_R_MX0: i64 = 22
211const PM_R_MY0: i64 = 23
212const PM_R_MX1: i64 = 24
213const PM_R_MY1: i64 = 25
214const PM_R_STOMION_Y: i64 = 26
215const PM_R_LIP: i64 = 27
216const PM_R_MOUTH_PIX: i64 = 28
217const PM_R_CELL: i64 = 29
218const PM_R_BLOBS: i64 = 30
219const PM_R_BLOBS_CAPPED: i64 = 31
220const PM_R_W: i64 = 32
221const PM_R_H: i64 = 33
222const PM_R_HX0: i64 = 34
223const PM_R_HY0: i64 = 35
224const PM_R_HX1: i64 = 36
225const PM_R_HY1: i64 = 37
226const PM_R_TB: i64 = 38
227const PM_R_ROUTE: i64 = 68
228const PM_R_FVOTES: i64 = 69
229const PM_R_ROLL: i64 = 70
230const PM_R_TILT_R_RAW: i64 = 71
231const PM_R_TILT_L_RAW: i64 = 72
232const PM_R_PAIR_FAIL: i64 = 73
233const PM_R_EYE_VOTES_R: i64 = 74
234const PM_R_EYE_VOTES_L: i64 = 75
235const PM_R_EX0: i64 = 76
236const PM_R_EY0: i64 = 77
237const PM_R_EX1: i64 = 78
238const PM_R_EY1: i64 = 79
239const PM_R_LX0: i64 = 80
240const PM_R_LY0: i64 = 81
241const PM_R_LX1: i64 = 82
242const PM_R_LY1: i64 = 83
243const PM_R_FISSURE_R: i64 = 84
244const PM_R_FISSURE_L: i64 = 85
245const PM_R_TILT_ERR: i64 = 86
246const PM_R_TILT_OK: i64 = 87
247const PM_R_CODEC: i64 = 88
248// AT44 (2026-09-18): the mouth as FIVE LANDMARKS, not a box -- labrale superius, stomion, labrale inferius, the image-left and the
249// image-right cheilion, each x then y, the order the render's lip truth (nx_twinfit_lib tw_lip_truth) and the oracle rows use
250const PM_R_LS_X: i64 = 89
251const PM_R_LS_Y: i64 = 90
252const PM_R_STO_X: i64 = 91
253const PM_R_STO_Y: i64 = 92
254const PM_R_LI_X: i64 = 93
255const PM_R_LI_Y: i64 = 94
256const PM_R_CHR_X: i64 = 95
257const PM_R_CHR_Y: i64 = 96
258const PM_R_CHL_X: i64 = 97
259const PM_R_CHL_Y: i64 = 98
260const PM_R_MOUTH_WHY: i64 = 99 // why the mouth is (not) measured: PM_MW_* by name
261const PM_R_SEAM_PROBE: i64 = 100 // the seam probe's reach in pixels, derived from the eye span
262const PM_R_SEAM_STRENGTH: i64 = 101 // the seam path's summed valley depth
263const PM_R_LIP_REACH: i64 = 102 // the vermilion search's reach from the seam, pixels
264const PM_R_SEAM_FLOOR: i64 = 103 // the valley depth a column beside the mouth offers: the seam path counts only depth above it
265const PM_R_SEAM_HYP: i64 = 104 // which seam finder the mouth rests on: PM_SEAM_HYP_WALK or PM_SEAM_HYP_GLOBAL
266const PM_R_PINNED: i64 = 105 // the canthi whose fissure ran onto its window's side (PM_PIN_* bits); any makes the pair PM_PF_PINNED
267const PM_R_EYE_WX0: i64 = 106 // the fissure windows: image-left eye x0..x1, image-right eye x0..x1 (the rows are the eye bands)
268const PM_R_EYE_WX1: i64 = 107
269const PM_R_EYE_LWX0: i64 = 108
270const PM_R_EYE_LWX1: i64 = 109
271const PM_R_LIP_REACH_LO: i64 = 110 // the lower vermilion search's reach from the seam, pixels
272const PM_R_N: i64 = 111
273const PM_SEAM_HYP_WALK: i64 = 1
274const PM_SEAM_HYP_GLOBAL: i64 = 2
275const PM_VALLEY_SPAN: i64 = PM_BYTE + PM_BYTE // a valley depth is a luma difference, -255..255: the histogram's bins
276// the mouth's named outcomes
277const PM_MW_OK: i64 = 0
278const PM_MW_NO_WINDOW: i64 = 1 // the window below the eyes has no rows or no columns inside the frame
279const PM_MW_NO_SEAM: i64 = 2 // no horizontal valley line inside the window
280const PM_MW_SEAM_PINNED: i64 = 3 // the seam runs onto the window's side: its end is the window, not a commissure
281const PM_MW_BORDER_PINNED: i64 = 4 // a vermilion change lands on its search's far end: the reach, not a border
282const PM_MW_NO_LIP: i64 = 5 // no colour change between the seam and one lip height on a side
283const PM_MW_SEAM_SHORT: i64 = 6 // the valley line is no longer than one lip reach: too short to be a mouth
284// the file door (AT33): ONE entry reads a mirrored photograph (JPEG) or an estate render (PNG), chosen by the first
285// signature byte, so the same ruler measures a reference still and our own procgen output; the codec taken is announced
286const PM_CODEC_NONE: i64 = 0
287const PM_CODEC_JPEG: i64 = 1
288const PM_CODEC_PNG: i64 = 2
289const PM_PNG_SIG0: i64 = 137
290const PM_PNG_DEPTH: i64 = 8
291const PM_CH_GRAY: i64 = 1
292const PM_CH_RGB: i64 = 3
293const PM_CH_RGBA: i64 = 4
294const PM_RGB_BPP: i64 = 3
295const PM_OK: i64 = 0
296const PM_E_READ: i64 = 0 - 1
297const PM_E_DECODE: i64 = 0 - 2
298const PM_E_NOFACE: i64 = 0 - 3
299const PM_E_NOEYES: i64 = 0 - 4
300const PM_E_EXISTS: i64 = 0 - 5
301const PM_PF_NONE: i64 = 0
302const PM_PF_EMPTY: i64 = 1
303const PM_PF_NARROW: i64 = 2
304const PM_PF_ASYM: i64 = 3
305const PM_PF_TILT: i64 = 4
306const PM_PF_ENDO: i64 = 5
307const PM_PF_ORDER: i64 = 6
308const PM_PF_PINNED: i64 = 7 // a fissure runs onto its window's side: that canthus is the window, not a landmark
309const PM_EYE_ROWS_PAD: i64 = 3 // the skin route's fissure rows: the blob's rows plus one fine cell above and two below
310const PM_CANTHI_XY: i64 = 8 // four canthi, x and y each, in the result's order (PM_R_RLAT_X .. PM_R_LLAT_Y)
311// the fissure grows to any pixel within two of one already grown: a lash line one pixel wide that crosses between two rows is drawn
312// half-dark in each, and MEASURED on seed 42 that split left one column of it below the level -- the grown fissure stopped at x 205
313// with the corner at 197.7 (a gap of one pixel is bridged; a gap of two is a separate structure)
314const PM_FIS_REACH: i64 = 2
315// the fissure record (pm_eye_fissure)
316const PM_FIS_LX: i64 = 0
317const PM_FIS_LY: i64 = 1
318const PM_FIS_RX: i64 = 2
319const PM_FIS_RY: i64 = 3
320const PM_FIS_COUNT: i64 = 4
321const PM_FIS_PIN_L: i64 = 5
322const PM_FIS_PIN_R: i64 = 6
323const PM_FIS_N: i64 = 7
324// which canthi were pinned, as bits in PM_R_PINNED
325const PM_PIN_RLAT: i64 = 1
326const PM_PIN_RMED: i64 = 2
327const PM_PIN_LMED: i64 = 4
328const PM_PIN_LLAT: i64 = 8
329const PM_SIDE_L: *u8 = "L"
330const PM_SIDE_R: *u8 = "R"
331const PM_SIDE_C: *u8 = "C"
332const PM_METHOD: *u8 = "measured"
333
334func pm_ruler_version() -> i64 { return 3 } // 3: AT44, the mouth from its seam and the fissure grown from its pupil
335func pm_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
336func pm_max(a: i64, b: i64) -> i64 { if a > b { return a } return b }
337func pm_min(a: i64, b: i64) -> i64 { if a < b { return a } return b }
338// integer square root (floor) by Newton iteration
339func pm_isqrt(v: i64) -> i64 {
340 if v <= 0 { return 0 }
341 var x: i64 = v
342 var y: i64 = (x + 1) / 2
343 while y < x { x = y; y = (x + v / x) / 2 }
344 return x
345}
346func pm_dist(x0: i64, y0: i64, x1: i64, y1: i64) -> i64 {
347 let dx: i64 = x1 - x0
348 let dy: i64 = y1 - y0
349 return pm_isqrt(dx * dx + dy * dy)
350}
351// pixel luma, Cr (JFIF, both in 0..255 byte units)
352func pm_luma(r: i64, g: i64, b: i64) -> i64 { return (SKS_YCC_Y_R * r + SKS_YCC_Y_G * g + SKS_YCC_Y_B * b) / SKS_E5 }
353func pm_luma_at(rgb: *u8, w: i64, x: i64, y: i64) -> i64 {
354 let o: i64 = (y * w + x) * PM_BPP
355 return pm_luma((rgb[o] as i64) & PM_BYTE, (rgb[o + 1] as i64) & PM_BYTE, (rgb[o + 2] as i64) & PM_BYTE)
356}
357// the angle of a rise over a run in deg10 (positive when rise > 0), through the estate's CORDIC
358func pm_canthal_tilt(rise: i64, run: i64) -> i64 {
359 let ctx: *i64 = fq_ctx()
360 let rad: i64 = fq_atan2(ctx, rise * FQ_ONE, pm_max(run, 1) * FQ_ONE)
361 let deg: i64 = fq_rad2deg(ctx, rad)
362 if deg >= 0 { return fq_to_int(deg * PM_TEN + FQ_HALF) }
363 return 0 - fq_to_int((0 - deg) * PM_TEN + FQ_HALF)
364}
365// largest 4-connected component of cells where mask[c] == 1; label[c] = 1 for its members. Returns its cell count and its
366// bbox in bb[0..3]; uses an explicit stack sized by the grid (no recursion)
367func pm_largest_component(mask: *u8, gw: i64, gh: i64, label: *u8, bb: *i64) -> i64 {
368 let n: i64 = gw * gh
369 let seen: *u8 = sys_mmap(n + 1)
370 let stack: *i64 = sys_mmap((n + 1) * PM_I64) as *i64
371 let cur: *u8 = sys_mmap(n + 1)
372 var best: i64 = 0
373 var c: i64 = 0
374 while c < n { label[c] = 0 as u8; c = c + 1 }
375 c = 0
376 while c < n {
377 if mask[c] == (1 as u8) { if seen[c] == (0 as u8) {
378 var sp: i64 = 0
379 var count: i64 = 0
380 var x0: i64 = gw
381 var y0: i64 = gh
382 var x1: i64 = 0 - 1
383 var y1: i64 = 0 - 1
384 stack[sp] = c; sp = sp + 1; seen[c] = 1 as u8
385 var k: i64 = 0
386 while k < n { cur[k] = 0 as u8; k = k + 1 }
387 while sp > 0 {
388 sp = sp - 1
389 let q: i64 = stack[sp]
390 cur[q] = 1 as u8
391 count = count + 1
392 let qx: i64 = q - (q / gw) * gw
393 let qy: i64 = q / gw
394 if qx < x0 { x0 = qx }
395 if qy < y0 { y0 = qy }
396 if qx > x1 { x1 = qx }
397 if qy > y1 { y1 = qy }
398 if qx > 0 { let l: i64 = q - 1; if mask[l] == (1 as u8) { if seen[l] == (0 as u8) { seen[l] = 1 as u8; stack[sp] = l; sp = sp + 1 } } }
399 if qx + 1 < gw { let r: i64 = q + 1; if mask[r] == (1 as u8) { if seen[r] == (0 as u8) { seen[r] = 1 as u8; stack[sp] = r; sp = sp + 1 } } }
400 if qy > 0 { let u: i64 = q - gw; if mask[u] == (1 as u8) { if seen[u] == (0 as u8) { seen[u] = 1 as u8; stack[sp] = u; sp = sp + 1 } } }
401 if qy + 1 < gh { let d: i64 = q + gw; if mask[d] == (1 as u8) { if seen[d] == (0 as u8) { seen[d] = 1 as u8; stack[sp] = d; sp = sp + 1 } } }
402 }
403 if count > best {
404 best = count
405 bb[0] = x0; bb[1] = y0; bb[2] = x1; bb[3] = y1
406 k = 0
407 while k < n { label[k] = cur[k]; k = k + 1 }
408 }
409 } }
410 c = c + 1
411 }
412 return best
413}
414// enumerate 4-connected blobs of cells where mask[c] == 1 into blobs[PM_BLOB_FIELDS each]; touch = the blob touches the
415// rectangle rx0..rx1 x ry0..ry1 boundary. Returns the blob count; capped[0] = 1 when more blobs existed than fit
416func pm_blobs(mask: *u8, gw: i64, gh: i64, rx0: i64, ry0: i64, rx1: i64, ry1: i64, blobs: *i64, cap: i64, capped: *i64) -> i64 {
417 let n: i64 = gw * gh
418 let seen: *u8 = sys_mmap(n + 1)
419 let stack: *i64 = sys_mmap((n + 1) * PM_I64) as *i64
420 var nb: i64 = 0
421 capped[0] = 0
422 var c: i64 = 0
423 while c < n {
424 if mask[c] == (1 as u8) { if seen[c] == (0 as u8) {
425 var sp: i64 = 0
426 var count: i64 = 0
427 var x0: i64 = gw
428 var y0: i64 = gh
429 var x1: i64 = 0 - 1
430 var y1: i64 = 0 - 1
431 var sx: i64 = 0
432 var sy: i64 = 0
433 var touch: i64 = 0
434 stack[sp] = c; sp = sp + 1; seen[c] = 1 as u8
435 while sp > 0 {
436 sp = sp - 1
437 let q: i64 = stack[sp]
438 count = count + 1
439 let qx: i64 = q - (q / gw) * gw
440 let qy: i64 = q / gw
441 sx = sx + qx
442 sy = sy + qy
443 if qx < x0 { x0 = qx }
444 if qy < y0 { y0 = qy }
445 if qx > x1 { x1 = qx }
446 if qy > y1 { y1 = qy }
447 if qx <= rx0 { touch = 1 }
448 if qx >= rx1 { touch = 1 }
449 if qy <= ry0 { touch = 1 }
450 if qy >= ry1 { touch = 1 }
451 if qx > 0 { let l: i64 = q - 1; if mask[l] == (1 as u8) { if seen[l] == (0 as u8) { seen[l] = 1 as u8; stack[sp] = l; sp = sp + 1 } } }
452 if qx + 1 < gw { let r: i64 = q + 1; if mask[r] == (1 as u8) { if seen[r] == (0 as u8) { seen[r] = 1 as u8; stack[sp] = r; sp = sp + 1 } } }
453 if qy > 0 { let u: i64 = q - gw; if mask[u] == (1 as u8) { if seen[u] == (0 as u8) { seen[u] = 1 as u8; stack[sp] = u; sp = sp + 1 } } }
454 if qy + 1 < gh { let d: i64 = q + gw; if mask[d] == (1 as u8) { if seen[d] == (0 as u8) { seen[d] = 1 as u8; stack[sp] = d; sp = sp + 1 } } }
455 }
456 if nb < cap {
457 let o: i64 = nb * PM_BLOB_FIELDS
458 blobs[o + PM_B_CELLS] = count
459 blobs[o + PM_B_X0] = x0
460 blobs[o + PM_B_Y0] = y0
461 blobs[o + PM_B_X1] = x1
462 blobs[o + PM_B_Y1] = y1
463 blobs[o + PM_B_SUMX] = sx
464 blobs[o + PM_B_SUMY] = sy
465 blobs[o + PM_B_TOUCH] = touch
466 nb = nb + 1
467 } else { capped[0] = 1 }
468 } }
469 c = c + 1
470 }
471 return nb
472}
473// THE FISSURE, GROWN FROM ITS INTERIOR (AT44, 2026-09-18). The first ruler took the extreme dark pixels of a box around each eye, so a
474// canthus was wherever darkness met the box's side: MEASURED on seed 42 both lateral canthi sat ON their search bands' edges (212 and
475// 302 of bands 212..253 and 259..303) while the construction put them at 197.7 and 313.3 -- outside the bands, which a cascade eye
476// box (sized to the iris) had cut short. Those numbers were the window, not the eye. Now the eye follows the lip ruler's pattern:
477// DARK IS LOCAL. A pixel belongs to the fissure by how much darker it is than the brightest pixel within one radius above or below
478// it (a vertical black top-hat: the lids around a lash line, a pupil or an iris are skin, and shading is not a feature). One level
479// for the whole box read the shaded side of a lit face as eye -- MEASURED on seed 42, a fissure grown against the band's mean
480// luma ran 577 pixels across the image-left cheek to the window's side.
481// THE INTERIOR EXTREMUM is the seed box's most local-dark pixel (the pupil); the fissure is every pixel 8-connected to it whose
482// local darkness reaches half the seed's (the stomion's half-depth rule), inside a window from the face's side to the OTHER eye's centre
483// over the whole eye box's rows (toward the corners the lash line rises into the box's top rows: seed 42's image-left corner at
484// rows 154..159 against a band of 158..169). The medial limit is the other eye, not an estimated midline: MEASURED on still 3 the
485// midline between the two cascade boxes fell 2 px from a correct medial canthus and pinned it.
486// THE TURNING POINTS are the fissure's two extreme columns, the canthi (y = the mean row of the fissure's pixels there), and a
487// fissure that runs onto the window's left or right side has found the window, not a corner: pinned, refused by name by the caller.
488// out7: left x, left y, right x, right y, pixel count, left pinned, right pinned. Returns the pixel count
489func pm_eye_fissure(rgb: *u8, w: i64, h: i64, sx0: i64, sy0: i64, sx1: i64, sy1: i64, wx0: i64, wy0: i64, wx1: i64, wy1: i64, rad: i64, out7: *i64) -> i64 {
490 var k: i64 = 0
491 while k < PM_FIS_N { out7[k] = 0; k = k + 1 }
492 let ax0: i64 = pm_max(wx0, 0)
493 let ay0: i64 = pm_max(wy0, 0)
494 let ax1: i64 = pm_min(wx1, w)
495 let ay1: i64 = pm_min(wy1, h)
496 let ww: i64 = ax1 - ax0
497 let wh: i64 = ay1 - ay0
498 if ww < 1 { return 0 }
499 if wh < 1 { return 0 }
500 let r: i64 = pm_max(rad, 1)
501 // the local darkness of every window pixel: the brightest pixel within r rows of it in its column, less its own luma
502 let td: *i64 = sys_mmap(ww * wh * PM_I64) as *i64
503 var y: i64 = 0
504 while y < wh {
505 var x: i64 = 0
506 while x < ww {
507 let gx: i64 = ax0 + x
508 let gy: i64 = ay0 + y
509 var top: i64 = 0
510 var yy: i64 = pm_max(gy - r, 0)
511 while yy <= pm_min(gy + r, h - 1) { let l: i64 = pm_luma_at(rgb, w, gx, yy); if l > top { top = l } yy = yy + 1 }
512 td[y * ww + x] = top - pm_luma_at(rgb, w, gx, gy)
513 x = x + 1
514 }
515 y = y + 1
516 }
517 // the interior extremum: the seed box's most local-dark pixel, clipped to the window
518 var bx: i64 = 0 - 1
519 var by: i64 = 0 - 1
520 var bt: i64 = 0
521 y = pm_max(sy0, ay0)
522 while y < pm_min(sy1, ay1) {
523 var x2: i64 = pm_max(sx0, ax0)
524 while x2 < pm_min(sx1, ax1) {
525 let t: i64 = td[(y - ay0) * ww + (x2 - ax0)]
526 if t > bt { bt = t; bx = x2 - ax0; by = y - ay0 }
527 x2 = x2 + 1
528 }
529 y = y + 1
530 }
531 if bx < 0 { return 0 }
532 // THE LEVEL is half the seed's own local darkness, the half-depth rule the stomion uses. The window's Otsu split was tried and
533 // MEASURED worse on both populations (renders found 26 of 29 at half depth, 15 of 29 at Otsu's level: the looser level let the
534 // lids' creases join the fissure and run it onto the window's side)
535 let level: i64 = (bt + 1) / 2
536 // grow the fissure: 8-connected pixels at least as locally dark as the level
537 let seen: *u8 = sys_mmap(ww * wh + 1)
538 let stack: *i64 = sys_mmap((ww * wh + 1) * PM_I64) as *i64
539 var sp: i64 = 0
540 stack[sp] = by * ww + bx; sp = sp + 1
541 seen[by * ww + bx] = 1 as u8
542 var count: i64 = 0
543 var minx: i64 = ww
544 var maxx: i64 = 0 - 1
545 while sp > 0 {
546 sp = sp - 1
547 let q: i64 = stack[sp]
548 let qx: i64 = q - (q / ww) * ww
549 let qy: i64 = q / ww
550 count = count + 1
551 if qx < minx { minx = qx }
552 if qx > maxx { maxx = qx }
553 var dy: i64 = 0 - PM_FIS_REACH
554 while dy <= PM_FIS_REACH {
555 var dx: i64 = 0 - PM_FIS_REACH
556 while dx <= PM_FIS_REACH {
557 let nx: i64 = qx + dx
558 let ny: i64 = qy + dy
559 if nx >= 0 { if nx < ww { if ny >= 0 { if ny < wh {
560 let nq: i64 = ny * ww + nx
561 if seen[nq] == (0 as u8) { if td[nq] >= level {
562 seen[nq] = 1 as u8
563 stack[sp] = nq
564 sp = sp + 1
565 } }
566 } } } }
567 dx = dx + 1
568 }
569 dy = dy + 1
570 }
571 }
572 // the mean row of the fissure's pixels in each extreme column
573 var sl: i64 = 0
574 var nl: i64 = 0
575 var sr: i64 = 0
576 var nr: i64 = 0
577 var rr: i64 = 0
578 while rr < wh {
579 if seen[rr * ww + minx] == (1 as u8) { sl = sl + rr; nl = nl + 1 }
580 if seen[rr * ww + maxx] == (1 as u8) { sr = sr + rr; nr = nr + 1 }
581 rr = rr + 1
582 }
583 out7[PM_FIS_LX] = ax0 + minx
584 out7[PM_FIS_LY] = ay0 + sl / pm_max(nl, 1)
585 out7[PM_FIS_RX] = ax0 + maxx
586 out7[PM_FIS_RY] = ay0 + sr / pm_max(nr, 1)
587 out7[PM_FIS_COUNT] = count
588 if minx == 0 { out7[PM_FIS_PIN_L] = 1 }
589 if maxx == ww - 1 { out7[PM_FIS_PIN_R] = 1 }
590 return count
591}
592// the pair's consistency: two fissures of like width, each tilted less than the human range, spaced like eyes, ordered.
593// Returns PM_PF_NONE when the pair holds, else the named failure
594func pm_pair_check(res: *i64) -> i64 {
595 let frw: i64 = pm_dist(res[PM_R_RLAT_X], res[PM_R_RLAT_Y], res[PM_R_RMED_X], res[PM_R_RMED_Y])
596 let flw: i64 = pm_dist(res[PM_R_LMED_X], res[PM_R_LMED_Y], res[PM_R_LLAT_X], res[PM_R_LLAT_Y])
597 let endo: i64 = pm_dist(res[PM_R_RMED_X], res[PM_R_RMED_Y], res[PM_R_LMED_X], res[PM_R_LMED_Y])
598 if res[PM_R_EYE_PIX_R] == 0 { return PM_PF_EMPTY }
599 if res[PM_R_EYE_PIX_L] == 0 { return PM_PF_EMPTY }
600 if frw < 2 { return PM_PF_NARROW }
601 if flw < 2 { return PM_PF_NARROW }
602 if pm_abs(frw - flw) * PM_PERMIL > PM_FISSURE_ASYM_PERMIL * pm_max(frw, flw) { return PM_PF_ASYM }
603 if pm_abs(res[PM_R_RMED_Y] - res[PM_R_RLAT_Y]) * PM_PERMIL > PM_TILT_MAX_RISE_PERMIL * pm_max(pm_abs(res[PM_R_RMED_X] - res[PM_R_RLAT_X]), 1) { return PM_PF_TILT }
604 if pm_abs(res[PM_R_LMED_Y] - res[PM_R_LLAT_Y]) * PM_PERMIL > PM_TILT_MAX_RISE_PERMIL * pm_max(pm_abs(res[PM_R_LLAT_X] - res[PM_R_LMED_X]), 1) { return PM_PF_TILT }
605 if endo * PM_PERMIL < PM_ENDO_MIN_PERMIL * pm_min(frw, flw) { return PM_PF_ENDO }
606 if endo * PM_TEN > PM_ENDO_MAX_X10 * pm_max(frw, flw) { return PM_PF_ENDO }
607 if res[PM_R_RMED_X] >= res[PM_R_LMED_X] { return PM_PF_ORDER }
608 return PM_PF_NONE
609}
610// the scale-free axes from four placed canthi: raw tilts per side in the image frame, the head's roll off the medial line
611// (positive when the image-right medial canthus is lower), tilts corrected for that roll, and the two canon ratios
612func pm_axes(res: *i64) -> i64 {
613 res[PM_R_EYES] = 2
614 res[PM_R_TILT_R_RAW] = pm_canthal_tilt(res[PM_R_RMED_Y] - res[PM_R_RLAT_Y], pm_abs(res[PM_R_RMED_X] - res[PM_R_RLAT_X]))
615 res[PM_R_TILT_L_RAW] = pm_canthal_tilt(res[PM_R_LMED_Y] - res[PM_R_LLAT_Y], pm_abs(res[PM_R_LLAT_X] - res[PM_R_LMED_X]))
616 res[PM_R_ROLL] = pm_canthal_tilt(res[PM_R_LMED_Y] - res[PM_R_RMED_Y], pm_abs(res[PM_R_LMED_X] - res[PM_R_RMED_X]))
617 res[PM_R_TILT_R] = res[PM_R_TILT_R_RAW] - res[PM_R_ROLL]
618 res[PM_R_TILT_L] = res[PM_R_TILT_L_RAW] + res[PM_R_ROLL]
619 let endo: i64 = pm_dist(res[PM_R_RMED_X], res[PM_R_RMED_Y], res[PM_R_LMED_X], res[PM_R_LMED_Y])
620 let exo: i64 = pm_dist(res[PM_R_RLAT_X], res[PM_R_RLAT_Y], res[PM_R_LLAT_X], res[PM_R_LLAT_Y])
621 let fr: i64 = pm_dist(res[PM_R_RLAT_X], res[PM_R_RLAT_Y], res[PM_R_RMED_X], res[PM_R_RMED_Y])
622 let fl2: i64 = pm_dist(res[PM_R_LMED_X], res[PM_R_LMED_Y], res[PM_R_LLAT_X], res[PM_R_LLAT_Y])
623 res[PM_R_ICI] = endo * PM_PERMIL / pm_max(exo, 1)
624 res[PM_R_ICF] = endo * PM_PERMIL * 2 / pm_max(fr + fl2, 1)
625 res[PM_R_FISSURE_R] = fr
626 res[PM_R_FISSURE_L] = fl2
627 res[PM_R_TILT_ERR] = pm_canthal_tilt(PM_Y_ERR_PX, pm_max(pm_min(fr, fl2), 1))
628 res[PM_R_TILT_OK] = 0
629 if res[PM_R_TILT_ERR] <= PM_TILT_ERR_MAX_DEG10 { res[PM_R_TILT_OK] = 1 }
630 return 0
631}
632func pm_mouth_why_name(k: i64) -> *u8 {
633 if k == PM_MW_OK { return "OK" as *u8 }
634 if k == PM_MW_NO_WINDOW { return "NO-WINDOW" as *u8 }
635 if k == PM_MW_NO_SEAM { return "NO-SEAM" as *u8 }
636 if k == PM_MW_SEAM_PINNED { return "SEAM-PINNED-TO-WINDOW" as *u8 }
637 if k == PM_MW_BORDER_PINNED { return "BORDER-PINNED-TO-REACH" as *u8 }
638 if k == PM_MW_SEAM_SHORT { return "SEAM-SHORTER-THAN-A-LIP" as *u8 }
639 return "NO-LIP" as *u8
640}
641// the colour of one row of a column band [x0, x1] (clipped): the band's summed R, G and B into out3, the pixel count returned
642func pm_band_rgb(rgb: *u8, w: i64, x0: i64, x1: i64, y: i64, out3: *i64) -> i64 {
643 out3[0] = 0
644 out3[1] = 0
645 out3[2] = 0
646 var n: i64 = 0
647 var x: i64 = pm_max(x0, 0)
648 while x <= pm_min(x1, w - 1) {
649 let o: i64 = (y * w + x) * PM_BPP
650 out3[0] = out3[0] + ((rgb[o] as i64) & PM_BYTE)
651 out3[1] = out3[1] + ((rgb[o + 1] as i64) & PM_BYTE)
652 out3[2] = out3[2] + ((rgb[o + 2] as i64) & PM_BYTE)
653 n = n + 1
654 x = x + 1
655 }
656 return n
657}
658// THE HUE of a colour as an angle in deg10: atan2(G - B, R - G). It is blind to shading (a darker or a brighter copy of one surface
659// has the same angle), which is what lets one rule read a lit and a shaded lip alike; a vermilion reads LOWER than the skin
660// around it (red with little green over blue: 17 deg on the seed-42 render, -6 on a pink photographed lip) against 37..42 for
661// skin. ctx is the estate's CORDIC context, made once per mouth
662func pm_hue_deg10(ctx: *i64, r: i64, g: i64, b: i64) -> i64 {
663 let rad: i64 = fq_atan2(ctx, (g - b) * FQ_ONE, (r - g) * FQ_ONE)
664 let deg: i64 = fq_rad2deg(ctx, rad)
665 if deg >= 0 { return fq_to_int(deg * PM_TEN + FQ_HALF) }
666 return 0 - fq_to_int((0 - deg) * PM_TEN + FQ_HALF)
667}
668// the chroma of a colour: max channel less min channel -- the weight a row's hue carries, so a specular highlight (near-white, its
669// hue a rounding accident) votes for nothing while a saturated vermilion or skin votes in full
670func pm_chroma(r: i64, g: i64, b: i64) -> i64 { return pm_max(pm_max(r, g), b) - pm_min(pm_min(r, g), b) }
671// the seam's valley depth at pixel (x, y) above the window's noise floor
672func pm_seam_v(rgb: *u8, w: i64, x: i64, y: i64, d: i64, floor_v: i64) -> i64 {
673 return pm_min(pm_luma_at(rgb, w, x, y - d), pm_luma_at(rgb, w, x, y + d)) - pm_luma_at(rgb, w, x, y) - floor_v
674}
675// ONE HALF OF THE SEAM, WALKED from the seed (window column sc, window row sr) toward dir (+1 right, -1 left). Each step goes one
676// column along the eye line's own slope (a rolled head's mouth runs parallel to its eyes) and may move one row off it to follow the
677// valley's ridge, never two columns running -- so the walk bends with a curving seam but cannot slide down a diagonal onto another
678// structure, which a best-path search did (MEASURED 2026-09-18, still 17: a max-sum path from the seed left the weak middle of the
679// seam and ran 44 rows down onto the lower lip's highlight edge). The walk's running depth is summed; the half ENDS at the column
680// where that sum peaked -- past it the valley has faded into skin, the TURNING POINT, the commissure. prow[c] receives the walk's
681// window row per window column (valid from the seed to the end); sum_out[0] the peak sum. Returns the end's window column; a walk
682// that leaves the window's rows stops there, and an end on the window's last column is the caller's to refuse
683func pm_seam_half(rgb: *u8, w: i64, mx0: i64, cols: i64, ya: i64, rows: i64, d: i64, floor_v: i64, sc: i64, sr: i64, dir: i64, slope_num: i64, slope_den: i64, prow: *i64, sum_out: *i64) -> i64 {
684 var y: i64 = ya + sr
685 var run: i64 = pm_seam_v(rgb, w, mx0 + sc, y, d, floor_v)
686 var best: i64 = run
687 var bestc: i64 = sc
688 prow[sc] = sr
689 var acc: i64 = 0
690 var moved: i64 = 0
691 var c: i64 = sc + dir
692 var going: i64 = 1
693 while going == 1 {
694 if c < 0 { going = 0 }
695 if c >= cols { going = 0 }
696 if going == 1 {
697 let x: i64 = mx0 + c
698 // the eye line's slope, carried as a remainder so a gentle roll accumulates to whole rows
699 acc = acc + dir * slope_num
700 while acc >= slope_den { y = y + 1; acc = acc - slope_den }
701 while acc <= 0 - slope_den { y = y - 1; acc = acc + slope_den }
702 var ny: i64 = y
703 var nv: i64 = PM_DP_UNREACHED
704 if y >= ya { if y < ya + rows { nv = pm_seam_v(rgb, w, x, y, d, floor_v) } }
705 if moved == 0 {
706 if y - 1 >= ya { if y - 1 < ya + rows { let vu: i64 = pm_seam_v(rgb, w, x, y - 1, d, floor_v); if vu > nv { nv = vu; ny = y - 1 } } }
707 if y + 1 >= ya { if y + 1 < ya + rows { let vd: i64 = pm_seam_v(rgb, w, x, y + 1, d, floor_v); if vd > nv { nv = vd; ny = y + 1 } } }
708 }
709 if nv == PM_DP_UNREACHED { going = 0 } else {
710 if ny != y { moved = 1 } else { moved = 0 }
711 y = ny
712 run = run + nv
713 prow[c] = y - ya
714 if run > best { best = run; bestc = c }
715 c = c + dir
716 }
717 }
718 }
719 sum_out[0] = best
720 return bestc
721}
722// the weighted squared deviation of v over [a, b) from its own weighted mean, from prefix sums (pw, pv, pvv hold n + 1 entries)
723func pm_seg_cost(pw: *i64, pv: *i64, pvv: *i64, a: i64, b: i64) -> i64 {
724 let sw: i64 = pw[b] - pw[a]
725 if sw <= 0 { return 0 }
726 let sv: i64 = pv[b] - pv[a]
727 return (pvv[b] - pvv[a]) - sv * sv / sw
728}
729func pm_seg_mean(pw: *i64, pv: *i64, a: i64, b: i64) -> i64 { return (pv[b] - pv[a]) / pm_max(pw[b] - pw[a], 1) }
730// the natural log of n in permil, from its bit length and a linear step inside the octave: a penalty's scale, not a measurement
731func pm_ln_permil(n: i64) -> i64 {
732 if n <= 1 { return 0 }
733 var b: i64 = 0
734 var p: i64 = 1
735 while p * 2 <= n { p = p * 2; b = b + 1 }
736 return (b * PM_PERMIL + (n - p) * PM_PERMIL / p) * PM_LN2_PERMIL / PM_PERMIL
737}
738// THE FIRST BORDER FROM THE SEAM. A single best split finds the STRONGEST change inside the reach, and MEASURED on the default-tone
739// renders that is the chin shadow meeting the skin, not the pale lower lip meeting the shadow: the lip's own border is the FIRST
740// change out from the seam. So the hue (weighted by chroma) is fitted with one, two and three constant segments, and the BAYESIAN
741// INFORMATION CRITERION picks how many the data support -- each extra segment must lower the fit's squared deviation by more than
742// ln(n) of the profile's own noise, estimated from successive rows (the median of the weighted squared step over the median of a
743// one-degree chi-square, so the steps AT a border cannot inflate it). The border is the one nearest the seam; the lip side is the
744// segment against the seam and the skin side the farthest segment, which is the one a vermilion must be redder than (a shadow
745// between them may be redder than a pale lip). seam_first = 1 when index 0 lies against the seam. Returns the border index b: the
746// lip is [0, b) when seam_first, else [b, n); 0 when one segment is all the data support. out3: lip mean, skin mean, segments
747func pm_first_border(v: *i64, wt: *i64, n: i64, seam_first: i64, out3: *i64) -> i64 {
748 out3[2] = 1
749 if n < 2 { return 0 }
750 let pw: *i64 = sys_mmap((n + 1) * PM_I64) as *i64
751 let pv: *i64 = sys_mmap((n + 1) * PM_I64) as *i64
752 let pvv: *i64 = sys_mmap((n + 1) * PM_I64) as *i64
753 var i: i64 = 0
754 while i < n { pw[i + 1] = pw[i] + wt[i]; pv[i + 1] = pv[i] + wt[i] * v[i]; pvv[i + 1] = pvv[i] + wt[i] * v[i] * v[i]; i = i + 1 }
755 // the noise: the median weighted squared step between neighbouring rows
756 let st: *i64 = sys_mmap(n * PM_I64) as *i64
757 var ns: i64 = 0
758 i = 0
759 while i + 1 < n {
760 let ws: i64 = wt[i] + wt[i + 1]
761 if wt[i] > 0 { if wt[i + 1] > 0 {
762 let dv: i64 = v[i + 1] - v[i]
763 let e: i64 = dv * dv * (wt[i] * wt[i + 1] / ws)
764 var j: i64 = ns
765 var moving: i64 = 1
766 while moving == 1 { if j > 0 { if st[j - 1] > e { st[j] = st[j - 1]; j = j - 1 } else { moving = 0 } } else { moving = 0 } }
767 st[j] = e
768 ns = ns + 1
769 } }
770 i = i + 1
771 }
772 var sig: i64 = 1
773 if ns > 0 { sig = pm_max(1, st[ns / 2] * PM_PERMIL / PM_CHI1_MEDIAN_PERMIL) }
774 let pen: i64 = pm_ln_permil(n)
775 // one segment
776 let s1: i64 = pm_seg_cost(pw, pv, pvv, 0, n)
777 let bic1: i64 = s1 * PM_PERMIL / sig + pen
778 // two segments
779 var s2: i64 = 0 - 1
780 var k2: i64 = 0
781 var k: i64 = 1
782 while k < n {
783 let c2: i64 = pm_seg_cost(pw, pv, pvv, 0, k) + pm_seg_cost(pw, pv, pvv, k, n)
784 if s2 < 0 { s2 = c2; k2 = k } else { if c2 < s2 { s2 = c2; k2 = k } }
785 k = k + 1
786 }
787 let bic2: i64 = s2 * PM_PERMIL / sig + PM_BIC_PARAMS_2 * pen
788 // three segments
789 var s3: i64 = 0 - 1
790 var ka: i64 = 0
791 var kb: i64 = 0
792 var a: i64 = 1
793 while a < n - 1 {
794 let ca: i64 = pm_seg_cost(pw, pv, pvv, 0, a)
795 var b: i64 = a + 1
796 while b < n {
797 let c3: i64 = ca + pm_seg_cost(pw, pv, pvv, a, b) + pm_seg_cost(pw, pv, pvv, b, n)
798 if s3 < 0 { s3 = c3; ka = a; kb = b } else { if c3 < s3 { s3 = c3; ka = a; kb = b } }
799 b = b + 1
800 }
801 a = a + 1
802 }
803 var bic3: i64 = bic2 + 1
804 if s3 >= 0 { bic3 = s3 * PM_PERMIL / sig + PM_BIC_PARAMS_3 * pen }
805 if bic1 <= bic2 { if bic1 <= bic3 { return 0 } }
806 if bic2 <= bic3 {
807 out3[2] = 2
808 if seam_first == 1 { out3[0] = pm_seg_mean(pw, pv, 0, k2); out3[1] = pm_seg_mean(pw, pv, k2, n) } else { out3[0] = pm_seg_mean(pw, pv, k2, n); out3[1] = pm_seg_mean(pw, pv, 0, k2) }
809 return k2
810 }
811 // three segments: the skin side is whichever of the two beyond the lip is the LEAST red (the highest hue) -- a shadow between
812 // lip and skin can be redder than a pale lip, and so can a fold beyond the skin (MEASURED on the default-tone renders, where
813 // the lower search's far segment was the red chin fold and a true lower lip read "not redder than its skin")
814 out3[2] = 3
815 let mid: i64 = pm_seg_mean(pw, pv, ka, kb)
816 if seam_first == 1 { out3[0] = pm_seg_mean(pw, pv, 0, ka); out3[1] = pm_max(mid, pm_seg_mean(pw, pv, kb, n)); return ka }
817 out3[0] = pm_seg_mean(pw, pv, kb, n)
818 out3[1] = pm_max(mid, pm_seg_mean(pw, pv, 0, ka))
819 return kb
820}
821// the hue and the chroma weight of every row y0..y0+n-1 of the column band [x0, x1] into hv and wt
822func pm_band_profile(ctx: *i64, rgb: *u8, w: i64, x0: i64, x1: i64, y0: i64, n: i64, hv: *i64, wt: *i64) -> i64 {
823 let c3: *i64 = sys_mmap(3 * PM_I64) as *i64
824 var i: i64 = 0
825 while i < n {
826 let cnt: i64 = pm_max(pm_band_rgb(rgb, w, x0, x1, y0 + i, c3), 1)
827 let r: i64 = c3[0] / cnt
828 let g: i64 = c3[1] / cnt
829 let b: i64 = c3[2] / cnt
830 hv[i] = pm_hue_deg10(ctx, r, g, b)
831 wt[i] = pm_chroma(r, g, b)
832 i = i + 1
833 }
834 return n
835}
836// THE GLOBAL SEAM: the path of greatest summed depth anywhere in the window, moving at most one row per column (a running sum that
837// restarts where it would go negative, so the path starts and ends where the valley does). Fills prow for its columns and se[0], se[1]
838// with its first and last window columns; returns its sum (0 when no column holds a valley above the floor)
839func pm_seam_global(rgb: *u8, w: i64, mx0: i64, cols: i64, ya: i64, rows: i64, d: i64, floor_v: i64, prow: *i64, se: *i64) -> i64 {
840 let sm: *i64 = sys_mmap(rows * cols * PM_I64) as *i64
841 let bk: *i64 = sys_mmap(rows * cols * PM_I64) as *i64
842 var best: i64 = 0
843 var bc: i64 = 0 - 1
844 var br: i64 = 0 - 1
845 var c: i64 = 0
846 while c < cols {
847 let x: i64 = mx0 + c
848 var r: i64 = 0
849 while r < rows {
850 let v: i64 = pm_seam_v(rgb, w, x, ya + r, d, floor_v)
851 var prev: i64 = 0
852 var from: i64 = 0
853 if c > 0 {
854 let o: i64 = (c - 1) * rows
855 if r > 0 { if sm[o + r - 1] > prev { prev = sm[o + r - 1]; from = PM_DP_FROM_UP } }
856 if sm[o + r] > prev { prev = sm[o + r]; from = PM_DP_FROM_SAME }
857 if r + 1 < rows { if sm[o + r + 1] > prev { prev = sm[o + r + 1]; from = PM_DP_FROM_DOWN } }
858 }
859 sm[c * rows + r] = v + prev
860 bk[c * rows + r] = from
861 if v + prev > best { best = v + prev; bc = c; br = r }
862 r = r + 1
863 }
864 c = c + 1
865 }
866 if bc < 0 { return 0 }
867 var tc: i64 = bc
868 var tr: i64 = br
869 var tracing: i64 = 1
870 while tracing == 1 {
871 prow[tc] = tr
872 let f: i64 = bk[tc * rows + tr]
873 if f == 0 { se[0] = tc; tracing = 0 } else { tr = tr + (f - PM_DP_FROM_SAME); tc = tc - 1 }
874 }
875 se[1] = bc
876 return best
877}
878// ONE SEAM HYPOTHESIS, VERIFIED: from a seam path (window columns xs..xe, window rows prow) the stomion, the two vermilion lines and the
879// lip ratio into the mouth slots of hr; returns PM_MW_OK or the named reason the hypothesis fails. The vermilion test is what tells a
880// seam from its look-alikes: a valley at the lip region's EDGE (the shadow under a lower lip, the crease above an upper one) has skin,
881// not a second lip, on one side, and the change point that should find that lip lands on the reach or finds no redder side
882func pm_mouth_hypothesis(rgb: *u8, w: i64, h: i64, hr: *i64, mx0: i64, ya: i64, cols: i64, prow: *i64, xs: i64, xe: i64, d: i64, reach: i64, reach_lo: i64, ipd: i64, ctx: *i64) -> i64 {
883 hr[PM_R_MOUTH] = 0
884 hr[PM_R_LIP] = 0
885 hr[PM_R_MX0] = 0; hr[PM_R_MY0] = 0; hr[PM_R_MX1] = 0; hr[PM_R_MY1] = 0; hr[PM_R_MOUTH_PIX] = 0
886 var k0: i64 = PM_R_LS_X
887 while k0 <= PM_R_CHL_Y { hr[k0] = 0; k0 = k0 + 1 }
888 hr[PM_R_CHR_X] = mx0 + xs; hr[PM_R_CHR_Y] = ya + prow[xs]
889 hr[PM_R_CHL_X] = mx0 + xe; hr[PM_R_CHL_Y] = ya + prow[xe]
890 // the stomion at the centre column: the centre of the valley's darker half
891 let xcc: i64 = (xs + xe) / 2
892 let xc: i64 = mx0 + xcc
893 let yc: i64 = ya + prow[xcc]
894 let floor: i64 = pm_luma_at(rgb, w, xc, yc)
895 let walls: i64 = pm_min(pm_luma_at(rgb, w, xc, yc - d), pm_luma_at(rgb, w, xc, yc + d))
896 let level: i64 = (floor + walls) / 2
897 var top: i64 = yc
898 var go: i64 = 1
899 while go == 1 { if top - 1 > yc - reach { if top - 1 >= 0 { if pm_luma_at(rgb, w, xc, top - 1) <= level { top = top - 1 } else { go = 0 } } else { go = 0 } } else { go = 0 } }
900 var bot: i64 = yc
901 go = 1
902 while go == 1 { if bot + 1 < yc + reach { if bot + 1 < h { if pm_luma_at(rgb, w, xc, bot + 1) <= level { bot = bot + 1 } else { go = 0 } } else { go = 0 } } else { go = 0 } }
903 let sto: i64 = (top + bot) / 2
904 hr[PM_R_STO_X] = xc; hr[PM_R_STO_Y] = sto
905 hr[PM_R_STOMION_Y] = sto
906 if xs == 0 { return PM_MW_SEAM_PINNED }
907 if xe == cols - 1 { return PM_MW_SEAM_PINNED }
908 // a mouth is wider than a lip is tall (about 50 mm against at most 15), so a seam no longer than one lip reach is a nostril's edge
909 // or a pore, not a mouth -- MEASURED: seed 6's two-column dimple under the nose passed every other test
910 if (xe - xs) * PM_PERMIL < PM_SEAM_MIN_PERMIL * ipd { return PM_MW_SEAM_SHORT }
911 // the vermilion lines: the first hue border out from the seam, on a band one probe wide either side of the centre column
912 let m3: *i64 = sys_mmap(3 * PM_I64) as *i64
913 let hv: *i64 = sys_mmap((pm_max(reach, reach_lo) + 1) * PM_I64) as *i64
914 let wt: *i64 = sys_mmap((pm_max(reach, reach_lo) + 1) * PM_I64) as *i64
915 // above: rows [top - reach, top - 1], ordered from the far end toward the seam
916 let ua: i64 = pm_max(top - reach, 0)
917 let un: i64 = top - ua
918 if un < 2 { return PM_MW_NO_LIP }
919 pm_band_profile(ctx, rgb, w, xc - d, xc + d, ua, un, hv, wt)
920 let ku: i64 = pm_first_border(hv, wt, un, 0, m3)
921 if ku == 0 { return PM_MW_NO_LIP }
922 if m3[0] >= m3[1] { return PM_MW_NO_LIP } // the lip must be redder (a lower hue angle) than the skin beyond it
923 let ls: i64 = ua + ku
924 hr[PM_R_LS_X] = xc; hr[PM_R_LS_Y] = ls
925 // below: rows [bot + 1, bot + reach], ordered from the seam outward
926 let ln: i64 = pm_min(reach_lo, h - 1 - bot)
927 if ln < 2 { return PM_MW_NO_LIP }
928 pm_band_profile(ctx, rgb, w, xc - d, xc + d, bot + 1, ln, hv, wt)
929 let kl: i64 = pm_first_border(hv, wt, ln, 1, m3)
930 if kl == 0 { return PM_MW_NO_LIP }
931 if m3[0] >= m3[1] { return PM_MW_NO_LIP }
932 let li: i64 = bot + kl
933 hr[PM_R_LI_X] = xc; hr[PM_R_LI_Y] = li
934 if ku <= 1 { return PM_MW_BORDER_PINNED }
935 if kl >= ln - 1 { return PM_MW_BORDER_PINNED }
936 let upper: i64 = top - ls
937 let lower: i64 = li - bot
938 if upper <= 0 { return PM_MW_NO_LIP }
939 if lower <= 0 { return PM_MW_NO_LIP }
940 hr[PM_R_LIP] = upper * PM_PERMIL / lower
941 hr[PM_R_MOUTH] = 1
942 // the box and the stomion row every existing reader draws (the media judge's overlay): the five landmarks' extent
943 hr[PM_R_MX0] = hr[PM_R_CHR_X]
944 hr[PM_R_MX1] = hr[PM_R_CHL_X]
945 hr[PM_R_MY0] = ls
946 hr[PM_R_MY1] = li
947 hr[PM_R_MOUTH_PIX] = xe - xs + 1
948 return PM_MW_OK
949}
950// the mouth's slots, from one hypothesis record to another
951func pm_mouth_copy(dst: *i64, src: *i64) -> i64 {
952 dst[PM_R_MOUTH] = src[PM_R_MOUTH]
953 dst[PM_R_MX0] = src[PM_R_MX0]; dst[PM_R_MY0] = src[PM_R_MY0]; dst[PM_R_MX1] = src[PM_R_MX1]; dst[PM_R_MY1] = src[PM_R_MY1]
954 dst[PM_R_STOMION_Y] = src[PM_R_STOMION_Y]
955 dst[PM_R_LIP] = src[PM_R_LIP]
956 dst[PM_R_MOUTH_PIX] = src[PM_R_MOUTH_PIX]
957 var k: i64 = PM_R_LS_X
958 while k <= PM_R_CHL_Y { dst[k] = src[k]; k = k + 1 }
959 dst[PM_R_MOUTH_WHY] = src[PM_R_MOUTH_WHY]
960 dst[PM_R_SEAM_STRENGTH] = src[PM_R_SEAM_STRENGTH]
961 dst[PM_R_SEAM_HYP] = src[PM_R_SEAM_HYP]
962 return 0
963}
964// THE MOUTH (AT44, 2026-09-18), FOUND FROM ITS SEAM. The first ruler took red-chroma pixels below the eyes, the densest row band of
965// them and the darkest row inside it -- and on the seed-42 render that band ran from the nose's shadow to the shadow under the lower
966// lip (both redder than the specular upper lip), the darkest row was the chin fold, and the lip ratio read 4909 permil against a
967// constructed 612. The seam is the one feature a closed mouth shows under any light, so it comes first:
968// (1) THE SEAM is a horizontal valley line in the window below the eyes: per pixel the valley depth at the seam's own scale (how
969// much darker than the darker of the two pixels one seam thickness above and below) above the window's noise floor. TWO
970// INDEPENDENT FINDERS propose it, because each is MEASURED to fail where the other holds: the WALK from the interior extremum
971// (the deepest valley on the face's midline band, walked outward along the eye line's slope to the turning points of its
972// running depth) and the GLOBAL path (the greatest summed depth anywhere). On the default-tone render the shadow under the
973// lower lip outsums the seam globally and only the walk finds the seam; on a photographed glossed lip (still 17) a highlight
974// edge on the lower lip is deeper at the midline than the pressed seam, and only the global path finds it.
975// (2) EACH HYPOTHESIS IS VERIFIED by the vermilion test (pm_mouth_hypothesis): the stomion at the centre of the valley's darker
976// half, the two vermilion lines as the change points of the chroma-weighted HUE within one lip reach of the seam, each lip side
977// redder than its skin side and neither change on the reach's far end. A verified hypothesis beats a refused one; between two
978// verified the deeper seam wins. A seam whose end is the window's side has found the window and is refused by name.
979// Each vermilion is measured to its OWN inner edge (the valley's half-depth edge on its side), the definition the oracle rows use
980// for a parted mouth; on a seam one row thick it is the stomion itself.
981func pm_mouth(rgb: *u8, w: i64, h: i64, res: *i64) -> i64 {
982 let ex_r: i64 = (res[PM_R_RLAT_X] + res[PM_R_RMED_X]) / 2
983 let ex_l: i64 = (res[PM_R_LMED_X] + res[PM_R_LLAT_X]) / 2
984 let ey: i64 = (res[PM_R_RLAT_Y] + res[PM_R_RMED_Y] + res[PM_R_LMED_Y] + res[PM_R_LLAT_Y]) / 4
985 let ipd: i64 = pm_max(ex_l - ex_r, 1)
986 var mx0: i64 = ex_r - ipd * PM_MOUTH_X_PAD_PERMIL / PM_PERMIL
987 var mx1: i64 = ex_l + ipd * PM_MOUTH_X_PAD_PERMIL / PM_PERMIL
988 var my0: i64 = ey + ipd * PM_MOUTH_Y_LO_PERMIL / PM_PERMIL
989 var my1: i64 = ey + ipd * PM_MOUTH_Y_HI_PERMIL / PM_PERMIL
990 if mx0 < 0 { mx0 = 0 }
991 if my0 < 0 { my0 = 0 }
992 if mx1 > w { mx1 = w }
993 if my1 > h { my1 = h }
994 res[PM_R_MOUTH_WHY] = PM_MW_NO_WINDOW
995 let d: i64 = pm_max(1, (ipd * PM_SEAM_PROBE_PERMIL + PM_ROUND) / PM_PERMIL)
996 let reach: i64 = pm_max(2, (ipd * PM_LIP_REACH_PERMIL + PM_ROUND) / PM_PERMIL)
997 let reach_lo: i64 = pm_max(2, (ipd * PM_LIP_REACH_LOWER_PERMIL + PM_ROUND) / PM_PERMIL)
998 res[PM_R_SEAM_PROBE] = d
999 res[PM_R_LIP_REACH] = reach
1000 res[PM_R_LIP_REACH_LO] = reach_lo
1001 // rows where the probe stays inside the frame
1002 let ya: i64 = pm_max(my0, d)
1003 let yb: i64 = pm_min(my1, h - d)
1004 let rows: i64 = yb - ya
1005 let cols: i64 = mx1 - mx0
1006 if rows < 1 { return PM_OK }
1007 if cols < 2 { return PM_OK }
1008 // THE NOISE FLOOR of a valley, from the window itself: the window is about twice the mouth's width, so more than half its
1009 // columns lie beside the mouth, and the MEDIAN column's deepest valley is what a column with no seam offers (skin texture,
1010 // folds, compression). Only depth above that floor extends a seam -- MEASURED 2026-09-18 on seed 42: summing raw depth let the
1011 // path run from one nasolabial fold through the seam to the other and onto the window's side
1012 let hist: *i64 = sys_mmap((PM_VALLEY_SPAN + 1) * PM_I64) as *i64
1013 var c: i64 = 0
1014 while c < cols {
1015 let xh: i64 = mx0 + c
1016 var cmax: i64 = 0 - PM_BYTE
1017 var rh: i64 = 0
1018 while rh < rows {
1019 let vh: i64 = pm_seam_v(rgb, w, xh, ya + rh, d, 0)
1020 if vh > cmax { cmax = vh }
1021 rh = rh + 1
1022 }
1023 hist[cmax + PM_BYTE] = hist[cmax + PM_BYTE] + 1
1024 c = c + 1
1025 }
1026 var floor_v: i64 = 0
1027 var seen: i64 = 0
1028 var hb: i64 = 0
1029 while hb <= PM_VALLEY_SPAN { seen = seen + hist[hb]; if seen * 2 >= cols { floor_v = hb - PM_BYTE; hb = PM_VALLEY_SPAN + 1 } else { hb = hb + 1 } }
1030 res[PM_R_SEAM_FLOOR] = floor_v
1031 let ctx: *i64 = fq_ctx()
1032 let hw: *i64 = sys_mmap(PM_R_N * PM_I64) as *i64
1033 let hg: *i64 = sys_mmap(PM_R_N * PM_I64) as *i64
1034 hw[PM_R_MOUTH_WHY] = PM_MW_NO_SEAM
1035 hg[PM_R_MOUTH_WHY] = PM_MW_NO_SEAM
1036 hw[PM_R_SEAM_HYP] = PM_SEAM_HYP_WALK
1037 hg[PM_R_SEAM_HYP] = PM_SEAM_HYP_GLOBAL
1038 // HYPOTHESES FROM THE WALK: the face's midline band is the middle quarter of the eye span, centred on the line through the eyes'
1039 // midpoint perpendicular to the eye line (a rolled head's mouth is sought where it is). Every row whose deepest band valley is a
1040 // local maximum within one probe of it seeds a walk -- the seam, the chin fold under the lower lip and the crease above the upper
1041 // one are all horizontal valleys there, and MEASURED on the default-tone renders the fold is the deepest at the midline, so a
1042 // walk from the single deepest seed found the fold, failed the vermilion test and lost the mouth. Each walk is verified; the
1043 // verified walk with the greatest depth is the walk's hypothesis
1044 let eyr: i64 = (res[PM_R_RLAT_Y] + res[PM_R_RMED_Y]) / 2
1045 let eyl: i64 = (res[PM_R_LMED_Y] + res[PM_R_LLAT_Y]) / 2
1046 let xm: i64 = (ex_r + ex_l) / 2
1047 let ym: i64 = (eyr + eyl) / 2
1048 let band: i64 = pm_max(1, ipd * PM_SEAM_SEED_PERMIL / PM_PERMIL)
1049 let rowv: *i64 = sys_mmap(rows * PM_I64) as *i64
1050 let rowx: *i64 = sys_mmap(rows * PM_I64) as *i64
1051 var r0: i64 = 0
1052 while r0 < rows {
1053 let y0: i64 = ya + r0
1054 let xmid: i64 = xm + (y0 - ym) * (eyr - eyl) / ipd
1055 rowv[r0] = 0
1056 rowx[r0] = 0 - 1
1057 var x0: i64 = pm_max(xmid - band, mx0)
1058 while x0 <= pm_min(xmid + band, mx1 - 1) {
1059 let v0: i64 = pm_seam_v(rgb, w, x0, y0, d, floor_v)
1060 if v0 > rowv[r0] { rowv[r0] = v0; rowx[r0] = x0 - mx0 }
1061 x0 = x0 + 1
1062 }
1063 r0 = r0 + 1
1064 }
1065 let hc: *i64 = sys_mmap(PM_R_N * PM_I64) as *i64
1066 let prow: *i64 = sys_mmap(cols * PM_I64) as *i64
1067 let sumr: *i64 = sys_mmap(PM_I64) as *i64
1068 let suml: *i64 = sys_mmap(PM_I64) as *i64
1069 r0 = 0
1070 while r0 < rows {
1071 var peak: i64 = 0
1072 if rowx[r0] >= 0 { peak = 1 }
1073 var j: i64 = 1
1074 while j <= d {
1075 if r0 - j >= 0 { if rowv[r0 - j] > rowv[r0] { peak = 0 } }
1076 if r0 + j < rows { if rowv[r0 + j] > rowv[r0] { peak = 0 } }
1077 if r0 - j >= 0 { if rowv[r0 - j] == rowv[r0] { if j == 1 { peak = 0 } } } // a plateau seeds once, at its lowest row
1078 j = j + 1
1079 }
1080 if peak == 1 {
1081 let sc: i64 = rowx[r0]
1082 let xe: i64 = pm_seam_half(rgb, w, mx0, cols, ya, rows, d, floor_v, sc, r0, 1, eyl - eyr, ipd, prow, sumr)
1083 let xs: i64 = pm_seam_half(rgb, w, mx0, cols, ya, rows, d, floor_v, sc, r0, 0 - 1, eyl - eyr, ipd, prow, suml)
1084 hc[PM_R_SEAM_HYP] = PM_SEAM_HYP_WALK
1085 hc[PM_R_SEAM_STRENGTH] = sumr[0] + suml[0] - rowv[r0]
1086 hc[PM_R_MOUTH_WHY] = pm_mouth_hypothesis(rgb, w, h, hc, mx0, ya, cols, prow, xs, xe, d, reach, reach_lo, ipd, ctx)
1087 var take: i64 = 0
1088 let cok: i64 = (hc[PM_R_MOUTH_WHY] == PM_MW_OK) as i64
1089 let wok0: i64 = (hw[PM_R_MOUTH_WHY] == PM_MW_OK) as i64
1090 if cok > wok0 { take = 1 }
1091 if cok == wok0 { if hc[PM_R_SEAM_STRENGTH] > hw[PM_R_SEAM_STRENGTH] { take = 1 } }
1092 if take == 1 { pm_mouth_copy(hw, hc) }
1093 }
1094 r0 = r0 + 1
1095 }
1096 // HYPOTHESIS 2, THE GLOBAL PATH
1097 let gprow: *i64 = sys_mmap(cols * PM_I64) as *i64
1098 let se: *i64 = sys_mmap(2 * PM_I64) as *i64
1099 let gsum: i64 = pm_seam_global(rgb, w, mx0, cols, ya, rows, d, floor_v, gprow, se)
1100 if gsum > 0 {
1101 hg[PM_R_SEAM_STRENGTH] = gsum
1102 hg[PM_R_MOUTH_WHY] = pm_mouth_hypothesis(rgb, w, h, hg, mx0, ya, cols, gprow, se[0], se[1], d, reach, reach_lo, ipd, ctx)
1103 }
1104 // a verified hypothesis beats a refused one; between two alike the deeper seam wins
1105 var pick: *i64 = hw
1106 var wok: i64 = 0
1107 var gok: i64 = 0
1108 if hw[PM_R_MOUTH_WHY] == PM_MW_OK { wok = 1 }
1109 if hg[PM_R_MOUTH_WHY] == PM_MW_OK { gok = 1 }
1110 if gok > wok { pick = hg }
1111 if gok == wok { if hg[PM_R_SEAM_STRENGTH] > hw[PM_R_SEAM_STRENGTH] { pick = hg } }
1112 pm_mouth_copy(res, pick)
1113 return PM_OK
1114}
1115// THE SKIN ROUTE: packed RGB in, res[PM_R_*] out. Returns PM_OK, PM_E_NOFACE or PM_E_NOEYES (the mouth is optional: a
1116// missing mouth leaves PM_R_MOUTH 0 with the eye axes still measured)
1117func pm_landmarks(rgb: *u8, w: i64, h: i64, res: *i64) -> i64 {
1118 var k: i64 = 0
1119 while k < PM_R_N { res[k] = 0; k = k + 1 }
1120 res[PM_R_W] = w
1121 res[PM_R_H] = h
1122 res[PM_R_ROUTE] = PM_ROUTE_SKIN
1123 var cell: i64 = pm_min(w, h) / PM_GRID
1124 if cell < PM_MIN_CELL { cell = PM_MIN_CELL }
1125 res[PM_R_CELL] = cell
1126 let gw: i64 = w / cell
1127 let gh: i64 = h / cell
1128 if gw < 2 { return PM_E_NOFACE }
1129 if gh < 2 { return PM_E_NOFACE }
1130 let n: i64 = gw * gh
1131 let skinp: *i64 = sys_mmap(n * PM_I64) as *i64
1132 let lumam: *i64 = sys_mmap(n * PM_I64) as *i64
1133 let area: i64 = cell * cell
1134 // the coarse grid: skin share and mean luma per cell
1135 var gy: i64 = 0
1136 while gy < gh {
1137 var gx: i64 = 0
1138 while gx < gw {
1139 var sk: i64 = 0
1140 var sl: i64 = 0
1141 var y: i64 = gy * cell
1142 while y < (gy + 1) * cell {
1143 var x: i64 = gx * cell
1144 while x < (gx + 1) * cell {
1145 let o: i64 = (y * w + x) * PM_BPP
1146 let r: i64 = (rgb[o] as i64) & PM_BYTE
1147 let g: i64 = (rgb[o + 1] as i64) & PM_BYTE
1148 let b: i64 = (rgb[o + 2] as i64) & PM_BYTE
1149 sk = sk + sks_is_skin(r, g, b)
1150 sl = sl + pm_luma(r, g, b)
1151 x = x + 1
1152 }
1153 y = y + 1
1154 }
1155 let ci: i64 = gy * gw + gx
1156 skinp[ci] = sk * PM_PERMIL / area
1157 lumam[ci] = sl / area
1158 gx = gx + 1
1159 }
1160 gy = gy + 1
1161 }
1162 // the face: the largest skin component
1163 let smask: *u8 = sys_mmap(n + 1)
1164 k = 0
1165 while k < n { if skinp[k] >= PM_SKIN_CELL_PERMIL { smask[k] = 1 as u8 } else { smask[k] = 0 as u8 } k = k + 1 }
1166 let face: *u8 = sys_mmap(n + 1)
1167 let bb: *i64 = sys_mmap(4 * PM_I64) as *i64
1168 let fcells: i64 = pm_largest_component(smask, gw, gh, face, bb)
1169 res[PM_R_FACE_CELLS] = fcells
1170 if fcells < PM_MIN_FACE_CELLS { return PM_E_NOFACE }
1171 res[PM_R_FACE] = 1
1172 res[PM_R_FX0] = bb[0] * cell
1173 res[PM_R_FY0] = bb[1] * cell
1174 res[PM_R_FX1] = (bb[2] + 1) * cell
1175 res[PM_R_FY1] = (bb[3] + 1) * cell
1176 // the face's mean luma over its skin cells
1177 var fl: i64 = 0
1178 k = 0
1179 while k < n { if face[k] == (1 as u8) { fl = fl + lumam[k] } k = k + 1 }
1180 let face_luma: i64 = fl / fcells
1181 let dark: i64 = face_luma * PM_DARK_PERMIL / PM_PERMIL
1182 // THE HEAD BOX: a skin component can be a whole body; the head is its top. Its width is the widest row among the top
1183 // rows of the component, its height a fixed multiple of that width, its columns centred on those rows' skin cells
1184 let comp_h: i64 = bb[3] - bb[1] + 1
1185 var top_rows: i64 = comp_h * PM_HEAD_TOP_PERMIL / PM_PERMIL
1186 if top_rows < 1 { top_rows = 1 }
1187 var head_w: i64 = 0
1188 var head_sx: i64 = 0
1189 var head_sn: i64 = 0
1190 var ry: i64 = bb[1]
1191 while ry < bb[1] + top_rows {
1192 var rx: i64 = bb[0]
1193 var rowcount: i64 = 0
1194 while rx <= bb[2] {
1195 if face[ry * gw + rx] == (1 as u8) { rowcount = rowcount + 1; head_sx = head_sx + rx; head_sn = head_sn + 1 }
1196 rx = rx + 1
1197 }
1198 if rowcount > head_w { head_w = rowcount }
1199 ry = ry + 1
1200 }
1201 if head_w < 2 { return PM_E_NOEYES }
1202 let head_cx: i64 = head_sx / pm_max(head_sn, 1)
1203 var hx0: i64 = head_cx - head_w * PM_HEAD_HALFW_PERMIL / PM_PERMIL
1204 var hx1: i64 = head_cx + head_w * PM_HEAD_HALFW_PERMIL / PM_PERMIL
1205 var hy0: i64 = bb[1]
1206 var hy1: i64 = bb[1] + head_w * PM_HEAD_HEIGHT_X10 / PM_TEN
1207 if hx0 < bb[0] { hx0 = bb[0] }
1208 if hx1 > bb[2] { hx1 = bb[2] }
1209 if hy1 > bb[3] { hy1 = bb[3] }
1210 res[PM_R_HX0] = hx0 * cell
1211 res[PM_R_HY0] = hy0 * cell
1212 res[PM_R_HX1] = (hx1 + 1) * cell
1213 res[PM_R_HY1] = (hy1 + 1) * cell
1214 // ENCLOSED cells: a coarse cell with skin to its left and right on its row and above and below on its column inside
1215 // the head box is a hole in the face (eye, brow, nostril, mouth); hair at the sides of the box is not enclosed
1216 let enclosed: *u8 = sys_mmap(n + 1)
1217 var cy: i64 = hy0
1218 while cy <= hy1 {
1219 var cx: i64 = hx0
1220 while cx <= hx1 {
1221 var l: i64 = 0
1222 var r: i64 = 0
1223 var u: i64 = 0
1224 var d: i64 = 0
1225 var t: i64 = hx0
1226 while t < cx { if face[cy * gw + t] == (1 as u8) { l = 1; t = cx } else { t = t + 1 } }
1227 t = cx + 1
1228 while t <= hx1 { if face[cy * gw + t] == (1 as u8) { r = 1; t = hx1 + 1 } else { t = t + 1 } }
1229 t = hy0
1230 while t < cy { if face[t * gw + cx] == (1 as u8) { u = 1; t = cy } else { t = t + 1 } }
1231 t = cy + 1
1232 while t <= hy1 { if face[t * gw + cx] == (1 as u8) { d = 1; t = hy1 + 1 } else { t = t + 1 } }
1233 if l + r + u + d == 4 { enclosed[cy * gw + cx] = 1 as u8 } else { enclosed[cy * gw + cx] = face[cy * gw + cx] }
1234 cx = cx + 1
1235 }
1236 cy = cy + 1
1237 }
1238 // eye candidates on a FINER grid over the head box's eye zone: dark cells whose coarse cell is enclosed by skin
1239 var celle: i64 = cell / PM_EYE_FINE_DIV
1240 if celle < PM_MIN_CELL { celle = PM_MIN_CELL }
1241 let fx0: i64 = hx0 * cell
1242 let fx1: i64 = (hx1 + 1) * cell
1243 let fy_top: i64 = hy0 * cell
1244 let fh_px: i64 = (hy1 - hy0 + 1) * cell
1245 let zy0: i64 = fy_top + fh_px * PM_EYE_ZONE_TOP_PERMIL / PM_PERMIL
1246 let zy1: i64 = fy_top + fh_px * PM_EYE_ZONE_BOT_PERMIL / PM_PERMIL
1247 let gwe: i64 = (fx1 - fx0) / celle
1248 let ghe: i64 = (zy1 - zy0) / celle
1249 if gwe < 3 { return PM_E_NOEYES }
1250 if ghe < 3 { return PM_E_NOEYES }
1251 let ne: i64 = gwe * ghe
1252 let dmask: *u8 = sys_mmap(ne + 1)
1253 let areae: i64 = celle * celle
1254 var ey2: i64 = 0
1255 while ey2 < ghe {
1256 var ex2: i64 = 0
1257 while ex2 < gwe {
1258 var sl2: i64 = 0
1259 var yy: i64 = zy0 + ey2 * celle
1260 while yy < zy0 + (ey2 + 1) * celle {
1261 var xx: i64 = fx0 + ex2 * celle
1262 while xx < fx0 + (ex2 + 1) * celle { sl2 = sl2 + pm_luma_at(rgb, w, xx, yy); xx = xx + 1 }
1263 yy = yy + 1
1264 }
1265 let ccx: i64 = (fx0 + ex2 * celle) / cell
1266 let ccy: i64 = (zy0 + ey2 * celle) / cell
1267 var cand: i64 = 0
1268 if sl2 / areae < dark { if enclosed[ccy * gw + ccx] == (1 as u8) { cand = 1 } }
1269 dmask[ey2 * gwe + ex2] = cand as u8
1270 ex2 = ex2 + 1
1271 }
1272 ey2 = ey2 + 1
1273 }
1274 // the blob table is sized by the grid, never by a guess: a blob needs one cell, a checkerboard needs half of them
1275 let bcap: i64 = ne / 2 + 1
1276 let blobs: *i64 = sys_mmap(bcap * PM_BLOB_FIELDS * PM_I64) as *i64
1277 let capped: *i64 = sys_mmap(PM_I64) as *i64
1278 let nb: i64 = pm_blobs(dmask, gwe, ghe, 0, 0, gwe - 1, ghe - 1, blobs, bcap, capped)
1279 res[PM_R_BLOBS] = nb
1280 res[PM_R_BLOBS_CAPPED] = capped[0]
1281 // the largest blobs, in pixels, for the receipt (the instrument that explains a miss)
1282 let taken: *u8 = sys_mmap(nb + 1)
1283 var tb: i64 = 0
1284 while tb < PM_TOP_BLOBS {
1285 var bi: i64 = 0 - 1
1286 var bc: i64 = 0
1287 var q: i64 = 0
1288 while q < nb { if taken[q] == (0 as u8) { if blobs[q * PM_BLOB_FIELDS + PM_B_CELLS] > bc { bc = blobs[q * PM_BLOB_FIELDS + PM_B_CELLS]; bi = q } } q = q + 1 }
1289 let slot: i64 = PM_R_TB + tb * PM_TB_FIELDS
1290 if bi >= 0 {
1291 taken[bi] = 1 as u8
1292 let ob2: i64 = bi * PM_BLOB_FIELDS
1293 res[slot] = fx0 + blobs[ob2 + PM_B_X0] * celle
1294 res[slot + 1] = zy0 + blobs[ob2 + PM_B_Y0] * celle
1295 res[slot + 2] = fx0 + (blobs[ob2 + PM_B_X1] + 1) * celle
1296 res[slot + 3] = zy0 + (blobs[ob2 + PM_B_Y1] + 1) * celle
1297 res[slot + 4] = blobs[ob2 + PM_B_CELLS]
1298 } else { res[slot] = 0; res[slot + 1] = 0; res[slot + 2] = 0; res[slot + 3] = 0; res[slot + 4] = 0 }
1299 tb = tb + 1
1300 }
1301 let fw: i64 = fx1 - fx0
1302 // pair search with pixel-level consistency: the best-scoring plausible pair whose refined canthi agree with an eye pair;
1303 // a pair that fails the consistency check is remembered and the next best is tried
1304 let triedp: *i64 = sys_mmap(2 * PM_PAIR_TRIES * PM_I64) as *i64
1305 var ntried: i64 = 0
1306 let e5: *i64 = sys_mmap(PM_FIS_N * PM_I64) as *i64
1307 var found: i64 = 0
1308 var attempt: i64 = 0
1309 while attempt < PM_PAIR_TRIES {
1310 var best: i64 = 0 - 1
1311 var bestb: i64 = 0 - 1
1312 var bests: i64 = 0
1313 var a: i64 = 0
1314 while a < nb {
1315 let oa: i64 = a * PM_BLOB_FIELDS
1316 var b2: i64 = a + 1
1317 while b2 < nb {
1318 var skip: i64 = 0
1319 var ti: i64 = 0
1320 while ti < ntried { if triedp[2 * ti] == a { if triedp[2 * ti + 1] == b2 { skip = 1 } } ti = ti + 1 }
1321 if skip == 0 {
1322 let ob: i64 = b2 * PM_BLOB_FIELDS
1323 var left: i64 = oa
1324 var right: i64 = ob
1325 if blobs[ob + PM_B_SUMX] * blobs[oa + PM_B_CELLS] < blobs[oa + PM_B_SUMX] * blobs[ob + PM_B_CELLS] { left = ob; right = oa }
1326 let lc: i64 = blobs[left + PM_B_CELLS]
1327 let rc: i64 = blobs[right + PM_B_CELLS]
1328 let lcx: i64 = blobs[left + PM_B_SUMX] * celle / lc
1329 let lcy: i64 = blobs[left + PM_B_SUMY] * celle / lc
1330 let rcx: i64 = blobs[right + PM_B_SUMX] * celle / rc
1331 let rcy: i64 = blobs[right + PM_B_SUMY] * celle / rc
1332 var okp: i64 = 1
1333 if blobs[left + PM_B_TOUCH] == 1 { okp = 0 }
1334 if blobs[right + PM_B_TOUCH] == 1 { okp = 0 }
1335 if pm_abs(lcy - rcy) * PM_PERMIL > PM_PAIR_DY_PERMIL * fw { okp = 0 }
1336 let dx: i64 = rcx - lcx
1337 if dx * PM_PERMIL < PM_PAIR_DX_LO_PERMIL * fw { okp = 0 }
1338 if dx * PM_PERMIL > PM_PAIR_DX_HI_PERMIL * fw { okp = 0 }
1339 let lw: i64 = blobs[left + PM_B_X1] - blobs[left + PM_B_X0] + 1
1340 let lh: i64 = blobs[left + PM_B_Y1] - blobs[left + PM_B_Y0] + 1
1341 let rw: i64 = blobs[right + PM_B_X1] - blobs[right + PM_B_X0] + 1
1342 let rh: i64 = blobs[right + PM_B_Y1] - blobs[right + PM_B_Y0] + 1
1343 if lw * celle * PM_PERMIL > PM_EYE_MAXW_PERMIL * fw { okp = 0 }
1344 if rw * celle * PM_PERMIL > PM_EYE_MAXW_PERMIL * fw { okp = 0 }
1345 if lw * PM_TEN > PM_BROW_ASPECT_X10 * lh { okp = 0 }
1346 if rw * PM_TEN > PM_BROW_ASPECT_X10 * rh { okp = 0 }
1347 if lc > rc * PM_SIZE_RATIO_MAX { okp = 0 }
1348 if rc > lc * PM_SIZE_RATIO_MAX { okp = 0 }
1349 if okp == 1 {
1350 let score: i64 = lc + rc
1351 if score > bests { bests = score; best = left; bestb = right }
1352 }
1353 }
1354 b2 = b2 + 1
1355 }
1356 a = a + 1
1357 }
1358 if best < 0 { attempt = PM_PAIR_TRIES } else {
1359 let ia: i64 = best / PM_BLOB_FIELDS
1360 let ib: i64 = bestb / PM_BLOB_FIELDS
1361 if ia < ib { triedp[2 * ntried] = ia; triedp[2 * ntried + 1] = ib } else { triedp[2 * ntried] = ib; triedp[2 * ntried + 1] = ia }
1362 ntried = ntried + 1
1363 // refine both at pixel level inside padded boxes (image-left blob = the subject's RIGHT eye, unmirrored still)
1364 // each blob seeds its fissure (AT44): grown inside rows padded one fine cell above and two below the blob and columns from
1365 // the head box's side to the other blob's centre, so a fissure longer than its blob is followed to its end
1366 let bcx_r: i64 = fx0 + blobs[best + PM_B_SUMX] * celle / blobs[best + PM_B_CELLS]
1367 let bcx_l: i64 = fx0 + blobs[bestb + PM_B_SUMX] * celle / blobs[bestb + PM_B_CELLS]
1368 res[PM_R_EYE_WX0] = res[PM_R_HX0]; res[PM_R_EYE_WX1] = bcx_l; res[PM_R_EYE_LWX0] = bcx_r; res[PM_R_EYE_LWX1] = res[PM_R_HX1]
1369 pm_eye_fissure(rgb, w, h, fx0 + blobs[best + PM_B_X0] * celle, zy0 + blobs[best + PM_B_Y0] * celle, fx0 + (blobs[best + PM_B_X1] + 1) * celle, zy0 + (blobs[best + PM_B_Y1] + 1) * celle, res[PM_R_HX0], zy0 + blobs[best + PM_B_Y0] * celle - celle, bcx_l, zy0 + (blobs[best + PM_B_Y1] + 2) * celle, (blobs[best + PM_B_Y1] - blobs[best + PM_B_Y0] + PM_EYE_ROWS_PAD) * celle / 2, e5)
1370 var pinned: i64 = 0
1371 res[PM_R_EYE_PIX_R] = e5[PM_FIS_COUNT]
1372 res[PM_R_RLAT_X] = e5[PM_FIS_LX]
1373 res[PM_R_RLAT_Y] = e5[PM_FIS_LY]
1374 res[PM_R_RMED_X] = e5[PM_FIS_RX]
1375 res[PM_R_RMED_Y] = e5[PM_FIS_RY]
1376 if e5[PM_FIS_PIN_L] == 1 { pinned = pinned + PM_PIN_RLAT }
1377 if e5[PM_FIS_PIN_R] == 1 { pinned = pinned + PM_PIN_RMED }
1378 pm_eye_fissure(rgb, w, h, fx0 + blobs[bestb + PM_B_X0] * celle, zy0 + blobs[bestb + PM_B_Y0] * celle, fx0 + (blobs[bestb + PM_B_X1] + 1) * celle, zy0 + (blobs[bestb + PM_B_Y1] + 1) * celle, bcx_r, zy0 + blobs[bestb + PM_B_Y0] * celle - celle, res[PM_R_HX1], zy0 + (blobs[bestb + PM_B_Y1] + 2) * celle, (blobs[bestb + PM_B_Y1] - blobs[bestb + PM_B_Y0] + PM_EYE_ROWS_PAD) * celle / 2, e5)
1379 res[PM_R_EYE_PIX_L] = e5[PM_FIS_COUNT]
1380 res[PM_R_LMED_X] = e5[PM_FIS_LX]
1381 res[PM_R_LMED_Y] = e5[PM_FIS_LY]
1382 res[PM_R_LLAT_X] = e5[PM_FIS_RX]
1383 res[PM_R_LLAT_Y] = e5[PM_FIS_RY]
1384 if e5[PM_FIS_PIN_L] == 1 { pinned = pinned + PM_PIN_LMED }
1385 if e5[PM_FIS_PIN_R] == 1 { pinned = pinned + PM_PIN_LLAT }
1386 res[PM_R_PINNED] = pinned
1387 var pf: i64 = PM_PF_PINNED
1388 if pinned == 0 { pf = pm_pair_check(res) }
1389 // If fissure growth joins a lid-connected tail, adjudicate the original connected eye cores as a second witness.
1390 // The core bounds are measured blobs, not a relaxed threshold; they must pass the same pair geometry checks.
1391 if pf == PM_PF_ENDO {
1392 let r0: i64 = fx0 + blobs[best + PM_B_X0] * celle
1393 let r1: i64 = fx0 + (blobs[best + PM_B_X1] + 1) * celle
1394 let ryc: i64 = zy0 + blobs[best + PM_B_SUMY] * celle / pm_max(blobs[best + PM_B_CELLS], 1)
1395 let l0: i64 = fx0 + blobs[bestb + PM_B_X0] * celle
1396 let l1: i64 = fx0 + (blobs[bestb + PM_B_X1] + 1) * celle
1397 let lyc: i64 = zy0 + blobs[bestb + PM_B_SUMY] * celle / pm_max(blobs[bestb + PM_B_CELLS], 1)
1398 res[PM_R_RLAT_X] = r0; res[PM_R_RLAT_Y] = ryc; res[PM_R_RMED_X] = r1; res[PM_R_RMED_Y] = ryc
1399 res[PM_R_LMED_X] = l0; res[PM_R_LMED_Y] = lyc; res[PM_R_LLAT_X] = l1; res[PM_R_LLAT_Y] = lyc
1400 res[PM_R_EYE_PIX_R] = blobs[best + PM_B_CELLS]; res[PM_R_EYE_PIX_L] = blobs[bestb + PM_B_CELLS]
1401 res[PM_R_PINNED] = 0
1402 let core_pf: i64 = pm_pair_check(res)
1403 if core_pf == PM_PF_NONE { pf = core_pf; res[PM_R_CODEC] = 1 }
1404 }
1405 res[PM_R_PAIR_FAIL] = pf
1406 if pf == PM_PF_NONE { found = 1; attempt = PM_PAIR_TRIES } else { attempt = attempt + 1 }
1407 }
1408 }
1409 if found == 0 { return PM_E_NOEYES }
1410 pm_axes(res)
1411 return pm_mouth(rgb, w, h, res)
1412}
1413// THE CASCADE ROUTE: the face box and both eye boxes from the loaded cascades over the luma plane, the canthi as the dark
1414// fissure's extremes inside a widened band of each eye box thresholded against that band's own mean luma, the same pair
1415// check, axes and mouth as the skin route. Returns PM_OK, PM_E_NOFACE or PM_E_NOEYES
1416func pm_landmarks_cascade(rgb: *u8, w: i64, h: i64, res: *i64, face_c: *HaarCascade, eye_c: *HaarCascade) -> i64 {
1417 var k: i64 = 0
1418 while k < PM_R_N { res[k] = 0; k = k + 1 }
1419 res[PM_R_W] = w
1420 res[PM_R_H] = h
1421 res[PM_R_ROUTE] = PM_ROUTE_CASCADE
1422 let gray: *u8 = sys_mmap(w * h + 16)
1423 var y: i64 = 0
1424 while y < h { var x: i64 = 0; while x < w { gray[y * w + x] = pm_luma_at(rgb, w, x, y) as u8; x = x + 1 } y = y + 1 }
1425 let fr: *i64 = sys_mmap(FF_R_N * PM_I64) as *i64
1426 let frc: i64 = ff_find(face_c, eye_c, gray, w, h, w, FF_MIN_FACE_PERMIL, fr)
1427 if fr[FF_R_FACE] == 0 { return PM_E_NOFACE }
1428 res[PM_R_FACE] = 1
1429 res[PM_R_FX0] = fr[FF_R_FX]
1430 res[PM_R_FY0] = fr[FF_R_FY]
1431 res[PM_R_FX1] = fr[FF_R_FX] + fr[FF_R_FW]
1432 res[PM_R_FY1] = fr[FF_R_FY] + fr[FF_R_FH]
1433 res[PM_R_HX0] = res[PM_R_FX0]
1434 res[PM_R_HY0] = res[PM_R_FY0]
1435 res[PM_R_HX1] = res[PM_R_FX1]
1436 res[PM_R_HY1] = res[PM_R_FY1]
1437 res[PM_R_FVOTES] = fr[FF_R_FVOTES]
1438 res[PM_R_FACE_CELLS] = fr[FF_R_RAW]
1439 if frc != FF_OK { return PM_E_NOEYES }
1440 res[PM_R_EYE_VOTES_R] = fr[FF_R_RVOTES]
1441 res[PM_R_EYE_VOTES_L] = fr[FF_R_LVOTES]
1442 // each eye: the widened band sets the local dark level (its mean luma), the cascade's eye box seeds the fissure (the pupil), and
1443 // the fissure grows inside the band's rows from the face box's side to the midline between the two eye boxes (AT44). The image-
1444 // left eye is the subject's RIGHT: its lateral canthus is the fissure's left extreme
1445 let e5: *i64 = sys_mmap(PM_FIS_N * PM_I64) as *i64
1446 let rw_: i64 = fr[FF_R_RW]
1447 let lw_: i64 = fr[FF_R_LW]
1448 let rcx: i64 = fr[FF_R_RX] + rw_ / 2
1449 let lcx: i64 = fr[FF_R_LX] + lw_ / 2
1450 let rx0: i64 = fr[FF_R_RX] - rw_ * PM_EYE_WIDEN_PERMIL / PM_PERMIL
1451 let rx1: i64 = fr[FF_R_RX] + rw_ + rw_ * PM_EYE_WIDEN_PERMIL / PM_PERMIL
1452 let ry0: i64 = fr[FF_R_RY] + fr[FF_R_RH] * PM_EYE_BAND_TOP_PERMIL / PM_PERMIL
1453 let ry1: i64 = fr[FF_R_RY] + fr[FF_R_RH] * PM_EYE_BAND_BOT_PERMIL / PM_PERMIL
1454 res[PM_R_EX0] = rx0; res[PM_R_EY0] = ry0; res[PM_R_EX1] = rx1; res[PM_R_EY1] = ry1
1455 res[PM_R_EYE_WX0] = res[PM_R_FX0]; res[PM_R_EYE_WX1] = lcx
1456 pm_eye_fissure(rgb, w, h, fr[FF_R_RX], ry0, fr[FF_R_RX] + rw_, ry1, res[PM_R_FX0], fr[FF_R_RY], lcx, fr[FF_R_RY] + fr[FF_R_RH], fr[FF_R_RH] / 2, e5)
1457 var pinned: i64 = 0
1458 res[PM_R_EYE_PIX_R] = e5[PM_FIS_COUNT]
1459 res[PM_R_RLAT_X] = e5[PM_FIS_LX]
1460 res[PM_R_RLAT_Y] = e5[PM_FIS_LY]
1461 res[PM_R_RMED_X] = e5[PM_FIS_RX]
1462 res[PM_R_RMED_Y] = e5[PM_FIS_RY]
1463 if e5[PM_FIS_PIN_L] == 1 { pinned = pinned + PM_PIN_RLAT }
1464 if e5[PM_FIS_PIN_R] == 1 { pinned = pinned + PM_PIN_RMED }
1465 // the image-right eye (the subject's LEFT): its lateral canthus is the fissure's right extreme
1466 let lx0: i64 = fr[FF_R_LX] - lw_ * PM_EYE_WIDEN_PERMIL / PM_PERMIL
1467 let lx1: i64 = fr[FF_R_LX] + lw_ + lw_ * PM_EYE_WIDEN_PERMIL / PM_PERMIL
1468 let ly0: i64 = fr[FF_R_LY] + fr[FF_R_LH] * PM_EYE_BAND_TOP_PERMIL / PM_PERMIL
1469 let ly1: i64 = fr[FF_R_LY] + fr[FF_R_LH] * PM_EYE_BAND_BOT_PERMIL / PM_PERMIL
1470 res[PM_R_LX0] = lx0; res[PM_R_LY0] = ly0; res[PM_R_LX1] = lx1; res[PM_R_LY1] = ly1
1471 res[PM_R_EYE_LWX0] = rcx; res[PM_R_EYE_LWX1] = res[PM_R_FX1]
1472 pm_eye_fissure(rgb, w, h, fr[FF_R_LX], ly0, fr[FF_R_LX] + lw_, ly1, rcx, fr[FF_R_LY], res[PM_R_FX1], fr[FF_R_LY] + fr[FF_R_LH], fr[FF_R_LH] / 2, e5)
1473 res[PM_R_EYE_PIX_L] = e5[PM_FIS_COUNT]
1474 res[PM_R_LMED_X] = e5[PM_FIS_LX]
1475 res[PM_R_LMED_Y] = e5[PM_FIS_LY]
1476 res[PM_R_LLAT_X] = e5[PM_FIS_RX]
1477 res[PM_R_LLAT_Y] = e5[PM_FIS_RY]
1478 if e5[PM_FIS_PIN_L] == 1 { pinned = pinned + PM_PIN_LMED }
1479 if e5[PM_FIS_PIN_R] == 1 { pinned = pinned + PM_PIN_LLAT }
1480 res[PM_R_PINNED] = pinned
1481 if pinned != 0 { res[PM_R_PAIR_FAIL] = PM_PF_PINNED; return PM_E_NOEYES }
1482 let pf: i64 = pm_pair_check(res)
1483 res[PM_R_PAIR_FAIL] = pf
1484 if pf != PM_PF_NONE { return PM_E_NOEYES }
1485 pm_axes(res)
1486 return pm_mouth(rgb, w, h, res)
1487}
1488// the route chooser: the cascade route when both cascades are loaded and it finds a face; the skin route otherwise
1489func pm_landmarks_auto(rgb: *u8, w: i64, h: i64, res: *i64, face_c: *HaarCascade, eye_c: *HaarCascade) -> i64 {
1490 var have: i64 = 1
1491 if (face_c as i64) == 0 { have = 0 }
1492 if (eye_c as i64) == 0 { have = 0 }
1493 if have == 1 {
1494 let rc: i64 = pm_landmarks_cascade(rgb, w, h, res, face_c, eye_c)
1495 if rc != PM_E_NOFACE && rc != PM_E_NOEYES { return rc }
1496 // A loaded cascade can correctly reject synthetic or stylized eye texture while the skin route can still measure it.
1497 // Preserve the route in PM_R_ROUTE; the caller can distinguish cascade success from documented fallback.
1498 }
1499 return pm_landmarks(rgb, w, h, res)
1500}
1501// DECODE a JPEG and landmark it (skin route)
1502func pm_landmarks_file(path: *u8, res: *i64) -> i64 {
1503 return pm_landmarks_file_auto(path, res, 0 as *HaarCascade, 0 as *HaarCascade)
1504}
1505// expand a decoded PNG plane to interleaved RGB: a 3-channel plane passes through untouched, gray and gray+alpha are
1506// replicated, RGBA drops its alpha; a channel count the ruler does not read returns 0 and the caller names it a decode
1507// refusal rather than measuring garbage
1508func pm_png_rgb(px: *u8, w: i64, h: i64, ch: i64) -> *u8 {
1509 if ch == PM_CH_RGB { return px }
1510 if ch < PM_CH_GRAY { return 0 as *u8 }
1511 if ch > PM_CH_RGBA { return 0 as *u8 }
1512 let n: i64 = w * h
1513 let rgb: *u8 = sys_mmap(n * PM_RGB_BPP)
1514 var i: i64 = 0
1515 while i < n {
1516 let s: i64 = i * ch
1517 let d: i64 = i * PM_RGB_BPP
1518 if ch == PM_CH_RGBA {
1519 rgb[d] = px[s]; rgb[d + 1] = px[s + 1]; rgb[d + 2] = px[s + 2]
1520 } else {
1521 rgb[d] = px[s]; rgb[d + 1] = px[s]; rgb[d + 2] = px[s]
1522 }
1523 i = i + 1
1524 }
1525 return rgb
1526}
1527// DECODE a JPEG or a PNG (chosen by the file's first signature byte) and landmark it through the route chooser; the
1528// codec taken lands in PM_R_CODEC after the landmarker has zeroed and filled the result
1529// DECODE a JPEG or a PNG (chosen by the file's first signature byte) to interleaved RGB; out3: width, height, codec. Returns PM_OK,
1530// PM_E_READ or PM_E_DECODE; the pixels land in pix[0]
1531func pm_decode_file(path: *u8, pix: *i64, out3: *i64) -> i64 {
1532 let fl: *i64 = sys_mmap(16) as *i64
1533 fl[0] = 0
1534 let raw: *u8 = sys_read_file(path, fl)
1535 if (raw as i64) == 0 { return PM_E_READ }
1536 if fl[0] <= 0 { return PM_E_READ }
1537 if (raw[0] as i64) == PM_PNG_SIG0 {
1538 let pr: *NxPngResult = nx_png_decode(raw, fl[0])
1539 if (pr as i64) == 0 { return PM_E_DECODE }
1540 if pr.error_code != 0 { return PM_E_DECODE }
1541 let hdr: *NxPngHeader = pr.header
1542 if (hdr as i64) == 0 { return PM_E_DECODE }
1543 if hdr.bit_depth != PM_PNG_DEPTH { return PM_E_DECODE }
1544 let w: i64 = hdr.width
1545 let h: i64 = hdr.height
1546 if w <= 0 { return PM_E_DECODE }
1547 if h <= 0 { return PM_E_DECODE }
1548 if pr.pixels_size < w * h * pr.n_channels { return PM_E_DECODE }
1549 let prgb: *u8 = pm_png_rgb(pr.pixels, w, h, pr.n_channels)
1550 if (prgb as i64) == 0 { return PM_E_DECODE }
1551 pix[0] = prgb as i64
1552 out3[0] = w
1553 out3[1] = h
1554 out3[2] = PM_CODEC_PNG
1555 return PM_OK
1556 }
1557 let orgb: *i64 = sys_mmap(8) as *i64
1558 let ow: *i64 = sys_mmap(8) as *i64
1559 let oh: *i64 = sys_mmap(8) as *i64
1560 let rc: i64 = nx_jpeg_decode_rgb(raw, fl[0], orgb, ow, oh)
1561 if rc != NX_JPEG_ASCII_OK { return PM_E_DECODE }
1562 pix[0] = orgb[0]
1563 out3[0] = ow[0]
1564 out3[1] = oh[0]
1565 out3[2] = PM_CODEC_JPEG
1566 return PM_OK
1567}
1568func pm_landmarks_file_auto(path: *u8, res: *i64, face_c: *HaarCascade, eye_c: *HaarCascade) -> i64 {
1569 let pix: *i64 = sys_mmap(PM_I64) as *i64
1570 let d3: *i64 = sys_mmap(3 * PM_I64) as *i64
1571 let dc: i64 = pm_decode_file(path, pix, d3)
1572 if dc != PM_OK { return dc }
1573 let r: i64 = pm_landmarks_auto(pix[0] as *u8, d3[0], d3[1], res, face_c, eye_c)
1574 res[PM_R_CODEC] = d3[2]
1575 return r
1576}
1577// THE MOUTH ANCHORED ON GIVEN CANTHI (AT44): the mouth ruler run with four canthi supplied from outside (an oracle's, a render's own
1578// truth) in place of the eye ruler's, so the mouth is judged on every subject whether or not the eye pair passed -- the mouth's
1579// accuracy and the eye ruler's coverage are two measurements, and one refusal must not hide the other. canthi8: RLAT x y, RMED x y,
1580// LMED x y, LLAT x y in pixels; res receives the mouth slots
1581func pm_mouth_anchored(rgb: *u8, w: i64, h: i64, canthi8: *i64, res: *i64) -> i64 {
1582 var k: i64 = 0
1583 while k < PM_R_N { res[k] = 0; k = k + 1 }
1584 k = 0
1585 while k < PM_CANTHI_XY { res[PM_R_RLAT_X + k] = canthi8[k]; k = k + 1 }
1586 res[PM_R_W] = w
1587 res[PM_R_H] = h
1588 return pm_mouth(rgb, w, h, res)
1589}
1590// ---- the referee (AT31): the oracle rows against the ruler ----
1591// parse a signed integer inside buf[s..e), stopping at the first byte that is not a digit; out[0] = value; returns 1 when read
1592func pm_span_int(buf: *u8, s: i64, e: i64, out: *i64) -> i64 {
1593 var p: i64 = s
1594 var neg: i64 = 0
1595 if p < e { if (buf[p] as i64) == PM_MINUS { neg = 1; p = p + 1 } }
1596 var v: i64 = 0
1597 var any: i64 = 0
1598 var go: i64 = 1
1599 while go == 1 {
1600 if p >= e { go = 0 } else {
1601 let c: i64 = (buf[p] as i64) & PM_BYTE
1602 if c >= PM_D0 { if c <= PM_D9 { v = v * PM_TEN + (c - PM_D0); any = 1; p = p + 1 } else { go = 0 } } else { go = 0 }
1603 }
1604 }
1605 if any == 0 { return 0 }
1606 if neg == 1 { v = 0 - v }
1607 out[0] = v
1608 out[1] = p
1609 return 1
1610}
1611// the bounds of pipe field idx of the row buf[ls..le); out[0] = start, out[1] = end; returns 1 when the field exists
1612func pm_field(buf: *u8, ls: i64, le: i64, idx: i64, out: *i64) -> i64 {
1613 var p: i64 = ls
1614 var f: i64 = 0
1615 var fs: i64 = ls
1616 while p <= le {
1617 var endf: i64 = 0
1618 if p == le { endf = 1 } else { if (buf[p] as i64) == PM_PIPE { endf = 1 } }
1619 if endf == 1 {
1620 if f == idx { out[0] = fs; out[1] = p; return 1 }
1621 f = f + 1
1622 fs = p + 1
1623 }
1624 p = p + 1
1625 }
1626 return 0
1627}
1628// an "x,y" field in oracle units (pixels x10) into pixels; out[0] = x, out[1] = y; returns 1 when both read
1629func pm_field_xy(buf: *u8, ls: i64, le: i64, idx: i64, out: *i64) -> i64 {
1630 let fb: *i64 = sys_mmap(2 * PM_I64) as *i64
1631 if pm_field(buf, ls, le, idx, fb) == 0 { return 0 }
1632 let t: *i64 = sys_mmap(2 * PM_I64) as *i64
1633 if pm_span_int(buf, fb[0], fb[1], t) == 0 { return 0 }
1634 let x10: i64 = t[0]
1635 if t[1] >= fb[1] { return 0 }
1636 if (buf[t[1]] as i64) != PM_COMMA { return 0 }
1637 if pm_span_int(buf, t[1] + 1, fb[1], t) == 0 { return 0 }
1638 out[0] = x10 / PM_ORACLE_X10
1639 out[1] = t[0] / PM_ORACLE_X10
1640 return 1
1641}
1642func pm_field_int(buf: *u8, ls: i64, le: i64, idx: i64) -> i64 {
1643 let fb: *i64 = sys_mmap(2 * PM_I64) as *i64
1644 if pm_field(buf, ls, le, idx, fb) == 0 { return 0 - 1 }
1645 let t: *i64 = sys_mmap(2 * PM_I64) as *i64
1646 if pm_span_int(buf, fb[0], fb[1], t) == 0 { return 0 - 1 }
1647 return t[0]
1648}
1649func pm_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
1650func pm_putn(v: i64) -> i64 {
1651 let b: *u8 = sys_mmap(32)
1652 var m: i64 = v
1653 var neg: i64 = 0
1654 if m < 0 { neg = 1; m = 0 - m }
1655 var k: i64 = 0
1656 if m == 0 { b[0] = PM_D0 as u8; k = 1 }
1657 while m > 0 { b[k] = (PM_D0 + (m % PM_TEN)) as u8; m = m / PM_TEN; k = k + 1 }
1658 let o: *u8 = sys_mmap(34)
1659 var j: i64 = 0
1660 if neg == 1 { o[0] = PM_MINUS as u8; j = 1 }
1661 var i: i64 = 0
1662 while i < k { o[j + i] = b[k - 1 - i]; i = i + 1 }
1663 sys_write(1, o, j + k)
1664 return 0
1665}
1666func pm_kv(k: *u8, v: i64) -> i64 { pm_puts(k); pm_putn(v); return 0 }
1667func pm_putspan(buf: *u8, s: i64, e: i64) -> i64 { if e > s { sys_write(1, (buf as i64 + s) as *u8, e - s) } return 0 }
1668// one canthus error as permil of the oracle's outer-canthal span
1669func pm_nme(ox: i64, oy: i64, mx: i64, my: i64, exo: i64) -> i64 { return pm_dist(ox, oy, mx, my) * PM_PERMIL / pm_max(exo, 1) }
1670// THE LIP SCORE of one mouth record against one oracle row (AT44): per landmark the NME in permil of the oracle's outer-canthal span --
1671// labrale superius, the stomion (the midpoint of the oracle's two inner lip lines: the seam of a closed mouth, the middle of a parted
1672// one), labrale inferius, both commissures -- printed under the given prefix with the ratio pair and a verdict. lacc accumulates the
1673// summed mean NME [0], the summed ratio error [1], found [2], placed-off [3], unmeasured [4]; lout[0] the worst NME. The oracle's
1674// lip points are read into the six scratch pairs. Returns the verdict (PM_LIPV_*)
1675func pm_lip_score(buf: *u8, ls: i64, le: i64, res: *i64, exo: i64, prefix: *u8, lacc: *i64, lout: *i64, olt: *i64, oui: *i64, oli: *i64, olb: *i64, omr: *i64, oml: *i64) -> i64 {
1676 if res[PM_R_MOUTH] != 1 {
1677 lacc[PM_LA_UNMEASURED] = lacc[PM_LA_UNMEASURED] + 1
1678 pm_puts(" " as *u8); pm_puts(prefix); pm_puts("lip_verdict=UNMEASURED " as *u8); pm_puts(pm_mouth_why_name(res[PM_R_MOUTH_WHY]))
1679 return PM_LIPV_UNMEASURED
1680 }
1681 pm_field_xy(buf, ls, le, PM_OF_ULIP_TOP, olt)
1682 pm_field_xy(buf, ls, le, PM_OF_ULIP_IN, oui)
1683 pm_field_xy(buf, ls, le, PM_OF_LLIP_IN, oli)
1684 pm_field_xy(buf, ls, le, PM_OF_LLIP_BOT, olb)
1685 pm_field_xy(buf, ls, le, PM_OF_MOUTH_R, omr)
1686 pm_field_xy(buf, ls, le, PM_OF_MOUTH_L, oml)
1687 let l1: i64 = pm_nme(olt[0], olt[1], res[PM_R_LS_X], res[PM_R_LS_Y], exo)
1688 let l2: i64 = pm_nme((oui[0] + oli[0]) / 2, (oui[1] + oli[1]) / 2, res[PM_R_STO_X], res[PM_R_STO_Y], exo)
1689 let l3: i64 = pm_nme(olb[0], olb[1], res[PM_R_LI_X], res[PM_R_LI_Y], exo)
1690 let l4: i64 = pm_nme(omr[0], omr[1], res[PM_R_CHR_X], res[PM_R_CHR_Y], exo)
1691 let l5: i64 = pm_nme(oml[0], oml[1], res[PM_R_CHL_X], res[PM_R_CHL_Y], exo)
1692 let lnmax: i64 = pm_max(pm_max(pm_max(l1, l2), pm_max(l3, l4)), l5)
1693 let lnmean: i64 = (l1 + l2 + l3 + l4 + l5) / 5
1694 let olip: i64 = pm_field_int(buf, ls, le, PM_OF_LIP)
1695 lout[0] = lnmax
1696 lacc[PM_LA_NME_SUM] = lacc[PM_LA_NME_SUM] + lnmean
1697 lacc[PM_LA_RATIO_ERR_SUM] = lacc[PM_LA_RATIO_ERR_SUM] + pm_abs(res[PM_R_LIP] - olip)
1698 pm_puts(" " as *u8); pm_puts(prefix); pm_kv("lnme_LS=" as *u8, l1); pm_kv(" lnme_STO=" as *u8, l2); pm_kv(" lnme_LI=" as *u8, l3); pm_kv(" lnme_CHR=" as *u8, l4); pm_kv(" lnme_CHL=" as *u8, l5)
1699 pm_kv(" lnme_mean=" as *u8, lnmean); pm_kv(" lnme_max=" as *u8, lnmax); pm_kv(" lip=" as *u8, res[PM_R_LIP]); pm_kv("/" as *u8, olip)
1700 pm_puts(" " as *u8); pm_puts(prefix)
1701 if lnmax <= PM_NME_FOUND_PERMIL { lacc[PM_LA_FOUND] = lacc[PM_LA_FOUND] + 1; pm_puts("lip_verdict=FOUND" as *u8); return PM_LIPV_FOUND }
1702 lacc[PM_LA_OFF] = lacc[PM_LA_OFF] + 1
1703 pm_puts("lip_verdict=PLACED-OFF" as *u8)
1704 return PM_LIPV_OFF
1705}
1706// THE REFEREE: every oracle row against the ruler; prints one REFEREE line per row and a REFEREE-SUMMARY whose partitions sum;
1707// returns the number of rows judged, or PM_E_READ when the oracle file is unreadable. sum[PM_RS_*] carries the same partitions and
1708// the last judged row's NME back to the caller. Three measurements per still, each with its own partition of the judged rows: the
1709// four canthi; the mouth as the ruler places it (on its own eyes, so it exists only where the eye pair passed); and the mouth
1710// ANCHORED on the oracle's canthi (pm_mouth_anchored) -- the mouth ruler judged on EVERY still, whatever the eye ruler did.
1711func pm_referee_sum(oracle_path: *u8, dir: *u8, face_c: *HaarCascade, eye_c: *HaarCascade, sum: *i64) -> i64 {
1712 var sz: i64 = 0
1713 while sz < PM_RS_N { sum[sz] = 0; sz = sz + 1 }
1714 var last_nmax: i64 = 0
1715 var last_nmean: i64 = 0
1716 let fl: *i64 = sys_mmap(16) as *i64
1717 fl[0] = 0
1718 let buf: *u8 = sys_read_file(oracle_path, fl)
1719 if (buf as i64) == 0 { return PM_E_READ }
1720 let n: i64 = fl[0]
1721 if n <= 0 { return PM_E_READ }
1722 let path: *u8 = sys_mmap(PM_PATHB)
1723 let res: *i64 = sys_mmap(PM_R_N * PM_I64) as *i64
1724 let ares: *i64 = sys_mmap(PM_R_N * PM_I64) as *i64
1725 let fb: *i64 = sys_mmap(2 * PM_I64) as *i64
1726 let orl: *i64 = sys_mmap(2 * PM_I64) as *i64
1727 let orm: *i64 = sys_mmap(2 * PM_I64) as *i64
1728 let olm: *i64 = sys_mmap(2 * PM_I64) as *i64
1729 let oll: *i64 = sys_mmap(2 * PM_I64) as *i64
1730 let oref: *i64 = sys_mmap(PM_R_N * PM_I64) as *i64
1731 let olt: *i64 = sys_mmap(2 * PM_I64) as *i64
1732 let oui: *i64 = sys_mmap(2 * PM_I64) as *i64
1733 let oli: *i64 = sys_mmap(2 * PM_I64) as *i64
1734 let olb: *i64 = sys_mmap(2 * PM_I64) as *i64
1735 let omr: *i64 = sys_mmap(2 * PM_I64) as *i64
1736 let oml: *i64 = sys_mmap(2 * PM_I64) as *i64
1737 let lacc: *i64 = sys_mmap(PM_LA_N * PM_I64) as *i64
1738 let aacc: *i64 = sys_mmap(PM_LA_N * PM_I64) as *i64
1739 let lout: *i64 = sys_mmap(PM_I64) as *i64
1740 let pix: *i64 = sys_mmap(PM_I64) as *i64
1741 let d3: *i64 = sys_mmap(3 * PM_I64) as *i64
1742 let c8: *i64 = sys_mmap(PM_CANTHI_XY * PM_I64) as *i64
1743 var rows: i64 = 0
1744 var judged: i64 = 0
1745 var unjudged: i64 = 0
1746 var found: i64 = 0
1747 var placed_off: i64 = 0
1748 var unmeasured: i64 = 0
1749 var unreadable: i64 = 0
1750 var nme_sum_found: i64 = 0
1751 var tilt_hits: i64 = 0
1752 var measured: i64 = 0
1753 var s_rlat: i64 = 0
1754 var s_rmed: i64 = 0
1755 var s_lmed: i64 = 0
1756 var s_llat: i64 = 0
1757 var last_lnmax: i64 = 0
1758 var last_lratio: i64 = 0
1759 var last_anmax: i64 = 0
1760 var ls: i64 = 0
1761 while ls < n {
1762 var le: i64 = ls
1763 while le < n { if (buf[le] as i64) == PM_NL { le = n + le } else { le = le + 1 } }
1764 if le > n { le = le - n }
1765 var isrow: i64 = 0
1766 if le > ls { if (buf[ls] as i64) != PM_HASH { if pm_field(buf, ls, le, PM_OF_FILE, fb) == 1 { isrow = 1 } } }
1767 if isrow == 1 {
1768 rows = rows + 1
1769 let faces: i64 = pm_field_int(buf, ls, le, PM_OF_FACES)
1770 pm_puts("REFEREE file=" as *u8)
1771 pm_putspan(buf, fb[0], fb[1])
1772 if faces != 1 { unjudged = unjudged + 1; pm_puts(" verdict=UNJUDGED (the oracle placed no face)\n" as *u8) } else {
1773 judged = judged + 1
1774 // the path: dir + file
1775 var o: i64 = 0
1776 var i: i64 = 0
1777 while dir[i] != (0 as u8) { path[o] = dir[i]; o = o + 1; i = i + 1 }
1778 i = fb[0]
1779 while i < fb[1] { path[o] = buf[i]; o = o + 1; i = i + 1 }
1780 path[o] = 0 as u8
1781 let rc: i64 = pm_landmarks_file_auto(path, res, face_c, eye_c)
1782 pm_field_xy(buf, ls, le, PM_OF_RLAT, orl)
1783 pm_field_xy(buf, ls, le, PM_OF_RMED, orm)
1784 pm_field_xy(buf, ls, le, PM_OF_LMED, olm)
1785 pm_field_xy(buf, ls, le, PM_OF_LLAT, oll)
1786 let exo: i64 = pm_field_int(buf, ls, le, PM_OF_EXO)
1787 // the oracle's own axes through the same arithmetic, so tilts compare roll-corrected to roll-corrected
1788 oref[PM_R_RLAT_X] = orl[0]; oref[PM_R_RLAT_Y] = orl[1]; oref[PM_R_RMED_X] = orm[0]; oref[PM_R_RMED_Y] = orm[1]
1789 oref[PM_R_LMED_X] = olm[0]; oref[PM_R_LMED_Y] = olm[1]; oref[PM_R_LLAT_X] = oll[0]; oref[PM_R_LLAT_Y] = oll[1]
1790 pm_axes(oref)
1791 pm_kv(" route=" as *u8, res[PM_R_ROUTE]); pm_kv(" face=" as *u8, res[PM_R_FACE]); pm_kv(" eyes=" as *u8, res[PM_R_EYES]); pm_kv(" pair_fail=" as *u8, res[PM_R_PAIR_FAIL])
1792 if rc == PM_E_READ { unreadable = unreadable + 1; pm_puts(" verdict=UNREADABLE\n" as *u8) } else {
1793 if rc == PM_E_DECODE { unreadable = unreadable + 1; pm_puts(" verdict=UNREADABLE (decode)\n" as *u8) } else {
1794 // the mouth as the ruler places it: only where its eye pair passed
1795 if res[PM_R_EYES] == 2 {
1796 pm_lip_score(buf, ls, le, res, exo, "" as *u8, lacc, lout, olt, oui, oli, olb, omr, oml)
1797 if res[PM_R_MOUTH] == 1 { last_lnmax = lout[0]; last_lratio = res[PM_R_LIP] }
1798 } else { lacc[PM_LA_UNMEASURED] = lacc[PM_LA_UNMEASURED] + 1; pm_puts(" lip_verdict=UNMEASURED eyes-refused" as *u8) }
1799 // the mouth anchored on the oracle's canthi: judged on every readable still
1800 c8[0] = orl[0]; c8[1] = orl[1]; c8[2] = orm[0]; c8[3] = orm[1]; c8[4] = olm[0]; c8[5] = olm[1]; c8[6] = oll[0]; c8[7] = oll[1]
1801 if pm_decode_file(path, pix, d3) == PM_OK {
1802 pm_mouth_anchored(pix[0] as *u8, d3[0], d3[1], c8, ares)
1803 pm_lip_score(buf, ls, le, ares, exo, "anch_" as *u8, aacc, lout, olt, oui, oli, olb, omr, oml)
1804 if ares[PM_R_MOUTH] == 1 { last_anmax = lout[0] }
1805 } else { aacc[PM_LA_UNMEASURED] = aacc[PM_LA_UNMEASURED] + 1; pm_puts(" anch_lip_verdict=UNMEASURED undecodable" as *u8) }
1806 if res[PM_R_EYES] != 2 { unmeasured = unmeasured + 1; pm_puts(" verdict=UNMEASURED\n" as *u8) } else {
1807 let n1: i64 = pm_nme(orl[0], orl[1], res[PM_R_RLAT_X], res[PM_R_RLAT_Y], exo)
1808 let n2: i64 = pm_nme(orm[0], orm[1], res[PM_R_RMED_X], res[PM_R_RMED_Y], exo)
1809 let n3: i64 = pm_nme(olm[0], olm[1], res[PM_R_LMED_X], res[PM_R_LMED_Y], exo)
1810 let n4: i64 = pm_nme(oll[0], oll[1], res[PM_R_LLAT_X], res[PM_R_LLAT_Y], exo)
1811 let nmax: i64 = pm_max(pm_max(n1, n2), pm_max(n3, n4))
1812 let nmean: i64 = (n1 + n2 + n3 + n4) / 4
1813 last_nmax = nmax
1814 last_nmean = nmean
1815 measured = measured + 1
1816 s_rlat = s_rlat + n1; s_rmed = s_rmed + n2; s_lmed = s_lmed + n3; s_llat = s_llat + n4
1817 pm_kv(" nme_R_lat=" as *u8, n1); pm_kv(" nme_R_med=" as *u8, n2); pm_kv(" nme_L_med=" as *u8, n3); pm_kv(" nme_L_lat=" as *u8, n4); pm_kv(" nme_mean=" as *u8, nmean); pm_kv(" nme_max=" as *u8, nmax)
1818 pm_kv(" tilt_R=" as *u8, res[PM_R_TILT_R]); pm_kv("/" as *u8, oref[PM_R_TILT_R]); pm_kv(" tilt_L=" as *u8, res[PM_R_TILT_L]); pm_kv("/" as *u8, oref[PM_R_TILT_L]); pm_kv(" roll=" as *u8, res[PM_R_ROLL]); pm_kv("/" as *u8, oref[PM_R_ROLL])
1819 pm_kv(" ici=" as *u8, res[PM_R_ICI]); pm_kv("/" as *u8, oref[PM_R_ICI]); pm_kv(" icf=" as *u8, res[PM_R_ICF]); pm_kv("/" as *u8, oref[PM_R_ICF])
1820 pm_kv(" eye_votes=" as *u8, res[PM_R_EYE_VOTES_R]); pm_kv("," as *u8, res[PM_R_EYE_VOTES_L])
1821 if nmax <= PM_NME_FOUND_PERMIL {
1822 found = found + 1
1823 nme_sum_found = nme_sum_found + nmean
1824 if pm_abs(res[PM_R_TILT_R] - oref[PM_R_TILT_R]) <= PM_TILT_TOL_DEG10 { if pm_abs(res[PM_R_TILT_L] - oref[PM_R_TILT_L]) <= PM_TILT_TOL_DEG10 { tilt_hits = tilt_hits + 1 } }
1825 pm_puts(" verdict=FOUND\n" as *u8)
1826 } else { placed_off = placed_off + 1; pm_puts(" verdict=PLACED-OFF\n" as *u8) }
1827 }
1828 } }
1829 }
1830 }
1831 ls = le + 1
1832 }
1833 let lmeas: i64 = lacc[PM_LA_FOUND] + lacc[PM_LA_OFF]
1834 let ameas: i64 = aacc[PM_LA_FOUND] + aacc[PM_LA_OFF]
1835 pm_kv("REFEREE-SUMMARY rows=" as *u8, rows); pm_kv(" judged=" as *u8, judged); pm_kv(" unjudged=" as *u8, unjudged)
1836 pm_kv(" found=" as *u8, found); pm_kv(" placed_off=" as *u8, placed_off); pm_kv(" unmeasured=" as *u8, unmeasured); pm_kv(" unreadable=" as *u8, unreadable)
1837 pm_kv(" partition_sum=" as *u8, found + placed_off + unmeasured + unreadable); pm_kv(" of_judged=" as *u8, judged)
1838 pm_kv(" mean_nme_found=" as *u8, nme_sum_found / pm_max(found, 1)); pm_kv(" tilt_within_tol_of_found=" as *u8, tilt_hits); pm_kv(" found_bar_permil=" as *u8, PM_NME_FOUND_PERMIL); pm_kv(" tilt_tol_deg10=" as *u8, PM_TILT_TOL_DEG10)
1839 let mdiv: i64 = pm_max(measured, 1)
1840 pm_kv(" measured=" as *u8, measured); pm_kv(" mean_nme_measured_R_lat=" as *u8, s_rlat / mdiv); pm_kv(" R_med=" as *u8, s_rmed / mdiv); pm_kv(" L_med=" as *u8, s_lmed / mdiv); pm_kv(" L_lat=" as *u8, s_llat / mdiv)
1841 pm_kv(" lip_found=" as *u8, lacc[PM_LA_FOUND]); pm_kv(" lip_placed_off=" as *u8, lacc[PM_LA_OFF]); pm_kv(" lip_unmeasured=" as *u8, lacc[PM_LA_UNMEASURED])
1842 pm_kv(" lip_partition_sum=" as *u8, lmeas + lacc[PM_LA_UNMEASURED] + unreadable); pm_kv(" lip_measured=" as *u8, lmeas)
1843 pm_kv(" lip_mean_nme_measured=" as *u8, lacc[PM_LA_NME_SUM] / pm_max(lmeas, 1)); pm_kv(" lip_ratio_abs_err_mean_permil=" as *u8, lacc[PM_LA_RATIO_ERR_SUM] / pm_max(lmeas, 1))
1844 pm_kv(" anchored_lip_found=" as *u8, aacc[PM_LA_FOUND]); pm_kv(" anchored_lip_placed_off=" as *u8, aacc[PM_LA_OFF]); pm_kv(" anchored_lip_unmeasured=" as *u8, aacc[PM_LA_UNMEASURED])
1845 pm_kv(" anchored_lip_partition_sum=" as *u8, ameas + aacc[PM_LA_UNMEASURED] + unreadable)
1846 pm_kv(" anchored_lip_mean_nme=" as *u8, aacc[PM_LA_NME_SUM] / pm_max(ameas, 1)); pm_kv(" anchored_lip_ratio_abs_err_mean_permil=" as *u8, aacc[PM_LA_RATIO_ERR_SUM] / pm_max(ameas, 1))
1847 pm_puts(" verdict=MEASURED\n" as *u8)
1848 sum[PM_RS_ROWS] = rows; sum[PM_RS_JUDGED] = judged; sum[PM_RS_UNJUDGED] = unjudged
1849 sum[PM_RS_FOUND] = found; sum[PM_RS_PLACED_OFF] = placed_off; sum[PM_RS_UNMEASURED] = unmeasured; sum[PM_RS_UNREADABLE] = unreadable
1850 sum[PM_RS_NME_MEAN_FOUND] = nme_sum_found / pm_max(found, 1); sum[PM_RS_TILT_HITS] = tilt_hits
1851 sum[PM_RS_LAST_NME_MAX] = last_nmax; sum[PM_RS_LAST_NME_MEAN] = last_nmean
1852 sum[PM_RS_LIP_FOUND] = lacc[PM_LA_FOUND]; sum[PM_RS_LIP_PLACED_OFF] = lacc[PM_LA_OFF]; sum[PM_RS_LIP_UNMEASURED] = lacc[PM_LA_UNMEASURED]
1853 sum[PM_RS_LIP_NME_MEAN_MEASURED] = lacc[PM_LA_NME_SUM] / pm_max(lmeas, 1); sum[PM_RS_LAST_LIP_NME_MAX] = last_lnmax
1854 sum[PM_RS_NME_RLAT_MEASURED] = s_rlat / mdiv; sum[PM_RS_NME_RMED_MEASURED] = s_rmed / mdiv
1855 sum[PM_RS_NME_LMED_MEASURED] = s_lmed / mdiv; sum[PM_RS_NME_LLAT_MEASURED] = s_llat / mdiv
1856 sum[PM_RS_MEASURED] = measured; sum[PM_RS_LIP_MEASURED] = lmeas
1857 sum[PM_RS_LIP_RATIO_ERR_MEAN] = lacc[PM_LA_RATIO_ERR_SUM] / pm_max(lmeas, 1); sum[PM_RS_LAST_LIP_RATIO] = last_lratio
1858 sum[PM_RS_ANCH_FOUND] = aacc[PM_LA_FOUND]; sum[PM_RS_ANCH_OFF] = aacc[PM_LA_OFF]; sum[PM_RS_ANCH_UNMEASURED] = aacc[PM_LA_UNMEASURED]
1859 sum[PM_RS_ANCH_NME_MEAN] = aacc[PM_LA_NME_SUM] / pm_max(ameas, 1); sum[PM_RS_ANCH_RATIO_ERR] = aacc[PM_LA_RATIO_ERR_SUM] / pm_max(ameas, 1)
1860 sum[PM_RS_LAST_ANCH_NME_MAX] = last_anmax
1861 return judged
1862}
1863// the print-only entry every existing caller uses
1864func pm_referee(oracle_path: *u8, dir: *u8, face_c: *HaarCascade, eye_c: *HaarCascade) -> i64 {
1865 let sum: *i64 = sys_mmap(PM_RS_N * PM_I64) as *i64
1866 return pm_referee_sum(oracle_path, dir, face_c, eye_c, sum)
1867}
1868// per-side and centre axis rows: axis|<section>|<id>|<side>|<value>|measured|<refkey>|<when>
1869func pm_row(out: *u8, section: *u8, id: *u8, side: *u8, value: i64, refkey: *u8, when: *u8) -> i64 {
1870 var o: i64 = ri_cat(out, 0, "axis|" as *u8)
1871 o = ri_cat(out, o, section)
1872 out[o] = RI_PIPE as u8; o = o + 1
1873 o = ri_cat(out, o, id)
1874 out[o] = RI_PIPE as u8; o = o + 1
1875 o = ri_cat(out, o, side)
1876 out[o] = RI_PIPE as u8; o = o + 1
1877 o = ri_catn(out, o, value)
1878 out[o] = RI_PIPE as u8; o = o + 1
1879 o = ri_cat(out, o, PM_METHOD)
1880 out[o] = RI_PIPE as u8; o = o + 1
1881 o = ri_cat(out, o, refkey)
1882 out[o] = RI_PIPE as u8; o = o + 1
1883 o = ri_cat(out, o, when)
1884 out[o] = RI_NL as u8; o = o + 1
1885 out[o] = 0 as u8
1886 return o
1887}
1888func pm_row_exists(card: *u8, n: i64, section: *u8, id: *u8, side: *u8) -> i64 {
1889 let key: *u8 = sys_mmap(RI_ROWB)
1890 let pos: *i64 = sys_mmap(TC_POS_N * RI_I64) as *i64
1891 tc_key3(key, "axis" as *u8, section, id, side)
1892 return tc_row_find(card, n, key, pos)
1893}
1894// WRITE the measured rows a landmarked photograph supports onto the card: canthal_tilt L and R (eyes), intercanthal_index,
1895// intercanthal_eq_fissure and, when the mouth was seen, upper_over_lower_lip (face). Refuses when any of those rows exists.
1896// Returns rows appended, PM_E_EXISTS, or PM_E_READ when the card is unreadable
1897func pm_card_write(slug: *u8, refkey: *u8, when: *u8, res: *i64) -> i64 {
1898 let path: *u8 = sys_mmap(RI_PATHB)
1899 tc_card_path(slug, path)
1900 let cl: *i64 = sys_mmap(16) as *i64
1901 let card: *u8 = ri_load(path, cl)
1902 if cl[0] <= 0 { return PM_E_READ }
1903 var exists: i64 = 0
1904 // a tilt row is written only when its own error bar is within PM_TILT_ERR_MAX_DEG10; the ratios always
1905 if res[PM_R_TILT_OK] == 1 {
1906 exists = exists + pm_row_exists(card, cl[0], "eyes" as *u8, "canthal_tilt" as *u8, PM_SIDE_L)
1907 exists = exists + pm_row_exists(card, cl[0], "eyes" as *u8, "canthal_tilt" as *u8, PM_SIDE_R)
1908 }
1909 exists = exists + pm_row_exists(card, cl[0], "face" as *u8, "intercanthal_index" as *u8, PM_SIDE_C)
1910 exists = exists + pm_row_exists(card, cl[0], "face" as *u8, "intercanthal_eq_fissure" as *u8, PM_SIDE_C)
1911 if res[PM_R_MOUTH] == 1 { exists = exists + pm_row_exists(card, cl[0], "face" as *u8, "upper_over_lower_lip" as *u8, PM_SIDE_C) }
1912 if exists > 0 { return PM_E_EXISTS }
1913 let row: *u8 = sys_mmap(RI_ROWB)
1914 let fd: i64 = sys_openat_append(path, RI_MODE644)
1915 if fd < 0 { return PM_E_READ }
1916 var n: i64 = 0
1917 var o: i64 = 0
1918 if res[PM_R_TILT_OK] == 1 {
1919 o = pm_row(row, "eyes" as *u8, "canthal_tilt" as *u8, PM_SIDE_L, res[PM_R_TILT_L], refkey, when); sys_write(fd, row, o); n = n + 1
1920 o = pm_row(row, "eyes" as *u8, "canthal_tilt" as *u8, PM_SIDE_R, res[PM_R_TILT_R], refkey, when); sys_write(fd, row, o); n = n + 1
1921 }
1922 o = pm_row(row, "face" as *u8, "intercanthal_index" as *u8, PM_SIDE_C, res[PM_R_ICI], refkey, when); sys_write(fd, row, o); n = n + 1
1923 o = pm_row(row, "face" as *u8, "intercanthal_eq_fissure" as *u8, PM_SIDE_C, res[PM_R_ICF], refkey, when); sys_write(fd, row, o); n = n + 1
1924 if res[PM_R_MOUTH] == 1 { o = pm_row(row, "face" as *u8, "upper_over_lower_lip" as *u8, PM_SIDE_C, res[PM_R_LIP], refkey, when); sys_write(fd, row, o); n = n + 1 }
1925 sys_close(fd)
1926 return n
1927}