## https://sploitus.com/exploit?id=AE10FCF7-3BC3-5373-8A1A-33766F16723C
# CPCS โ Camera-Based Passenger Counting System
> **Proof of concept** ยท first-year ECE intern project ยท SPowerZ Solutions
> Mounts two cameras at a bus doorway, counts boarding and alighting passengers at every stop, and reconciles counts against the on-board ticket machine (POS) to detect revenue leakage โ all running locally on edge hardware with no video upload to the cloud.
---
## Problem statement
Indian city bus operators lose significant revenue to fare evasion and have no independent check on their ticket machines. Manual headcounts are inconsistent; existing camera systems produce video but not structured data. CPCS turns a pair of commodity USB cameras into a real-time passenger counter whose output can be audited stop-by-stop and compared to what the POS system says it collected.
---
## How it works
```
2ร USB cameras (bus doorway)
โ
โผ
YOLOv8 person detection โโโบ ByteTrack multi-object tracker
โ
โผ
DoorCounter v6 (four recovery layers, every count audited)
โโ live โ zone transition observed while continuously tracked
โโ coast โ box lost at the line; crossing completed from measured velocity
โโ stitch โ fragmented track reconnected by velocity prediction
โโ fallback โ born one side of line, died the other
โ
โผ
TripRecorder โโโบ SQLite (per-stop boardings, alightings, occupancy, POS reconciliation)
โ
โผ
build_dashboard.py โโโบ cpcs_dashboard.html
(self-contained, no server, bilingual EN/HI, print-ready)
```
**Spec targets**
| Target | Status |
|---|---|
| โฅ 95% recall on boarding/alighting events | Validated on benchmark clip; production target |
| Runs fully locally on Orange Pi 5 (RK3588S, 6 TOPS NPU) | Pipeline runs; RKNN export in progress |
| No video upload to cloud | Enforced by design โ only counts leave the device |
| Per-stop POS reconciliation + revenue leakage flagging | Implemented and demoed |
| Scalable to 500-bus fleet | SQLite โ Postgres is one connection-string change |
---
## Repository structure
```
cpcs-prototype/
โ
โโโ cpcs_poc.py โ main capture app: runs the camera, counts, writes to DB
โโโ build_dashboard.py โ generates cpcs_dashboard.html from the DB
โโโ requirements.txt
โ
โโโ counter/ โ development history (each version is a standalone script)
โโโ v2_basic.py dead-zone hysteresis crossing
โโโ v3_hysteresis.py EMA smoothing + state machine
โโโ v4_stitch.py velocity-predicted track stitching
โโโ v5_predictive_stitch.py full stitch + in-band birth + diagnostic logs
```
The production counting engine (v6) lives inside `cpcs_poc.py` as the `DoorCounter` class. The `counter/` folder preserves the iterative development history.
---
## Quick start
**Requirements:** Python 3.10+, a webcam or video file.
```bash
git clone https://github.com/Saikarthik-Ramakrishnan/cpcs-prototype.git
cd cpcs-prototype
pip install -r requirements.txt
```
**Run on the benchmark clip** (download `test_1.mp4` separately โ see below):
```bash
python cpcs_poc.py --source test_1.mp4 --route "47A" --bus "DL-1PC-4432"
```
**Controls while the video window is open:**
| Key | Action |
|-----|--------|
| `n` | Commit the current stop and move to the next |
| `p` | Toggle simulated POS ticket counts |
| `q` | End the trip and quit |
**Build the dashboard:**
```bash
python build_dashboard.py
# opens cpcs_dashboard.html โ double-click it in any browser
```
**Recommended settings for best accuracy:**
```bash
python cpcs_poc.py \
--source test_1.mp4 \
--model yolov8s.pt \
--imgsz 960 \
--route "47A" \
--bus "DL-1PC-4432"
```
`yolov8s.pt` downloads automatically (~22 MB) on first run.
---
## Dashboard features
The generated `cpcs_dashboard.html` is a self-contained single-file report โ no server, no framework, no dependencies beyond a browser.
**Fleet overview tab** (commissioner / management view)
- Aggregate KPIs across all logged trips: total passengers, flagged stops, revenue at risk
- Per-trip bar charts for boardings and revenue at risk
- Trip table with one-click drill-down
**Trip detail tab** (operations / revenue view)
- Occupancy curve with capacity reference line and flag pins at discrepant stops
- Diverging boardings/alightings bar chart per stop
- Counting method breakdown per stop (live / coast / fallback) โ the audit trail
- Timeline scatter of every individual crossing event, colored by method
- Sortable per-stop table with per-stop data confidence percentage
- Plain-language revenue reconciliation with rupee-at-risk figures
**Controls**
- Live fare (โน) and capacity inputs โ all figures recompute instantly
- `เคนเคฟเคเคฆเฅ` button โ full bilingual toggle (English / Hindi)
- `Print report` โ switches to light theme, prints both tabs, restores your theme
- Dark / light theme toggle
---
## Counting engine: accuracy layers
The v6 `DoorCounter` runs four layers in descending order of evidence quality:
### 1. `live` โ direct observation
A track's smoothed centroid crosses the hysteresis band in a single direction. Requires `MIN_AGE = 2` frames of existence and was not previously counted. This is the high-confidence path.
### 2. `coast` โ dead reckoning
When a box vanishes (detection void at the line, the dominant failure mode on low-res footage), the track's last measured velocity is extrapolated forward for up to `COAST_MAX_GAP = 12` frames. If the predicted position crosses the band, the crossing fires. Guards: `MIN_AGE โฅ 4`, `|vy| โฅ 2.0 px/frame`. Disable with `--no-coast`.
### 3. `stitch` โ fragment reconnection
A new track born near where a quiet track *would be now* (velocity ร gap) inherits the old track's history. This reconnects a fragmented crossing across a detection void without requiring the missing boxes.
### 4. `fallback` โ birth-to-death trajectory
A track that was born on one side of the line and retired on the other, without ever being counted, produces a crossing event on cleanup. Last resort.
Every event is written to the DB with its `how` tag. The dashboard shows the breakdown per stop, and a **data confidence** figure (fraction of counts that were `live`) rather than an unverifiable accuracy claim.
---
## Running on Orange Pi 5 (edge deployment)
The Orange Pi 5 (RK3588S) has a 6 TOPS NPU that can run a quantised RKNN model much faster than the ARM CPU. The planned path:
```
yolov8n.pt โ export to ONNX โ rknn-toolkit2 INT8 quantisation โ .rknn model
```
CPU inference (`yolov8n.pt`) already runs on the Orange Pi 5 at ~8โ12 fps on 640px input, which is sufficient for bus-door counting. NPU inference targets โฅ 20 fps. Edge deployment is week 3 of the one-month roadmap.
---
## One-month roadmap
| Week | Phase | Deliverable |
|---|---|---|
| 1 | Detection & counting pipeline | Laptop counter, 2 cameras, IN/OUT logged |
| 2 | Tracking, Re-ID & state machine | Full trip logged to SQLite per-stop |
| 3 | Edge deployment & API layer | โฅ 20 fps on Orange Pi 5 |
| 4 | Integration, dashboard & validation | 10-trip accuracy report, clean repo |
---
## Tech stack
| Component | Choice | Why |
|---|---|---|
| Detection | YOLOv8n/s (Ultralytics) | Fast, ONNX-exportable, RKNN-convertible |
| Tracker | ByteTrack (built into Ultralytics) | No appearance features needed; works on limited hardware |
| Backend | SQLite (local) โ Postgres (production) | One connection-string change to scale |
| Edge inference | RKNN Toolkit 2 + INT8 quantisation | Official RK3588S NPU SDK |
| Dashboard | Self-contained HTML + ECharts | No server; works offline on-bus; printable |
---
## Known limitations and next steps
- **Re-ID across a 60-second gap** (someone who exits and re-boards): not implemented in month 1. Occupancy math is still correct for this case; it produces one alighting and one boarding, which is accurate for load analysis. OSNet-based Re-ID is month 2.
- **Pose classification** (seated vs standee): deferred.
- **POS integration**: currently simulated with a configurable fare-evasion rate. Real integration requires the POS vendor API, which is a month-3 deliverable.
- **Coast counts are inferences**: `--no-coast` disables dead reckoning entirely for use cases that prefer a missed count over a potentially wrong one. The confidence figure in the dashboard always reflects this distinction.
---
## Hardware
| Role | Hardware |
|---|---|
| Edge device | Orange Pi 5 (RK3588S, 6 TOPS NPU) |
| Cameras | 2ร ELP USB wide-angle, 1080p, 120ยฐ FOV |
| Dev machine | Any laptop running Python 3.10+ |
---
## Benchmark video
The test clip used during development (`test_1.mp4`, 402ร300, 43 s) is from the [saimj7/People-Counting-in-Real-Time](https://github.com/saimj7/People-Counting-in-Real-Time) repository and is not included here due to file size. Download it and place it next to `cpcs_poc.py` to reproduce the benchmark results.
---
## Author
**Sai Karthik Ramakrishnan**
First-year ECE, Shiv Nadar University Delhi
Intern โ SPowerZ Solutions
[github.com/Saikarthik-Ramakrishnan](https://github.com/Saikarthik-Ramakrishnan)
---
*This is a proof of concept. POS figures in the dashboard are simulated. Production deployment on a live fleet is a separate phase pending hardware integration and field validation.*