Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 13 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,42 +123,33 @@ This example loads a camera parameter file, a marker (pattern or barcode), and a
Generate NFT (Natural Feature Tracking) marker files compatible with ARnft and NFT-Marker-Creator-App:

```bash
# Pure-Rust default (no C++ toolchain required) — produces .iset + .fset.
# Pass --yes to skip the interactive "no .fset3" confirmation prompt
# (recommended for scripted / CI use):
# Pure Rust, no C++ toolchain — produces a COMPLETE marker (.iset + .fset + .fset3):
cargo run --release --example nft_marker_gen -- \
--input path/to/image.jpg \
--output path/to/output_name \
--dpi 220 \
--yes

# Full output including .fset3 (FREAK descriptors) — opt-in C++ backend:
# .fset3 generation still uses CppFreakMatcher today; the runtime KPM
# tracker reads .fset3 files in pure Rust, only the marker-creation step
# needs the C++ FREAK extractor. Wiring nft_marker_gen to RustFreakMatcher
# for .fset3 is a follow-up task.
cargo run --release --features ffi-backend --example nft_marker_gen -- \
--input path/to/image.jpg \
--output path/to/output_name \
--dpi 220

# With SIMD acceleration + Rayon parallelism (recommended for x86_64):
cargo run --release --features "ffi-backend,simd-x86-sse41,simd-x86-avx2" \
cargo run --release --features "simd-x86-sse41,simd-x86-avx2" \
--example nft_marker_gen -- \
--input path/to/image.jpg \
--output path/to/output_name \
--dpi 220
```

> Since #179, `.fset3` (FREAK descriptors) is generated by the pure-Rust
> `RustFreakMatcher` — a single invocation produces a complete marker with no
> C++ toolchain.

> **Important:** Always use the `--release` flag. Debug builds are 5–10× slower due to missing compiler optimizations.

Output files (depends on whether `ffi-backend` is enabled):
Output files (all produced on the pure-Rust default):

| File | Default (pure Rust) | With `ffi-backend` | Description |
|---|:---:|:---:|---|
| `<output>.iset` | ✅ | ✅ | JPEG-compressed image pyramid (~300 KB, matches C++ `ar2WriteImageSet()` format) |
| `<output>.fset` | ✅ | ✅ | Feature map with one entry per pyramid level |
| `<output>.fset3` | ❌ (skipped, with warning) | ✅ | FREAK descriptors for KPM-based recognition |
| File | Description |
|---|---|
| `<output>.iset` | JPEG-compressed image pyramid (~300 KB, matches C++ `ar2WriteImageSet()` format) |
| `<output>.fset` | Feature map with one entry per pyramid level |
| `<output>.fset3` | FREAK descriptors for KPM-based recognition (pure-Rust `RustFreakMatcher`) |

**Options:**

Expand All @@ -168,14 +159,12 @@ Output files (depends on whether `ffi-backend` is enabled):
| `-o` | `--output` | Output base path (without extension) | required |
| `-d` | `--dpi` | Source image resolution in DPI | required |
| `-l` | `--level` | Tracking extraction level (0–4) | `2` |
| `-n` | `--search-feature-num` | Max features per pyramid level | `250` |
| `-y` | `--yes` | Skip the "no .fset3" confirmation prompt (for scripted / CI use; only relevant when built without `ffi-backend`) | (interactive) |

**Performance feature flags:**

| Feature flag | What it enables |
|---|---|
| `ffi-backend` | C++ FREAK backend (opt-in since M9-3 #142). Enables `.fset3` generation in `nft_marker_gen` (the `.iset`/`.fset` outputs work on the pure-Rust default) and powers development cross-validation (`dual-mode`, `cross_stack_parity`). Runtime NFT tracking with existing `.fset3` files works on the pure-Rust default. |
| `ffi-backend` | C++ FREAK backend (opt-in since M9-3 #142). Used only for development cross-validation (`dual-mode`, `cross_stack_parity`); all of `nft_marker_gen` (`.iset` + `.fset` + `.fset3`) and runtime NFT tracking run on the pure-Rust default. |
| `simd-x86-sse41` | SSE4.1 SIMD acceleration for feature map correlation (`get_similarity`) |
| `simd-x86-avx2` | AVX2+FMA SIMD acceleration for feature map correlation (faster than SSE4.1) |
| `simd` | Umbrella flag — enables all SIMD optimizations (SSE4.1, AVX2, WASM SIMD, image, pattern) |
Expand Down
67 changes: 14 additions & 53 deletions crates/core/examples/nft_marker_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,43 +37,35 @@
//! NFT marker creation example.
//!
//! A pure-Rust replacement for the C/Emscripten `markerCreator` tool.
//! Generates `.iset`, `.fset`, and (with `--features ffi-backend`) `.fset3`
//! from a JPEG input image.
//! Generates a complete NFT marker (`.iset` + `.fset` + `.fset3`) from a JPEG
//! input image with no C++ toolchain (#179 — `.fset3` now uses the pure-Rust
//! `RustFreakMatcher`).
//!
//! ## Usage
//!
//! ```bash
//! # Pure-Rust output (.iset + .fset only):
//! cargo run --example nft_marker_gen -- --input marker.jpg --output marker --dpi 72
//!
//! # Full output including .fset3 (requires C++ FREAK backend):
//! cargo run --example nft_marker_gen --features ffi-backend -- \
//! cargo run --release --example nft_marker_gen -- \
//! --input marker.jpg --output marker --dpi 72
//! ```
//!
//! ## Output files
//!
//! | File | Contents |
//! |---|---|
//! | `<output>.iset` | Multi-resolution image pyramid (always generated) |
//! | `<output>.fset` | AR2 feature points per scale (always generated) |
//! | `<output>.fset3` | KPM/FREAK keypoints (`ffi-backend` only) |
//! | `<output>.iset` | Multi-resolution image pyramid |
//! | `<output>.fset` | AR2 feature points per scale |
//! | `<output>.fset3` | KPM/FREAK keypoints (pure-Rust `RustFreakMatcher`) |

use chrono::Local;
use clap::Parser;
use std::path::Path;
use std::time::Instant;
use webarkitlib_rs::ar2::{ar2_gen_feature_map, ar2_gen_image_set};
#[cfg(not(feature = "ffi-backend"))]
use webarkitlib_rs::arlog_rel;
#[cfg(feature = "ffi-backend")]
use webarkitlib_rs::arlog_w;
use webarkitlib_rs::{arlog_e, arlog_i};
use webarkitlib_rs::{arlog_e, arlog_i, arlog_w};

/// KPM feature density for `.fset3` generation (default extraction level 1).
///
/// Matches `KPM_SURF_FEATURE_DENSITY_L1` in `markerCreator.cpp`.
#[cfg(feature = "ffi-backend")]
const KPM_SURF_FEATURE_DENSITY: i32 = 100;

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -110,10 +102,6 @@ struct Cli {
/// Matches the `-level=n` flag in the C `markerCreator` tool.
#[arg(long, default_value_t = 2)]
level: u8,

/// Skip the "no .fset3" confirmation prompt (for CI / scripted use).
#[arg(short, long)]
yes: bool,
}

// ---------------------------------------------------------------------------
Expand All @@ -137,31 +125,8 @@ fn main() {
let start = Instant::now();
let start_time = Local::now();

// -----------------------------------------------------------------------
// Warn the user if .fset3 won't be generated (no ffi-backend).
// -----------------------------------------------------------------------
#[cfg(not(feature = "ffi-backend"))]
{
use std::io::Write as _;
if !cli.yes {
arlog_rel!("");
arlog_rel!(
"Warning: built without `ffi-backend` feature — .fset3 will NOT be generated."
);
arlog_rel!("An NFT marker without .fset3 cannot be used for initialization/detection.");
eprint!("Continue anyway? [y/N] ");
let _ = std::io::stdout().flush();
let mut answer = String::new();
std::io::stdin()
.read_line(&mut answer)
.expect("failed to read input");
if !answer.trim().eq_ignore_ascii_case("y") {
arlog_rel!("Aborted.");
std::process::exit(1);
}
arlog_rel!("");
}
}
// .fset3 is now generated by the pure-Rust RustFreakMatcher (#179), so the
// old "no .fset3 without ffi-backend" warning + confirmation prompt are gone.

// -----------------------------------------------------------------------
// Header
Expand Down Expand Up @@ -289,12 +254,11 @@ fn main() {
);

// -----------------------------------------------------------------------
// Step 3: generate KPM/FREAK keypoints → .fset3 (ffi-backend only)
// Step 3: generate KPM/FREAK keypoints → .fset3 (pure-Rust, #179)
// -----------------------------------------------------------------------
#[cfg(feature = "ffi-backend")]
{
use webarkitlib_rs::kpm::types::KpmRefDataSet;
use webarkitlib_rs::kpm::{CppFreakMatcher, KpmCompMode, KpmError, KpmProcMode};
use webarkitlib_rs::kpm::{KpmCompMode, KpmError, KpmProcMode, RustFreakMatcher};

arlog_i!("Generating FeatureSet3...");
let step_start = Instant::now();
Expand All @@ -307,8 +271,8 @@ fn main() {
let max_feat = KPM_SURF_FEATURE_DENSITY * scale.xsize * scale.ysize / (480 * 360);

// Fresh matcher for each scale (scales may have different dimensions).
let mut matcher = CppFreakMatcher::new(scale.xsize, scale.ysize).unwrap_or_else(|e| {
arlog_e!("Error: CppFreakMatcher::new failed: {:?}", e);
let mut matcher = RustFreakMatcher::new(scale.xsize, scale.ysize).unwrap_or_else(|e| {
arlog_e!("Error: RustFreakMatcher::new failed: {:?}", e);
std::process::exit(1);
});

Expand Down Expand Up @@ -387,9 +351,6 @@ fn main() {
}
}

#[cfg(not(feature = "ffi-backend"))]
arlog_i!("[fset3] skipped (build with --features ffi-backend to generate)");

// -----------------------------------------------------------------------
// Done
// -----------------------------------------------------------------------
Expand Down
Loading