nx_skin_pore_detail.nx source
↩ module page · 115 lines · 4118 B
1// nx_skin_pore_detail.nx -- high-frequency pore-scale texture detector.
2//
3// At 8k+ render quality real skin shows individual pores: tiny dark
4// micro-spots of pore-scale (1-2 px at full res) creating high-
5// frequency texture. Mannequin / SDXL-smooth renders suppress this:
6// skin is uniform within local 3x3 neighborhoods. The substrate
7// measures MEAN |Y(x,y) - mean(Y, 3x3)| over skin-mask pixels — the
8// classic high-pass-vs-flat metric.
9//
10// Math:
11// For each skin-mask pixel:
12// local_mean = sum of 3x3 luminance / 9
13// residual = |luminance - local_mean|
14// detail_score = sum(residual) / count(skin_pixels)
15//
16// < 3 → SMOOTH_MANNEQUIN (no pores)
17// 3..8 → MODERATE_TEXTURE (acceptable)
18// ≥ 8 → DETAILED_NATURAL (visible pore-scale detail)
19//
20// Output flat-array:
21// result[0] = pixel_count
22// result[1] = mean_residual
23// result[2] = max_residual
24// result[3] = pore_candidate_count (pixels with residual > 12)
25// result[4] = verdict
26
27// nx_safety_envelope:
28// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
29// sil_target: SIL1
30// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
31// verdict: NOT_YET_EVALUATED
32
33import "nx_syscalls.nx"
34import "nx_image.nx"
35
36const NX_PORE_RES_PIXELS: i64 = 0
37const NX_PORE_RES_MEAN_RES: i64 = 1
38const NX_PORE_RES_MAX_RES: i64 = 2
39const NX_PORE_RES_CANDIDATES: i64 = 3
40const NX_PORE_RES_VERDICT: i64 = 4
41const NX_PORE_RES_FIELDS: i64 = 5
42
43const NX_PORE_VERDICT_SMOOTH_MANNEQUIN: i64 = 0
44const NX_PORE_VERDICT_MODERATE_TEXTURE: i64 = 1
45const NX_PORE_VERDICT_DETAILED_NATURAL: i64 = 2
46
47const NX_PORE_THRESH_SMOOTH: i64 = 3
48const NX_PORE_THRESH_DETAILED: i64 = 8
49const NX_PORE_CANDIDATE_THRESH: i64 = 12
50
51// Compute 3x3 mean luminance at (x, y). Boundary pixels skipped by caller.
52func nx_pore_local_mean(rgb: *Image, x: i64, y: i64) -> i64 {
53 var sum: i64 = 0
54 var dy: i64 = -1
55 while dy <= 1 {
56 var dx: i64 = -1
57 while dx <= 1 {
58 let r: i64 = nx_image_get(rgb, x + dx, y + dy, 0)
59 let g: i64 = nx_image_get(rgb, x + dx, y + dy, 1)
60 let b: i64 = nx_image_get(rgb, x + dx, y + dy, 2)
61 let yv: i64 = (77 * r + 150 * g + 29 * b) / 256
62 sum = sum + yv
63 dx = dx + 1
64 }
65 dy = dy + 1
66 }
67 return sum / 9
68}
69
70func nx_skin_pore_detail(rgb: *Image, skin_mask: *Image, result: *i64) -> i64 {
71 var i: i64 = 0
72 while i < NX_PORE_RES_FIELDS { result[i] = 0; i = i + 1 }
73 let w: i64 = rgb.width
74 let h: i64 = rgb.height
75
76 var sum_res: i64 = 0
77 var max_res: i64 = 0
78 var candidates: i64 = 0
79 var pixels: i64 = 0
80
81 var y: i64 = 1
82 while y < h - 1 {
83 var x: i64 = 1
84 while x < w - 1 {
85 if nx_image_get(skin_mask, x, y, 0) > 0 {
86 let r: i64 = nx_image_get(rgb, x, y, 0)
87 let g: i64 = nx_image_get(rgb, x, y, 1)
88 let b: i64 = nx_image_get(rgb, x, y, 2)
89 let yv: i64 = (77 * r + 150 * g + 29 * b) / 256
90 let local_mean: i64 = nx_pore_local_mean(rgb, x, y)
91 var residual: i64 = yv - local_mean
92 if residual < 0 { residual = -residual }
93 sum_res = sum_res + residual
94 if residual > max_res { max_res = residual }
95 if residual > NX_PORE_CANDIDATE_THRESH { candidates = candidates + 1 }
96 pixels = pixels + 1
97 }
98 x = x + 1
99 }
100 y = y + 1
101 }
102
103 result[NX_PORE_RES_PIXELS] = pixels
104 if pixels == 0 { return 0 }
105 let mean_res: i64 = sum_res / pixels
106 result[NX_PORE_RES_MEAN_RES] = mean_res
107 result[NX_PORE_RES_MAX_RES] = max_res
108 result[NX_PORE_RES_CANDIDATES] = candidates
109
110 var verdict: i64 = NX_PORE_VERDICT_MODERATE_TEXTURE
111 if mean_res < NX_PORE_THRESH_SMOOTH { verdict = NX_PORE_VERDICT_SMOOTH_MANNEQUIN }
112 if mean_res >= NX_PORE_THRESH_DETAILED { verdict = NX_PORE_VERDICT_DETAILED_NATURAL }
113 result[NX_PORE_RES_VERDICT] = verdict
114 return 0
115}