nx_lbp_core.nx source
↩ module page · 130 lines · 6419 B
1// nx_lbp_core.nx -- shared SOVEREIGN LBP descriptor primitives (NO main). Hardware-up, integer-only,
2// weight-free. Used by nx_lbp.nx (rung-1 KAT gate) and nx_lbp_img.nx (real-image descriptor rung).
3// license_tier: ORIGINAL
4import "nx_syscalls.nx"
5
6func lp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
7func ln(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; var neg: i64=0; if m<0{neg=1;m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; if neg==1{b[0]=45;i=1}; var j: i64=0; while j<k{b[i]=t[k-1-j];i=i+1;j=j+1}; sys_write(1,b,i); return 0 }
8
9// 8-bit LBP code at interior pixel (x,y) of a w-wide grayscale image.
10// neighbor order (bit0->bit7), clockwise from top-left: TL,T,TR,R,BR,B,BL,L; bit set when neighbor >= center.
11func lbp_code(img: *u8, w: i64, x: i64, y: i64) -> i64 {
12 let c: i64 = img[y*w+x] as i64
13 var code: i64 = 0
14 if (img[(y-1)*w+(x-1)] as i64) >= c { code = code + 1 }
15 if (img[(y-1)*w+x] as i64) >= c { code = code + 2 }
16 if (img[(y-1)*w+(x+1)] as i64) >= c { code = code + 4 }
17 if (img[y*w+(x+1)] as i64) >= c { code = code + 8 }
18 if (img[(y+1)*w+(x+1)] as i64) >= c { code = code + 16 }
19 if (img[(y+1)*w+x] as i64) >= c { code = code + 32 }
20 if (img[(y+1)*w+(x-1)] as i64) >= c { code = code + 64 }
21 if (img[y*w+(x-1)] as i64) >= c { code = code + 128 }
22 return code
23}
24
25// 256-bin LBP histogram over a rectangular region [x0,x1) x [y0,y1) of a w-wide image (caller zeroes hist).
26// region must stay 1px inside the image edges so the 3x3 neighborhood is valid.
27func lbp_hist_region(img: *u8, w: i64, x0: i64, y0: i64, x1: i64, y1: i64, hist: *i64) -> i64 {
28 var y: i64 = y0
29 while y < y1 { var x: i64 = x0; while x < x1 { let cd: i64 = lbp_code(img, w, x, y); hist[cd] = hist[cd] + 1; x = x + 1 } y = y + 1 }
30 return 0
31}
32
33// whole-image interior histogram convenience
34func lbp_hist(img: *u8, w: i64, h: i64, hist: *i64) -> i64 { lbp_hist_region(img, w, 1, 1, w-1, h-1, hist); return 0 }
35
36// chi-square distance * scale (integer) over bins [off, off+n): sum (a-b)^2 * scale / (a+b), a+b>0
37func chi2_range(a: *i64, b: *i64, off: i64, n: i64, scale: i64) -> i64 {
38 var s: i64 = 0; var i: i64 = off; let e: i64 = off+n
39 while i < e { let av: i64 = a[i]; let bv: i64 = b[i]; let den: i64 = av+bv; if den > 0 { let d: i64 = av-bv; s = s + (d*d*scale)/den } i = i + 1 }
40 return s
41}
42// single 256-bin chi-square
43func chi2(a: *i64, b: *i64, scale: i64) -> i64 { return chi2_range(a, b, 0, 256, scale) }
44
45func zero256(p: *i64) -> i64 { var i: i64=0; while i<256 { p[i]=0; i=i+1 } return 0 }
46func hsum(p: *i64) -> i64 { var s: i64=0; var i: i64=0; while i<256 { s=s+p[i]; i=i+1 } return s }
47
48// integer luma grayscale: gray[i] = (77*R + 150*G + 29*B) >> 8 ; rgb = w*h*nch interleaved, nch>=3
49func rgb_to_gray(rgb: *u8, gray: *u8, w: i64, h: i64, nch: i64) -> i64 {
50 var i: i64 = 0; let npx: i64 = w*h
51 while i < npx {
52 let r: i64 = rgb[i*nch+0] as i64
53 let g: i64 = rgb[i*nch+1] as i64
54 let b: i64 = rgb[i*nch+2] as i64
55 gray[i] = ((77*r + 150*g + 29*b) >> 8) as u8
56 i = i + 1
57 }
58 return 0
59}
60
61// box-average grayscale resize: src(sw x sh) -> dst(dw x dh). Integer area average -> no aliasing/holes.
62func gray_resize_box(src: *u8, sw: i64, sh: i64, dst: *u8, dw: i64, dh: i64) -> i64 {
63 var dy: i64 = 0
64 while dy < dh {
65 let sy0: i64 = (dy*sh)/dh; var sy1: i64 = ((dy+1)*sh)/dh; if sy1 <= sy0 { sy1 = sy0+1 }
66 var dx: i64 = 0
67 while dx < dw {
68 let sx0: i64 = (dx*sw)/dw; var sx1: i64 = ((dx+1)*sw)/dw; if sx1 <= sx0 { sx1 = sx0+1 }
69 var sum: i64 = 0; var cnt: i64 = 0
70 var yy: i64 = sy0
71 while yy < sy1 { var xx: i64 = sx0; while xx < sx1 { sum = sum + (src[yy*sw+xx] as i64); cnt = cnt+1; xx = xx+1 } yy = yy+1 }
72 dst[dy*dw+dx] = (sum/cnt) as u8
73 dx = dx + 1
74 }
75 dy = dy + 1
76 }
77 return 0
78}
79
80// spatial-grid LBP descriptor: g x g cells, each a 256-bin LBP histogram, packed into descr (length g*g*256,
81// caller zeroes). Each interior pixel's code is binned by which cell its CENTER falls in (neighbors always read
82// from the full image, so cell boundaries stay correct). This keeps WHERE texture occurs -> face-discriminative.
83func lbp_grid_descr(gray: *u8, w: i64, h: i64, g: i64, descr: *i64) -> i64 {
84 var y: i64 = 1
85 while y < h-1 {
86 let cy: i64 = (y*g)/h
87 var x: i64 = 1
88 while x < w-1 {
89 let cx: i64 = (x*g)/w
90 let cd: i64 = lbp_code(gray, w, x, y)
91 let cell: i64 = cy*g + cx
92 descr[cell*256 + cd] = descr[cell*256 + cd] + 1
93 x = x + 1
94 }
95 y = y + 1
96 }
97 return 0
98}
99
100// distance over a full g x g grid descriptor = sum of per-cell chi-square
101func grid_chi2(a: *i64, b: *i64, g: i64, scale: i64) -> i64 { return chi2_range(a, b, 0, g*g*256, scale) }
102
103// chi-square between descriptor i and descriptor j packed in buf (each length d)
104func chi2_at(buf: *i64, i: i64, j: i64, d: i64, scale: i64) -> i64 {
105 var s: i64 = 0; let bi: i64 = i*d; let bj: i64 = j*d; var k: i64 = 0
106 while k < d { let av: i64 = buf[bi+k]; let bv: i64 = buf[bj+k]; let den: i64 = av+bv; if den > 0 { let dd: i64 = av-bv; s = s + (dd*dd*scale)/den } k = k + 1 }
107 return s
108}
109
110// greedy single-pass clustering of m descriptors (each length d) packed in buf. Each descriptor joins the
111// nearest existing cluster whose representative is within thresh (chi-square*scale), else starts a new cluster.
112// assign[i] = cluster id; reps[c] = index of cluster c's representative; returns the cluster count.
113func cluster_greedy(buf: *i64, m: i64, d: i64, thresh: i64, scale: i64, assign: *i64, reps: *i64) -> i64 {
114 var nc: i64 = 0
115 var i: i64 = 0
116 while i < m {
117 var best: i64 = 0 - 1; var bestd: i64 = 0
118 var c: i64 = 0
119 while c < nc {
120 let dist: i64 = chi2_at(buf, i, reps[c], d, scale)
121 if c == 0 { best = 0; bestd = dist } else { if dist < bestd { best = c; bestd = dist } }
122 c = c + 1
123 }
124 var made: i64 = 0
125 if nc > 0 { if bestd < thresh { assign[i] = best; made = 1 } }
126 if made == 0 { reps[nc] = i; assign[i] = nc; nc = nc + 1 }
127 i = i + 1
128 }
129 return nc
130}