code wiki / (root) / nx_image_clarity.nx

nx_image_clarity.nx source

↩ module page · 113 lines · 4030 B

1// nx_clarity.nx -- photographic vs image-level clarity. 2// Per-region Sobel + salience-peak window + focal-vs-ambient ratio. 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_salience.nx" 13 14const NX_CLARITY_NONE: i64 = 0 15const NX_CLARITY_PHOTOGRAPHIC: i64 = 1 16const NX_CLARITY_IMAGE_LEVEL: i64 = 2 17const NX_CLARITY_MIXED: i64 = 3 18 19const NX_CLARITY_FOCAL_RADIUS_Q10: i64 = 128 20const NX_CLARITY_SHARP_FLOOR_Q10: i64 = 256 21const NX_CLARITY_PHOTO_RATIO_Q10: i64 = 2048 22const NX_CLARITY_LEVEL_RATIO_Q10: i64 = 1280 23const NX_CLARITY_RATIO_CAP: i64 = 10240 24const NX_CLARITY_SOBEL_TO_Q10: i64 = 16 25 26struct ClarityVerdict { 27 kind: i64, 28 focus_x: i64, 29 focus_y: i64, 30 sharpness_focal_q10: i64, 31 sharpness_ambient_q10: i64, 32 sharpness_ratio_q10: i64, 33 fidelity_q10: i64, 34} 35 36func nx_clarity_compute(gray: *Image, verdict: *ClarityVerdict) -> i64 { 37 let w: i64 = gray.width 38 let h: i64 = gray.height 39 if w <= 0 { return 1 } 40 if h <= 0 { return 1 } 41 verdict.kind = NX_CLARITY_NONE 42 verdict.focus_x = 0 43 verdict.focus_y = 0 44 verdict.sharpness_focal_q10 = 0 45 verdict.sharpness_ambient_q10 = 0 46 verdict.sharpness_ratio_q10 = 0 47 verdict.fidelity_q10 = 0 48 49 let sal: *Image = nx_salience_intensity(gray) 50 let fx: *i64 = (sys_mmap(8)) as *i64 51 let fy: *i64 = (sys_mmap(8)) as *i64 52 nx_salience_peak(sal, fx, fy) 53 verdict.focus_x = fx[0] 54 verdict.focus_y = fy[0] 55 56 var min_dim: i64 = w 57 if h < min_dim { min_dim = h } 58 let radius: i64 = (min_dim * NX_CLARITY_FOCAL_RADIUS_Q10) / 1024 59 60 let gx: *ImageS64 = nx_image_sobel_x(gray) 61 let gy: *ImageS64 = nx_image_sobel_y(gray) 62 let mag: *ImageS64 = nx_image_gradient_magnitude(gx, gy) 63 64 var focal_sum: i64 = 0; var focal_count: i64 = 0 65 var amb_sum: i64 = 0; var amb_count: i64 = 0 66 var y: i64 = 0 67 while y < h { 68 var x: i64 = 0 69 while x < w { 70 var ax: i64 = x - verdict.focus_x 71 if ax < 0 { ax = -ax } 72 var ay: i64 = y - verdict.focus_y 73 if ay < 0 { ay = -ay } 74 var in_focal: i64 = 0 75 if ax <= radius { 76 if ay <= radius { in_focal = 1 } 77 } 78 let s: i64 = nx_image_s64_get(mag, x, y) 79 if in_focal == 1 { focal_sum = focal_sum + s; focal_count = focal_count + 1 } 80 if in_focal == 0 { amb_sum = amb_sum + s; amb_count = amb_count + 1 } 81 x = x + 1 82 } 83 y = y + 1 84 } 85 var focal_mean: i64 = 0 86 var amb_mean: i64 = 0 87 if focal_count > 0 { focal_mean = focal_sum / focal_count } 88 if amb_count > 0 { amb_mean = amb_sum / amb_count } 89 90 var focal_q10: i64 = focal_mean * NX_CLARITY_SOBEL_TO_Q10 91 if focal_q10 > 1024 { focal_q10 = 1024 } 92 var amb_q10: i64 = amb_mean * NX_CLARITY_SOBEL_TO_Q10 93 if amb_q10 > 1024 { amb_q10 = 1024 } 94 verdict.sharpness_focal_q10 = focal_q10 95 verdict.sharpness_ambient_q10 = amb_q10 96 97 var ratio_q10: i64 = NX_CLARITY_RATIO_CAP 98 if amb_q10 > 0 { 99 ratio_q10 = (focal_q10 * 1024) / amb_q10 100 if ratio_q10 > NX_CLARITY_RATIO_CAP { ratio_q10 = NX_CLARITY_RATIO_CAP } 101 } 102 verdict.sharpness_ratio_q10 = ratio_q10 103 104 if focal_q10 >= NX_CLARITY_SHARP_FLOOR_Q10 { 105 if ratio_q10 >= NX_CLARITY_PHOTO_RATIO_Q10 { verdict.kind = NX_CLARITY_PHOTOGRAPHIC } 106 if ratio_q10 < NX_CLARITY_PHOTO_RATIO_Q10 { 107 if ratio_q10 >= NX_CLARITY_LEVEL_RATIO_Q10 { verdict.kind = NX_CLARITY_MIXED } 108 if ratio_q10 < NX_CLARITY_LEVEL_RATIO_Q10 { verdict.kind = NX_CLARITY_IMAGE_LEVEL } 109 } 110 } 111 verdict.fidelity_q10 = focal_q10 112 return 0 113}