nx_garment_extract_measurements_test.nx source
↩ module page · 151 lines · 6971 B
1// nx_garment_extract_measurements_test.nx -- smoke for product-photo extractor.
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"
8import "nx_garment_extract_measurements.nx"
9
10func _paint_band(sil: *Image, y0: nx_int, y1: nx_int, cx: nx_int, hw: nx_int) -> nx_int {
11 let w: nx_int = sil.width
12 var y: nx_int = y0
13 while y < y1 {
14 var x: nx_int = cx - hw
15 let xe: nx_int = cx + hw
16 if x < 0 { x = 0 }
17 while x < xe {
18 if x < w {
19 nx_image_set(sil, x, y, 0, 1)
20 }
21 x = x + 1
22 }
23 y = y + 1
24 }
25 return 0
26}
27
28func main() -> nx_int {
29 // === Test 1: synthetic dress silhouette, verify mm output ===
30 //
31 // Dress in a 128x150 frame:
32 // top (shoulder) row 10, half-width 18 (full 36 px)
33 // bust row 25, half-width 20 (full 40 px)
34 // waist row 50, half-width 14 (full 28 px)
35 // hip row 75, half-width 22 (full 44 px)
36 // hem row 110
37 // sleeveless
38 //
39 // Reference: 50 px = 100 mm (so 1 px = 2 mm).
40 let sil: *Image = nx_image_alloc(128, 150, 1)
41 _paint_band(sil, 10, 25, 64, 18) // top of dress (shoulder seam)
42 _paint_band(sil, 25, 50, 64, 20) // bust
43 _paint_band(sil, 50, 75, 64, 14) // waist (narrowest)
44 _paint_band(sil, 75, 110, 64, 22) // hip + hem flare
45
46 let landmarks: *nx_int = (sys_mmap(NX_GME_LM_COUNT * NX_SIZEOF_NX_INT)) as *nx_int
47 landmarks[NX_GME_LM_TOP] = 10
48 landmarks[NX_GME_LM_BUST] = 30 // mid-bust band
49 landmarks[NX_GME_LM_WAIST] = 60 // mid-waist band
50 landmarks[NX_GME_LM_HIP] = 85 // mid-hip band
51 landmarks[NX_GME_LM_HEM] = 110
52 landmarks[NX_GME_LM_SLEEVE_END] = 0 // sleeveless
53 landmarks[NX_GME_LM_INSEAM_TOP] = 0 // not pants
54
55 let rep: *GarmentExtractReport = (sys_mmap(16 * NX_SIZEOF_NX_INT)) as *GarmentExtractReport
56 let rc: nx_int = nx_garment_extract_measurements(
57 sil, 50, 100, landmarks, NX_FIT_CUT_FIT_AND_FLARE, rep
58 )
59 if rc != 0 { return 1 }
60 if rep.scale_px_per_mm_q10 != 512 { return 2 }
61 // Top width: row 10 in the 10-25 band, half-width 18 -> 36 px -> 72 mm
62 if rep.garment.shoulder_width_mm != 72 { return 3 }
63 // Bust: row 30 in 25-50 band, half-width 20 -> 40 px -> 80 mm
64 if rep.garment.bust_mm != 80 { return 4 }
65 // Waist: row 60 in 50-75 band, half-width 14 -> 28 px -> 56 mm
66 if rep.garment.waist_mm != 56 { return 5 }
67 // Hip: row 85 in 75-110 band, half-width 22 -> 44 px -> 88 mm
68 if rep.garment.hip_mm != 88 { return 6 }
69 // Length: (110 - 10) * 2 = 200 mm
70 if rep.garment.length_mm != 200 { return 7 }
71 // Sleeveless -> sleeve_length_mm == 0
72 if rep.garment.sleeve_length_mm != 0 { return 8 }
73 // Not pants -> inseam_mm == 0
74 if rep.garment.inseam_mm != 0 { return 9 }
75 // Cut propagated
76 if rep.garment.cut != NX_FIT_CUT_FIT_AND_FLARE { return 10 }
77
78 // === Test 2: fidelity HIGH on clean silhouette ===
79 if rep.fidelity_band != NX_GME_FIDELITY_HIGH { return 20 }
80
81 // === Test 3: invalid scale (zero reference) -> error ===
82 let rep2: *GarmentExtractReport = (sys_mmap(16 * NX_SIZEOF_NX_INT)) as *GarmentExtractReport
83 if nx_garment_extract_measurements(sil, 0, 100, landmarks,
84 NX_FIT_CUT_FIT_AND_FLARE, rep2) == 0 { return 30 }
85 if nx_garment_extract_measurements(sil, 50, 0, landmarks,
86 NX_FIT_CUT_FIT_AND_FLARE, rep2) == 0 { return 31 }
87
88 // === Test 4: sleeved dress -- sleeve_end_y populated ===
89 let sil_sleeved: *Image = nx_image_alloc(128, 150, 1)
90 // Same dress shape with sleeves dropping to row 50
91 _paint_band(sil_sleeved, 10, 25, 64, 18)
92 _paint_band(sil_sleeved, 25, 50, 64, 20)
93 _paint_band(sil_sleeved, 50, 75, 64, 14)
94 _paint_band(sil_sleeved, 75, 110, 64, 22)
95 let lm_s: *nx_int = (sys_mmap(NX_GME_LM_COUNT * NX_SIZEOF_NX_INT)) as *nx_int
96 lm_s[NX_GME_LM_TOP] = 10
97 lm_s[NX_GME_LM_BUST] = 30
98 lm_s[NX_GME_LM_WAIST] = 60
99 lm_s[NX_GME_LM_HIP] = 85
100 lm_s[NX_GME_LM_HEM] = 110
101 lm_s[NX_GME_LM_SLEEVE_END] = 50 // drops to mid-torso (3/4 sleeve)
102 lm_s[NX_GME_LM_INSEAM_TOP] = 0
103 nx_garment_extract_measurements(sil_sleeved, 50, 100, lm_s,
104 NX_FIT_CUT_SHEATH, rep2)
105 // sleeve length: (50 - 10) * 2 = 80 mm
106 if rep2.garment.sleeve_length_mm != 80 { return 40 }
107
108 // === Test 5: pants -- inseam_top_y populated ===
109 let sil_pants: *Image = nx_image_alloc(128, 150, 1)
110 _paint_band(sil_pants, 10, 30, 64, 18) // waistband
111 _paint_band(sil_pants, 30, 50, 64, 22) // hip
112 _paint_band(sil_pants, 50, 110, 50, 8) // left leg
113 _paint_band(sil_pants, 50, 110, 78, 8) // right leg
114 let lm_p: *nx_int = (sys_mmap(NX_GME_LM_COUNT * NX_SIZEOF_NX_INT)) as *nx_int
115 lm_p[NX_GME_LM_TOP] = 10
116 lm_p[NX_GME_LM_BUST] = 20 // not applicable for pants -- use waistband
117 lm_p[NX_GME_LM_WAIST] = 20 // waistband
118 lm_p[NX_GME_LM_HIP] = 40 // hip
119 lm_p[NX_GME_LM_HEM] = 110
120 lm_p[NX_GME_LM_SLEEVE_END] = 0
121 lm_p[NX_GME_LM_INSEAM_TOP] = 50 // crotch
122 nx_garment_extract_measurements(sil_pants, 50, 100, lm_p,
123 NX_FIT_CUT_SHIFT, rep2)
124 // inseam: (110 - 50) * 2 = 120 mm
125 if rep2.garment.inseam_mm != 120 { return 50 }
126
127 // === Test 6: extracted dims feed into nx_garment_fit_match ===
128 // Use Test 1's garment vs a body that's a perfect match.
129 let body: *BodyDimensions = (sys_mmap(8 * NX_SIZEOF_NX_INT)) as *BodyDimensions
130 body.bust_mm = 80 // matches garment bust exactly
131 body.waist_mm = 56 // matches
132 body.hip_mm = 88 // matches
133 body.height_mm = 1650
134 body.shoulder_width_mm = 72
135 body.arm_length_mm = 600
136 body.inseam_mm = 0
137 body.archetype = NX_FIT_ARCHETYPE_AVERAGE
138 let fit_rep: *FitMatchReport = (sys_mmap(16 * NX_SIZEOF_NX_INT)) as *FitMatchReport
139 nx_garment_fit_match(rep.garment, body, fit_rep)
140 // All axes exact match -> PERFECT_TAILORED on a FIT_AND_FLARE for AVERAGE archetype
141 if fit_rep.verdict != NX_FIT_PERFECT_TAILORED { return 60 }
142
143 // === Test 7: sealed-enum validity ===
144 if nx_garment_extract_fidelity_is_valid(NX_GME_FIDELITY_HIGH) != 1 { return 70 }
145 if nx_garment_extract_fidelity_is_valid(NX_GME_FIDELITY_UNUSABLE) != 1 { return 71 }
146 if nx_garment_extract_fidelity_is_valid(99) != 0 { return 72 }
147 if nx_garment_extract_landmark_is_valid(NX_GME_LM_HEM) != 1 { return 73 }
148 if nx_garment_extract_landmark_is_valid(99) != 0 { return 74 }
149
150 return 0
151}