nx_imgkp.nx
buildroot/runtime/nx_imgkp.nx
about
nx_imgkp.nx -- LOCAL FEATURES: FAST corners + BRIEF binary descriptors, integer, deterministic.
The global descriptors (dHash, edge-orientation layout) hit a hard ceiling the ruler MEASURED:
crop-30pct = 562, letterbox = 656 permille. That ceiling is structural -- a global fingerprint
summarises the WHOLE frame, so removing a third of the pixels or padding a border changes it no
matter how the summary is computed. TinEye clears exactly this by matching LOCAL regions and then
checking they agree GEOMETRICALLY: even if half the picture is gone, the surviving corners still
line up under one consistent shift.
Every piece here is integer + zero-float + deterministic, which is the whole reason it belongs in
the sovereign engine rather than an OpenCV shell-out:
* FAST-9 corner test (Rosten & Drummond 2006) -- pure intensity comparisons on a Bresenham circle.
* BRIEF-256 descriptor (Calonder 2010) -- 256 intensity-pair comparisons -> a 256-bit string whose
distance is popcount Hamming, i.e. the nx_simhash kernel the engine already rides.
* A FIXED sampling pattern regenerated from one seed every call, so index-time and query-time
descriptors are bit-identical without any stored state.
HONEST SCOPE, stated so it is not mistaken for more: plain BRIEF is NOT rotation-invariant. That is
deliberate and correct here -- the orientation axis (mirror / quarter-turn) is already owned by the
dihedral tier at the global level, and the two classes this rung targets (crop, letterbox) are pure
translations. Oriented-BRIEF (ORB) to combine rotation WITH occlusion is a later refinement, filed.
PACKED LAYOUT (one image's feature set as a flat i64 block, so it fits the tier vtable's fixed
descriptor slot): out[0] = keypoint count; then KP_MAX records of 6 i64 each = [x, y, d0,d1,d2,d3].
genealogy_id: rosten_2006_fast + calonder_2010_brief. license_tier: ORIGINAL
dependencies 2 imports · 1 importers
imports: syscalls.nxnx_simhash.nx
imported by: nx_imgsearch_tier.nx
structs
| none |
consts
| 29 | const KP_MAX: i64 = 24 // keypoints kept per image (spatially spread by NMS) |
| 30 | const KP_WORDS: i64 = 4 // 256-bit BRIEF descriptor = 4 x i64 |
| 31 | const KP_REC: i64 = 6 // per keypoint: x, y, 4 descriptor words |
| 32 | const KP_PACK: i64 = 145 // 1 + KP_MAX*KP_REC -- the tier idx_dim (UNCHANGED by the pyramid) |
| 33 | const KP_FAST_N: i64 = 9 // contiguous arc length for a FAST-9 corner |
| 34 | const KP_FAST_T: i64 = 16 // intensity threshold (of 0..255) |
| 35 | const KP_PATCH: i64 = 6 // BRIEF samples drawn within +/- this of the keypoint |
| 36 | const KP_NMS_R: i64 = 3 // non-max suppression radius (spread keypoints out) |
| 37 | const KP_CAND_PEROCT: i64 = 8192 // per-octave candidate cap |
| 38 | const KP_CAND_MAX: i64 = 8192 // total candidate cap = KP_CAND_PEROCT * KP_OCTAVES (bounds per-call memory) |
| 39 | const KP_BRIEF_BITS: i64 = 256 |
| 67 | const KP_OCTAVES: i64 = 1 // INDEX stays single-scale, deliberately -- see above |
| 68 | const KP_QSCALES: i64 = 5 // query-side scale sweep width |
| 69 | const KP_QPIXCAP: i64 = 40000000 // skip a sweep scale whose resampled image exceeds this |
| 339 | const KP_MATCH_MAXHAM: i64 = 96 // absolute Hamming ceiling for a plausible match (of 256 bits) |
functions
| 73 | func kp_qscale_num(i: i64) -> i64 called by 1: nx_imgkp_describe_query |
| 80 | func kp_qscale_den(i: i64) -> i64 called by 1: nx_imgkp_describe_query |
| 88 | func kp_pack_dim() -> i64 { return KP_PACK } |
| 92 | func kp_circle(cx: *i64, cy: *i64) -> i64 called by 1: nx_imgkp_describe |
| 112 | func kp_rng(s: *i64) -> i64 { let x: i64 = s[0] * 0x5851F42D4C957F2D + 0x14057B7EF767814F; s[0] = x; return x } called by 1: kp_brief_pattern |
| 117 | func kp_brief_pattern(pat: *i64) -> i64 |
| 137 | func kp_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } |
| 143 | func kp_is_corner(gray: *u8, w: i64, h: i64, x: i64, y: i64, cx: *i64, cy: *i64, bright: *i64, dark: *i64, score: *i64) -> i64 |
| 172 | func kp_brief_at(gray: *u8, w: i64, h: i64, x: i64, y: i64, pat: *i64, desc: *i64) -> i64 called by 1: nx_imgkp_describe |
| 189 | func kp_downscale(src: *u8, sw: i64, sh: i64, dst: *u8, dw: i64, dh: i64) -> i64 |
| 205 | func kp_oct_dim(o: i64, base: i64) -> i64 called by 1: nx_imgkp_describe |
| 216 | func nx_imgkp_describe(gray: *u8, w: i64, h: i64, out: *i64) -> i64 |
| 324 | func nx_imgkp_count(pk: *i64) -> i64 { return pk[0] } |
| 327 | func kp_desc_hamming(pk_a: *i64, ia: i64, pk_b: *i64, ib: i64) -> i64 |
| 340 | func nx_imgkp_match(q: *i64, d: *i64, out_qx: *i64, out_qy: *i64, out_dx: *i64, out_dy: *i64, cap: i64) -> i64 |
| 380 | func nx_imgkp_query_dim() -> i64 { return KP_PACK * KP_QSCALES } called by 1: nx_imgtier_local_init |
| 382 | func nx_imgkp_describe_query(gray: *u8, w: i64, h: i64, out: *i64) -> i64 called by 1: it_local_describe_query calls 5: kp_qscale_numkp_qscale_dennx_imgkp_describekp_downscalesys_munmap |