Fix small-marker detection under downsampling (AKAZE) — #53#64
Conversation
- emcc version > 3.1.40 cause issue with isnan
… to wasm_simd128 for consistency
…lly, keyframes, and BHC clusters The matcher had three `std::unordered_map` typedefs whose iteration order depended on the STL implementation (libstdc++ on Linux, MSVC STL on Windows, libc++ on macOS / Emscripten). Code paths that iterate these maps and pick a winner-on-tie produced different results on different platforms, causing the matcher to be non-deterministic across builds. Concretely: 1. `HoughSimilarityVoting::hash_t` (vote tally) is consumed by `getMaximumNumberOfVotes`, which iterates and picks the bin with the highest count. Ties between Hough bins are common at borderline matches and were broken inconsistently per platform. 2. `VisualDatabase::keyframe_map_t` is iterated by `query()`. Ties on inlier count between keyframes are broken first-wins, so the winning keyframe at borderline ties depended on which iteration order the platform's STL chose. 3. `BinaryHierarchicalClustering::cluster_map_t` is iterated during BHC tree construction; ordering affects the resulting topology and therefore which features cluster together, which propagates into the eventual inlier set. All three typedefs become `std::map<...>`. `std::map`'s ascending- key iteration is consistent across STL implementations (and matches the BTreeMap fix on the pure-Rust port, webarkit/WebARKitLib-rs issue #170). API surface change: none. `std::map` and `std::unordered_map` share the operations used here (`operator[]`, `find`, `insert`, `erase`, `clear`, `iterator`). Performance: `O(log N)` lookup instead of `O(1) amortized`, but N is small for all three maps (number of keyframes ~1-10, number of Hough bins voted for in a query ~10s, number of BHC clusters per level ~1-100), so the difference is negligible. `VisualDatabaseImpl::point3d_map_t` in `facade/visual_database_facade.cpp` is left as `std::unordered_map` because it is used lookup-only (`map[image_id] = ...`, `return map[image_id]`); changing it has no functional benefit and would be cosmetic only. Motivation + measurements live in webarkit/WebARKitLib-rs issue #170, which has the cross-platform repro from CI.
Integrate the WebARKit OCVT tracker line: dev → master
- Add full-resolution retry in processFrame when pyrDown'd detection yields <= minRequiredDetectedFeatures (small markers fell below the detector threshold after downsampling). - Pass the actual detection scale factor into MatchFeatures so matched keypoints are rescaled correctly on both the downsampled and the full-res-retry paths. - Align AKAZE path with artoolkitX OCVT: threshold 3e-4 -> 1e-3, nn_match_ratio 0.7 -> 0.8 (new AKAZE_NN_MATCH_RATIO), minNumMatches 40 -> 15. - Add per-level keypoint-count logging for diagnosability. Co-authored-by: kalwalt <1275858+kalwalt@users.noreply.github.com>
…shold - Drop misleading `f` suffix on double constants (DEFAULT/TEBLID/AKAZE NN_MATCH_RATIO); use EXPECT_DOUBLE_EQ in the config test. - Retry condition uses `<` (not `<=`) to match the `>` matching gate, so a frame with exactly minRequiredDetectedFeatures keypoints is not needlessly retried. Co-authored-by: kalwalt <1275858+kalwalt@users.noreply.github.com>
PR Summary by QodoFix AKAZE small-marker detection after frame downsampling
AI Description
Diagram
High-Level Assessment
Files changed (4)
|
Code Review by Qodo
1.
|
Addresses Qodo review findings on #64: - The full-res retry fired only when keypoints < minRequiredDetectedFeatures, while matching required > minRequiredDetectedFeatures. A frame with exactly minRequiredDetectedFeatures keypoints hit neither path. Widen the retry condition to <= so that boundary case is covered. - Correct the AKAZE threshold comment: raising the detector response threshold from 3e-4 to 1e-3 is stricter, not looser, even though the OCVT-aligned value was validated to fix small-marker detection. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
Investigates and fixes #53: the small-marker scene
pinball-demo.jpg(2000×1500, detection pyrLevel=1) was not acquired by theC++/WASM tracker even though the same reference + scene track in WebARKitLib-rs
and jsartoolkitNFT. Root cause analysis + a minimal fix on the AKAZE path,
validated end-to-end via the webarkit-testing harness.
Features accomplished
processFrame: if the downsampleddetectionFrameyields too few keypoints (
< minRequiredDetectedFeatures) andpyrLevel > 0,detection retries once at full resolution before giving up.
MatchFeaturesnowtakes the actual detection scale factor so matched keypoints are rescaled
correctly on both the downsampled and the retry path. Preserves the Restore downsampled feature detection + single-marker detection guard for performance (reconcile with ArtoolkitX OCVT) #44 fast path.
3e-4→1e-3,nn_match_ratio0.7→0.8(newAKAZE_NN_MATCH_RATIO),minNumMatches40→15.EXPECT_DOUBLE_EQ(double literals, no float suffix).Issues resolved
scene via webarkit-testing): AKAZE goes from NOT FOUND → FOUND (attempt 2,
1809 keypoints/matches on the downsampled frame). The effective change was the
OCVT-aligned AKAZE params; the full-res retry isn't even needed for this scene.
What's next
failure is downstream of keypoint detection (ratio-test → homography RANSAC;
1000 keypoints + 1000 knn candidates per frame, yet no valid homography), not the
downsampling, so it's a separate root cause to be fixed in a follow-up PR.
test/small-marker-pinball-demo-fix-53.