code wiki / (root) / nx_img_segment.nx

nx_img_segment.nx source

↩ module page · 74 lines · 3861 B

1// nx_img_segment.nx -- R3 of the sovereign scanning stack: binary image -> connected components (glyph boxes). 2// 8-connected component labeling by iterative flood-fill (explicit stack, no recursion), accumulating each 3// component's bounding box as it grows. Ink = 0, paper = 255 (the R2 binarization convention). Each component is a 4// candidate glyph/mark that R4 (OCR classify) will recognize. Integer-exact, zero deps. license_tier: ORIGINAL 5import "nx_syscalls.nx" 6import "nx_img_threshold.nx" // -> nx_img_core (pixel buffer, decode, Otsu) for end-to-end gating 7 8const SEG_BOXF: i64 = 4 // per box: [minx, miny, maxx, maxy] 9 10// label 8-connected ink components; fill labels[] (0 = paper/background, >=1 = component id) and boxes[] (bbox per 11// component, index id-1). Returns the component count (capped at maxcomps). npix scratch stack allocated internally. 12func seg_components(pix: *u8, w: i64, h: i64, labels: *i64, boxes: *i64, maxcomps: i64) -> i64 { 13 let npix: i64 = w * h 14 var i: i64 = 0 15 while i < npix { labels[i] = 0; i = i + 1 } 16 let stack: *i64 = sys_mmap((npix + 1) * 8) as *i64 17 var ncomp: i64 = 0 18 var p: i64 = 0 19 while p < npix { 20 if pix[p] == (0 as u8) { if labels[p] == 0 { 21 ncomp = ncomp + 1 22 if ncomp > maxcomps { return ncomp } 23 let id: i64 = ncomp 24 let cx0: i64 = p % w; let cy0: i64 = p / w 25 boxes[(id-1)*SEG_BOXF+0] = cx0 26 boxes[(id-1)*SEG_BOXF+1] = cy0 27 boxes[(id-1)*SEG_BOXF+2] = cx0 28 boxes[(id-1)*SEG_BOXF+3] = cy0 29 var sp: i64 = 0 30 stack[sp] = p; sp = sp + 1 31 labels[p] = id 32 while sp > 0 { 33 sp = sp - 1 34 let cur: i64 = stack[sp] 35 let cx: i64 = cur % w; let cy: i64 = cur / w 36 var dy: i64 = 0 - 1 37 while dy <= 1 { 38 var dx: i64 = 0 - 1 39 while dx <= 1 { 40 var is_self: i64 = 0 41 if dx == 0 { if dy == 0 { is_self = 1 } } 42 if is_self == 0 { 43 let nxp: i64 = cx + dx 44 let nyp: i64 = cy + dy 45 if nxp >= 0 { if nxp < w { if nyp >= 0 { if nyp < h { 46 let np: i64 = nyp * w + nxp 47 if pix[np] == (0 as u8) { if labels[np] == 0 { 48 labels[np] = id 49 stack[sp] = np; sp = sp + 1 50 if nxp < boxes[(id-1)*SEG_BOXF+0] { boxes[(id-1)*SEG_BOXF+0] = nxp } 51 if nyp < boxes[(id-1)*SEG_BOXF+1] { boxes[(id-1)*SEG_BOXF+1] = nyp } 52 if nxp > boxes[(id-1)*SEG_BOXF+2] { boxes[(id-1)*SEG_BOXF+2] = nxp } 53 if nyp > boxes[(id-1)*SEG_BOXF+3] { boxes[(id-1)*SEG_BOXF+3] = nyp } 54 } } 55 } } } } 56 } 57 dx = dx + 1 58 } 59 dy = dy + 1 60 } 61 } 62 } } 63 p = p + 1 64 } 65 return ncomp 66} 67 68// bounding-box accessors (id is 1-indexed). 69func seg_box_minx(boxes: *i64, id: i64) -> i64 { return boxes[(id-1)*SEG_BOXF+0] } 70func seg_box_miny(boxes: *i64, id: i64) -> i64 { return boxes[(id-1)*SEG_BOXF+1] } 71func seg_box_maxx(boxes: *i64, id: i64) -> i64 { return boxes[(id-1)*SEG_BOXF+2] } 72func seg_box_maxy(boxes: *i64, id: i64) -> i64 { return boxes[(id-1)*SEG_BOXF+3] } 73func seg_box_width(boxes: *i64, id: i64) -> i64 { return boxes[(id-1)*SEG_BOXF+2] - boxes[(id-1)*SEG_BOXF+0] + 1 } 74func seg_box_height(boxes: *i64, id: i64) -> i64 { return boxes[(id-1)*SEG_BOXF+3] - boxes[(id-1)*SEG_BOXF+1] + 1 }