252 lines
11 KiB
Bash
252 lines
11 KiB
Bash
# =============================================================================
|
||
# Edge RK3588 production counter + dashboard
|
||
# Shared config for: counter_live_rknn_bytetrack.py + counter_dashboard.py
|
||
# Copy to .env on device: cp config.env.example .env && nano .env
|
||
# =============================================================================
|
||
|
||
# --- Core paths ---
|
||
# Root output directory (logs, DB, video, CSV)
|
||
OUTPUT_DIR=/opt/bytetrack-counter
|
||
# SQLite database path for daily counter records & crossing logs
|
||
DB_PATH=/opt/bytetrack-counter/bytetrack_counter.db
|
||
# JSON file persisting the current active counting day state
|
||
STATE_FILE=/tmp/bytetrack_current_counter.json
|
||
|
||
# --- Input source ---
|
||
# RTSP / HTTP live stream, or a local video file path
|
||
SOURCE=rtsp://user:pass@192.168.0.100:554/stream1
|
||
# FFmpeg capture options passed to cv2.VideoCapture (RTSP low-latency flags)
|
||
OPENCV_FFMPEG_CAPTURE_OPTIONS=rtsp_transport;tcp|fflags;nobuffer|flags;low_delay
|
||
|
||
# --- RKNN model ---
|
||
# Path to exported .rknn model (YOLO format, e.g. yolo11n.rknn)
|
||
MODEL_PATH=/opt/models/yolo9t.rknn
|
||
# Input image size for the model (square, e.g. 320 → 320×320)
|
||
IMGSZ=320
|
||
# Use FP16 inference on NPU (true/false); currently unused in ByteTrack variant
|
||
HALF=false
|
||
# NPU core mask: 1=core0, 2=core1, 3=core0+core1, 7=all three
|
||
CORE_MASK=7
|
||
# Compute device index (reserved; not used at runtime)
|
||
DEVICE=0
|
||
|
||
# --- YOLO decoder ---
|
||
# Number of object classes the model outputs
|
||
NUM_CLASSES=2
|
||
# Apply sigmoid to raw class scores (true/false); set true if model head uses BCE logits
|
||
SCORE_SIGMOID=false
|
||
|
||
# --- Detection ---
|
||
# Confidence threshold – detections below this are discarded before NMS
|
||
CONF=0.3
|
||
# NMS IoU threshold (0-1): boxes overlapping the top box by more than this are
|
||
# suppressed. RAISE it (e.g. 0.6-0.7) if two close/overlapping objects are being
|
||
# merged into one and their separate boxes get suppressed. Lower = more aggressive
|
||
# merging. Default 0.45.
|
||
NMS_IOU=0.45
|
||
|
||
# --- ByteTrack tracking ---
|
||
# Detections with score >= this get priority matching in the first association stage
|
||
TRACK_HIGH_THRESH=0.5
|
||
# Detections with score between this and TRACK_HIGH_THRESH are matched in the second stage
|
||
TRACK_LOW_THRESH=0.1
|
||
# IoU threshold for the first-stage association (0–1). Higher = stricter overlap required
|
||
TRACK_MATCH_THRESH=0.8
|
||
# Frames a track survives without a match before being permanently removed
|
||
TRACK_BUFFER=30
|
||
# Minimum consecutive (or total) hits needed before a track is considered confirmed
|
||
TRACK_MIN_HITS=3
|
||
|
||
# --- ID-switch counting guards ---
|
||
# When a track's ID changes right at the counting line, one physical object can be
|
||
# counted twice (two IDs cross) or missed (neither ID sees the full transition).
|
||
# These two guards correct for that.
|
||
#
|
||
# Dedup guard (prevents double counting): after a crossing, a second crossing in
|
||
# the SAME direction within DEDUP_FRAMES frames and DEDUP_PX horizontal pixels is
|
||
# ignored (treated as the same object under a new ID).
|
||
DEDUP_FRAMES=15
|
||
DEDUP_PX=60
|
||
# To DISABLE the dedup guard, set DEDUP_PX=-1 (distance check can never match).
|
||
#
|
||
# Inheritance guard (prevents missed counting): when a brand-new track appears, it
|
||
# inherits the last position of a recently-seen nearby track (within INHERIT_SEC
|
||
# seconds and INHERIT_PX horizontal pixels) so the crossing is still detected
|
||
# across the ID switch.
|
||
INHERIT_SEC=1.0
|
||
INHERIT_PX=60
|
||
# To DISABLE the inheritance guard, set INHERIT_PX=-1 (distance check can never match).
|
||
|
||
# --- Display ---
|
||
# Site name shown on the dashboard header (top-right)
|
||
SITE_NAME=ZenAi
|
||
|
||
# --- Object class names ---
|
||
# Camera / location identifier shown in HUD and stored in DB
|
||
CAMERA_NAME=ZenAi
|
||
# Label used for batch grouping in the database
|
||
OBJECT_LABEL=object
|
||
# Class name for the counted object (must match model class order)
|
||
CLASS_OBJECT=object
|
||
# Model class ID for the object being counted (default 0)
|
||
OBJECT_CLASS_ID=0
|
||
|
||
# --- Line crossing ---
|
||
# Two horizontal counting lines with sequence logic:
|
||
# Line 1 (IN): top→down counts IN (OUT→IN sequence, or IN-only)
|
||
# Line 2 (OUT): bottom→up counts OUT (IN→OUT sequence, or OUT-only)
|
||
# Passing the opposite line first only arms; the destination line still counts
|
||
# even if the opposite line was never crossed.
|
||
# Fixed y-coordinate for line 1/IN (overrides LINE_Y1_FRAC if set)
|
||
LINE_Y1=
|
||
# Fraction of frame height for line 1 (default 0.33)
|
||
LINE_Y1_FRAC=0.33
|
||
# Fixed y-coordinate for line 2 (overrides LINE_Y2_FRAC if set)
|
||
LINE_Y2=
|
||
# Fraction of frame height for line 2 (default 0.66)
|
||
LINE_Y2_FRAC=0.66
|
||
|
||
# --- Counting day management ---
|
||
# Daily cutoff time (HH:MM) – a new counting day starts after this time and the
|
||
# previous day's counter_in / counter_out totals are finalized in the database.
|
||
# CUTOFF_TIME is an alias used by the dashboard; DAILY_CUTOFF_TIME takes priority in counter_live_rknn.py.
|
||
DAILY_CUTOFF_TIME=20:00
|
||
CUTOFF_TIME=20:00
|
||
|
||
# --- CSV export ---
|
||
# Write per-crossing events to a CSV file (true/false)
|
||
EXPORT_CSV=true
|
||
# Path where the crossing CSV is written
|
||
CROSS_CSV=/opt/batch-counter/crossings.csv
|
||
|
||
# --- Crossing snapshots ---
|
||
# Save an annotated frame image every time an object crosses a line and the
|
||
# counter increases (true/false, default: false). Written to <DIR>/cross/
|
||
# (filename: <YYYYmmdd_HHMMSS_mmm>_<in|out>_id<track>_f<frame>.jpg)
|
||
SAVE_CROSS_SNAPSHOT=false
|
||
# Also save one snapshot the first time each object is detected, before it crosses
|
||
# (true/false, default: false). Written to <DIR>/detect/ with the same track id so
|
||
# it can be correlated with the crossing snapshot
|
||
# (filename: <YYYYmmdd_HHMMSS_mmm>_detect_id<track>_f<frame>.jpg)
|
||
SAVE_DETECT_SNAPSHOT=false
|
||
# Base directory for snapshots (detect/ and cross/ subfolders are created inside).
|
||
# The dashboard reads this same path to display the snapshot gallery, so keep it
|
||
# identical for both the counter and the dashboard.
|
||
CROSS_SNAPSHOT_DIR=/opt/batch-counter/snapshots
|
||
# JPEG quality for snapshots (1-100)
|
||
CROSS_SNAPSHOT_QUALITY=85
|
||
# Retention: keep at most this many snapshot files (detect + cross combined);
|
||
# oldest are deleted first (0 = unlimited)
|
||
CROSS_SNAPSHOT_MAX_FILES=1000
|
||
# Retention: delete snapshots older than this many days (0 = never by age)
|
||
CROSS_SNAPSHOT_MAX_AGE_DAYS=7
|
||
# Run the cleanup sweep at most once every N seconds
|
||
CROSS_SNAPSHOT_CLEANUP_SEC=60
|
||
|
||
# --- Rate / performance ---
|
||
# Enable motion detection pre-filter: skip inference on frames with no movement
|
||
# (true/false, default: false), saving NPU/CPU load. Motion is measured by the
|
||
# fraction of pixels that changed (localized-motion aware), NOT the whole-frame
|
||
# average, so an object entering the edge of the frame is detected immediately.
|
||
MOTION_DETECTION_ENABLED=false
|
||
# Per-pixel intensity change (0-255) for a pixel to count as "moved". Lower = more
|
||
# sensitive to subtle movement. Default 25.
|
||
MOTION_PIXEL_DELTA=25
|
||
# Fraction of frame pixels (0-1) that must change to trigger inference. Lower =
|
||
# more sensitive / detects smaller or farther objects sooner. Default 0.002 (0.2%).
|
||
MOTION_MIN_AREA_FRAC=0.002
|
||
# Heartbeat: always run inference at least every N frames even with no detected
|
||
# motion, so a slow or barely-moving object is never missed for long. Default 15.
|
||
MOTION_HEARTBEAT_FRAMES=15
|
||
# (Deprecated) old whole-frame mean-difference threshold; no longer used.
|
||
MOTION_THRESHOLD=5.0
|
||
|
||
# --- Runtime control (start/stop counting on the fly) ---
|
||
# When true, the counter watches a JSON control file and pauses/resumes object
|
||
# detection & counting based on its "counting" flag. The dashboard shows a
|
||
# COUNTING ON/OFF toggle button that writes this file. When false, the counter
|
||
# always counts and the dashboard hides the toggle. (true/false, default: false)
|
||
CONTROL_ENABLED=false
|
||
# Path to the shared control file. MUST be identical for the counter and the
|
||
# dashboard so the toggle takes effect. Contents: {"counting": true|false}
|
||
CONTROL_FILE=/opt/bytetrack-counter/control.json
|
||
# Counting state to assume on startup / when the control file does not exist yet.
|
||
CONTROL_DEFAULT_COUNTING=true
|
||
# How often (seconds) the counter re-reads the control file. Default 1.0.
|
||
CONTROL_POLL_SEC=1.0
|
||
# Optional TCP control socket (requires CONTROL_ENABLED=true). Lets you toggle
|
||
# counting over the network with line commands. It updates the same control file,
|
||
# so changes apply within CONTROL_POLL_SEC. (true/false, default: false)
|
||
# Commands (newline-terminated): START|RESUME|ON, STOP|PAUSE|OFF, TOGGLE, STATUS
|
||
# e.g. printf 'STOP\n' | nc 127.0.0.1 5090
|
||
CONTROL_SOCKET_ENABLED=false
|
||
# Bind address for the control socket. Use 127.0.0.1 for local-only, 0.0.0.0 to
|
||
# allow remote clients (no auth — protect with firewall / trusted network).
|
||
CONTROL_SOCKET_HOST=127.0.0.1
|
||
# Control socket TCP port.
|
||
CONTROL_SOCKET_PORT=5090
|
||
|
||
# --- Status webhook gate (count only when status is NYALA) ---
|
||
# When true, counting runs only while the webhook state file reports NYALA
|
||
# (or STATUS_WEBHOOK_ACTIVE_VALUE). Any other/missing status pauses counting.
|
||
STATUS_WEBHOOK_ENABLED=false
|
||
# JSON file written by the external status webhook.
|
||
# Example active: {"status": "IN / OUT"}
|
||
# Example inactive: {"status": "OFF"}
|
||
STATUS_WEBHOOK_FILE=/opt/zenai-kpc-python/status_webhook_state
|
||
# Status value that allows counting (case-insensitive match).
|
||
STATUS_WEBHOOK_ACTIVE_VALUE=IN, OUT
|
||
# Expected inactive status from the webhook (shown on overlay when paused).
|
||
STATUS_WEBHOOK_INACTIVE_VALUE=OFF
|
||
|
||
# Sliding window in seconds for computing the crossing rate (objects/minute)
|
||
RATE_WINDOW_SEC=60
|
||
# Number of frames to discard at startup to let the stream buffer stabilise
|
||
WARMUP_FRAMES=30
|
||
# Delay in seconds between stream reconnection attempts
|
||
RECONNECT_DELAY_SEC=3
|
||
# Maximum reconnection attempts (0 = infinite)
|
||
MAX_RECONNECT_ATTEMPTS=0
|
||
# Seconds after which a tracked but unseen object is pruned from the active set
|
||
TRACKED_PRUNE_SEC=300
|
||
|
||
# --- Video recording ---
|
||
# Session mode (default): ByteTrack-style — encode pending MP4 in SHM_DIR, start on
|
||
# webhook OFF→IN/OUT (or control resume), stop on IN/OUT→OFF, then background-copy to
|
||
# VIDEO_OUTPUT_DIR/<counting_date>/. Point VIDEO_OUTPUT_DIR at NFS for remote store.
|
||
# segment mode: legacy continuous dumps under OUTPUT_DIR every VIDEO_SEGMENT_SEC.
|
||
RECORD_VIDEO=true
|
||
RECORD_MODE=session
|
||
SHM_DIR=/dev/shm/zenai-kpc-counter
|
||
VIDEO_OUTPUT_DIR=/opt/bytetrack-counter
|
||
RECORD_END_DELAY=1
|
||
RECORD_DISCARD_EMPTY=true
|
||
RECORD_RETENTION_DAYS=3
|
||
RECORD_CRF=18
|
||
RECORD_PRESET=slow
|
||
# Legacy segment length (RECORD_MODE=segment only)
|
||
VIDEO_SEGMENT_SEC=3600
|
||
# Output video FPS (fallback if source FPS is unknown or ≤ 1)
|
||
OUTPUT_FPS=15
|
||
|
||
# --- Live stream snapshot ---
|
||
# Periodically write the latest annotated frame as JPEG for an external web server
|
||
LIVE_STREAM_ENABLED=false
|
||
# Path to the shared-memory snapshot file (served by nginx / lighttpd)
|
||
LIVE_STREAM_FRAME_PATH=/dev/shm/zenai-kpc-counter/live_frame.jpg
|
||
# JPEG quality (1–100)
|
||
LIVE_STREAM_QUALITY=75
|
||
# Write the snapshot every N frames (lower = more frequent updates)
|
||
LIVE_STREAM_EVERY_N=2
|
||
|
||
# --- Dashboard (counter_dashboard.py) ---
|
||
# Flask secret key for session/cookie signing — change in production!
|
||
SECRET_KEY=change-me-in-production
|
||
# Bind address for the Flask web server
|
||
DASHBOARD_HOST=0.0.0.0
|
||
# Listen port for the dashboard web UI
|
||
DASHBOARD_PORT=5000
|
||
# Enable Flask debug mode (true/false) — auto-reloads on code changes; disable in production
|
||
FLASK_DEBUG=false
|
||
# Fallback name for the active counting-day JSON state file used by the dashboard
|
||
CURRENT_COUNTER_PATH=/tmp/bytetrack_current_counter.json |