LOAD MP4 on an OUTPUT_DIR file copies it into UPLOAD_DIR (chunked, progress via /api/copy-progress) before spawning RECOUNT_CMD; collision in UPLOAD_DIR fails with 400. New read-only folder-grouped browser mirrors recounting_dashboard.py. Docs updated.
64 lines
5.5 KiB
Markdown
64 lines
5.5 KiB
Markdown
# AGENTS.md — bytetrack-counter
|
|
|
|
## Architecture
|
|
|
|
- **Edge AI counter**: RTSP camera → YOLO RKNN (NPU) → tracking → line-crossing → SQLite + JSON state → Flask dashboard.
|
|
- **2 counter scripts**, only 1 deployed:
|
|
- `counter_live.py` — Jetson TensorRT artifact (CUDA, NOT used on RK3588).
|
|
- **`counter_live_rknn_bytetrack.py`** — RK3588 with ByteTrack. **This is what systemd runs.** Reference for C++ port.
|
|
- `batch_store.py` — shared SQLite persistence + batch state machine.
|
|
- `counter_dashboard.py` — Flask dashboard on port 5000, same DB.
|
|
- `recounting_dashboard.py` — Flask recounting dashboard on port 5002, consumes live + recount APIs.
|
|
- `recounting_dashboard_upload.py` — Flask upload+recount dashboard on port 5003; uploads MP4 first, then **LOAD MP4** spawns `RECOUNT_CMD` (process loads and pauses), then **START RECOUNT** touches `{SHM_DIR}/.continue` to begin counting.
|
|
|
|
## No build / test / lint
|
|
|
|
There is no build system, no test framework, no linter config, no typechecker.
|
|
Do not try to run `pytest`, `ruff`, `mypy`, etc. — they don't exist here.
|
|
|
|
## How to run
|
|
|
|
```bash
|
|
# Copy env (required, .env is gitignored)
|
|
cp config.env.example .env
|
|
|
|
# Venv (must use system-site-packages for RKNN toolkit)
|
|
python3 -m venv --system-site-packages venv
|
|
source venv/bin/pip install -r requirements.txt
|
|
|
|
# Run counter (RK3588 only — needs rknn-toolkit-lite2 & RKNN model)
|
|
PYTHONNOUSERSITE=1 venv/bin/python counter_live_rknn_bytetrack.py
|
|
|
|
# Run dashboard
|
|
PYTHONNOUSERSITE=1 venv/bin/python counter_dashboard.py
|
|
|
|
# Run upload recounting dashboard
|
|
PYTHONNOUSERSITE=1 venv/bin/python recounting_dashboard_upload.py
|
|
```
|
|
|
|
## Key environment & install quirks
|
|
|
|
- **`PYTHONNOUSERSITE=1`** is mandatory when running from the venv — without it, system/user packages leak in.
|
|
- **`.env` is gitignored** — always copy from `config.env.example` first.
|
|
- **`numpy<2`** is required for `rknn-toolkit-lite2` compatibility.
|
|
- **Install path in service files is `/opt/bytetrack-counter`** (not the `/opt/jetson-counter` mentioned in README/DEPLOY). The `.env.example` also reflects `/opt/bytetrack-counter`.
|
|
- Service user is **`root`**, not `jetson` (despite README saying otherwise).
|
|
- Three systemd units: `bytetrack-counter.service`, `bytetrack-counter-dashboard.service`, and `bytetrack-recounting-upload-dashboard.service` (upload recounting dashboard, port 5003).
|
|
- `counter_live.py` (TensorRT) is Jetson-only and won't work on RK3588.
|
|
- **Reset flow**: `POST /api/reset` → deletes state JSON + touches `{SHM_DIR}/.reset`. `batch_store._check_reset_signal()` watches this marker and clears in-memory state on next crossing.
|
|
- **Recount continue flow** (upload dashboard, port 5003): `POST /api/load-mp4 {path}` kills the old process, spawns `RECOUNT_CMD` (loads + pauses), and POSTs `/api/reset` to the recount node. `POST /api/start-recount` (no body, requires a loaded file) touches `{SHM_DIR}/.continue`; the C++ process consumes that marker to begin counting. The dashboard never clears the marker.
|
|
- **OUTPUT_DIR browser + copy-on-load** (upload dashboard, port 5003): `GET /api/mp4-files` lists MP4s from `OUTPUT_DIR` folder-grouped (mirrors `recounting_dashboard.py`'s `/api/mp4-files` shape). Selecting one and pressing LOAD MP4 makes `POST /api/load-mp4` copy it into `UPLOAD_DIR` first (chunked, progress reported via `GET /api/copy-progress` → `{active,name,total,done,pct}`, frontend polls every 250 ms to drive the progress bar). Name collision in `UPLOAD_DIR` → 400 error (no overwrite). The OUTPUT_DIR browser is read-only (no delete).
|
|
- **Recount STOP sweep**: `POST /api/stop-recount` kills the tracked process group (`killpg` TERM→5s→KILL) then sweeps the host via `/proc` for any process whose argv matches the `RECOUNT_CMD` binary name (basename of the first token, so it also catches orphans from a dashboard restart); TERM→`_SWEEP_GRACE_SEC`→KILL with `_SWEEP_MAX_RETRIES` retries, all under `_recount_lock`. Returns `{success, killed, remaining}`; `remaining` is 0 in the normal path, and survivors (e.g. D-state) are logged rather than force-hanging.
|
|
- **Upload filename contract**: uploads are expected as `batch_XX_YYYYMMDD_HHmmSS.mp4` (XX = batch number, timestamp = date). `recounting_dashboard_upload.py` parses both to query the live API's `/api/day-detail/<YYYY-MM-DD>` for the recorded count.
|
|
- **`RECOUNT_CMD`** in the upload dashboard is a template with a `{path}` placeholder, e.g. `bytetrack-counter-cpp config.env --source {path}`; the uploaded file path is `shlex.quote()`d before substitution, and the command is spawned by **LOAD MP4** (loads + pauses until `.continue` is touched).
|
|
- **Cross-dashboard header links**: `UPLOAD_DASHBOARD_URL` (live → upload dashboard, `counter_dashboard.py`) and `LIVE_DASHBOARD_URL` (upload → live dashboard, `recounting_dashboard_upload.py`); empty value hides the header link.
|
|
|
|
## Code conventions
|
|
|
|
- All config lives in `.env` (dotenv), read via `os.getenv()` at module top-level in each script.
|
|
- The 2 counter scripts share drawing/batch helpers. `counter_live_rknn_bytetrack.py` is the reference for C++ port.
|
|
- `batch_store.py` has its own threading (cutoff watcher, batch timeout timer) — thread safety is via a single `state_lock`.
|
|
- The dashboard re-creates DB tables on startup (`_ensure_db()`) independently from `batch_store.py`.
|
|
- No formal version tracking exists anywhere in this codebase.
|
|
- Dashboard template selectable via `DASHBOARD_TEMPLATE` env var — supports `dashboard.html`, `dashboard_lamborghini.html`, `dashboard_tesla.html`.
|