nx_colordesc.nx source
↩ module page · 77 lines · 3082 B
1// nx_colordesc.nx -- CHROMA color-layout descriptor (brightness-invariant), the color complement to
2// nx_visdesc. dHash and the edge/tone descriptor both run on GRAYSCALE -- so two fields of equal luma
3// but different hue (a red field vs a green field) collapse to the SAME representation and are
4// indistinguishable. This descriptor carries the color axis grayscale throws away: a 4x4 spatial grid
5// of average chroma (Cb,Cr) -> a 32-dim integer vector ranked by L1. Chroma is computed as (channel -
6// luma), so a uniform additive brightness/exposure shift cancels EXACTLY -> brightness-invariant by
7// construction. Integer + deterministic + zero-dep. Pairs with nx_visdesc (structure/tone) for a full
8// visual signature; "Google-lite" color-layout, the MPEG-7 CLD lineage done sovereignly.
9//
10// genealogy_id: kasson_1992_ycc + manjunath_2001_mpeg7_cld (color-layout descriptor)
11// license_tier: ORIGINAL
12import "syscalls.nx"
13
14const CD_GRID: i64 = 4
15const CD_DIM: i64 = 32 // CD_GRID*CD_GRID*2 (Cb,Cr per cell)
16
17func nx_colordesc_dim() -> i64 { return CD_DIM }
18
19func cd_clamp255(v: i64) -> i64 { if v < 0 { return 0 } if v > 255 { return 255 } return v }
20
21// extract the 32-dim chroma-layout descriptor of a w*h interleaved RGB (3 bytes/pixel) buffer into
22// out[0..32); returns CD_DIM. Cb=(B-Y)*0.564, Cr=(R-Y)*0.713 (integer, *256), centered at 128.
23func nx_colordesc_extract(rgb: *u8, w: i64, h: i64, out: *i64) -> i64 {
24 var cy: i64 = 0
25 while cy < CD_GRID {
26 let y0: i64 = cy*h/CD_GRID
27 let y1: i64 = (cy+1)*h/CD_GRID
28 var cx: i64 = 0
29 while cx < CD_GRID {
30 let x0: i64 = cx*w/CD_GRID
31 let x1: i64 = (cx+1)*w/CD_GRID
32 var sr: i64 = 0
33 var sg: i64 = 0
34 var sb: i64 = 0
35 var cnt: i64 = 0
36 var y: i64 = y0
37 while y < y1 {
38 var x: i64 = x0
39 while x < x1 {
40 let idx: i64 = (y*w+x)*3
41 sr = sr + (rgb[idx] as i64)
42 sg = sg + (rgb[idx+1] as i64)
43 sb = sb + (rgb[idx+2] as i64)
44 cnt = cnt + 1
45 x = x + 1
46 }
47 y = y + 1
48 }
49 var ar: i64 = 0
50 var ag: i64 = 0
51 var ab: i64 = 0
52 if cnt > 0 { ar = sr/cnt; ag = sg/cnt; ab = sb/cnt }
53 let yv: i64 = (77*ar + 150*ag + 29*ab) / 256
54 let cb: i64 = (ab - yv)*144/256 + 128
55 let cr: i64 = (ar - yv)*183/256 + 128
56 let base: i64 = (cy*CD_GRID + cx)*2
57 out[base] = cd_clamp255(cb)
58 out[base+1] = cd_clamp255(cr)
59 cx = cx + 1
60 }
61 cy = cy + 1
62 }
63 return CD_DIM
64}
65
66// L1 (Manhattan) distance between two chroma descriptors; smaller = more similar in color.
67func nx_colordesc_l1(a: *i64, b: *i64) -> i64 {
68 var d: i64 = 0
69 var i: i64 = 0
70 while i < CD_DIM {
71 var t: i64 = a[i] - b[i]
72 if t < 0 { t = 0 - t }
73 d = d + t
74 i = i + 1
75 }
76 return d
77}