code wiki / (root) / nx_image_grade_v2.nx

nx_image_grade_v2.nx source

↩ module page · 543 lines · 20419 B

1// nx_image_grade_v2.nx -- landmark-aware image grading composer. 2// 3// CAPABILITY_COMPLETENESS: PARTIAL 4// MISSING_CAPABILITIES: 5// - face keypoint detection (eye/nose/mouth within head bbox): 6// blocks nx_micro_expression's full invocation; v2 returns 7// head BBOX but doesn't yet sub-detect facial features 8// - hand keypoint detection / finger segmentation: blocks 9// nx_finger_count's full invocation 10// - pose joint-angle extraction: blocks nx_pose_naturalness 11// from running end-to-end 12// - VLM grader source (triangulation second-source) 13// 14// COVERED (PROMOTED FROM v1): 15// - Segmenter + landmark labeler composed 16// - nx_silhouette_integrity invoked on torso blob -> SHARP / 17// SOFT / MELTED verdict 18// - nx_limb_count invoked with landmark output -> OK / 19// EXTRA / FLOATING_LIMB / MISSING / etc. 20// - Skin-localized Lab + ITA + undertone (same as v1) 21// - Skin coverage Q10 (same as v1) 22// - 6 anatomical-region bboxes exposed in report for caller 23// to feed downstream tier-axes 24// 25// Composes: 26// nx_png_decoder + nx_jpeg_decoder (format dispatch) 27// nx_segmenter_classical (skin connected-components) 28// nx_landmark_anatomy (head/torso/arm/leg labeling) 29// nx_silhouette_integrity (melty detector on torso) 30// nx_limb_count (anatomy coherence) 31// nx_lab_from_rgb + nx_skin_tone_ita + nx_undertone_class 32// nx_verdict_phrase 33// 34// genealogy_id: substrate_image_grade_v2_landmark_aware_2026_05_16 35// lineage_id: nx_image_grade_v2 36 37import "nx_syscalls.nx" 38import "nx_runtime.nx" 39import "nx_tier.nx" 40import "nx_png_decoder.nx" 41import "nx_jpeg_decoder.nx" 42import "nx_verdict_phrase.nx" 43import "nx_lab_from_rgb.nx" 44import "nx_skin_tone_ita.nx" 45import "nx_undertone_class.nx" 46import "nx_segmenter_classical.nx" 47import "nx_landmark_anatomy.nx" 48import "nx_silhouette_integrity.nx" 49import "nx_limb_count.nx" 50const NX_MAGIC_1024: i64 = 1024 51 52// ===== sealed: format + error codes ============================== 53 54const NX_IGV2_FORMAT_UNKNOWN: nx_int = 0 55const NX_IGV2_FORMAT_PNG: nx_int = 1 56const NX_IGV2_FORMAT_JPEG: nx_int = 2 57 58const NX_IGV2_OK: nx_int = 0 59const NX_IGV2_ERR_TOO_SHORT: nx_int = 1 60const NX_IGV2_ERR_UNKNOWN_FORMAT: nx_int = 2 61const NX_IGV2_ERR_DECODER: nx_int = 3 62const NX_IGV2_ERR_NULL_PIXELS: nx_int = 4 63 64// ===== axis status (extended from v1) ============================ 65 66const NX_IGV2_AXIS_OK: nx_int = 0 67const NX_IGV2_AXIS_PENDING_KEYPOINT: nx_int = 1 // need face/hand keypoint detection 68const NX_IGV2_AXIS_PENDING_POSE_ESTIMATE: nx_int = 2 // need joint-angle extractor 69const NX_IGV2_AXIS_PENDING_VLM: nx_int = 3 70const NX_IGV2_AXIS_NOT_APPLICABLE: nx_int = 4 71const NX_IGV2_AXIS_NO_SKIN_DETECTED: nx_int = 5 72const NX_IGV2_AXIS_INSUFFICIENT_LANDMARKS: nx_int = 6 73 74// ===== per-axis verdict ========================================== 75 76struct NxIgv2AxisVerdict { 77 axis_id: nx_int, 78 status: nx_int, 79 verdict_value: nx_int, 80 score_q10: nx_int, 81 phrase_buf: *u8, 82 phrase_len: nx_int, 83} 84 85const NX_IGV2_AXIS_VERDICT_BYTES: nx_size = 48 86 87// ===== top-level report ========================================== 88 89struct NxIgv2Report { 90 source_format: nx_int, 91 width: nx_int, 92 height: nx_int, 93 error_code: nx_int, 94 n_axes: nx_int, 95 axes: *NxIgv2AxisVerdict, 96 mean_r: nx_int, 97 mean_g: nx_int, 98 mean_b: nx_int, 99 skin_pixel_count: nx_int, 100 skin_mean_r: nx_int, 101 skin_mean_g: nx_int, 102 skin_mean_b: nx_int, 103 skin_coverage_q10: nx_int, 104 n_skin_blobs: nx_int, 105 landmarks: *NxAnatomyLandmarks, 106} 107 108const NX_IGV2_REPORT_BYTES: nx_size = 112 109 110// Axis IDs (canonical; same as v1). 111const NX_IGV2_AX_DIMENSIONS: nx_int = 0 112const NX_IGV2_AX_LUMINANCE: nx_int = 1 113const NX_IGV2_AX_SKIN_TONE_ITA: nx_int = 4 114const NX_IGV2_AX_UNDERTONE: nx_int = 12 115const NX_IGV2_AX_SILHOUETTE: nx_int = 28 116const NX_IGV2_AX_VEIN_SIGNAL: nx_int = 16 117const NX_IGV2_AX_SUBSURFACE: nx_int = 17 118const NX_IGV2_AX_MICRO_EXPRESSION: nx_int = 22 119const NX_IGV2_AX_LIMB_COUNT: nx_int = 25 120const NX_IGV2_AX_POSE_NATURALNESS: nx_int = 26 121const NX_IGV2_AX_FINGER_COUNT: nx_int = 24 122const NX_IGV2_AX_SHADOW_DIRECTION: nx_int = 23 123const NX_IGV2_AX_SURFACE_PLANE: nx_int = 27 124const NX_IGV2_AX_SKIN_COVERAGE: nx_int = 100 125const NX_IGV2_AX_LANDMARK_CONF: nx_int = 101 126 127const NX_IGV2_N_AXES: nx_int = 15 128 129// ===== format detect ============================================= 130 131func _igv2_detect_format(buf: *u8, n: nx_int) -> nx_int { 132 if n < 4 { return NX_IGV2_FORMAT_UNKNOWN } 133 if buf[0] == (137 as u8) { 134 if buf[1] == (80 as u8) { 135 if buf[2] == (78 as u8) { 136 if buf[3] == (71 as u8) { 137 return NX_IGV2_FORMAT_PNG 138 } 139 } 140 } 141 } 142 if buf[0] == (255 as u8) { 143 if buf[1] == (216 as u8) { 144 return NX_IGV2_FORMAT_JPEG 145 } 146 } 147 return NX_IGV2_FORMAT_UNKNOWN 148} 149 150// ===== Kovac skin-pixel test (same as v1) ======================== 151 152func _igv2_is_skin_pixel(r: nx_int, g: nx_int, b: nx_int) -> nx_int { 153 if r <= 95 { return 0 } 154 if g <= 40 { return 0 } 155 if b <= 20 { return 0 } 156 var max_v: nx_int = r 157 if g > max_v { max_v = g } 158 if b > max_v { max_v = b } 159 var min_v: nx_int = r 160 if g < min_v { min_v = g } 161 if b < min_v { min_v = b } 162 if (max_v - min_v) <= 15 { return 0 } 163 var rg_diff: nx_int = r - g 164 if rg_diff < 0 { rg_diff = 0 - rg_diff } 165 if rg_diff <= 15 { return 0 } 166 if r <= g { return 0 } 167 if r <= b { return 0 } 168 return 1 169} 170 171// ===== axis filler =============================================== 172 173func _igv2_set_axis(axes: *NxIgv2AxisVerdict, idx: nx_int, 174 axis_id: nx_int, status: nx_int, 175 verdict_value: nx_int, score_q10: nx_int) -> nx_int { 176 let slot: *NxIgv2AxisVerdict = 177 (axes as *u8 + (idx as nx_size) * NX_IGV2_AXIS_VERDICT_BYTES) as *NxIgv2AxisVerdict 178 slot.axis_id = axis_id 179 slot.status = status 180 slot.verdict_value = verdict_value 181 slot.score_q10 = score_q10 182 let phrase_buf: *u8 = (sys_mmap(128)) as *u8 183 if status == NX_IGV2_AXIS_OK { 184 let p: *u8 = nx_verdict_phrase_for(axis_id, verdict_value, NX_VP_MODE_TERSE) 185 var c: nx_int = 0 186 var safety: nx_int = 0 187 while safety < 127 { 188 let ch: nx_int = (p[c] as nx_int) & 255 189 if ch == 0 { break } 190 phrase_buf[c] = ch as u8 191 c = c + 1 192 safety = safety + 1 193 } 194 phrase_buf[c] = 0 as u8 195 slot.phrase_buf = phrase_buf 196 slot.phrase_len = c 197 } else { 198 var pmsg: *u8 = "pending" as *u8 199 if status == NX_IGV2_AXIS_PENDING_KEYPOINT { pmsg = "pending keypoint detection" as *u8 } 200 if status == NX_IGV2_AXIS_PENDING_POSE_ESTIMATE { pmsg = "pending pose estimator" as *u8 } 201 if status == NX_IGV2_AXIS_PENDING_VLM { pmsg = "pending vlm grader" as *u8 } 202 if status == NX_IGV2_AXIS_NOT_APPLICABLE { pmsg = "not applicable" as *u8 } 203 if status == NX_IGV2_AXIS_NO_SKIN_DETECTED { pmsg = "no skin pixels detected" as *u8 } 204 if status == NX_IGV2_AXIS_INSUFFICIENT_LANDMARKS { pmsg = "insufficient landmarks" as *u8 } 205 var c2: nx_int = 0 206 var s2: nx_int = 0 207 while s2 < 127 { 208 let ch2: nx_int = (pmsg[c2] as nx_int) & 255 209 if ch2 == 0 { break } 210 phrase_buf[c2] = ch2 as u8 211 c2 = c2 + 1 212 s2 = s2 + 1 213 } 214 phrase_buf[c2] = 0 as u8 215 slot.phrase_buf = phrase_buf 216 slot.phrase_len = c2 217 } 218 return 0 219} 220 221// ===== top-level trigger ========================================= 222 223func nx_image_grade_v2(input: *u8, input_size: nx_int) -> *NxIgv2Report { 224 let r_ptr: *u8 = sys_mmap(NX_IGV2_REPORT_BYTES) 225 let r: *NxIgv2Report = r_ptr as *NxIgv2Report 226 r.error_code = NX_IGV2_OK 227 r.source_format = NX_IGV2_FORMAT_UNKNOWN 228 r.skin_pixel_count = 0 229 r.landmarks = 0 as *NxAnatomyLandmarks 230 231 if input_size < 8 { 232 r.error_code = NX_IGV2_ERR_TOO_SHORT 233 return r 234 } 235 236 let fmt: nx_int = _igv2_detect_format(input, input_size) 237 r.source_format = fmt 238 if fmt == NX_IGV2_FORMAT_UNKNOWN { 239 r.error_code = NX_IGV2_ERR_UNKNOWN_FORMAT 240 return r 241 } 242 243 let axes_bytes: nx_size = (NX_IGV2_N_AXES as nx_size) * NX_IGV2_AXIS_VERDICT_BYTES 244 let axes: *NxIgv2AxisVerdict = (sys_mmap(axes_bytes)) as *NxIgv2AxisVerdict 245 r.axes = axes 246 r.n_axes = NX_IGV2_N_AXES 247 248 var pixels: *u8 = 0 as *u8 249 var n_ch: nx_int = 0 250 var bpp: nx_int = 0 251 var w: nx_int = 0 252 var h: nx_int = 0 253 254 if fmt == NX_IGV2_FORMAT_PNG { 255 let png_r: *NxPngResult = nx_png_decode(input, input_size) 256 if png_r == (0 as *NxPngResult) { r.error_code = NX_IGV2_ERR_DECODER; return r } 257 if png_r.error_code != NX_PNG_OK { r.error_code = NX_IGV2_ERR_DECODER; return r } 258 pixels = png_r.pixels 259 n_ch = png_r.n_channels 260 bpp = png_r.bytes_per_pix 261 if png_r.header != (0 as *NxPngHeader) { 262 w = png_r.header.width 263 h = png_r.header.height 264 } 265 } 266 if fmt == NX_IGV2_FORMAT_JPEG { 267 let jpg_r: *NxJpgResult = nx_jpeg_decode(input, input_size) 268 if jpg_r == (0 as *NxJpgResult) { r.error_code = NX_IGV2_ERR_DECODER; return r } 269 if jpg_r.error_code != NX_JPG_OK { r.error_code = NX_IGV2_ERR_DECODER; return r } 270 pixels = jpg_r.rgb 271 n_ch = 3 272 bpp = 3 273 w = jpg_r.width 274 h = jpg_r.height 275 } 276 if pixels == (0 as *u8) { r.error_code = NX_IGV2_ERR_NULL_PIXELS; return r } 277 r.width = w 278 r.height = h 279 280 // Single-pass: mean R/G/B + skin-pixel sums + skin mask. 281 let n_pixels: nx_int = w * h 282 let mask_bytes: nx_size = (n_pixels as nx_size) * 8 283 let skin_mask: *nx_int = (sys_mmap(mask_bytes)) as *nx_int 284 var sum_r: nx_int = 0 285 var sum_g: nx_int = 0 286 var sum_b: nx_int = 0 287 var sum_sr: nx_int = 0 288 var sum_sg: nx_int = 0 289 var sum_sb: nx_int = 0 290 var n_skin: nx_int = 0 291 var y: nx_int = 0 292 while y < h { 293 var x: nx_int = 0 294 while x < w { 295 let pix_off: nx_int = (y * w + x) * bpp 296 var r_val: nx_int = (pixels[pix_off] as nx_int) & 255 297 var g_val: nx_int = r_val 298 var b_val: nx_int = r_val 299 if n_ch >= 3 { 300 g_val = (pixels[pix_off + 1] as nx_int) & 255 301 b_val = (pixels[pix_off + 2] as nx_int) & 255 302 } 303 sum_r = sum_r + r_val 304 sum_g = sum_g + g_val 305 sum_b = sum_b + b_val 306 let pix_idx: nx_int = y * w + x 307 let is_skin: nx_int = _igv2_is_skin_pixel(r_val, g_val, b_val) 308 skin_mask[pix_idx] = is_skin 309 if is_skin == 1 { 310 sum_sr = sum_sr + r_val 311 sum_sg = sum_sg + g_val 312 sum_sb = sum_sb + b_val 313 n_skin = n_skin + 1 314 } 315 x = x + 1 316 } 317 y = y + 1 318 } 319 r.mean_r = sum_r / n_pixels 320 r.mean_g = sum_g / n_pixels 321 r.mean_b = sum_b / n_pixels 322 r.skin_pixel_count = n_skin 323 r.skin_coverage_q10 = (n_skin * NX_MAGIC_1024) / n_pixels 324 325 // Segmenter. 326 let seg_r: *NxSegResult = nx_segmenter_connected(skin_mask, w, h) 327 if seg_r != (0 as *NxSegResult) { 328 r.n_skin_blobs = seg_r.n_blobs 329 } 330 331 // Landmark labeler. 332 let lm: *NxAnatomyLandmarks = nx_landmark_anatomy_label(seg_r, w, h) 333 r.landmarks = lm 334 335 // ---- per-axis verdict population ---- 336 337 // T1 luma. 338 let mean_luma: nx_int = (306 * r.mean_r + 601 * r.mean_g + 117 * r.mean_b) / NX_MAGIC_1024 339 _igv2_set_axis(axes, 0, NX_IGV2_AX_LUMINANCE, 340 NX_IGV2_AXIS_OK, 0, mean_luma * 4) 341 342 // T0 dimensions. 343 _igv2_set_axis(axes, 1, NX_IGV2_AX_DIMENSIONS, 344 NX_IGV2_AXIS_OK, 0, w * h) 345 346 // T1 skin coverage. 347 _igv2_set_axis(axes, 2, NX_IGV2_AX_SKIN_COVERAGE, 348 NX_IGV2_AXIS_OK, 0, r.skin_coverage_q10) 349 350 // T1 ITA + T4 undertone (skin-localized; same as v1). 351 if n_skin <= 0 { 352 r.skin_mean_r = 0 353 r.skin_mean_g = 0 354 r.skin_mean_b = 0 355 _igv2_set_axis(axes, 3, NX_IGV2_AX_SKIN_TONE_ITA, 356 NX_IGV2_AXIS_NO_SKIN_DETECTED, 0, 0) 357 _igv2_set_axis(axes, 4, NX_IGV2_AX_UNDERTONE, 358 NX_IGV2_AXIS_NO_SKIN_DETECTED, 0, 0) 359 } else { 360 r.skin_mean_r = sum_sr / n_skin 361 r.skin_mean_g = sum_sg / n_skin 362 r.skin_mean_b = sum_sb / n_skin 363 let lab: *NxLabColor = nx_lab_from_rgb(r.skin_mean_r, r.skin_mean_g, r.skin_mean_b) 364 let ita_r: *NxSkinToneIta = nx_skin_tone_ita_compute( 365 lab.l_q10, lab.a_q10, lab.b_q10) 366 _igv2_set_axis(axes, 3, NX_IGV2_AX_SKIN_TONE_ITA, 367 NX_IGV2_AXIS_OK, ita_r.band, ita_r.ita_deg_q10) 368 let und_r: *NxUndertoneResult = nx_undertone_classify( 369 lab.a_q10, lab.b_q10) 370 _igv2_set_axis(axes, 4, NX_IGV2_AX_UNDERTONE, 371 NX_IGV2_AXIS_OK, und_r.verdict, und_r.conf_q10) 372 } 373 374 // T101 landmark confidence (new in v2). 375 _igv2_set_axis(axes, 5, NX_IGV2_AX_LANDMARK_CONF, 376 NX_IGV2_AXIS_OK, 0, lm.confidence_q10) 377 378 // T6 silhouette integrity on torso blob (v2 promotion). 379 if lm.has_torso == 1 { 380 if seg_r != (0 as *NxSegResult) { 381 if seg_r.n_blobs >= 1 { 382 let largest: *NxSegBlob = nx_segmenter_largest_blob(seg_r) 383 if largest != (0 as *NxSegBlob) { 384 let silh: *NxSilhouetteResult = nx_silhouette_integrity( 385 pixels, w, h, bpp, n_ch, seg_r.label_map, largest) 386 if silh != (0 as *NxSilhouetteResult) { 387 _igv2_set_axis(axes, 6, NX_IGV2_AX_SILHOUETTE, 388 NX_IGV2_AXIS_OK, silh.verdict, silh.fraction_strong) 389 } else { 390 _igv2_set_axis(axes, 6, NX_IGV2_AX_SILHOUETTE, 391 NX_IGV2_AXIS_INSUFFICIENT_LANDMARKS, 0, 0) 392 } 393 } else { 394 _igv2_set_axis(axes, 6, NX_IGV2_AX_SILHOUETTE, 395 NX_IGV2_AXIS_INSUFFICIENT_LANDMARKS, 0, 0) 396 } 397 } else { 398 _igv2_set_axis(axes, 6, NX_IGV2_AX_SILHOUETTE, 399 NX_IGV2_AXIS_NO_SKIN_DETECTED, 0, 0) 400 } 401 } else { 402 _igv2_set_axis(axes, 6, NX_IGV2_AX_SILHOUETTE, 403 NX_IGV2_AXIS_INSUFFICIENT_LANDMARKS, 0, 0) 404 } 405 } else { 406 _igv2_set_axis(axes, 6, NX_IGV2_AX_SILHOUETTE, 407 NX_IGV2_AXIS_INSUFFICIENT_LANDMARKS, 0, 0) 408 } 409 410 // T6 limb count (v2 promotion using landmark output). 411 if lm.has_torso == 1 { 412 // Build candidate list from landmark bboxes. 413 let cands: *NxLimbCandidate = 414 (sys_mmap(NX_LIMB_CANDIDATE_BYTES * 4)) as *NxLimbCandidate 415 var n_cands: nx_int = 0 416 if lm.arm_l != (0 as *NxBBox) { 417 if lm.arm_l.valid == 1 { 418 let cl: *NxLimbCandidate = 419 (cands as *u8 + (n_cands as nx_size) * NX_LIMB_CANDIDATE_BYTES) as *NxLimbCandidate 420 cl.end_x = (lm.arm_l.x0 + lm.arm_l.x1) / 2 421 cl.end_y = lm.arm_l.y0 422 cl.role = NX_LIMB_ROLE_ARM 423 cl.bbox_x0 = lm.arm_l.x0 424 cl.bbox_y0 = lm.arm_l.y0 425 cl.bbox_x1 = lm.arm_l.x1 426 cl.bbox_y1 = lm.arm_l.y1 427 n_cands = n_cands + 1 428 } 429 } 430 if lm.arm_r != (0 as *NxBBox) { 431 if lm.arm_r.valid == 1 { 432 let cr: *NxLimbCandidate = 433 (cands as *u8 + (n_cands as nx_size) * NX_LIMB_CANDIDATE_BYTES) as *NxLimbCandidate 434 cr.end_x = (lm.arm_r.x0 + lm.arm_r.x1) / 2 435 cr.end_y = lm.arm_r.y0 436 cr.role = NX_LIMB_ROLE_ARM 437 cr.bbox_x0 = lm.arm_r.x0 438 cr.bbox_y0 = lm.arm_r.y0 439 cr.bbox_x1 = lm.arm_r.x1 440 cr.bbox_y1 = lm.arm_r.y1 441 n_cands = n_cands + 1 442 } 443 } 444 if lm.leg_l != (0 as *NxBBox) { 445 if lm.leg_l.valid == 1 { 446 let ll: *NxLimbCandidate = 447 (cands as *u8 + (n_cands as nx_size) * NX_LIMB_CANDIDATE_BYTES) as *NxLimbCandidate 448 ll.end_x = (lm.leg_l.x0 + lm.leg_l.x1) / 2 449 ll.end_y = lm.leg_l.y0 450 ll.role = NX_LIMB_ROLE_LEG 451 ll.bbox_x0 = lm.leg_l.x0 452 ll.bbox_y0 = lm.leg_l.y0 453 ll.bbox_x1 = lm.leg_l.x1 454 ll.bbox_y1 = lm.leg_l.y1 455 n_cands = n_cands + 1 456 } 457 } 458 if lm.leg_r != (0 as *NxBBox) { 459 if lm.leg_r.valid == 1 { 460 let lr: *NxLimbCandidate = 461 (cands as *u8 + (n_cands as nx_size) * NX_LIMB_CANDIDATE_BYTES) as *NxLimbCandidate 462 lr.end_x = (lm.leg_r.x0 + lm.leg_r.x1) / 2 463 lr.end_y = lm.leg_r.y0 464 lr.role = NX_LIMB_ROLE_LEG 465 lr.bbox_x0 = lm.leg_r.x0 466 lr.bbox_y0 = lm.leg_r.y0 467 lr.bbox_x1 = lm.leg_r.x1 468 lr.bbox_y1 = lm.leg_r.y1 469 n_cands = n_cands + 1 470 } 471 } 472 let lc: *NxLimbCountResult = nx_limb_count_check( 473 lm.torso.x0, lm.torso.y0, lm.torso.x1, lm.torso.y1, 474 cands, n_cands, 10) 475 if lc != (0 as *NxLimbCountResult) { 476 _igv2_set_axis(axes, 7, NX_IGV2_AX_LIMB_COUNT, 477 NX_IGV2_AXIS_OK, lc.verdict, lc.delta_from_norm) 478 } else { 479 _igv2_set_axis(axes, 7, NX_IGV2_AX_LIMB_COUNT, 480 NX_IGV2_AXIS_INSUFFICIENT_LANDMARKS, 0, 0) 481 } 482 } else { 483 _igv2_set_axis(axes, 7, NX_IGV2_AX_LIMB_COUNT, 484 NX_IGV2_AXIS_INSUFFICIENT_LANDMARKS, 0, 0) 485 } 486 487 // Still-pending axes (need keypoint / pose estimators upstream). 488 _igv2_set_axis(axes, 8, NX_IGV2_AX_VEIN_SIGNAL, 489 NX_IGV2_AXIS_PENDING_KEYPOINT, 0, 0) 490 _igv2_set_axis(axes, 9, NX_IGV2_AX_SUBSURFACE, 491 NX_IGV2_AXIS_PENDING_KEYPOINT, 0, 0) 492 _igv2_set_axis(axes, 10, NX_IGV2_AX_MICRO_EXPRESSION, 493 NX_IGV2_AXIS_PENDING_KEYPOINT, 0, 0) 494 _igv2_set_axis(axes, 11, NX_IGV2_AX_FINGER_COUNT, 495 NX_IGV2_AXIS_PENDING_KEYPOINT, 0, 0) 496 _igv2_set_axis(axes, 12, NX_IGV2_AX_POSE_NATURALNESS, 497 NX_IGV2_AXIS_PENDING_POSE_ESTIMATE, 0, 0) 498 _igv2_set_axis(axes, 13, NX_IGV2_AX_SHADOW_DIRECTION, 499 NX_IGV2_AXIS_PENDING_KEYPOINT, 0, 0) 500 _igv2_set_axis(axes, 14, NX_IGV2_AX_SURFACE_PLANE, 501 NX_IGV2_AXIS_PENDING_KEYPOINT, 0, 0) 502 503 return r 504} 505 506// ===== self-test ================================================= 507 508func main() -> nx_int { 509 // ---- TOO_SHORT path ---- 510 let s_short: *u8 = (sys_mmap(4)) as *u8 511 let r_short: *NxIgv2Report = nx_image_grade_v2(s_short, 4) 512 if r_short.error_code != NX_IGV2_ERR_TOO_SHORT { return 1 } 513 514 // ---- UNKNOWN_FORMAT path ---- 515 let s_unk: *u8 = (sys_mmap(8)) as *u8 516 var i: nx_int = 0 517 while i < 8 { 518 s_unk[i] = 0 as u8 519 i = i + 1 520 } 521 let r_unk: *NxIgv2Report = nx_image_grade_v2(s_unk, 8) 522 if r_unk.error_code != NX_IGV2_ERR_UNKNOWN_FORMAT { return 2 } 523 524 // ---- PNG magic detect ---- 525 let pm: *u8 = (sys_mmap(4)) as *u8 526 pm[0] = 137 as u8 527 pm[1] = 80 as u8 528 pm[2] = 78 as u8 529 pm[3] = 71 as u8 530 if _igv2_detect_format(pm, 4) != NX_IGV2_FORMAT_PNG { return 10 } 531 532 // ---- JPEG magic detect ---- 533 let jm: *u8 = (sys_mmap(4)) as *u8 534 jm[0] = 255 as u8 535 jm[1] = 216 as u8 536 if _igv2_detect_format(jm, 4) != NX_IGV2_FORMAT_JPEG { return 11 } 537 538 // ---- Kovac skin rule (sanity) ---- 539 if _igv2_is_skin_pixel(200, 170, 150) != 1 { return 20 } 540 if _igv2_is_skin_pixel(50, 50, 50) != 0 { return 21 } 541 542 return 0 543}