Lightweight computer vision application for retail analytics on Jetson Orin Nano (and NVIDIA GPU workstations). Built on Savant + DeepStream 7.
| Feature | Details |
|---|---|
| People detection | NVIDIA PeopleNet (ResNet34, quantized ONNX) |
| Tracking | NvSORT (lightweight for edge) |
| Tripwire / line counting | Configurable in/out counts per line per camera |
| Zone dwell time | Min / avg / max dwell by hour and by day |
| Live streams | HLS, RTSP, WebRTC via Savant always-on-sink |
| Analytics storage | Elasticsearch (2 indices: counting + dwell) |
| Dashboards | Built-in React UI + Kibana for advanced charts |
| Deployment | Docker Compose for x86 and Jetson L4T |
RTSP Camera(s)
│
▼ ZMQ (dealer+connect)
┌─────────────────────────────────┐
│ Savant Module │
│ PeopleNet → Tracker → │
│ LineCrossing pyfunc → │ ──► Elasticsearch
│ ZoneDwell pyfunc │ cv_lite_people_counting
└─────────────────────────────────┘ cv_lite_zone_dwell
│ ZMQ (pub)
▼
Always-On Sink ──► HLS :888 / RTSP :554 / WebRTC :8889
│
▼
React Frontend :3000
(live stream + stats)
FastAPI Backend :8000
├── SQLite (camera/line/zone config)
├── writes line_config.yml & zone_config.yml → /config volume
├── REST API for CRUD
└── Analytics API (queries Elasticsearch)
# 1. Copy env file
cp .env.example .env
# Edit .env — set SITE_ID, ES_PASSWORD, etc.
# 2. Start core services
docker compose up -d
# 3. Open UI
open http://localhost:3000
# Kibana (optional)
open http://localhost:5601- Go to Configuration → Add Camera
- Enter Camera ID (becomes Savant
source_id), name, RTSP URL - Click Snapshot to preview the frame
- Click Add Line → draw a tripwire on the frame → name it
- Click Add Zone → click polygon points → double-click to close → name it
For each camera start one source adapter container:
SOURCE_ID=cam-001 RTSP_URL=rtsp://user:pass@192.168.1.10:554/stream \
docker compose --profile sources up rtsp-source -d# Prerequisites: JetPack 6.x, Docker, nvidia-container-toolkit
cp .env.example .env
# Set ES_JAVA_OPTS=-Xms256m -Xmx256m in .env
docker compose -f docker-compose.l4t.yml up -d
# Kibana is optional — start with:
docker compose -f docker-compose.l4t.yml --profile kibana up kibana -dFirst run note: TensorRT engine compilation for PeopleNet takes ~10 min on the first start. Subsequent starts are instant from cache.
| Field | Type | Description |
|---|---|---|
@timestamp |
date | Event time |
site_id |
keyword | Site identifier |
camera_id |
keyword | Savant source ID |
line_id |
keyword | Tripwire line ID |
direction |
keyword | in or out |
count_in |
integer | Cumulative in count |
count_out |
integer | Cumulative out count |
| Field | Type | Description |
|---|---|---|
@timestamp |
date | Exit time |
zone_id |
keyword | Zone identifier |
track_id |
long | Person track ID |
enter_time |
date | Zone entry timestamp |
exit_time |
date | Zone exit timestamp |
dwell_seconds |
float | Time in zone (s) |
To enable your YOLOv8 attributes model:
- Set
ENABLE_ATTRIBUTES_MODEL=truein.env - Mount your model weights into the savant-module container
- Add a secondary
nvinfer@classifierelement insavant-module/module.yml
The backend writes two YAML files to the shared /config volume that the Savant pyfuncs poll every 30 seconds:
/config/line_config.yml— tripwire line coordinates per camera/config/zone_config.yml— zone polygon coordinates per camera
retail-cv-lite-edge/
├── savant-module/ # Savant/DeepStream pipeline
│ ├── module.yml # Pipeline definition
│ ├── Dockerfile.x86
│ ├── Dockerfile.l4t
│ └── analytics/
│ ├── line_crossing.py
│ ├── zone_dwell.py
│ ├── config_loader.py
│ ├── es_client.py
│ └── overlay.py
├── backend/ # FastAPI REST API
│ ├── app/
│ │ ├── main.py
│ │ ├── models.py
│ │ ├── config_writer.py
│ │ └── routers/
│ └── Dockerfile
├── frontend/ # React + Tailwind UI
│ ├── src/
│ │ ├── pages/Dashboard.jsx
│ │ ├── pages/Configuration.jsx
│ │ └── components/CanvasEditor.jsx
│ └── Dockerfile
├── assets/tracker/ # NvSORT tracker config
├── docker-compose.yml # x86 deployment
├── docker-compose.l4t.yml # Jetson deployment
└── .env.example
The UI uses a dark analytics theme (navy + sky-blue accents). To apply your brand colours, edit frontend/tailwind.config.js — update the brand colour palette.
Brand guidelines from the Google Slides presentation were not accessible at build time (requires Google auth). Share the colour hex codes and we can update the theme.