# sparsemap
A live **sparse feature map** built from your webcam, in one small Python file.
Two windows, three things happening at once:
- **Camera window** — your feed overlaid with sparse tracked feature points
(Good-Features-to-Track corners) and **Lucas-Kanade optical-flow vectors**,
drawn from previous to current position and colored by track age.
- **Map window** — a SLAM-style top-down map: the camera's 2D **trajectory**
(heading triangle) plus the sparse **landmarks** implied by those tracks,
which settle into place and freeze as their tracks die.
No exotic dependencies, no model downloads — just OpenCV and numpy,
managed by [pixi](https://pixi.sh).
```
pixi run start
```
That's it — pixi auto-installs Python, numpy, and OpenCV into a local
`.pixi/` environment on first run. (Prefer pip? `pip install -r
requirements.txt` then `python3 sparsemap.py` works too.)
## What you'll see
1. Green/cyan dots appear on strong corners in the image — those are the
sparse feature points.
2. Short vectors extend from where each point was last frame to where it is
now — that's sparse optical flow (Lucas-Kanade, pyramid LK).
3. The map window fills with a trail and a cloud of dots. Each dot is a
landmark: a tracked corner projected onto a world plane using a rigid
2D motion estimate (`estimateAffinePartial2D`, RANSAC) chained over time.
Track a dot long enough and it locks in when it leaves view.
4. Move your camera sideways, forward, or rotate it — the trail bends and
the landmark cloud reorganizes live.
## Controls
| key | action |
|-------|-----------------------------|
| `r` | reset tracks + landmarks |
| `f` | toggle flow vectors |
| `p` | toggle feature points |
| `m` | toggle map window |
| `s` | toggle stats overlay |
| space | pause / resume |
| `q` / Esc | quit |
## Flags
```
--camera 0 webcam index (try 1, 2 ... if you have several)
--width 1280 requested capture width
--height 720 requested capture height
--features 200 max tracked corners
--scale 0.5 processing downscale for slower machines
--map-size 560 map window size in px
--no-map start with the map hidden
```
Examples:
```
pixi run start -- --camera 1 --features 300 # second webcam, denser map
python3 sparsemap.py --scale 0.5 # cheaper LK on laptops
```
## How it works (honest version)
1. `goodFeaturesToTrack` finds corners; `calcOpticalFlowPyrLK` tracks them
frame to frame, with a forward-backward consistency check to prune drift.
2. `estimateAffinePartial2D` (RANSAC) fits a rigid 2D transform between the
previous and current point sets. If at least 8 inliers survive, the
transform is chained into a camera pose `(x, y, theta)`.
3. Each live track is projected onto a world plane at the current pose
(planar-motion assumption — think "looking down at my desk"). Dying
tracks freeze where they were projected, accumulating a sparse map.
This is a toy planar "SLAM-lite": great for visualizing sparse flow,
tracking quality, and 2D camera motion. It is **not** full 6-DoF SLAM —
no depth, no loop closure, no bundle adjustment. Rotating the camera in
place still produces a satisfying spinning trail, though.
## Requirements
- [pixi](https://pixi.sh) (`curl -fsSL https://pixi.sh/install.sh | bash`)
— or plain Python 3.8+ with `pip install -r requirements.txt`
- Any webcam OpenCV can open (Linux V4L2, macOS AVFoundation, Windows MSMF)
## Troubleshooting
- **`could not open camera N`** — try `--camera 1`. On Linux check
`/dev/video*` permissions (add yourself to the `video` group).
- **Laggy** — lower `--features`, or use `--scale 0.5`; or ask the camera
for `--width 640 --height 480`.
- **Map drifts** — expected; it's chained 2D estimation with no loop
closure. Press `r` to reset when it bothers you.
## License
MIT.