nx_likeness_lib.nx source
↩ module page · 1114 lines · 53971 B
1// nx_likeness_lib.nx -- THE IMAGE-LEVEL LIKENESS REFEREE (aesthetictwin AT45, 2026-09-18). The board had no ruler that reads a
2// RENDER against the PHOTOGRAPHS of the person it claims to be, so a render the operator called an ugly ventriloquist dummy was
3// published beside its reference with every gate green. This lib is that ruler, and every layer of the feedback loop above it
4// (fit, morph, texture, hair) is judged by it. It COMPOSES the estate's incumbents and re-implements none of them:
5// nx_photomark_lib decodes a JPEG or PNG through one door (pm_decode_file), places the four canthi and the five mouth
6// landmarks (pm_landmarks_cascade, pm_landmarks), measures the roll-corrected canthal tilts, and parses
7// the OUTSIDE oracle rows (pm_field, pm_span_int) the intake lane already banks per reference still
8// nx_visdesc the grayscale edge-orientation layout descriptor (80 integers) and its L1 distance -- the STRUCTURE axis
9// nx_cosine_similarity the signed Q10 cosine over the same two vectors -- structure read a second way
10// nx_skinsample_lib the skin chroma rule and the linear-light mean converted to CIE L*a*b* -- the TONE axis
11// THE SHAPE OF THE VERDICT. Two images are ALIGNED into one canvas by the similarity that maps each face's two eye centres onto
12// the canvas's two eye centres (the same normalisation on both sides, so a roll or a scale in either still is undone before
13// anything is compared), then SEVEN AXES are measured between the aligned pair and each is published on its own:
14// geom_nme_permil mean landmark distance in the canvas, permil of the canvas eye span (the four canthi, plus the five mouth
15// landmarks when both images carry a mouth); needs both images landmarked by a passing eye pair
16// tilt_delta_deg10 the larger of the two roll-corrected canthal-tilt differences; needs both tilts measured
17// struct_l1 nx_visdesc_l1 between the two aligned crops' descriptors
18// struct_cos_dist NX_COSINE_Q minus the Q10 cosine of the same descriptors (0 = parallel)
19// tone_dl_e3, tone_da_e3, tone_db_e3 the absolute L*, a*, b* differences (x1000) of the skin-masked linear mean of each crop;
20// need skin pixels in both crops
21// THE SAME-PERSON BAND is not a number anyone typed: `lk_band` measures every axis on EVERY PAIR of reference stills of the one
22// person and the band per axis is the WORST (largest) distance any same-person pair reached. A subject passes an axis iff its
23// distance to its NEAREST reference is within that band; the verdict is the count of axes inside, outside and unmeasured, and
24// INSIDE only when no measured axis is outside (the MIN-headline doctrine: no axis may be compensated by another, so there is no
25// weighted sum and no coefficient). The band prints the pair that set it and how many pairs derived it.
26// EVERY NUMBER ANNOUNCES ITS RESOURCE. The canvas side is TWICE THE SMALLEST REFERENCE EYE SPAN, floored to the descriptor's grid
27// (no reference is ever upsampled; a subject smaller than that is announced as upsampled); the eye line sits at 340 permil of the
28// canvas height because on the anchor oracle row (ri_diora_baird_14: brow top 127.8 px, eye line 197.3 px, eye span 103.4 px) the
29// brow top sits 680 permil of the eye span above the eye line, so with a canvas two spans wide the brow top lands on the canvas's
30// top edge and the bottom edge falls 1.32 spans below the eye line -- past the stomion (1.15 spans on the same row) and above the
31// chin (1.65). The smallest canvas is VD_GRID cells of three pixels, the least a Sobel neighbourhood needs.
32// ALIGNMENT SOURCES ARE NAMED, NEVER SILENT. A still with an oracle row (knowledge/refintake/<slug>.oracle, matched by basename)
33// aligns on the oracle's canthi at the oracle's tenth-pixel precision; a subject without one runs the photo ruler's cascade route,
34// then its skin route, and when the skin route REFUSES its eye pair but still holds candidate canthi, those candidates align the
35// crop while the geometry and tilt axes read UNMEASURED (the pair was refused, so its landmarks are not evidence). MEASURED the day
36// this was written: on the rejected render diora_baird_close.png the cascade's face box was the nose tip and no eye pair followed,
37// and the route chooser never reaches the skin route on that refusal -- a subject the ruler cannot align reads UNMEASURED, which is
38// neither INSIDE nor OUTSIDE, so the ladder exists to turn "could not look" into a named, degraded look wherever one is possible.
39// Sides follow the image: R is the image-left eye (the subject's right), as the photo ruler and the oracle rows agree.
40// LIB, no main. license_tier: ORIGINAL No hw writes (Rule 26).
41import "nx_syscalls.nx"
42import "nx_photomark_lib.nx"
43import "nx_visdesc.nx"
44import "nx_cosine_similarity.nx"
45
46const LK_I64: i64 = 8
47const LK_BPP: i64 = 3
48const LK_BYTE: i64 = 255
49const LK_DECI: i64 = 10 // landmarks are carried in tenths of a pixel: the oracle's own unit
50const LK_DECI_HALF: i64 = 5
51const LK_PERMIL: i64 = 1000
52const LK_Q8: i64 = 256 // bilinear weights in 1/256 of a pixel
53const LK_Q8_HALF: i64 = 128
54const LK_NL: i64 = 10
55const LK_CR: i64 = 13
56const LK_SPACE: i64 = 32
57const LK_TAB: i64 = 9
58const LK_HASH: i64 = 35
59const LK_AT: i64 = 64
60const LK_SLASH: i64 = 47
61const LK_PNG_SIG0: i64 = 137
62const LK_JPEG_SIG0: i64 = 255
63const LK_DIRECTIVE_ORACLE: *u8 = "@oracle "
64// the canvas (see the header for the derivation of every one of these)
65const LK_EYE_Y_PERMIL: i64 = 340
66const LK_EYE_Y_BASIS_BROW_PERMIL: i64 = 680
67const LK_EYE_Y_BASIS: *u8 = "ri_diora_baird_14"
68const LK_CANVAS_SPANS: i64 = 2
69const LK_CANVAS_QUARTER: i64 = 4 // the eye centres sit a quarter of the width in from each side: two spans wide
70const LK_SOBEL_CELL: i64 = 3 // the least pixels a descriptor cell needs to hold one interior Sobel neighbourhood
71// the oracle row's tilt columns (the header's own order after the brow point: tilt_R_deg10, tilt_L_deg10, ICI, ICF, lip, exo, bbox)
72const LK_OF_TILT_R: i64 = 21
73const LK_OF_TILT_L: i64 = 22
74// alignment sources
75const LK_AL_NONE: i64 = 0
76const LK_AL_ORACLE: i64 = 1
77const LK_AL_CASCADE: i64 = 2
78const LK_AL_SKIN: i64 = 3
79const LK_AL_CANDIDATES: i64 = 4
80// load statuses
81const LK_OK: i64 = 0
82const LK_E_READ: i64 = 0 - 1
83const LK_E_DECODE: i64 = 0 - 2
84const LK_E_UNALIGNED: i64 = 0 - 3
85// the landmark points, in the record's order
86const LK_P_RLAT: i64 = 0
87const LK_P_RMED: i64 = 1
88const LK_P_LMED: i64 = 2
89const LK_P_LLAT: i64 = 3
90const LK_P_LS: i64 = 4
91const LK_P_STO: i64 = 5
92const LK_P_LI: i64 = 6
93const LK_P_CHR: i64 = 7
94const LK_P_CHL: i64 = 8
95const LK_P_CANTHI: i64 = 4
96const LK_P_N: i64 = 9
97// the image record
98const LK_I_RGB: i64 = 0
99const LK_I_W: i64 = 1
100const LK_I_H: i64 = 2
101const LK_I_CODEC: i64 = 3
102const LK_I_ALIGN: i64 = 4
103const LK_I_LM: i64 = 5 // LK_P_N points, x then y, deci-pixels: slots 5..22
104const LK_I_MOUTH: i64 = 23
105const LK_I_TILT_R: i64 = 24
106const LK_I_TILT_L: i64 = 25
107const LK_I_TILT_OK: i64 = 26
108const LK_I_ERX: i64 = 27 // the eye centres (midpoint of each eye's canthi), deci-pixels
109const LK_I_ERY: i64 = 28
110const LK_I_ELX: i64 = 29
111const LK_I_ELY: i64 = 30
112const LK_I_IOD: i64 = 31 // the eye-centre span, deci-pixels
113const LK_I_EYES: i64 = 32 // 1 when the canthi are landmarks (a passing eye pair or an oracle row)
114const LK_I_STATUS: i64 = 33
115const LK_I_ROUTE: i64 = 34
116const LK_I_PAIRFAIL: i64 = 35
117const LK_I_UPSAMPLED: i64 = 36
118const LK_I_N: i64 = 37
119// the feature record of one aligned crop
120const LK_F_CROP: i64 = 0
121const LK_F_GRAY: i64 = 1
122const LK_F_SKIN_N: i64 = 2
123const LK_F_L: i64 = 3
124const LK_F_A: i64 = 4
125const LK_F_B: i64 = 5
126const LK_F_GRID: i64 = 6 // cells per side of the shading and texture grids
127const LK_F_SHADE: i64 = 7 // pointer: grid*grid cell-mean luminances
128const LK_F_TEX: i64 = 8 // pointer: grid*grid cell gradient energies, normalised to sum LK_PERMIL
129const LK_F_TEXE: i64 = 9 // gradient energy per interior pixel (|gx| + |gy|, Sobel)
130const LK_F_VD: i64 = 10 // nx_visdesc_dim() slots follow
131// the axes
132const LK_AX_GEOM: i64 = 0
133const LK_AX_TILT: i64 = 1
134const LK_AX_SL1: i64 = 2
135const LK_AX_SCOS: i64 = 3
136const LK_AX_TL: i64 = 4
137const LK_AX_TA: i64 = 5
138const LK_AX_TB: i64 = 6
139const LK_AX_SHADE: i64 = 7 // NX_COSINE_Q minus the cosine of the two CENTRED shading grids (Pearson r as a Q10 distance)
140const LK_AX_TEX: i64 = 8 // L1 between the two normalised texture-energy layouts (0..2 * LK_PERMIL)
141const LK_AX_TEXE: i64 = 9 // absolute difference of gradient energy per pixel
142const LK_AX_N: i64 = 10
143const LK_AXR_MEAS: i64 = 10 // LK_AX_N flags: 1 measured, 0 unmeasured (slots 10..19)
144const LK_AXR_GEOM_PTS: i64 = 20
145const LK_AXR_COS: i64 = 21
146const LK_AXR_N: i64 = 22
147// the band record: per axis the worst same-person distance, the mean, the pair that set the worst, the pairs that measured it
148const LK_B_MAX: i64 = 0 // LK_AX_N slots each
149const LK_B_MEAN: i64 = 10
150const LK_B_PI: i64 = 20
151const LK_B_PJ: i64 = 30
152const LK_B_PAIRS: i64 = 40
153const LK_B_TOTAL: i64 = 50
154const LK_B_N: i64 = 51
155// the score record of one subject against a reference set
156const LK_S_DIST: i64 = 0 // LK_AX_N slots: the nearest reference's distance per axis
157const LK_S_NEAR: i64 = 10 // LK_AX_N slots: which reference
158const LK_S_STATE: i64 = 20 // LK_AX_N slots: LK_ST_*
159const LK_S_INSIDE: i64 = 30
160const LK_S_OUTSIDE: i64 = 31
161const LK_S_UNMEASURED: i64 = 32
162const LK_S_VERDICT: i64 = 33
163const LK_S_N: i64 = 34
164const LK_ST_UNMEASURED: i64 = 0
165const LK_ST_INSIDE: i64 = 1
166const LK_ST_OUTSIDE: i64 = 2
167// verdicts (also the CLI's exit codes)
168const LK_V_INSIDE: i64 = 0
169const LK_V_OUTSIDE: i64 = 1
170const LK_V_UNMEASURED: i64 = 5
171const LK_V_NOBAND: i64 = 6
172// the context: the loaded cascades, the oracle rows, the reference list's oracle path
173const LK_C_FACE: i64 = 0
174const LK_C_EYE: i64 = 1
175const LK_C_OBUF: i64 = 2
176const LK_C_OLEN: i64 = 3
177const LK_C_OPATH: i64 = 4
178const LK_C_FACE_RC: i64 = 5
179const LK_C_EYE_RC: i64 = 6
180const LK_C_N: i64 = 7
181
182func lk_ruler_version() -> i64 { return 1 }
183func lkh_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
184func lkh_max(a: i64, b: i64) -> i64 { if a > b { return a } return b }
185func lkh_min(a: i64, b: i64) -> i64 { if a < b { return a } return b }
186// division rounded half away from zero; den > 0
187func lkh_div_round(num: i64, den: i64) -> i64 {
188 if num >= 0 { return (num + den / 2) / den }
189 return 0 - ((0 - num + den / 2) / den)
190}
191// floor division; den > 0
192func lkh_div_floor(num: i64, den: i64) -> i64 {
193 var q: i64 = num / den
194 if num < 0 { if q * den != num { q = q - 1 } }
195 return q
196}
197func lkh_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
198func lkh_streq(a: *u8, b: *u8) -> i64 {
199 var i: i64 = 0
200 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
201 if b[i] != (0 as u8) { return 0 }
202 return 1
203}
204// the basename of a path: the bytes after the last slash
205func lkh_basename(p: *u8) -> *u8 {
206 var i: i64 = 0
207 var last: i64 = 0
208 while p[i] != (0 as u8) { if (p[i] as i64) == LK_SLASH { last = i + 1 } i = i + 1 }
209 return (p as i64 + last) as *u8
210}
211// a NUL-terminated copy of buf[s..e)
212func lkh_span_copy(buf: *u8, s: i64, e: i64) -> *u8 {
213 let out: *u8 = sys_mmap(e - s + 1)
214 var i: i64 = s
215 var o: i64 = 0
216 while i < e { out[o] = buf[i]; o = o + 1; i = i + 1 }
217 out[o] = 0 as u8
218 return out
219}
220func lk_align_name(k: i64) -> *u8 {
221 if k == LK_AL_ORACLE { return "oracle" as *u8 }
222 if k == LK_AL_CASCADE { return "ruler-cascade" as *u8 }
223 if k == LK_AL_SKIN { return "ruler-skin" as *u8 }
224 if k == LK_AL_CANDIDATES { return "ruler-candidates-pair-refused" as *u8 }
225 return "none" as *u8
226}
227func lk_axis_name(k: i64) -> *u8 {
228 if k == LK_AX_GEOM { return "geom_nme_permil" as *u8 }
229 if k == LK_AX_TILT { return "tilt_delta_deg10" as *u8 }
230 if k == LK_AX_SL1 { return "struct_l1" as *u8 }
231 if k == LK_AX_SCOS { return "struct_cos_dist_q10" as *u8 }
232 if k == LK_AX_TL { return "tone_dl_e3" as *u8 }
233 if k == LK_AX_TA { return "tone_da_e3" as *u8 }
234 if k == LK_AX_TB { return "tone_db_e3" as *u8 }
235 if k == LK_AX_SHADE { return "shade_cos_dist_q10" as *u8 }
236 if k == LK_AX_TEX { return "texture_layout_l1_permil" as *u8 }
237 if k == LK_AX_TEXE { return "texture_energy_per_px_delta" as *u8 }
238 return "axis" as *u8
239}
240func lk_state_name(k: i64) -> *u8 {
241 if k == LK_ST_INSIDE { return "INSIDE" as *u8 }
242 if k == LK_ST_OUTSIDE { return "OUTSIDE" as *u8 }
243 return "UNMEASURED" as *u8
244}
245func lk_verdict_name(k: i64) -> *u8 {
246 if k == LK_V_INSIDE { return "INSIDE" as *u8 }
247 if k == LK_V_OUTSIDE { return "OUTSIDE" as *u8 }
248 if k == LK_V_NOBAND { return "NO-BAND" as *u8 }
249 return "UNMEASURED" as *u8
250}
251
252// ---- the context ------------------------------------------------------------------------------------------------------------
253func lk_ctx_new() -> *i64 {
254 let ctx: *i64 = sys_mmap(LK_C_N * LK_I64) as *i64
255 let rc: *i64 = sys_mmap(2 * LK_I64) as *i64
256 ctx[LK_C_FACE] = ff_load_face(rc) as i64
257 ctx[LK_C_FACE_RC] = rc[0]
258 rc[0] = 0
259 ctx[LK_C_EYE] = ff_load_eye(rc) as i64
260 ctx[LK_C_EYE_RC] = rc[0]
261 return ctx
262}
263func lk_ctx_cascades(ctx: *i64) -> i64 {
264 if ctx[LK_C_FACE] == 0 { return 0 }
265 if ctx[LK_C_EYE] == 0 { return 0 }
266 return 1
267}
268// load the oracle rows; returns the byte count read, 0 when absent (the ruler then aligns everything itself)
269func lk_ctx_oracle(ctx: *i64, path: *u8) -> i64 {
270 ctx[LK_C_OPATH] = path as i64
271 let fl: *i64 = sys_mmap(2 * LK_I64) as *i64
272 fl[0] = 0
273 let buf: *u8 = sys_read_file(path, fl)
274 if (buf as i64) == 0 { ctx[LK_C_OBUF] = 0; ctx[LK_C_OLEN] = 0; return 0 }
275 ctx[LK_C_OBUF] = buf as i64
276 ctx[LK_C_OLEN] = fl[0]
277 return fl[0]
278}
279
280// ---- the image record --------------------------------------------------------------------------------------------------------
281func lk_img_new() -> *i64 {
282 let img: *i64 = sys_mmap(LK_I_N * LK_I64) as *i64
283 var k: i64 = 0
284 while k < LK_I_N { img[k] = 0; k = k + 1 }
285 return img
286}
287func lk_img_set_rgb(img: *i64, rgb: *u8, w: i64, h: i64) -> i64 {
288 img[LK_I_RGB] = rgb as i64
289 img[LK_I_W] = w
290 img[LK_I_H] = h
291 return 0
292}
293func lk_pt_x(img: *i64, p: i64) -> i64 { return img[LK_I_LM + 2 * p] }
294func lk_pt_y(img: *i64, p: i64) -> i64 { return img[LK_I_LM + 2 * p + 1] }
295func lk_pt_set(img: *i64, p: i64, x: i64, y: i64) -> i64 { img[LK_I_LM + 2 * p] = x; img[LK_I_LM + 2 * p + 1] = y; return 0 }
296// derive the eye centres and the span from the four canthi; returns 1 when the span is positive
297func lk_img_finish(img: *i64) -> i64 {
298 img[LK_I_ERX] = lkh_div_round(lk_pt_x(img, LK_P_RLAT) + lk_pt_x(img, LK_P_RMED), 2)
299 img[LK_I_ERY] = lkh_div_round(lk_pt_y(img, LK_P_RLAT) + lk_pt_y(img, LK_P_RMED), 2)
300 img[LK_I_ELX] = lkh_div_round(lk_pt_x(img, LK_P_LMED) + lk_pt_x(img, LK_P_LLAT), 2)
301 img[LK_I_ELY] = lkh_div_round(lk_pt_y(img, LK_P_LMED) + lk_pt_y(img, LK_P_LLAT), 2)
302 let dx: i64 = img[LK_I_ELX] - img[LK_I_ERX]
303 let dy: i64 = img[LK_I_ELY] - img[LK_I_ERY]
304 img[LK_I_IOD] = pm_isqrt(dx * dx + dy * dy)
305 if img[LK_I_IOD] <= 0 { img[LK_I_STATUS] = LK_E_UNALIGNED; img[LK_I_ALIGN] = LK_AL_NONE; return 0 }
306 img[LK_I_STATUS] = LK_OK
307 return 1
308}
309// an "x10,y10" oracle field kept at the oracle's own tenth-pixel unit (pm_field_xy divides down to pixels, which throws the tenth
310// away on a 45-pixel eye span); out2 = x10, y10
311func lk_field_xy10(buf: *u8, ls: i64, le: i64, idx: i64, out2: *i64) -> i64 {
312 let fb: *i64 = sys_mmap(2 * LK_I64) as *i64
313 if pm_field(buf, ls, le, idx, fb) == 0 { return 0 }
314 let t: *i64 = sys_mmap(2 * LK_I64) as *i64
315 if pm_span_int(buf, fb[0], fb[1], t) == 0 { return 0 }
316 let x10: i64 = t[0]
317 if t[1] >= fb[1] { return 0 }
318 if (buf[t[1]] as i64) != PM_COMMA { return 0 }
319 if pm_span_int(buf, t[1] + 1, fb[1], t) == 0 { return 0 }
320 out2[0] = x10
321 out2[1] = t[0]
322 return 1
323}
324// the end of the line that starts at ls (the offset of its newline, or n)
325func lkh_line_end(buf: *u8, ls: i64, n: i64) -> i64 {
326 var le: i64 = ls
327 var go: i64 = 1
328 while go == 1 {
329 if le >= n { go = 0 } else { if (buf[le] as i64) == LK_NL { go = 0 } else { le = le + 1 } }
330 }
331 return le
332}
333// find the oracle row whose file field is base; out2 = row start, row end; returns 1 when found
334func lk_oracle_row(ctx: *i64, base: *u8, out2: *i64) -> i64 {
335 let buf: *u8 = ctx[LK_C_OBUF] as *u8
336 let n: i64 = ctx[LK_C_OLEN]
337 if n <= 0 { return 0 }
338 let fb: *i64 = sys_mmap(2 * LK_I64) as *i64
339 let bl: i64 = lkh_slen(base)
340 var ls: i64 = 0
341 while ls < n {
342 let le: i64 = lkh_line_end(buf, ls, n)
343 if le > ls { if (buf[ls] as i64) != LK_HASH { if pm_field(buf, ls, le, PM_OF_FILE, fb) == 1 {
344 if fb[1] - fb[0] == bl {
345 var same: i64 = 1
346 var i: i64 = 0
347 while i < bl { if buf[fb[0] + i] != base[i] { same = 0 } i = i + 1 }
348 if same == 1 { out2[0] = ls; out2[1] = le; return 1 }
349 }
350 } } }
351 ls = le + 1
352 }
353 return 0
354}
355// landmarks from an oracle row: the four canthi, the mouth as the five points this ruler carries (labrale superius = ulip_top,
356// stomion = the midpoint of the two inner vermilion edges, labrale inferius = llip_bot, the two cheilia), the two tilts
357func lk_landmarks_from_oracle(buf: *u8, ls: i64, le: i64, img: *i64) -> i64 {
358 if pm_field_int(buf, ls, le, PM_OF_FACES) != 1 { return 0 }
359 let xy: *i64 = sys_mmap(2 * LK_I64) as *i64
360 var ok: i64 = 1
361 if lk_field_xy10(buf, ls, le, PM_OF_RLAT, xy) == 0 { ok = 0 } else { lk_pt_set(img, LK_P_RLAT, xy[0], xy[1]) }
362 if lk_field_xy10(buf, ls, le, PM_OF_RMED, xy) == 0 { ok = 0 } else { lk_pt_set(img, LK_P_RMED, xy[0], xy[1]) }
363 if lk_field_xy10(buf, ls, le, PM_OF_LMED, xy) == 0 { ok = 0 } else { lk_pt_set(img, LK_P_LMED, xy[0], xy[1]) }
364 if lk_field_xy10(buf, ls, le, PM_OF_LLAT, xy) == 0 { ok = 0 } else { lk_pt_set(img, LK_P_LLAT, xy[0], xy[1]) }
365 if ok == 0 { return 0 }
366 img[LK_I_EYES] = 1
367 var mok: i64 = 1
368 if lk_field_xy10(buf, ls, le, PM_OF_ULIP_TOP, xy) == 0 { mok = 0 } else { lk_pt_set(img, LK_P_LS, xy[0], xy[1]) }
369 var ix: i64 = 0
370 var iy: i64 = 0
371 if lk_field_xy10(buf, ls, le, PM_OF_ULIP_IN, xy) == 0 { mok = 0 } else { ix = xy[0]; iy = xy[1] }
372 if lk_field_xy10(buf, ls, le, PM_OF_LLIP_IN, xy) == 0 { mok = 0 } else { lk_pt_set(img, LK_P_STO, lkh_div_round(ix + xy[0], 2), lkh_div_round(iy + xy[1], 2)) }
373 if lk_field_xy10(buf, ls, le, PM_OF_LLIP_BOT, xy) == 0 { mok = 0 } else { lk_pt_set(img, LK_P_LI, xy[0], xy[1]) }
374 if lk_field_xy10(buf, ls, le, PM_OF_MOUTH_R, xy) == 0 { mok = 0 } else { lk_pt_set(img, LK_P_CHR, xy[0], xy[1]) }
375 if lk_field_xy10(buf, ls, le, PM_OF_MOUTH_L, xy) == 0 { mok = 0 } else { lk_pt_set(img, LK_P_CHL, xy[0], xy[1]) }
376 img[LK_I_MOUTH] = mok
377 img[LK_I_TILT_R] = pm_field_int(buf, ls, le, LK_OF_TILT_R)
378 img[LK_I_TILT_L] = pm_field_int(buf, ls, le, LK_OF_TILT_L)
379 img[LK_I_TILT_OK] = 1
380 img[LK_I_ALIGN] = LK_AL_ORACLE
381 return 1
382}
383// landmarks from the photo ruler's result record (pixels -> deci-pixels)
384func lk_landmarks_from_ruler(res: *i64, img: *i64, align: i64) -> i64 {
385 lk_pt_set(img, LK_P_RLAT, res[PM_R_RLAT_X] * LK_DECI, res[PM_R_RLAT_Y] * LK_DECI)
386 lk_pt_set(img, LK_P_RMED, res[PM_R_RMED_X] * LK_DECI, res[PM_R_RMED_Y] * LK_DECI)
387 lk_pt_set(img, LK_P_LMED, res[PM_R_LMED_X] * LK_DECI, res[PM_R_LMED_Y] * LK_DECI)
388 lk_pt_set(img, LK_P_LLAT, res[PM_R_LLAT_X] * LK_DECI, res[PM_R_LLAT_Y] * LK_DECI)
389 var eyes: i64 = 0
390 if res[PM_R_EYES] == 2 { eyes = 1 }
391 img[LK_I_EYES] = eyes
392 img[LK_I_TILT_OK] = eyes
393 img[LK_I_TILT_R] = res[PM_R_TILT_R]
394 img[LK_I_TILT_L] = res[PM_R_TILT_L]
395 img[LK_I_MOUTH] = 0
396 if eyes == 1 { if res[PM_R_MOUTH] == 1 {
397 lk_pt_set(img, LK_P_LS, res[PM_R_LS_X] * LK_DECI, res[PM_R_LS_Y] * LK_DECI)
398 lk_pt_set(img, LK_P_STO, res[PM_R_STO_X] * LK_DECI, res[PM_R_STO_Y] * LK_DECI)
399 lk_pt_set(img, LK_P_LI, res[PM_R_LI_X] * LK_DECI, res[PM_R_LI_Y] * LK_DECI)
400 lk_pt_set(img, LK_P_CHR, res[PM_R_CHR_X] * LK_DECI, res[PM_R_CHR_Y] * LK_DECI)
401 lk_pt_set(img, LK_P_CHL, res[PM_R_CHL_X] * LK_DECI, res[PM_R_CHL_Y] * LK_DECI)
402 img[LK_I_MOUTH] = 1
403 } }
404 img[LK_I_ALIGN] = align
405 img[LK_I_ROUTE] = res[PM_R_ROUTE]
406 img[LK_I_PAIRFAIL] = res[PM_R_PAIR_FAIL]
407 return 1
408}
409// THE ALIGNMENT LADDER for a decoded image without an oracle row: the cascade route's eye pair, the skin route's eye pair, the
410// skin route's refused-pair candidates (alignment only). Returns 1 when something aligned it.
411func lk_align_ruler(ctx: *i64, img: *i64) -> i64 {
412 let rgb: *u8 = img[LK_I_RGB] as *u8
413 let w: i64 = img[LK_I_W]
414 let h: i64 = img[LK_I_H]
415 let res: *i64 = sys_mmap(PM_R_N * LK_I64) as *i64
416 var k: i64 = 0
417 if lk_ctx_cascades(ctx) == 1 {
418 pm_landmarks_cascade(rgb, w, h, res, ctx[LK_C_FACE] as *HaarCascade, ctx[LK_C_EYE] as *HaarCascade)
419 if res[PM_R_EYES] == 2 { lk_landmarks_from_ruler(res, img, LK_AL_CASCADE); return lk_img_finish(img) }
420 }
421 k = 0
422 while k < PM_R_N { res[k] = 0; k = k + 1 }
423 pm_landmarks(rgb, w, h, res)
424 if res[PM_R_EYES] == 2 { lk_landmarks_from_ruler(res, img, LK_AL_SKIN); return lk_img_finish(img) }
425 if res[PM_R_PAIR_FAIL] != PM_PF_NONE { if res[PM_R_PAIR_FAIL] != PM_PF_EMPTY {
426 var any: i64 = 0
427 if res[PM_R_RLAT_X] != 0 { any = 1 }
428 if res[PM_R_LLAT_X] != 0 { any = 1 }
429 if any == 1 {
430 lk_landmarks_from_ruler(res, img, LK_AL_CANDIDATES)
431 img[LK_I_EYES] = 0
432 img[LK_I_TILT_OK] = 0
433 img[LK_I_MOUTH] = 0
434 return lk_img_finish(img)
435 }
436 } }
437 img[LK_I_STATUS] = LK_E_UNALIGNED
438 img[LK_I_ALIGN] = LK_AL_NONE
439 return 0
440}
441// DECODE a still or a render and align it: the oracle row when one names its basename, the ruler's ladder otherwise
442func lk_load(ctx: *i64, path: *u8, img: *i64) -> i64 {
443 let pix: *i64 = sys_mmap(LK_I64) as *i64
444 let d3: *i64 = sys_mmap(3 * LK_I64) as *i64
445 let dc: i64 = pm_decode_file(path, pix, d3)
446 if dc == PM_E_READ { img[LK_I_STATUS] = LK_E_READ; return LK_E_READ }
447 if dc != PM_OK { img[LK_I_STATUS] = LK_E_DECODE; return LK_E_DECODE }
448 lk_img_set_rgb(img, pix[0] as *u8, d3[0], d3[1])
449 img[LK_I_CODEC] = d3[2]
450 let row: *i64 = sys_mmap(2 * LK_I64) as *i64
451 if lk_oracle_row(ctx, lkh_basename(path), row) == 1 {
452 if lk_landmarks_from_oracle(ctx[LK_C_OBUF] as *u8, row[0], row[1], img) == 1 {
453 if lk_img_finish(img) == 1 { return LK_OK }
454 }
455 }
456 if lk_align_ruler(ctx, img) == 1 { return LK_OK }
457 return LK_E_UNALIGNED
458}
459
460// ---- the similarity between two eye frames --------------------------------------------------------------------------------
461// map point p from frame A (eyes at aR, aL) to frame B (eyes at bR, bL): the one similarity that carries aR to bR and aL to bL,
462// as the complex ratio (bL - bR) / (aL - aR) applied to p - aR. All deci-pixels.
463func lk_map(aRx: i64, aRy: i64, aLx: i64, aLy: i64, bRx: i64, bRy: i64, bLx: i64, bLy: i64, px: i64, py: i64, out2: *i64) -> i64 {
464 let ax: i64 = aLx - aRx
465 let ay: i64 = aLy - aRy
466 let bx: i64 = bLx - bRx
467 let by: i64 = bLy - bRy
468 let n: i64 = ax * ax + ay * ay
469 if n == 0 { out2[0] = px; out2[1] = py; return 0 }
470 let rx: i64 = bx * ax + by * ay
471 let ry: i64 = by * ax - bx * ay
472 let vx: i64 = px - aRx
473 let vy: i64 = py - aRy
474 out2[0] = bRx + lkh_div_round(rx * vx - ry * vy, n)
475 out2[1] = bRy + lkh_div_round(rx * vy + ry * vx, n)
476 return 1
477}
478// bilinear sample of an interleaved RGB image at a deci-pixel position (pixel i covers [10i, 10i+10), its centre at 10i+5);
479// positions off the image clamp to its edge
480func lk_sample(rgb: *u8, w: i64, h: i64, sx: i64, sy: i64, out3: *i64) -> i64 {
481 let fx256: i64 = lkh_div_floor(sx * LK_Q8, LK_DECI) - LK_Q8_HALF
482 let fy256: i64 = lkh_div_floor(sy * LK_Q8, LK_DECI) - LK_Q8_HALF
483 let x0f: i64 = lkh_div_floor(fx256, LK_Q8)
484 let y0f: i64 = lkh_div_floor(fy256, LK_Q8)
485 let wx: i64 = fx256 - x0f * LK_Q8
486 let wy: i64 = fy256 - y0f * LK_Q8
487 var x0: i64 = x0f
488 var x1: i64 = x0f + 1
489 var y0: i64 = y0f
490 var y1: i64 = y0f + 1
491 if x0 < 0 { x0 = 0 }
492 if x1 < 0 { x1 = 0 }
493 if y0 < 0 { y0 = 0 }
494 if y1 < 0 { y1 = 0 }
495 if x0 > w - 1 { x0 = w - 1 }
496 if x1 > w - 1 { x1 = w - 1 }
497 if y0 > h - 1 { y0 = h - 1 }
498 if y1 > h - 1 { y1 = h - 1 }
499 let o00: i64 = (y0 * w + x0) * LK_BPP
500 let o10: i64 = (y0 * w + x1) * LK_BPP
501 let o01: i64 = (y1 * w + x0) * LK_BPP
502 let o11: i64 = (y1 * w + x1) * LK_BPP
503 var c: i64 = 0
504 while c < LK_BPP {
505 let v00: i64 = (rgb[o00 + c] as i64) & LK_BYTE
506 let v10: i64 = (rgb[o10 + c] as i64) & LK_BYTE
507 let v01: i64 = (rgb[o01 + c] as i64) & LK_BYTE
508 let v11: i64 = (rgb[o11 + c] as i64) & LK_BYTE
509 let top: i64 = v00 * (LK_Q8 - wx) + v10 * wx
510 let bot: i64 = v01 * (LK_Q8 - wx) + v11 * wx
511 out3[c] = lkh_div_round(top * (LK_Q8 - wy) + bot * wy, LK_Q8 * LK_Q8)
512 c = c + 1
513 }
514 return 0
515}
516// resample an aligned image into an ow x oh frame whose eye centres are dR and dL (deci-pixels): every destination pixel centre
517// is carried back into the source by the similarity and sampled bilinearly
518func lk_resample(img: *i64, dRx: i64, dRy: i64, dLx: i64, dLy: i64, ow: i64, oh: i64) -> *u8 {
519 let out: *u8 = sys_mmap(ow * oh * LK_BPP + 16)
520 let p2: *i64 = sys_mmap(2 * LK_I64) as *i64
521 let s3: *i64 = sys_mmap(3 * LK_I64) as *i64
522 var y: i64 = 0
523 while y < oh {
524 var x: i64 = 0
525 while x < ow {
526 lk_map(dRx, dRy, dLx, dLy, img[LK_I_ERX], img[LK_I_ERY], img[LK_I_ELX], img[LK_I_ELY], x * LK_DECI + LK_DECI_HALF, y * LK_DECI + LK_DECI_HALF, p2)
527 lk_sample(img[LK_I_RGB] as *u8, img[LK_I_W], img[LK_I_H], p2[0], p2[1], s3)
528 let o: i64 = (y * ow + x) * LK_BPP
529 out[o] = s3[0] as u8
530 out[o + 1] = s3[1] as u8
531 out[o + 2] = s3[2] as u8
532 x = x + 1
533 }
534 y = y + 1
535 }
536 return out
537}
538// carry one image's landmarks into another frame (the planted-roll control builds its own record this way)
539func lk_img_map_landmarks(src: *i64, dst: *i64, dRx: i64, dRy: i64, dLx: i64, dLy: i64) -> i64 {
540 let p2: *i64 = sys_mmap(2 * LK_I64) as *i64
541 var p: i64 = 0
542 while p < LK_P_N {
543 lk_map(src[LK_I_ERX], src[LK_I_ERY], src[LK_I_ELX], src[LK_I_ELY], dRx, dRy, dLx, dLy, lk_pt_x(src, p), lk_pt_y(src, p), p2)
544 lk_pt_set(dst, p, p2[0], p2[1])
545 p = p + 1
546 }
547 dst[LK_I_MOUTH] = src[LK_I_MOUTH]
548 dst[LK_I_EYES] = src[LK_I_EYES]
549 dst[LK_I_TILT_OK] = src[LK_I_TILT_OK]
550 dst[LK_I_TILT_R] = src[LK_I_TILT_R]
551 dst[LK_I_TILT_L] = src[LK_I_TILT_L]
552 dst[LK_I_ALIGN] = src[LK_I_ALIGN]
553 return lk_img_finish(dst)
554}
555
556// ---- the canvas -------------------------------------------------------------------------------------------------------------
557// the canvas side from the smallest reference eye span (deci-pixels): two spans, floored to the descriptor grid, never below the
558// grid's Sobel floor
559func lk_canvas(min_iod_deci: i64) -> i64 {
560 var c: i64 = LK_CANVAS_SPANS * min_iod_deci / LK_DECI
561 c = c - c % VD_GRID
562 let floor_c: i64 = VD_GRID * LK_SOBEL_CELL
563 if c < floor_c { c = floor_c }
564 return c
565}
566func lk_canvas_eye_rx(c: i64) -> i64 { return (c / LK_CANVAS_QUARTER) * LK_DECI }
567func lk_canvas_eye_lx(c: i64) -> i64 { return (c - c / LK_CANVAS_QUARTER) * LK_DECI }
568func lk_canvas_eye_y(c: i64) -> i64 { return (c * LK_EYE_Y_PERMIL / LK_PERMIL) * LK_DECI }
569func lk_canvas_span(c: i64) -> i64 { return lk_canvas_eye_lx(c) - lk_canvas_eye_rx(c) }
570// the aligned crop of an image on a canvas of side c
571func lk_crop(img: *i64, c: i64) -> *u8 {
572 return lk_resample(img, lk_canvas_eye_rx(c), lk_canvas_eye_y(c), lk_canvas_eye_lx(c), lk_canvas_eye_y(c), c, c)
573}
574func lk_feat_new() -> *i64 {
575 let n: i64 = LK_F_VD + nx_visdesc_dim()
576 let f: *i64 = sys_mmap(n * LK_I64) as *i64
577 var k: i64 = 0
578 while k < n { f[k] = 0; k = k + 1 }
579 return f
580}
581func lk_feat_vd(feat: *i64) -> *i64 { return (feat as i64 + LK_F_VD * LK_I64) as *i64 }
582// THE SHADING AND TEXTURE GRIDS' CELL SIZE IS THE REFERENCE SET'S OWN LANDMARK SPREAD: the geometry band (the worst same-person
583// landmark NME, permil of the canvas eye span) is the spatial precision the set supports, so a cell is that many canvas pixels and
584// nothing finer is compared -- never below the Sobel floor. Returns cells per side.
585func lk_grid_from_band(c: i64, geom_band_permil: i64) -> i64 {
586 var cell: i64 = lkh_div_round(geom_band_permil * (lk_canvas_span(c) / LK_DECI), LK_PERMIL)
587 if cell < LK_SOBEL_CELL { cell = LK_SOBEL_CELL }
588 var g: i64 = c / cell
589 if g < 1 { g = 1 }
590 return g
591}
592// the features of one aligned crop: the edge-orientation descriptor, the shading grid, the texture-energy grid, the skin-masked Lab mean
593func lk_features(img: *i64, c: i64, grid: i64, feat: *i64) -> i64 {
594 let crop: *u8 = lk_crop(img, c)
595 feat[LK_F_CROP] = crop as i64
596 let gray: *u8 = sys_mmap(c * c + 16)
597 var i: i64 = 0
598 while i < c * c {
599 let o: i64 = i * LK_BPP
600 gray[i] = pm_luma((crop[o] as i64) & LK_BYTE, (crop[o + 1] as i64) & LK_BYTE, (crop[o + 2] as i64) & LK_BYTE) as u8
601 i = i + 1
602 }
603 feat[LK_F_GRAY] = gray as i64
604 nx_visdesc_extract(gray, c, c, lk_feat_vd(feat))
605 // the shading grid (cell-mean luminance) and the texture grid (cell gradient energy over interior pixels, Sobel from nx_image)
606 feat[LK_F_GRID] = grid
607 let ncell: i64 = grid * grid
608 let shade: *i64 = sys_mmap(ncell * LK_I64) as *i64
609 let tex: *i64 = sys_mmap(ncell * LK_I64) as *i64
610 let im: *Image = vd_wrap_gray(gray, c, c)
611 let gx: *ImageS64 = nx_image_sobel_x(im)
612 let gy: *ImageS64 = nx_image_sobel_y(im)
613 var total: i64 = 0
614 var interior: i64 = 0
615 var cy: i64 = 0
616 while cy < grid {
617 let y0: i64 = cy * c / grid
618 let y1: i64 = (cy + 1) * c / grid
619 var cx: i64 = 0
620 while cx < grid {
621 let x0: i64 = cx * c / grid
622 let x1: i64 = (cx + 1) * c / grid
623 var lsum: i64 = 0
624 var lcnt: i64 = 0
625 var esum: i64 = 0
626 var y: i64 = y0
627 while y < y1 {
628 var x: i64 = x0
629 while x < x1 {
630 lsum = lsum + ((gray[y * c + x] as i64) & LK_BYTE)
631 lcnt = lcnt + 1
632 if x > 0 { if x < c - 1 { if y > 0 { if y < c - 1 {
633 esum = esum + lkh_abs(nx_image_s64_get(gx, x, y)) + lkh_abs(nx_image_s64_get(gy, x, y))
634 interior = interior + 1
635 } } } }
636 x = x + 1
637 }
638 y = y + 1
639 }
640 let k: i64 = cy * grid + cx
641 shade[k] = 0
642 if lcnt > 0 { shade[k] = lsum / lcnt }
643 tex[k] = esum
644 total = total + esum
645 cx = cx + 1
646 }
647 cy = cy + 1
648 }
649 var k2: i64 = 0
650 while k2 < ncell { if total > 0 { tex[k2] = lkh_div_round(tex[k2] * LK_PERMIL, total) } else { tex[k2] = 0 } k2 = k2 + 1 }
651 feat[LK_F_SHADE] = shade as i64
652 feat[LK_F_TEX] = tex as i64
653 feat[LK_F_TEXE] = 0
654 if interior > 0 { feat[LK_F_TEXE] = total / interior }
655 let sk: *i64 = sys_mmap(SKS_R_N * LK_I64) as *i64
656 feat[LK_F_SKIN_N] = sks_measure_rgb(crop, c, c, sk)
657 feat[LK_F_L] = sk[SKS_R_L]
658 feat[LK_F_A] = sk[SKS_R_A]
659 feat[LK_F_B] = sk[SKS_R_B]
660 img[LK_I_UPSAMPLED] = 0
661 if img[LK_I_IOD] < lk_canvas_span(c) { img[LK_I_UPSAMPLED] = 1 }
662 return 0
663}
664// the cosine (Q10) of two grids after each is centred on its own mean: Pearson's r through the estate's cosine ruler
665func lk_centred_cos(a: *i64, b: *i64, n: i64) -> i64 {
666 let ca: *i64 = sys_mmap(n * LK_I64) as *i64
667 let cb: *i64 = sys_mmap(n * LK_I64) as *i64
668 var sa: i64 = 0
669 var sb: i64 = 0
670 var i: i64 = 0
671 while i < n { sa = sa + a[i]; sb = sb + b[i]; i = i + 1 }
672 let ma: i64 = lkh_div_round(sa, n)
673 let mb: i64 = lkh_div_round(sb, n)
674 i = 0
675 while i < n { ca[i] = a[i] - ma; cb[i] = b[i] - mb; i = i + 1 }
676 return nx_cosine_similarity(ca, n, cb, n)
677}
678// the geometry of one pair: mean landmark distance in the canvas, permil of the canvas eye span; out2 = permil, points used;
679// returns 1 when both images carry landmarks
680func lk_geom_pair(imgA: *i64, imgB: *i64, c: i64, out2: *i64) -> i64 {
681 out2[0] = 0
682 out2[1] = 0
683 if imgA[LK_I_EYES] != 1 { return 0 }
684 if imgB[LK_I_EYES] != 1 { return 0 }
685 let cRx: i64 = lk_canvas_eye_rx(c)
686 let cLx: i64 = lk_canvas_eye_lx(c)
687 let cY: i64 = lk_canvas_eye_y(c)
688 var pts: i64 = LK_P_CANTHI
689 if imgA[LK_I_MOUTH] == 1 { if imgB[LK_I_MOUTH] == 1 { pts = LK_P_N } }
690 let pa: *i64 = sys_mmap(2 * LK_I64) as *i64
691 let pb: *i64 = sys_mmap(2 * LK_I64) as *i64
692 var sum: i64 = 0
693 var p: i64 = 0
694 while p < pts {
695 lk_map(imgA[LK_I_ERX], imgA[LK_I_ERY], imgA[LK_I_ELX], imgA[LK_I_ELY], cRx, cY, cLx, cY, lk_pt_x(imgA, p), lk_pt_y(imgA, p), pa)
696 lk_map(imgB[LK_I_ERX], imgB[LK_I_ERY], imgB[LK_I_ELX], imgB[LK_I_ELY], cRx, cY, cLx, cY, lk_pt_x(imgB, p), lk_pt_y(imgB, p), pb)
697 let dx: i64 = pa[0] - pb[0]
698 let dy: i64 = pa[1] - pb[1]
699 sum = sum + pm_isqrt(dx * dx + dy * dy)
700 p = p + 1
701 }
702 out2[0] = lkh_div_round(sum * LK_PERMIL, pts * lk_canvas_span(c))
703 out2[1] = pts
704 return 1
705}
706// the geometry band alone (landmarks only, no crop): the worst same-person landmark NME over every pair; pairs_out the pairs measured
707func lk_band_geometry(imgs: *i64, n: i64, c: i64, pairs_out: *i64) -> i64 {
708 let g2: *i64 = sys_mmap(2 * LK_I64) as *i64
709 var worst: i64 = 0
710 var pairs: i64 = 0
711 var i: i64 = 0
712 while i < n {
713 var j: i64 = i + 1
714 while j < n {
715 if lk_geom_pair(imgs[i] as *i64, imgs[j] as *i64, c, g2) == 1 {
716 if g2[0] > worst { worst = g2[0] }
717 pairs = pairs + 1
718 }
719 j = j + 1
720 }
721 i = i + 1
722 }
723 pairs_out[0] = pairs
724 return worst
725}
726
727// ---- the axes ---------------------------------------------------------------------------------------------------------------
728func lk_axes(imgA: *i64, featA: *i64, imgB: *i64, featB: *i64, c: i64, ax: *i64) -> i64 {
729 var k: i64 = 0
730 while k < LK_AXR_N { ax[k] = 0; k = k + 1 }
731 // GEOMETRY: both landmark sets carried into the canvas, mean point distance as permil of the canvas eye span
732 let g2: *i64 = sys_mmap(2 * LK_I64) as *i64
733 if lk_geom_pair(imgA, imgB, c, g2) == 1 {
734 ax[LK_AX_GEOM] = g2[0]
735 ax[LK_AXR_MEAS + LK_AX_GEOM] = 1
736 ax[LK_AXR_GEOM_PTS] = g2[1]
737 }
738 // TILT
739 if imgA[LK_I_TILT_OK] == 1 { if imgB[LK_I_TILT_OK] == 1 {
740 ax[LK_AX_TILT] = lkh_max(lkh_abs(imgA[LK_I_TILT_R] - imgB[LK_I_TILT_R]), lkh_abs(imgA[LK_I_TILT_L] - imgB[LK_I_TILT_L]))
741 ax[LK_AXR_MEAS + LK_AX_TILT] = 1
742 } }
743 // STRUCTURE
744 let va: *i64 = lk_feat_vd(featA)
745 let vb: *i64 = lk_feat_vd(featB)
746 ax[LK_AX_SL1] = nx_visdesc_l1(va, vb)
747 ax[LK_AXR_MEAS + LK_AX_SL1] = 1
748 let cs: i64 = nx_cosine_similarity(va, nx_visdesc_dim(), vb, nx_visdesc_dim())
749 ax[LK_AXR_COS] = cs
750 ax[LK_AX_SCOS] = NX_COSINE_Q - cs
751 ax[LK_AXR_MEAS + LK_AX_SCOS] = 1
752 // SHADING LAYOUT and TEXTURE: the grids compare only when both crops were gridded alike
753 if featA[LK_F_GRID] > 0 { if featA[LK_F_GRID] == featB[LK_F_GRID] {
754 let ncell: i64 = featA[LK_F_GRID] * featA[LK_F_GRID]
755 ax[LK_AX_SHADE] = NX_COSINE_Q - lk_centred_cos(featA[LK_F_SHADE] as *i64, featB[LK_F_SHADE] as *i64, ncell)
756 ax[LK_AXR_MEAS + LK_AX_SHADE] = 1
757 let ta: *i64 = featA[LK_F_TEX] as *i64
758 let tb: *i64 = featB[LK_F_TEX] as *i64
759 var tl1: i64 = 0
760 var q: i64 = 0
761 while q < ncell { tl1 = tl1 + lkh_abs(ta[q] - tb[q]); q = q + 1 }
762 ax[LK_AX_TEX] = tl1
763 ax[LK_AXR_MEAS + LK_AX_TEX] = 1
764 ax[LK_AX_TEXE] = lkh_abs(featA[LK_F_TEXE] - featB[LK_F_TEXE])
765 ax[LK_AXR_MEAS + LK_AX_TEXE] = 1
766 } }
767 // TONE
768 if featA[LK_F_SKIN_N] > 0 { if featB[LK_F_SKIN_N] > 0 {
769 ax[LK_AX_TL] = lkh_abs(featA[LK_F_L] - featB[LK_F_L])
770 ax[LK_AX_TA] = lkh_abs(featA[LK_F_A] - featB[LK_F_A])
771 ax[LK_AX_TB] = lkh_abs(featA[LK_F_B] - featB[LK_F_B])
772 ax[LK_AXR_MEAS + LK_AX_TL] = 1
773 ax[LK_AXR_MEAS + LK_AX_TA] = 1
774 ax[LK_AXR_MEAS + LK_AX_TB] = 1
775 } }
776 return 0
777}
778
779// ---- the band ---------------------------------------------------------------------------------------------------------------
780// every pair of references: per axis the worst distance (the band), the mean, the pair that set the worst; returns the pair count
781func lk_band(imgs: *i64, feats: *i64, n: i64, c: i64, band: *i64) -> i64 {
782 var k: i64 = 0
783 while k < LK_B_N { band[k] = 0; k = k + 1 }
784 let ax: *i64 = sys_mmap(LK_AXR_N * LK_I64) as *i64
785 let sums: *i64 = sys_mmap(LK_AX_N * LK_I64) as *i64
786 k = 0
787 while k < LK_AX_N { sums[k] = 0; k = k + 1 }
788 var pairs: i64 = 0
789 var i: i64 = 0
790 while i < n {
791 var j: i64 = i + 1
792 while j < n {
793 lk_axes(imgs[i] as *i64, feats[i] as *i64, imgs[j] as *i64, feats[j] as *i64, c, ax)
794 var a: i64 = 0
795 while a < LK_AX_N {
796 if ax[LK_AXR_MEAS + a] == 1 {
797 if band[LK_B_PAIRS + a] == 0 { band[LK_B_MAX + a] = ax[a]; band[LK_B_PI + a] = i; band[LK_B_PJ + a] = j }
798 if ax[a] > band[LK_B_MAX + a] { band[LK_B_MAX + a] = ax[a]; band[LK_B_PI + a] = i; band[LK_B_PJ + a] = j }
799 sums[a] = sums[a] + ax[a]
800 band[LK_B_PAIRS + a] = band[LK_B_PAIRS + a] + 1
801 }
802 a = a + 1
803 }
804 pairs = pairs + 1
805 j = j + 1
806 }
807 i = i + 1
808 }
809 k = 0
810 while k < LK_AX_N { if band[LK_B_PAIRS + k] > 0 { band[LK_B_MEAN + k] = sums[k] / band[LK_B_PAIRS + k] } k = k + 1 }
811 band[LK_B_TOTAL] = pairs
812 return pairs
813}
814// one subject against the reference set: per axis the nearest reference and whether it lies within the band
815func lk_score_img(sub: *i64, subf: *i64, imgs: *i64, feats: *i64, n: i64, c: i64, band: *i64, sc: *i64) -> i64 {
816 var k: i64 = 0
817 while k < LK_S_N { sc[k] = 0; k = k + 1 }
818 let ax: *i64 = sys_mmap(LK_AXR_N * LK_I64) as *i64
819 let seen: *i64 = sys_mmap(LK_AX_N * LK_I64) as *i64
820 k = 0
821 while k < LK_AX_N { seen[k] = 0; k = k + 1 }
822 var i: i64 = 0
823 while i < n {
824 lk_axes(sub, subf, imgs[i] as *i64, feats[i] as *i64, c, ax)
825 var a: i64 = 0
826 while a < LK_AX_N {
827 if ax[LK_AXR_MEAS + a] == 1 {
828 if seen[a] == 0 { sc[LK_S_DIST + a] = ax[a]; sc[LK_S_NEAR + a] = i; seen[a] = 1 }
829 if ax[a] < sc[LK_S_DIST + a] { sc[LK_S_DIST + a] = ax[a]; sc[LK_S_NEAR + a] = i }
830 }
831 a = a + 1
832 }
833 i = i + 1
834 }
835 var a: i64 = 0
836 while a < LK_AX_N {
837 var st: i64 = LK_ST_UNMEASURED
838 if seen[a] == 1 { if band[LK_B_PAIRS + a] > 0 {
839 if sc[LK_S_DIST + a] <= band[LK_B_MAX + a] { st = LK_ST_INSIDE } else { st = LK_ST_OUTSIDE }
840 } }
841 sc[LK_S_STATE + a] = st
842 if st == LK_ST_INSIDE { sc[LK_S_INSIDE] = sc[LK_S_INSIDE] + 1 }
843 if st == LK_ST_OUTSIDE { sc[LK_S_OUTSIDE] = sc[LK_S_OUTSIDE] + 1 }
844 if st == LK_ST_UNMEASURED { sc[LK_S_UNMEASURED] = sc[LK_S_UNMEASURED] + 1 }
845 a = a + 1
846 }
847 var v: i64 = LK_V_UNMEASURED
848 if sc[LK_S_OUTSIDE] > 0 { v = LK_V_OUTSIDE } else { if sc[LK_S_INSIDE] > 0 { v = LK_V_INSIDE } }
849 if band[LK_B_TOTAL] == 0 { v = LK_V_NOBAND }
850 sc[LK_S_VERDICT] = v
851 return v
852}
853
854// ---- the reference list -----------------------------------------------------------------------------------------------------
855// a list file: one path per line, blank lines and lines starting with the comment byte skipped, an optional `@oracle <path>` directive;
856// a file that opens with a PNG or JPEG signature is a list of one. Returns the path array (NUL-terminated copies as i64), out_n the
857// count, or 0 with out_n = -1 when the file cannot be read
858func lk_list_read(ctx: *i64, path: *u8, out_n: *i64) -> *i64 {
859 let fl: *i64 = sys_mmap(2 * LK_I64) as *i64
860 fl[0] = 0
861 let buf: *u8 = sys_read_file(path, fl)
862 if (buf as i64) == 0 { out_n[0] = 0 - 1; return 0 as *i64 }
863 let n: i64 = fl[0]
864 if n <= 0 { out_n[0] = 0 - 1; return 0 as *i64 }
865 let first: i64 = (buf[0] as i64) & LK_BYTE
866 var single: i64 = 0
867 if first == LK_PNG_SIG0 { single = 1 }
868 if first == LK_JPEG_SIG0 { single = 1 }
869 if single == 1 {
870 let one: *i64 = sys_mmap(LK_I64) as *i64
871 one[0] = lkh_span_copy(path, 0, lkh_slen(path)) as i64
872 out_n[0] = 1
873 return one
874 }
875 var lines: i64 = 0
876 var ls: i64 = 0
877 while ls < n { let le: i64 = lkh_line_end(buf, ls, n); lines = lines + 1; ls = le + 1 }
878 let arr: *i64 = sys_mmap((lines + 1) * LK_I64) as *i64
879 var count: i64 = 0
880 let dlen: i64 = lkh_slen(LK_DIRECTIVE_ORACLE)
881 ls = 0
882 while ls < n {
883 var le: i64 = lkh_line_end(buf, ls, n)
884 var e: i64 = le
885 var go: i64 = 1
886 while go == 1 {
887 if e <= ls { go = 0 } else {
888 let cb: i64 = (buf[e - 1] as i64) & LK_BYTE
889 var ws: i64 = 0
890 if cb == LK_CR { ws = 1 }
891 if cb == LK_SPACE { ws = 1 }
892 if cb == LK_TAB { ws = 1 }
893 if ws == 1 { e = e - 1 } else { go = 0 }
894 }
895 }
896 if e > ls { if ((buf[ls] as i64) & LK_BYTE) != LK_HASH {
897 var isdir: i64 = 0
898 if e - ls > dlen {
899 isdir = 1
900 var i: i64 = 0
901 while i < dlen { if buf[ls + i] != LK_DIRECTIVE_ORACLE[i] { isdir = 0 } i = i + 1 }
902 }
903 if isdir == 1 {
904 // the list's directive names the oracle unless the caller already did (a CLI argument wins over the list)
905 if ctx[LK_C_OPATH] == 0 { lk_ctx_oracle(ctx, lkh_span_copy(buf, ls + dlen, e)) }
906 } else {
907 arr[count] = lkh_span_copy(buf, ls, e) as i64
908 count = count + 1
909 }
910 } }
911 ls = le + 1
912 }
913 out_n[0] = count
914 return arr
915}
916
917// ---- the receipts -----------------------------------------------------------------------------------------------------------
918func lk_print_img(label: *u8, path: *u8, img: *i64) -> i64 {
919 pm_puts("LIKENESS " as *u8); pm_puts(label); pm_puts("=" as *u8); pm_puts(path)
920 pm_puts(" align=" as *u8); pm_puts(lk_align_name(img[LK_I_ALIGN]))
921 pm_kv(" w=" as *u8, img[LK_I_W]); pm_kv(" h=" as *u8, img[LK_I_H]); pm_kv(" codec=" as *u8, img[LK_I_CODEC])
922 pm_kv(" iod_decipx=" as *u8, img[LK_I_IOD]); pm_kv(" eyes_landmarked=" as *u8, img[LK_I_EYES]); pm_kv(" mouth=" as *u8, img[LK_I_MOUTH])
923 pm_kv(" tilt_ok=" as *u8, img[LK_I_TILT_OK]); pm_kv(" tilt_R_deg10=" as *u8, img[LK_I_TILT_R]); pm_kv(" tilt_L_deg10=" as *u8, img[LK_I_TILT_L])
924 pm_kv(" eye_R=" as *u8, img[LK_I_ERX]); pm_kv("," as *u8, img[LK_I_ERY]); pm_kv(" eye_L=" as *u8, img[LK_I_ELX]); pm_kv("," as *u8, img[LK_I_ELY])
925 pm_kv(" ruler_route=" as *u8, img[LK_I_ROUTE]); pm_kv(" pair_fail=" as *u8, img[LK_I_PAIRFAIL]); pm_kv(" upsampled=" as *u8, img[LK_I_UPSAMPLED])
926 pm_puts("\n" as *u8)
927 return 0
928}
929func lk_print_feat(path: *u8, feat: *i64) -> i64 {
930 pm_puts("LIKENESS features=" as *u8); pm_puts(path)
931 pm_kv(" skin_pixels=" as *u8, feat[LK_F_SKIN_N]); pm_kv(" skin_L_e3=" as *u8, feat[LK_F_L]); pm_kv(" skin_a_e3=" as *u8, feat[LK_F_A]); pm_kv(" skin_b_e3=" as *u8, feat[LK_F_B])
932 pm_kv(" grid=" as *u8, feat[LK_F_GRID]); pm_kv(" texture_energy_per_px=" as *u8, feat[LK_F_TEXE])
933 pm_puts("\n" as *u8)
934 return 0
935}
936func lk_print_canvas(c: i64, min_iod: i64, min_path: *u8, nrefs: i64) -> i64 {
937 pm_puts("LIKENESS canvas" as *u8)
938 pm_kv(" ruler_version=" as *u8, lk_ruler_version())
939 pm_kv(" side_px=" as *u8, c); pm_kv(" eye_span_decipx=" as *u8, lk_canvas_span(c)); pm_kv(" eye_R_x=" as *u8, lk_canvas_eye_rx(c)); pm_kv(" eye_L_x=" as *u8, lk_canvas_eye_lx(c)); pm_kv(" eye_y=" as *u8, lk_canvas_eye_y(c))
940 pm_kv(" derived_from_smallest_reference_iod_decipx=" as *u8, min_iod); pm_puts(" of=" as *u8); pm_puts(min_path); pm_kv(" references=" as *u8, nrefs)
941 pm_kv(" spans_wide=" as *u8, LK_CANVAS_SPANS); pm_kv(" grid=" as *u8, VD_GRID); pm_kv(" sobel_floor_px=" as *u8, VD_GRID * LK_SOBEL_CELL)
942 pm_kv(" eye_y_permil=" as *u8, LK_EYE_Y_PERMIL); pm_puts(" eye_y_basis=" as *u8); pm_puts(LK_EYE_Y_BASIS); pm_kv(" brow_above_eyeline_permil_of_span=" as *u8, LK_EYE_Y_BASIS_BROW_PERMIL)
943 pm_puts("\n" as *u8)
944 return 0
945}
946func lk_print_band(band: *i64, paths: *i64) -> i64 {
947 var a: i64 = 0
948 while a < LK_AX_N {
949 pm_puts("LIKENESS band axis=" as *u8); pm_puts(lk_axis_name(a))
950 pm_kv(" pairs=" as *u8, band[LK_B_PAIRS + a]); pm_kv(" of=" as *u8, band[LK_B_TOTAL])
951 pm_kv(" band_max=" as *u8, band[LK_B_MAX + a]); pm_kv(" mean=" as *u8, band[LK_B_MEAN + a])
952 if band[LK_B_PAIRS + a] > 0 {
953 pm_puts(" worst_pair=" as *u8); pm_puts(lkh_basename(paths[band[LK_B_PI + a]] as *u8)); pm_puts("," as *u8); pm_puts(lkh_basename(paths[band[LK_B_PJ + a]] as *u8))
954 } else { pm_puts(" worst_pair=UNMEASURED" as *u8) }
955 pm_puts("\n" as *u8)
956 a = a + 1
957 }
958 return 0
959}
960func lk_print_score(path: *u8, sc: *i64, band: *i64, paths: *i64) -> i64 {
961 var a: i64 = 0
962 while a < LK_AX_N {
963 pm_puts("LIKENESS subject=" as *u8); pm_puts(lkh_basename(path)); pm_puts(" axis=" as *u8); pm_puts(lk_axis_name(a))
964 pm_kv(" dist=" as *u8, sc[LK_S_DIST + a]); pm_kv(" band=" as *u8, band[LK_B_MAX + a])
965 pm_puts(" nearest=" as *u8)
966 if sc[LK_S_STATE + a] == LK_ST_UNMEASURED { pm_puts("-" as *u8) } else { pm_puts(lkh_basename(paths[sc[LK_S_NEAR + a]] as *u8)) }
967 pm_puts(" state=" as *u8); pm_puts(lk_state_name(sc[LK_S_STATE + a]))
968 pm_puts("\n" as *u8)
969 a = a + 1
970 }
971 return 0
972}
973func lk_print_verdict(path: *u8, sc: *i64) -> i64 {
974 pm_puts("LIKENESS subject=" as *u8); pm_puts(lkh_basename(path))
975 pm_kv(" axes_inside=" as *u8, sc[LK_S_INSIDE]); pm_kv(" axes_outside=" as *u8, sc[LK_S_OUTSIDE]); pm_kv(" axes_unmeasured=" as *u8, sc[LK_S_UNMEASURED]); pm_kv(" of=" as *u8, LK_AX_N)
976 pm_puts(" verdict=" as *u8); pm_puts(lk_verdict_name(sc[LK_S_VERDICT])); pm_puts("\n" as *u8)
977 return 0
978}
979
980// ---- THE CONTRACT: the reference set loaded, aligned, banded; a subject scored and printed ----------------------------------
981// the set record handed back by lk_refs_load
982const LK_R_IMGS: i64 = 0
983const LK_R_FEATS: i64 = 1
984const LK_R_PATHS: i64 = 2
985const LK_R_N: i64 = 3
986const LK_R_LISTED: i64 = 4
987const LK_R_UNREADABLE: i64 = 5
988const LK_R_UNDECODABLE: i64 = 6
989const LK_R_UNALIGNED: i64 = 7
990const LK_R_CANVAS: i64 = 8
991const LK_R_MIN_IOD: i64 = 9
992const LK_R_MIN_PATH: i64 = 10
993const LK_R_BAND: i64 = 11
994const LK_R_PAIRS: i64 = 12
995const LK_R_ORACLE_BYTES: i64 = 13
996const LK_R_GRID: i64 = 14
997const LK_R_GEOM_BAND: i64 = 15
998const LK_R_GEOM_PAIRS: i64 = 16
999const LK_R_SLOTS: i64 = 17
1000// load every reference in the list, keep the aligned ones, derive the canvas and the band; returns the aligned count, or -1 when the
1001// list cannot be read. Prints the receipts.
1002func lk_refs_load(ctx: *i64, list: *u8, set: *i64) -> i64 {
1003 var k: i64 = 0
1004 while k < LK_R_SLOTS { set[k] = 0; k = k + 1 }
1005 let cnt: *i64 = sys_mmap(LK_I64) as *i64
1006 let paths: *i64 = lk_list_read(ctx, list, cnt)
1007 if cnt[0] < 0 { pm_puts("LIKENESS list=" as *u8); pm_puts(list); pm_puts(" status=UNREADABLE\n" as *u8); return 0 - 1 }
1008 set[LK_R_LISTED] = cnt[0]
1009 set[LK_R_ORACLE_BYTES] = ctx[LK_C_OLEN]
1010 pm_puts("LIKENESS list=" as *u8); pm_puts(list); pm_kv(" listed=" as *u8, cnt[0]); pm_kv(" oracle_bytes=" as *u8, ctx[LK_C_OLEN]); pm_kv(" cascades_loaded=" as *u8, lk_ctx_cascades(ctx)); pm_puts("\n" as *u8)
1011 let imgs: *i64 = sys_mmap((cnt[0] + 1) * LK_I64) as *i64
1012 let feats: *i64 = sys_mmap((cnt[0] + 1) * LK_I64) as *i64
1013 let kept: *i64 = sys_mmap((cnt[0] + 1) * LK_I64) as *i64
1014 var n: i64 = 0
1015 var i: i64 = 0
1016 while i < cnt[0] {
1017 let p: *u8 = paths[i] as *u8
1018 let img: *i64 = lk_img_new()
1019 let rc: i64 = lk_load(ctx, p, img)
1020 if rc == LK_OK {
1021 imgs[n] = img as i64
1022 kept[n] = p as i64
1023 n = n + 1
1024 lk_print_img("reference" as *u8, p, img)
1025 } else {
1026 pm_puts("LIKENESS reference=" as *u8); pm_puts(p)
1027 if rc == LK_E_READ { set[LK_R_UNREADABLE] = set[LK_R_UNREADABLE] + 1; pm_puts(" status=UNREADABLE\n" as *u8) }
1028 if rc == LK_E_DECODE { set[LK_R_UNDECODABLE] = set[LK_R_UNDECODABLE] + 1; pm_puts(" status=UNDECODABLE\n" as *u8) }
1029 if rc == LK_E_UNALIGNED { set[LK_R_UNALIGNED] = set[LK_R_UNALIGNED] + 1; pm_puts(" status=UNALIGNED (no oracle row, no eye pair, no candidates)\n" as *u8) }
1030 }
1031 i = i + 1
1032 }
1033 set[LK_R_IMGS] = imgs as i64
1034 set[LK_R_FEATS] = feats as i64
1035 set[LK_R_PATHS] = kept as i64
1036 set[LK_R_N] = n
1037 pm_puts("LIKENESS references" as *u8); pm_kv(" listed=" as *u8, cnt[0]); pm_kv(" aligned=" as *u8, n); pm_kv(" unreadable=" as *u8, set[LK_R_UNREADABLE]); pm_kv(" undecodable=" as *u8, set[LK_R_UNDECODABLE]); pm_kv(" unaligned=" as *u8, set[LK_R_UNALIGNED])
1038 pm_kv(" partition_sums=" as *u8, (n + set[LK_R_UNREADABLE] + set[LK_R_UNDECODABLE] + set[LK_R_UNALIGNED] == cnt[0]) as i64)
1039 pm_puts("\n" as *u8)
1040 if n == 0 { return 0 }
1041 var min_iod: i64 = 0
1042 var min_i: i64 = 0
1043 i = 0
1044 while i < n {
1045 let img: *i64 = imgs[i] as *i64
1046 if i == 0 { min_iod = img[LK_I_IOD] }
1047 if img[LK_I_IOD] < min_iod { min_iod = img[LK_I_IOD]; min_i = i }
1048 i = i + 1
1049 }
1050 let c: i64 = lk_canvas(min_iod)
1051 set[LK_R_CANVAS] = c
1052 set[LK_R_MIN_IOD] = min_iod
1053 set[LK_R_MIN_PATH] = kept[min_i]
1054 lk_print_canvas(c, min_iod, kept[min_i] as *u8, n)
1055 // the geometry band first (landmarks only): it sets the cell size every grid on this set is compared at
1056 let gp: *i64 = sys_mmap(LK_I64) as *i64
1057 let gband: i64 = lk_band_geometry(imgs, n, c, gp)
1058 let grid: i64 = lk_grid_from_band(c, gband)
1059 set[LK_R_GEOM_BAND] = gband
1060 set[LK_R_GEOM_PAIRS] = gp[0]
1061 set[LK_R_GRID] = grid
1062 pm_puts("LIKENESS grid" as *u8); pm_kv(" cells_per_side=" as *u8, grid); pm_kv(" cell_px=" as *u8, c / grid); pm_kv(" derived_from_geometry_band_permil=" as *u8, gband); pm_kv(" geometry_pairs=" as *u8, gp[0]); pm_kv(" sobel_floor_px=" as *u8, LK_SOBEL_CELL); pm_puts("\n" as *u8)
1063 i = 0
1064 while i < n {
1065 let f: *i64 = lk_feat_new()
1066 lk_features(imgs[i] as *i64, c, grid, f)
1067 feats[i] = f as i64
1068 lk_print_feat(kept[i] as *u8, f)
1069 i = i + 1
1070 }
1071 let band: *i64 = sys_mmap(LK_B_N * LK_I64) as *i64
1072 set[LK_R_PAIRS] = lk_band(imgs, feats, n, c, band)
1073 set[LK_R_BAND] = band as i64
1074 pm_puts("LIKENESS band" as *u8); pm_kv(" references=" as *u8, n); pm_kv(" pairs=" as *u8, set[LK_R_PAIRS]); pm_puts(" rule=max-over-same-person-pairs\n" as *u8)
1075 lk_print_band(band, kept)
1076 return n
1077}
1078// score one loaded set member or subject record: features on the set's canvas, then the nearest-reference test per axis
1079func lk_score_subject(set: *i64, path: *u8, sub: *i64, sc: *i64) -> i64 {
1080 let f: *i64 = lk_feat_new()
1081 lk_features(sub, set[LK_R_CANVAS], set[LK_R_GRID], f)
1082 lk_print_img("subject" as *u8, path, sub)
1083 lk_print_feat(path, f)
1084 let v: i64 = lk_score_img(sub, f, set[LK_R_IMGS] as *i64, set[LK_R_FEATS] as *i64, set[LK_R_N], set[LK_R_CANVAS], set[LK_R_BAND] as *i64, sc)
1085 lk_print_score(path, sc, set[LK_R_BAND] as *i64, set[LK_R_PATHS] as *i64)
1086 return v
1087}
1088// lk_score: the whole door -- the reference list loaded and banded, the subject decoded and aligned, every axis published, the
1089// verdict LAST. Returns LK_V_INSIDE, LK_V_OUTSIDE, LK_V_UNMEASURED, LK_V_NOBAND, or the negative load status of the subject/list.
1090func lk_score(ctx: *i64, subject: *u8, list: *u8, sc: *i64) -> i64 {
1091 let set: *i64 = sys_mmap(LK_R_SLOTS * LK_I64) as *i64
1092 let n: i64 = lk_refs_load(ctx, list, set)
1093 if n < 0 { return LK_E_READ }
1094 let sub: *i64 = lk_img_new()
1095 let rc: i64 = lk_load(ctx, subject, sub)
1096 if rc != LK_OK {
1097 pm_puts("LIKENESS subject=" as *u8); pm_puts(subject)
1098 if rc == LK_E_READ { pm_puts(" status=UNREADABLE verdict=UNMEASURED\n" as *u8); return LK_E_READ }
1099 if rc == LK_E_DECODE { pm_puts(" status=UNDECODABLE verdict=UNMEASURED\n" as *u8); return LK_E_DECODE }
1100 pm_puts(" status=UNALIGNED verdict=UNMEASURED\n" as *u8)
1101 return LK_E_UNALIGNED
1102 }
1103 if n == 0 {
1104 var k: i64 = 0
1105 while k < LK_S_N { sc[k] = 0; k = k + 1 }
1106 sc[LK_S_VERDICT] = LK_V_NOBAND
1107 lk_print_img("subject" as *u8, subject, sub)
1108 pm_puts("LIKENESS subject=" as *u8); pm_puts(lkh_basename(subject)); pm_puts(" references_aligned=0 verdict=NO-BAND\n" as *u8)
1109 return LK_V_NOBAND
1110 }
1111 let v: i64 = lk_score_subject(set, subject, sub, sc)
1112 lk_print_verdict(subject, sc)
1113 return v
1114}