code wiki / (root) / nx_body_measurement_extract_test.nx

nx_body_measurement_extract_test.nx source

↩ module page · 150 lines · 6723 B

1// nx_body_measurement_extract_test.nx -- smoke for body-from-silhouette. 2 3import "nx_syscalls.nx" 4import "nx_tier.nx" 5import "nx_image.nx" 6import "nx_garment_fit_match.nx" 7import "nx_body_measurement_extract.nx" 8 9// Helper: paint a rectangle of foreground 1s into a silhouette image 10// covering rows [y0, y1) and columns centered on cx with half-width hw. 11func _paint_band(sil: *Image, y0: nx_int, y1: nx_int, cx: nx_int, hw: nx_int) -> nx_int { 12 let w: nx_int = sil.width 13 var y: nx_int = y0 14 while y < y1 { 15 var x: nx_int = cx - hw 16 let xe: nx_int = cx + hw 17 if x < 0 { x = 0 } 18 while x < xe { 19 if x < w { 20 nx_image_set(sil, x, y, 0, 1) 21 } 22 x = x + 1 23 } 24 y = y + 1 25 } 26 return 0 27} 28 29func main() -> nx_int { 30 // === Test 1: synthetic silhouette, known dimensions, verify mm output === 31 // 32 // Build a 100-px-tall silhouette in a 128x150 frame: 33 // shoulders at row 20, half-width 16 (full width 32 px) 34 // bust at row 30, half-width 18 (full width 36 px) 35 // waist at row 50, half-width 14 (full width 28 px) 36 // hip at row 70, half-width 17 (full width 34 px) 37 // inseam at row 80 38 // ankle at row 100 39 // top-of-head at row 5 40 // 41 // Reference object: 50 pixels = 100 mm (so 1 px = 2 mm). 42 // 43 // Expected mm: 44 // shoulder_mm = 32 * 2 = 64 45 // bust_mm = 36 * 2 = 72 46 // waist_mm = 28 * 2 = 56 47 // hip_mm = 34 * 2 = 68 48 // height_mm = (100 - 5) * 2 = 190 49 // inseam_mm = (100 - 80) * 2 = 40 50 let sil: *Image = nx_image_alloc(128, 150, 1) 51 52 // Paint the silhouette in five bands (shoulders/bust/waist/hip/legs). 53 _paint_band(sil, 5, 20, 64, 8) // head/neck region 54 _paint_band(sil, 20, 30, 64, 16) // shoulders 55 _paint_band(sil, 30, 50, 64, 18) // torso narrowing to waist 56 _paint_band(sil, 50, 70, 64, 14) // waist (narrowest -- using 14 hw) 57 _paint_band(sil, 70, 80, 64, 17) // hip 58 _paint_band(sil, 80, 101, 64, 12) // legs 59 60 let landmarks: *nx_int = (sys_mmap(NX_BME_LM_COUNT * NX_SIZEOF_NX_INT)) as *nx_int 61 landmarks[NX_BME_LM_TOP_OF_HEAD] = 5 62 landmarks[NX_BME_LM_SHOULDER] = 25 // mid-shoulder band 63 landmarks[NX_BME_LM_BUST] = 40 // mid-torso band 64 landmarks[NX_BME_LM_WAIST] = 60 // mid-waist band 65 landmarks[NX_BME_LM_HIP] = 75 // mid-hip band 66 landmarks[NX_BME_LM_INSEAM_TOP] = 80 67 landmarks[NX_BME_LM_ANKLE] = 100 68 69 let rep: *BodyExtractReport = (sys_mmap(16 * NX_SIZEOF_NX_INT)) as *BodyExtractReport 70 // Reference: 50 px = 100 mm. Scale = 50/100 * Q10 = 512. 71 let rc: nx_int = nx_body_measurement_extract(sil, 50, 100, landmarks, 72 NX_FIT_ARCHETYPE_AVERAGE, rep) 73 if rc != 0 { return 1 } 74 // Scale check 75 if rep.scale_px_per_mm_q10 != 512 { return 2 } 76 // Shoulder width: 32 px * 2 mm/px = 64 mm 77 if rep.body.shoulder_width_mm != 64 { return 3 } 78 // Bust: paint at row 30-50 half-width 18 -> width 36 px -> 72 mm 79 if rep.body.bust_mm != 72 { return 4 } 80 // Waist: row 60 falls in the 50-70 band with half-width 14 -> 28 px -> 56 mm 81 if rep.body.waist_mm != 56 { return 5 } 82 // Hip: row 75 in 70-80 band, half-width 17 -> 34 px -> 68 mm 83 if rep.body.hip_mm != 68 { return 6 } 84 // Height: (100 - 5) * 2 = 190 mm 85 if rep.body.height_mm != 190 { return 7 } 86 // Inseam: (100 - 80) * 2 = 40 mm 87 if rep.body.inseam_mm != 40 { return 8 } 88 // Archetype propagated 89 if rep.body.archetype != NX_FIT_ARCHETYPE_AVERAGE { return 9 } 90 91 // === Test 2: fidelity-HIGH band for clean silhouette === 92 if rep.fidelity_band != NX_BME_FIDELITY_HIGH { return 10 } 93 if rep.fidelity_q10 < 768 { return 11 } 94 95 // === Test 3: reference_pixels = 0 -> error (invalid scale) === 96 let rep2: *BodyExtractReport = (sys_mmap(16 * NX_SIZEOF_NX_INT)) as *BodyExtractReport 97 if nx_body_measurement_extract(sil, 0, 100, landmarks, 98 NX_FIT_ARCHETYPE_AVERAGE, rep2) == 0 { return 20 } 99 100 // === Test 4: reference_mm = 0 -> error === 101 if nx_body_measurement_extract(sil, 50, 0, landmarks, 102 NX_FIT_ARCHETYPE_AVERAGE, rep2) == 0 { return 21 } 103 104 // === Test 5: empty silhouette (zero area) -> fidelity LOW or UNUSABLE === 105 let sil_empty: *Image = nx_image_alloc(64, 100, 1) 106 nx_body_measurement_extract(sil_empty, 50, 100, landmarks, 107 NX_FIT_ARCHETYPE_AVERAGE, rep2) 108 // Empty silhouette: zero widths everywhere -> fidelity dropped 109 // through several /2 reductions. Should fall in LOW or UNUSABLE. 110 if rep2.fidelity_band == NX_BME_FIDELITY_HIGH { return 30 } 111 112 // === Test 6: narrowest-width search helper === 113 // In the silhouette: 50-70 band has half-width 14 (width 28); the 114 // narrowest in that range. 115 let narrow: nx_int = nx_bme_narrowest_width_in_range(sil, 50, 70) 116 if narrow != 28 { return 40 } 117 118 // === Test 7: widest-width search helper === 119 // Bust band 30-50 has half-width 18 (width 36); widest in that 120 // range. 121 let widest: nx_int = nx_bme_widest_width_in_range(sil, 30, 50) 122 if widest != 36 { return 50 } 123 124 // === Test 8: extracted dims feed into nx_garment_fit_match === 125 // Body: 72/56/68/190. Garment: 75/58/70/180/65/sheath. 126 // Bust delta +3, waist delta +2, hip delta +2; all within +-5 -> PERFECT_TAILORED. 127 let g: *GarmentDimensions = (sys_mmap(10 * NX_SIZEOF_NX_INT)) as *GarmentDimensions 128 g.bust_mm = 75 129 g.waist_mm = 58 130 g.hip_mm = 70 131 g.length_mm = 180 132 g.shoulder_width_mm = 65 133 g.sleeve_length_mm = 0 134 g.inseam_mm = 0 135 g.cut = NX_FIT_CUT_WRAP 136 g.extracted_fidelity_q10 = 900 137 let fit_rep: *FitMatchReport = (sys_mmap(16 * NX_SIZEOF_NX_INT)) as *FitMatchReport 138 nx_garment_fit_match(g, rep.body, fit_rep) 139 // bust 75-72=3, waist 58-56=2, hip 70-68=2 all <=5 -> PERFECT 140 if fit_rep.verdict != NX_FIT_PERFECT_TAILORED { return 60 } 141 142 // === Test 9: sealed-enum validity === 143 if nx_body_measurement_fidelity_is_valid(NX_BME_FIDELITY_HIGH) != 1 { return 70 } 144 if nx_body_measurement_fidelity_is_valid(NX_BME_FIDELITY_UNUSABLE) != 1 { return 71 } 145 if nx_body_measurement_fidelity_is_valid(99) != 0 { return 72 } 146 if nx_body_measurement_landmark_is_valid(NX_BME_LM_WAIST) != 1 { return 73 } 147 if nx_body_measurement_landmark_is_valid(99) != 0 { return 74 } 148 149 return 0 150}