nx_blob.nx source
↩ module page · 148 lines · 5148 B
1// nx_blob.nx -- largest connected skin region (R2-refine). Fixes localization:
2// a global skin bbox spans the whole frame when skin is everywhere; the LARGEST
3// CONNECTED COMPONENT is the actual subject. Builds a GRIDxGRID occupancy map
4// (cell = skin if >=50% of its pixels are skin), labels it with two-pass
5// union-find connected-component labeling (4-connectivity), and returns the
6// biggest component's cell count + grid bounding box + box area.
7//
8// Coarse grid (not per-pixel CCL) keeps it cheap and robust: a person is a
9// coarse blob; scattered skin-coloured speckle never forms a big component.
10//
11// out[]: [0]=blob_cells [1..4]=grid bbox minx,miny,maxx,maxy [5]=bbox_cells
12// [6]=total_occupied_cells [7]=grid
13// genealogy_id: connected_component_labeling_union_find (record-hint)
14// lineage_id: occupancy_grid + two_pass_ccl + largest_component
15// license_tier: ORIGINAL
16import "syscalls.nx"
17import "nx_image.nx"
18import "nx_presence.nx"
19
20const NX_BLOB_GRID: i64 = 32
21
22func nx_blob_find(parent: *i64, x: i64) -> i64 {
23 var r: i64 = x
24 while parent[r] != r { r = parent[r] }
25 var c: i64 = x
26 while parent[c] != r { let n: i64 = parent[c]; parent[c] = r; c = n }
27 return r
28}
29func nx_blob_union(parent: *i64, a: i64, b: i64) -> i64 {
30 let ra: i64 = nx_blob_find(parent, a)
31 let rb: i64 = nx_blob_find(parent, b)
32 if ra != rb { parent[ra] = rb }
33 return 0
34}
35
36func nx_blob_largest(img: *Image, out: *i64) -> i64 {
37 let g: i64 = NX_BLOB_GRID
38 let w: i64 = img.width
39 let h: i64 = img.height
40 let ncell: i64 = g * g
41 let occ: *i64 = sys_mmap(ncell * 8) as *i64
42 let lab: *i64 = sys_mmap(ncell * 8) as *i64
43 let parent: *i64 = sys_mmap((ncell + 1) * 8) as *i64
44
45 // occupancy: a cell is occupied if >= 50% of its pixels are skin.
46 var occupied: i64 = 0
47 var cy: i64 = 0
48 while cy < g {
49 let y0: i64 = cy * h / g
50 let y1: i64 = (cy + 1) * h / g
51 var cx: i64 = 0
52 while cx < g {
53 let x0: i64 = cx * w / g
54 let x1: i64 = (cx + 1) * w / g
55 var sc: i64 = 0
56 var tot: i64 = 0
57 var y: i64 = y0
58 while y < y1 {
59 var x: i64 = x0
60 while x < x1 {
61 let rr: i64 = nx_image_get(img, x, y, 0)
62 let gg: i64 = nx_image_get(img, x, y, 1)
63 let bb: i64 = nx_image_get(img, x, y, 2)
64 if nx_skin_is_pixel(rr, gg, bb) == 1 { sc = sc + 1 }
65 tot = tot + 1
66 x = x + 1
67 }
68 y = y + 1
69 }
70 var o: i64 = 0
71 if tot > 0 { if sc * 2 >= tot { o = 1 } }
72 occ[cy * g + cx] = o
73 lab[cy * g + cx] = 0
74 if o == 1 { occupied = occupied + 1 }
75 cx = cx + 1
76 }
77 cy = cy + 1
78 }
79
80 // pass 1: provisional labels + unions (neighbours up and left)
81 var nl: i64 = 1
82 var i: i64 = 0
83 while i < ncell {
84 if occ[i] == 1 {
85 let ccx: i64 = i % g
86 let ccy: i64 = i / g
87 var up: i64 = 0
88 if ccy > 0 { if occ[i - g] == 1 { up = lab[i - g] } }
89 var lf: i64 = 0
90 if ccx > 0 { if occ[i - 1] == 1 { lf = lab[i - 1] } }
91 if up == 0 && lf == 0 { parent[nl] = nl; lab[i] = nl; nl = nl + 1 }
92 if up != 0 && lf == 0 { lab[i] = up }
93 if up == 0 && lf != 0 { lab[i] = lf }
94 if up != 0 && lf != 0 {
95 var m: i64 = up
96 if lf < m { m = lf }
97 lab[i] = m
98 if up != lf { nx_blob_union(parent, up, lf) }
99 }
100 }
101 i = i + 1
102 }
103
104 // pass 2: per-root count + bounding box
105 let cnt: *i64 = sys_mmap((ncell + 1) * 8) as *i64
106 let bminx: *i64 = sys_mmap((ncell + 1) * 8) as *i64
107 let bminy: *i64 = sys_mmap((ncell + 1) * 8) as *i64
108 let bmaxx: *i64 = sys_mmap((ncell + 1) * 8) as *i64
109 let bmaxy: *i64 = sys_mmap((ncell + 1) * 8) as *i64
110 var z: i64 = 0
111 while z <= ncell { cnt[z] = 0; bminx[z] = g; bminy[z] = g; bmaxx[z] = 0 - 1; bmaxy[z] = 0 - 1; z = z + 1 }
112 i = 0
113 while i < ncell {
114 if occ[i] == 1 {
115 let r: i64 = nx_blob_find(parent, lab[i])
116 cnt[r] = cnt[r] + 1
117 let px: i64 = i % g
118 let py: i64 = i / g
119 if px < bminx[r] { bminx[r] = px }
120 if px > bmaxx[r] { bmaxx[r] = px }
121 if py < bminy[r] { bminy[r] = py }
122 if py > bmaxy[r] { bmaxy[r] = py }
123 }
124 i = i + 1
125 }
126
127 // largest component
128 var best: i64 = 0
129 var bc: i64 = 0
130 var r2: i64 = 1
131 while r2 < nl {
132 if cnt[r2] > bc { bc = cnt[r2]; best = r2 }
133 r2 = r2 + 1
134 }
135 out[7] = g
136 out[6] = occupied
137 out[0] = bc
138 if bc == 0 {
139 out[1] = 0 - 1; out[2] = 0 - 1; out[3] = 0 - 1; out[4] = 0 - 1; out[5] = 0
140 return 0
141 }
142 out[1] = bminx[best]
143 out[2] = bminy[best]
144 out[3] = bmaxx[best]
145 out[4] = bmaxy[best]
146 out[5] = (bmaxx[best] - bminx[best] + 1) * (bmaxy[best] - bminy[best] + 1)
147 return 0
148}