Lucas Canade Optical Flow and Shi-Tomasi feature detection on Rust
High-performance Rust implementation of Lucas-Kanade optical flow and Shi-Tomasi feature detection, optimized for real-time applications and WebAssembly (Wasm) compatibility.
Features
- 🎯 Pyramidal Lucas-Kanade optical flow with per-point status and photometric error
- 🔁 Forward-backward consistency check to reject occlusions and outliers
- 🧭 Optional motion prediction (initial guess) for large inter-frame displacements
- 🔍 Shi-Tomasi feature detection, plus grid-based detection for uniform coverage
- ♻️ Zero-allocation steady-state path (
TrackerContext) for real-time per-frame tracking - ⚡ SIMD-accelerated gradients and pyramid: AVX2 (x86), NEON (aarch64),
simd128(wasm) - 🌐 Built on the
imagecrate; WebAssembly-ready
Usage
Add to your Cargo.toml:
[]
= "0.3"
Basic example — detect corners in one frame and track them into the next:
use ;
use ;
let prev: GrayImage = open.unwrap.into_luma8;
let next: GrayImage = open.unwrap.into_luma8;
let prev_pyr = build_pyramid;
let next_pyr = build_pyramid;
let mut corners = good_features_to_track;
corners.truncate;
let points: = corners.iter.map.collect;
// `None` = no initial guess; 21px window, 30 iterations per level.
let results = calc_optical_flow_ex;
for in points.iter.zip
Real-time tracking
For per-frame tracking (e.g. a VIO front-end or the web demo), reuse a
TrackerContext. After the first frame it performs no heap allocation, and
track_fb adds the forward-backward consistency check:
use ;
let mut ctx = new;
// Per frame pair (`prev`, `next` are `&GrayImage`):
ctx.prepare;
let results = ctx.track_fb;
// Points flagged `TrackStatus::FbInconsistent` failed the round-trip.
The original
calc_optical_flowis still available but deprecated since 0.3.0 — prefercalc_optical_flow_ex(status + error) orTrackerContext.
Live demo
A browser demo runs the tracker entirely client-side in WebAssembly: point your phone's camera at a scene and tap to drop points (or hit Auto to detect Shi-Tomasi corners) and watch them ride the optical flow.
Source and build instructions are in web-demo/.
WebAssembly
The hot image kernels — the Scharr gradients and the pyramid downsample — have
hand-written simd128 paths that are selected automatically on wasm32
— but only when the target is built with SIMD enabled, since WASM has no
runtime feature detection. The bundled .cargo/config.toml
sets this for you:
[]
= ["-C", "target-feature=+simd128"]
If you build from a different working directory (so that config is not picked up), pass it yourself:
RUSTFLAGS="-C target-feature=+simd128"
Without +simd128 the crate still works, falling back to scalar loops. For
production WASM, also run wasm-opt -O3 on the output (wasm-pack does this
automatically). On a 640×480 per-frame track step, the simd128 build plus the
bounds-check-free bilinear sampler is roughly 2× faster than the scalar
build in V8 (Node).