code wiki / (root) / nx_mise_en_scene.nx

nx_mise_en_scene.nx source

↩ module page · 99 lines · 3325 B

1// nx_mise_en_scene.nx -- background-clutter detector. 2// V2 calibrated thresholds (F1=0.86 at 50-150 band). 3 4// nx_safety_envelope: 5// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 6// sil_target: SIL1 7// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 8// verdict: NOT_YET_EVALUATED 9 10import "nx_syscalls.nx" 11import "nx_image.nx" 12import "nx_color_v2.nx" 13import "nx_region.nx" 14const NX_MAGIC_1024: i64 = 1024 15 16const NX_MES_CLEAN: i64 = 0 17const NX_MES_MODERATE: i64 = 1 18const NX_MES_CLUTTERED: i64 = 2 19const NX_MES_CHAOTIC: i64 = 3 20 21const NX_MES_EDGE_THRESH: i64 = 40 22const NX_MES_CLEAN_MAX_Q10: i64 = 50 23const NX_MES_MOD_MAX_Q10: i64 = 100 24const NX_MES_CLUT_MAX_Q10: i64 = 200 25 26struct MiseEnSceneVerdict { 27 kind: i64, 28 background_area: i64, 29 background_edge_pixels: i64, 30 edge_density_q10: i64, 31 severity_q10: i64, 32 fidelity_q10: i64, 33} 34 35func nx_mise_en_scene_compute(rgb: *Image, verdict: *MiseEnSceneVerdict) -> i64 { 36 verdict.kind = NX_MES_CLEAN 37 verdict.background_area = 0 38 verdict.background_edge_pixels = 0 39 verdict.edge_density_q10 = 0 40 verdict.severity_q10 = 0 41 verdict.fidelity_q10 = 0 42 43 let set: *RegionSet = nx_region_segment(rgb) 44 let bg_idx: i64 = nx_region_find_kind(set, NX_REGION_BACKGROUND) 45 if bg_idx < 0 { return 0 } 46 let bg_area: i64 = nx_region_get_field(set, bg_idx, NX_REGION_F_AREA_PX) 47 verdict.background_area = bg_area 48 if bg_area <= 0 { return 0 } 49 50 let mask: *Image = nx_color_skin_mask(rgb) 51 let gray: *Image = nx_image_to_grayscale(rgb) 52 let gx: *ImageS64 = nx_image_sobel_x(gray) 53 let gy: *ImageS64 = nx_image_sobel_y(gray) 54 let mag: *ImageS64 = nx_image_gradient_magnitude(gx, gy) 55 56 let w: i64 = gray.width 57 let h: i64 = gray.height 58 var edge_count: i64 = 0 59 var y: i64 = 0 60 while y < h { 61 var x: i64 = 0 62 while x < w { 63 if nx_image_get(mask, x, y, 0) == 0 { 64 if nx_image_s64_get(mag, x, y) > NX_MES_EDGE_THRESH { 65 edge_count = edge_count + 1 66 } 67 } 68 x = x + 1 69 } 70 y = y + 1 71 } 72 verdict.background_edge_pixels = edge_count 73 let density: i64 = (edge_count * NX_MAGIC_1024) / bg_area 74 verdict.edge_density_q10 = density 75 76 if density < NX_MES_CLEAN_MAX_Q10 { verdict.kind = NX_MES_CLEAN } 77 if density >= NX_MES_CLEAN_MAX_Q10 { 78 if density < NX_MES_MOD_MAX_Q10 { verdict.kind = NX_MES_MODERATE } 79 if density >= NX_MES_MOD_MAX_Q10 { 80 if density < NX_MES_CLUT_MAX_Q10 { verdict.kind = NX_MES_CLUTTERED } 81 if density >= NX_MES_CLUT_MAX_Q10 { verdict.kind = NX_MES_CHAOTIC } 82 } 83 } 84 85 var sev: i64 = 0 86 if verdict.kind == NX_MES_MODERATE { sev = 256 } 87 if verdict.kind == NX_MES_CLUTTERED { sev = 640 } 88 if verdict.kind == NX_MES_CHAOTIC { sev = NX_MAGIC_1024 } 89 verdict.severity_q10 = sev 90 91 let total: i64 = w * h 92 if total > 0 { 93 var fid: i64 = (bg_area * NX_MAGIC_1024) / total 94 fid = fid * 2 95 if fid > NX_MAGIC_1024 { fid = NX_MAGIC_1024 } 96 verdict.fidelity_q10 = fid 97 } 98 return 0 99}