A webcam-based CPR coaching system that provides live feedback on elbow form, compression frequency, and compression depth — grounded in AHA guidelines (100–120 BPM, 5–6 cm depth).
Built with MMPose pose estimation + a Random Forest classifier, running at 30 FPS on consumer-grade hardware.
This is a hybrid assessment system — combining a trained ML classifier for form detection with rule-based physics calculations for frequency and depth.
─────────────────────────────────────────────────────────────
OFFLINE: Training Pipeline (run once)
─────────────────────────────────────────────────────────────
Labelled Videos (Good / Bad CPR)
│
▼
extract_keypoints.py
→ Runs person detection (Faster R-CNN) + pose estimation (HRNet)
→ Saves raw keypoint sequences as .npy per video
│
▼
Feature_extraction.py
→ Computes elbow angle per frame (shoulder → elbow → wrist)
→ Aggregates: mean, min, std across each video
→ Saves features.npy and labels.npy
│
▼
train_model.py
→ Trains a Random Forest classifier (scikit-learn)
→ Saves cpr_model.pkl
─────────────────────────────────────────────────────────────
ONLINE: Real-Time Assessment Pipeline (webcam)
─────────────────────────────────────────────────────────────
Webcam Feed
│
▼
realtime_pose.py (development/test script)
→ Validates pose estimation is working live
│
▼
realtime_cpr_detection_freq_depth.py ← MAIN SCRIPT
│
├─► Elbow Angle (live, per frame)
│ └─► 90-frame rolling buffer → Random Forest → PASS / FAIL
│ [ML-based classification]
│
├─► Wrist Y-position (live, per frame)
│ └─► Peak detection → Compression count → BPM → PASS / WARN / FAIL
│ [Rule-based, AHA: 100–120 BPM]
│
└─► Peak-to-Trough displacement (pixels → cm)
└─► Mean depth per cycle → PASS / WARN / FAIL
[Rule-based, AHA: 5–6 cm]
│
▼
Live HUD Overlay on Screen
| Metric | Method | PASS | WARN | FAIL |
|---|---|---|---|---|
| Elbow Form | Random Forest (ML) | Straight arms | — | Bent elbows |
| Frequency | Rule-based | 100–120 BPM | 90–99 or 121–130 BPM | < 90 or > 130 BPM |
| Depth | Rule-based | 5.0–6.0 cm | 4.0–4.9 or 6.1–7.0 cm | < 4.0 or > 7.0 cm |
realtime-cpr-assessment/
│
├── README.md
├── requirements.txt
│
├── src/
│ ├── realtime_cpr_detection_freq_depth.py # Main real-time assessment script
│ ├── realtime_pose.py # Pose estimation test/dev script
│ ├── extract_keypoints.py # Step 1: Extract keypoints from labelled videos
│ ├── Feature_extraction.py # Step 2: Compute angle features + save .npy
│ └── train_model.py # Step 3: Train Random Forest classifier
│
├── models/
│ └── cpr_model.pkl # Trained classifier
│
└── assets/
└── demo_1.gif # Demo recording
- Python 3.11
- CUDA-capable GPU recommended (CPU works but slower)
- Conda environment recommended
git clone https://github.com/austindarian/realtime-cpr-assessment.git
cd realtime-cpr-assessmentpip install -r requirements.txt
⚠️ MMPose and MMDetection require specific installation steps.
Follow the MMPose installation guide if you run into issues.
Download the following pretrained checkpoints and place them in a checkpoints/ folder:
| Model | Purpose | File |
|---|---|---|
| Faster R-CNN | Person detection | faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth |
| HRNet-W32 | Pose estimation | hrnet_w32_coco_256x192-c78dce93_20200708.pth |
Update the checkpoint paths in src/extract_keypoints.py and src/realtime_pose.py to point to your local checkpoints/ folder.
The trained cpr_model.pkl is included. Just run the main script:
python src/realtime_cpr_detection_freq_depth.pyPress q to quit.
Step 1: Organise your labelled videos:
Dataset/
├── Good/ ← videos of correct CPR form
└── Bad/ ← videos of incorrect CPR form
Step 2: Extract keypoints from each video:
python src/extract_keypoints.py→ Saves per-video .npy keypoint sequences to a Features/ folder.
Step 3: Compute angle features:
python src/Feature_extraction.py→ Saves features.npy and labels.npy.
Step 4: Train the classifier:
python src/train_model.py→ Saves cpr_model.pkl.
Step 5: Run the assessment:
python src/realtime_cpr_detection_freq_depth.pyDepth estimation relies on PIXELS_PER_CM in realtime_cpr_detection_freq_depth.py (default: 10.0).
To calibrate for your setup:
- Place a ruler or known-size object in front of the camera
- Measure how many pixels correspond to 1 cm in the frame
- Update
PIXELS_PER_CMaccordingly
| Tool | Purpose |
|---|---|
| Python 3.11 | Core language |
| MMPose 1.3.2 | Pose estimation (HRNet-W32) |
| MMDetection | Person detection (Faster R-CNN) |
| OpenCV | Webcam capture & HUD rendering |
| scikit-learn | Random Forest classifier |
| SciPy | Peak detection (find_peaks) |
| NumPy | Signal processing |
| Matplotlib | Live wrist Y-signal plot |
| joblib | Model serialisation |
Austin Darian Pratama
Master of Data Science — Western Sydney University
LinkedIn · GitHub
