nx_imgsearch_tier.nx
buildroot/runtime/nx_imgsearch_tier.nx
about
nx_imgsearch_tier.nx -- THE REVERSE-IMAGE TIER BASE CLASS (vtable OOP).
A production reverse-image engine is never ONE matcher. TinEye is a copy-detector; Google Lens is a
semantic recogniser; Yandex fuses both plus faces and text. Our own ruler (nx_imgbench) proves why:
no single descriptor covers the modification space -- dHash owns photometric edits and is blind to
geometry, the edge-orientation descriptor owns crops and is blind to mirroring, and neither knows
what the picture is OF.
So the engine is defined as a FUSION OF TIERS, and a tier is an object with a vtable. Adding the
next rung -- local keypoints + geometric verification, or a quantised semantic embedding -- is
implemented as a NEW SUBTYPE with its own constructor. It is never an edit to the query engine.
That is the whole point of the base class: the engine below it must not grow an `if` per rung.
ASYMMETRIC DESCRIPTORS, deliberately. A tier declares TWO widths:
idx_dim -- i64 slots persisted per corpus image (storage cost, multiplied by 10^8 images)
qry_dim -- i64 slots computed per query (compute cost, paid once per search)
They differ for good reasons. The dihedral tier stores ONE fingerprint but computes EIGHT at query
time, buying full mirror/rotation invariance for zero storage. A future local-feature tier will
store a compact bag-of-visual-words vector but compute a full keypoint set per query. Forcing both
sides to one width would have taxed every stored image to pay for a query-side trick.
Distances are INTEGER and each tier publishes max_dist, so scores are comparable across tiers
without floats -- the same integer-quantisation discipline the text engine rides.
license_tier: ORIGINAL
dependencies 5 imports · 1 importers
imports: nx_phash.nxnx_visdesc.nxnx_imgorient.nxnx_imgkp.nxnx_imgransac.nx
imported by: nx_imgsearch_engine.nx
structs
| 99 | struct nx_imgtier |
consts
| 31 | const NX_IT_KIND_COPY: i64 = 1 // near-duplicate: dHash Hamming (TinEye-class copy detection) |
| 32 | const NX_IT_KIND_ORIENT: i64 = 2 // dihedral-invariant dHash (mirrors + quarter turns) |
| 33 | const NX_IT_KIND_SIMILAR: i64 = 3 // edge-orientation layout L1 (crop/recompose tolerant) |
| 34 | const NX_IT_KIND_LOCAL: i64 = 4 // RESERVED: local keypoints + geometric verification |
| 35 | const NX_IT_KIND_SEMANTIC: i64 = 5 // RESERVED: quantised learned embedding |
| 45 | const NX_IT_CLASS_IDENTITY: i64 = 1 |
| 46 | const NX_IT_CLASS_SIMILAR: i64 = 2 |
| 51 | const NX_IT_COPY_THRESH: i64 = 10 // live nx_image_search default; ruler det@10 confirms it |
| 52 | const NX_IT_COPY_MAX: i64 = 64 // dHash is 64 bits |
| 57 | const NX_IT_LOCAL_MAX: i64 = 24 // == KP_MAX |
| 63 | const NX_IT_LOCAL_MININLIERS: i64 = 5 |
| 72 | const NX_IT_LOCAL_SWEPT_MININLIERS: i64 = 7 |
| 73 | const NX_IT_LOCAL_THRESH: i64 = 19 // KP_MAX - NX_IT_LOCAL_MININLIERS |
| 74 | const NX_IT_SIMILAR_MAX: i64 = 20480 // 80 dims x 256 max per-dim spread |
| 81 | const NX_IT_SIMILAR_THRESH: i64 = 1767 |
| 94 | const NX_IT_MIN_BITS: i64 = 6 |
| 97 | const NX_IT_MIN_CELLS: i64 = 4 |
| 116 | const NX_IMGTIER_BYTES: i64 = 96 |
functions
| 119 | func nx_imgtier_kind(t: *nx_imgtier) -> i64 { return t.kind } called by 1: ie_tier_is_hash |
| 120 | func nx_imgtier_klass(t: *nx_imgtier) -> i64 { return t.klass } |
| 121 | func nx_imgtier_name(t: *nx_imgtier) -> *u8 { return t.name_ptr as *u8 } |
| 122 | func nx_imgtier_idx_dim(t: *nx_imgtier) -> i64 { return t.idx_dim } |
| 123 | func nx_imgtier_qry_dim(t: *nx_imgtier) -> i64 { return t.qry_dim } |
| 124 | func nx_imgtier_threshold(t: *nx_imgtier) -> i64 { return t.threshold } |
| 125 | func nx_imgtier_max_dist(t: *nx_imgtier) -> i64 { return t.max_dist } |
| 126 | func nx_imgtier_weight(t: *nx_imgtier) -> i64 { return t.weight } |
| 136 | func nx_imgtier_describe_index(t: *nx_imgtier, gray: *u8, rgb: *u8, w: i64, h: i64, out: *i64) -> i64 |
| 140 | func nx_imgtier_describe_query(t: *nx_imgtier, gray: *u8, rgb: *u8, w: i64, h: i64, out: *i64) -> i64 |
| 144 | func nx_imgtier_distance(t: *nx_imgtier, q: *i64, d: *i64) -> i64 |
| 148 | func nx_imgtier_informative(t: *nx_imgtier, d: *i64) -> i64 |
| 155 | func nx_imgtier_score(t: *nx_imgtier, dist: i64) -> i64 |
| 167 | func it_copy_describe(t: *nx_imgtier, gray: *u8, rgb: *u8, w: i64, h: i64, out: *i64) -> i64 calls 1: nx_phash_dhash |
| 171 | func it_copy_distance(t: *nx_imgtier, q: *i64, d: *i64) -> i64 calls 1: nx_simhash_hamming |
| 177 | func it_hash_informative(t: *nx_imgtier, d: *i64) -> i64 calls 1: nx_simhash_hamming |
| 184 | func nx_imgtier_copy_init(t: *nx_imgtier, threshold: i64, weight: i64) -> i64 called by 1: nx_imgtier_new |
| 204 | func it_orient_describe_index(t: *nx_imgtier, gray: *u8, rgb: *u8, w: i64, h: i64, out: *i64) -> i64 calls 1: nx_phash_dhash |
| 208 | func it_orient_describe_query(t: *nx_imgtier, gray: *u8, rgb: *u8, w: i64, h: i64, out: *i64) -> i64 calls 1: nx_orient_hashes |
| 211 | func it_orient_distance(t: *nx_imgtier, q: *i64, d: *i64) -> i64 calls 1: nx_orient_best_hamming |
| 215 | func nx_imgtier_orient_init(t: *nx_imgtier, threshold: i64, weight: i64) -> i64 called by 1: nx_imgtier_new |
| 235 | func it_similar_describe(t: *nx_imgtier, gray: *u8, rgb: *u8, w: i64, h: i64, out: *i64) -> i64 calls 1: nx_visdesc_extract |
| 238 | func it_similar_distance(t: *nx_imgtier, q: *i64, d: *i64) -> i64 calls 1: nx_visdesc_l1 |
| 243 | func it_similar_informative(t: *nx_imgtier, d: *i64) -> i64 |
| 254 | func nx_imgtier_similar_init(t: *nx_imgtier, threshold: i64, weight: i64) -> i64 called by 1: nx_imgtier_new |
| 276 | func it_local_describe(t: *nx_imgtier, gray: *u8, rgb: *u8, w: i64, h: i64, out: *i64) -> i64 calls 1: nx_imgkp_describe |
| 281 | func it_local_describe_query(t: *nx_imgtier, gray: *u8, rgb: *u8, w: i64, h: i64, out: *i64) -> i64 calls 1: nx_imgkp_describe_query |
| 284 | func it_local_distance_one(q: *i64, d: *i64) -> i64 |
| 314 | func it_local_distance(t: *nx_imgtier, q: *i64, d: *i64) -> i64 |
| 339 | func it_local_informative(t: *nx_imgtier, d: *i64) -> i64 calls 1: nx_imgkp_count |
| 344 | func nx_imgtier_local_init(t: *nx_imgtier, threshold: i64, weight: i64) -> i64 |
| 361 | func nx_imgtier_new(kind: i64, threshold: i64, weight: i64) -> *nx_imgtier |