nx_outfit_coverage.nx source
↩ module page · 107 lines · 3915 B
1// nx_outfit_coverage.nx -- outfit-coverage detector.
2//
3// Non-skin density inside expanded torso bbox -> NUDE/MINIMAL/PARTIAL/DRESSED.
4
5// nx_safety_envelope:
6// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
7// sil_target: SIL1
8// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
9// verdict: NOT_YET_EVALUATED
10
11import "nx_syscalls.nx"
12import "nx_image.nx"
13import "nx_color_v2.nx"
14import "nx_region.nx"
15const NX_MAGIC_1024: i64 = 1024
16
17const NX_OC_UNKNOWN: i64 = 0
18const NX_OC_NUDE: i64 = 1
19const NX_OC_MINIMAL: i64 = 2
20const NX_OC_PARTIAL: i64 = 3
21const NX_OC_DRESSED: i64 = 4
22
23const NX_OC_NUDE_MAX_Q10: i64 = 154
24const NX_OC_MINIMAL_MAX_Q10: i64 = 358
25const NX_OC_PARTIAL_MAX_Q10: i64 = 614
26const NX_OC_EXPAND_PCT: i64 = 30
27
28struct OutfitCoverageVerdict {
29 kind: i64,
30 torso_present: i64,
31 bbox_area: i64,
32 expanded_bbox_area: i64,
33 skin_pixels: i64,
34 non_skin_pixels: i64,
35 coverage_q10: i64,
36 severity_q10: i64,
37 fidelity_q10: i64,
38}
39
40func nx_outfit_coverage_compute(rgb: *Image, verdict: *OutfitCoverageVerdict) -> i64 {
41 verdict.kind = NX_OC_UNKNOWN
42 verdict.torso_present = 0
43 verdict.bbox_area = 0
44 verdict.expanded_bbox_area = 0
45 verdict.skin_pixels = 0
46 verdict.non_skin_pixels = 0
47 verdict.coverage_q10 = 0
48 verdict.severity_q10 = 0
49 verdict.fidelity_q10 = 0
50
51 let set: *RegionSet = nx_region_segment(rgb)
52 let t_idx: i64 = nx_region_find_kind(set, NX_REGION_TORSO)
53 if t_idx < 0 { return 0 }
54 verdict.torso_present = 1
55
56 let w: i64 = rgb.width
57 let h: i64 = rgb.height
58 let t_min_x: i64 = nx_region_get_field(set, t_idx, NX_REGION_F_BBOX_MIN_X)
59 let t_min_y: i64 = nx_region_get_field(set, t_idx, NX_REGION_F_BBOX_MIN_Y)
60 let t_max_x: i64 = nx_region_get_field(set, t_idx, NX_REGION_F_BBOX_MAX_X)
61 let t_max_y: i64 = nx_region_get_field(set, t_idx, NX_REGION_F_BBOX_MAX_Y)
62 let t_w: i64 = t_max_x - t_min_x + 1
63 let t_h: i64 = t_max_y - t_min_y + 1
64 verdict.bbox_area = t_w * t_h
65
66 let exp_x: i64 = (t_w * NX_OC_EXPAND_PCT) / 100
67 let exp_y: i64 = (t_h * NX_OC_EXPAND_PCT) / 100
68 var ex_min_x: i64 = t_min_x - exp_x
69 var ex_min_y: i64 = t_min_y - exp_y
70 var ex_max_x: i64 = t_max_x + exp_x
71 var ex_max_y: i64 = t_max_y + exp_y
72 if ex_min_x < 0 { ex_min_x = 0 }
73 if ex_min_y < 0 { ex_min_y = 0 }
74 if ex_max_x >= w { ex_max_x = w - 1 }
75 if ex_max_y >= h { ex_max_y = h - 1 }
76 verdict.expanded_bbox_area = (ex_max_x - ex_min_x + 1) * (ex_max_y - ex_min_y + 1)
77
78 let mask: *Image = nx_color_skin_mask(rgb)
79 var skin_count: i64 = 0
80 var nonskin_count: i64 = 0
81 var y: i64 = ex_min_y
82 while y <= ex_max_y {
83 var x: i64 = ex_min_x
84 while x <= ex_max_x {
85 if nx_image_get(mask, x, y, 0) > 0 { skin_count = skin_count + 1 }
86 if nx_image_get(mask, x, y, 0) == 0 { nonskin_count = nonskin_count + 1 }
87 x = x + 1
88 }
89 y = y + 1
90 }
91 verdict.skin_pixels = skin_count
92 verdict.non_skin_pixels = nonskin_count
93 let total: i64 = skin_count + nonskin_count
94 if total > 0 { verdict.coverage_q10 = (nonskin_count * NX_MAGIC_1024) / total }
95
96 let cov: i64 = verdict.coverage_q10
97 if cov < NX_OC_NUDE_MAX_Q10 { verdict.kind = NX_OC_NUDE }
98 if cov >= NX_OC_NUDE_MAX_Q10 {
99 if cov < NX_OC_MINIMAL_MAX_Q10 { verdict.kind = NX_OC_MINIMAL }
100 if cov >= NX_OC_MINIMAL_MAX_Q10 {
101 if cov < NX_OC_PARTIAL_MAX_Q10 { verdict.kind = NX_OC_PARTIAL }
102 if cov >= NX_OC_PARTIAL_MAX_Q10 { verdict.kind = NX_OC_DRESSED }
103 }
104 }
105 verdict.fidelity_q10 = nx_region_get_field(set, t_idx, NX_REGION_F_CONF_Q10)
106 return 0
107}