code wiki / (root) / nx_featmatch.nx

nx_featmatch.nx source

↩ module page · 130 lines · 4878 B

1// nx_featmatch.nx -- THE FEATURE-MATCH WIRE: two images in, CORRESPONDENCES out, with nothing hand-supplied. 2// This is the last classical link the cadtwin census lists as missing on P1. Every reconstruction rung built 3// so far (essential matrix, pose refinement, dense sweep, fusion) has been fed correspondences that the GATE 4// constructed from ground truth -- which quietly assumes away the hardest part of a real capture. 5// Uses the EXISTING nx_features Harris + non-max-suppression rather than a second corner detector; the only 6// new work is bridging the flat i64 intensity buffers this reconstruction chain uses into the *Image struct 7// nx_features expects, plus the matcher itself. license_tier: ORIGINAL 8import "nx_planesweep.nx" 9import "nx_features.nx" 10 11const FM_RATIO: i64 = 80 // winner must beat the runner-up by 20% or the match is AMBIGUOUS -> dropped 12const FM_EXCL: i64 = 3 // runner-up must be this far from the winner (see fm_match_one) 13 14// bridge: flat i64 intensities -> the 1-channel *Image that nx_features consumes 15func fm_load(src: *i64, w: i64, h: i64) -> *Image { 16 let img: *Image = nx_image_alloc(w, h, 1) 17 var i: i64 = 0 18 while i < w * h { 19 var v: i64 = src[i] 20 if v < 0 { v = 0 } 21 if v > 255 { v = 255 } 22 img.pixels[i] = v as u8 23 i = i + 1 24 } 25 return img 26} 27// detect corners via the existing Harris + NMS; writes (x,y) pairs into out. Returns the count. 28func fm_corners(img: *Image, thresh: i64, out: *i64, maxn: i64) -> i64 { 29 let resp: *ImageS64 = nx_feat_harris_response(img) 30 let mask: *Image = nx_feat_corners_nms(resp, thresh) 31 let w: i64 = mask.width 32 let h: i64 = mask.height 33 var n: i64 = 0 34 var y: i64 = 0 35 while y < h { 36 var x: i64 = 0 37 while x < w { 38 if mask.pixels[y * w + x] != (0 as u8) { 39 if n < maxn { 40 out[n * 2 + 0] = x 41 out[n * 2 + 1] = y 42 n = n + 1 43 } 44 } 45 x = x + 1 46 } 47 y = y + 1 48 } 49 return n 50} 51// Match one corner of A into B by scanning a window, with a RATIO TEST. 52// ⚠the runner-up is taken only from OUTSIDE a small exclusion radius around the winner. SAD varies smoothly, 53// so the second-best score is otherwise always the pixel NEXT TO the winner -- the ratio is then ~1 and the 54// test throws away every correct match. The competitor that matters is a DIFFERENT candidate location. 55func fm_match_one(ia: *i64, ib: *i64, w: i64, h: i64, ax: i64, ay: i64, rad: i64, out2: *i64) -> i64 { 56 var best: i64 = PS_BAD 57 var bx: i64 = 0 - 1 58 var by: i64 = 0 - 1 59 var dy: i64 = 0 - rad 60 while dy <= rad { 61 var dx: i64 = 0 - rad 62 while dx <= rad { 63 let sc: i64 = ps_sad(ia, ib, w, h, ax, ay, ax + dx, ay + dy) 64 if sc >= 0 { 65 if sc < best { 66 best = sc 67 bx = ax + dx 68 by = ay + dy 69 } 70 } 71 dx = dx + 1 72 } 73 dy = dy + 1 74 } 75 if bx < 0 { return 0 - 1 } 76 var second: i64 = PS_BAD 77 dy = 0 - rad 78 while dy <= rad { 79 var dx2: i64 = 0 - rad 80 while dx2 <= rad { 81 let cx: i64 = ax + dx2 82 let cy: i64 = ay + dy 83 var far: i64 = 0 84 var ddx: i64 = cx - bx 85 var ddy: i64 = cy - by 86 if ddx < 0 { ddx = 0 - ddx } 87 if ddy < 0 { ddy = 0 - ddy } 88 if ddx > FM_EXCL { far = 1 } 89 if ddy > FM_EXCL { far = 1 } 90 if far == 1 { 91 let sc2: i64 = ps_sad(ia, ib, w, h, ax, ay, cx, cy) 92 if sc2 >= 0 { 93 if sc2 < second { second = sc2 } 94 } 95 } 96 dx2 = dx2 + 1 97 } 98 dy = dy + 1 99 } 100 if second < PS_BAD { 101 if best * 100 > second * FM_RATIO { return 0 - 1 } 102 } 103 out2[0] = bx 104 out2[1] = by 105 return 1 106} 107// Full wire: detect in A, match into B, emit surviving correspondences as (ax,ay,bx,by). Returns the count. 108func fm_wire(ia: *i64, ib: *i64, w: i64, h: i64, thresh: i64, rad: i64, pairs: *i64, maxn: i64) -> i64 { 109 let imga: *Image = fm_load(ia, w, h) 110 let corners: *i64 = sys_mmap(maxn * 16 + 64) as *i64 111 let nc: i64 = fm_corners(imga, thresh, corners, maxn) 112 let o2: *i64 = sys_mmap(32) as *i64 113 var n: i64 = 0 114 var i: i64 = 0 115 while i < nc { 116 let ax: i64 = corners[i * 2 + 0] 117 let ay: i64 = corners[i * 2 + 1] 118 if fm_match_one(ia, ib, w, h, ax, ay, rad, o2) == 1 { 119 if n < maxn { 120 pairs[n * 4 + 0] = ax 121 pairs[n * 4 + 1] = ay 122 pairs[n * 4 + 2] = o2[0] 123 pairs[n * 4 + 3] = o2[1] 124 n = n + 1 125 } 126 } 127 i = i + 1 128 } 129 return n 130}