86 lines
4.4 KiB
Markdown
86 lines
4.4 KiB
Markdown
# SPEC — ByteTrack Counter C++
|
|
|
|
## §G — Goal
|
|
|
|
Real-time object detection + tracking + line-cross counting pipeline on RK3588 NPU.
|
|
Two-class (ayam|talenan). Ayam crossing → increment batch count.
|
|
Talenan crossing → close batch. Persist to SQLite + JSON.
|
|
|
|
## §C — Constraints
|
|
|
|
- RK3588 ARM64, RKNN NPU (librknnrt)
|
|
- Model `zenai_apc_cicalengka_20260609.rknn` — dynamic input shape
|
|
- C++17, OpenCV 4, Eigen3, SQLite3
|
|
- systemd service, root, `ProtectSystem=full`
|
|
- 2 classes: `ayam_class_id=0`, `talenan_class_id=1`
|
|
- Single counting line, crossing direction `rtl`|`ltr`|`both`
|
|
- Shift-based counting: day boundary at `DAILY_CUTOFF_TIME`
|
|
|
|
## §I — Interfaces
|
|
|
|
```
|
|
env: .env KEY=VALUE file — all config per config.env.example
|
|
env: SHM_DIR → shared memory directory (/dev/shm/bytetrack-counter)
|
|
env: LIVE_STREAM_FRAME_PATH → JPEG snapshot path
|
|
env: RECORD_VIDEO, RECORD_DELAY_SECONDS, RECORD_END_DELAY, RECORD_RETENTION_DAYS
|
|
cmd: bytetrack-counter <config.env> [--source <rtsp://|http://|/path/to/file.mp4>]
|
|
db: SQLite batches(id,counting_date,batch_number,camera_name,object_label,count,start_time,end_time)
|
|
db: SQLite daily_summaries(id,counting_date,camera_name,object_label,total_count,total_batches)
|
|
file: JSON state — batch_number,count,start_time,last_detection_time,counted_event_ids[]
|
|
file: CSV crossing log — batch,frame,timestamp,chicken_id
|
|
file: SHM_DIR/.reset → trigger full state reset
|
|
file: SHM_DIR/batch_N_HHMMSS.mp4 → per-batch video recording (moved to OUTPUT_DIR/<batch counting_date>/)
|
|
sig: SIGTERM|SIGINT → shutdown_requested → graceful exit
|
|
```
|
|
|
|
## §V — Invariants
|
|
|
|
```
|
|
V1: ∀ detection → score ≥ conf threshold before tracking
|
|
V2: rknn_set_input_shapes called before rknn_inputs_set (dynamic shape model !)
|
|
V3: ∀ track_id → counted ≤ once per batch (dedup via counted_event_ids)
|
|
V4: talenan crossing → close batch only after ignore_batch_label_timeout (30s) from batch start
|
|
V5: batch (count < min_object | duration < min_duration) → discard ⊥ persist
|
|
V6: batch inactivity ≥ batch_timeout (300s) → auto-close
|
|
V7: counting_date rolls at DAILY_CUTOFF_TIME → cutoff_watcher thread
|
|
V8: ayam & talenan → separate ByteTrack instances (match_thresh_0=0.8, match_thresh_1=0.6)
|
|
V9: live_stream_enabled → parent dir of LIVE_STREAM_FRAME_PATH created @ startup
|
|
V10: stale track (> TRACKED_PRUNE_SEC unseen) → prune from external tracking maps
|
|
V11: all dirs (output_dir, shm_dir, live_stream parent) created @ startup
|
|
V12: SHM_DIR/.reset → BatchStore::reset() — end active batch, clear memory, delete state_file, write blank live JPEG
|
|
V13: video recording: motion → SPECULATING (≤RECORD_DELAY_SECONDS), ayam → CONFIRMED → talenan/batch-end → STOPPING (+RECORD_END_DELAY) → move to OUTPUT_DIR/<batch counting_date>/ via fork
|
|
V14: video retention: day-dirs > RECORD_RETENTION_DAYS old → removed @ startup & @ each CUTOFF_TIME (localtime cutoff matches folder names)
|
|
```
|
|
|
|
## §T — Tasks
|
|
|
|
```
|
|
id|status|task|cites
|
|
T1|x|YOLO inference via RKNN NPU (letterbox, BGR2RGB, coordinate rescale)|V1,V2
|
|
T2|x|ByteTrack Kalman filter + two-stage IoU matching|V8
|
|
T3|x|Line-cross detection (rtl|ltr|both) with prev→current cx|V3
|
|
T4|x|Batch persistence: SQLite + JSON state file|V5,V6
|
|
T5|x|Batch dedup via counted_event_ids[]|V3
|
|
T6|x|Talenan-closes-batch logic with ignore-on-start timeout|V4
|
|
T7|x|Batch timeout thread (generational counter, detached)|V6
|
|
T8|x|Daily cutoff watcher thread|V7
|
|
T9|x|Optional motion detection skip-inference|C.motion
|
|
T10|x|Config from .env file (get_env with defaults)|I.env
|
|
T11|x|Drawing: HUD, hero count, line, boxes, popups, skeleton|-
|
|
T12|x|CsvLogger for crossing events|I.file
|
|
T13|x|Live JPEG snapshot to shared memory|V9
|
|
T14|x|systemd service with ProtectSystem + ReadWritePaths|I.cmd
|
|
T15|x|Graceful shutdown: SIGTERM → close batch → save state|I.sig
|
|
T16|x|Video per-batch recording: motion-triggered → speculating in shm, confirmed when ayam detected ≤ RECORD_DELAY_SECONDS, stopped RECORD_END_DELAY after batch close, moved to OUTPUT_DIR/<batch counting_date>/ via fork, retention cleanup at startup & cutoff. Unconfirmed recordings deleted.|V13,V14
|
|
T17|~?|Python DEBUG_TRACKING equivalent (per-frame score/track dump)|-
|
|
T18|x|CLI --source/-s/--source= override of SOURCE (rtsp|http|local mp4; file = single pass)|I.cmd
|
|
```
|
|
|
|
## §B — Bugs
|
|
|
|
```
|
|
id|date|cause|fix
|
|
```
|
|
|
|
(base empty — distilled from code, no bug history)
|