Share
## 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.*