How were these numbers made?
A result nobody can trace is not a result. Every figure on this site is produced by a numbered Python script and written to a file; this site only draws it.
Provenance
Which weights file, which commit, which moment.
- Run id
- phasenet_ubfc_20260727_182729
- Created (UTC)
- Mon, 27 Jul 2026 16:27:29 GMT
- Git commit
- 6eb9a14
- Weights file
- phasenet_ubfc_epoch9.pth
- Weights SHA-256
- 3206d2da888d4efaa451e19712fae9bd7df30d20f02a2c58a10eef9737935a30
- Model
- PHASE-Net · pretrained (authors' UBFC weights)
Environment
Library versions drift and change results quietly, so they are recorded, not assumed.
- Python
- 3.12.7
- PyTorch
- 2.6.0+cu124
- NumPy
- 2.5.0
- SciPy
- 1.18.0
- OpenCV
- 4.13.0
- Platform
- Windows-11-10.0.26200-SP0
- Device
- cuda
- GPU
- NVIDIA GeForce RTX 3050 4GB Laptop GPU
- GPU memory
- 4 GB
Parameter count
Counted from the released weights file. The paper's efficiency claim rests on this number.
Everything stored in the released file.
Helps the model learn; never runs when measuring a heart rate.
What actually runs. Bars share one scale.
The training-only part never runs at inference, so excluding it is fair. What remains is still about 2.8× the published figure. An observation, not an accusation: the counts may simply differ in what each side counted.
No accuracy figure on this site depends on it — inference uses the released weights unchanged. The same file also carries four temporal layers where the paper's own ablation settles on three.
Pipeline
Each step writes its own file, so any figure traces back to the script that made it without re-running the ones before.
- 0101_load_phasenet.pyLoads the released weights; every tensor matches the model exactly.
- 0204_ubfc_eval.pyScores the model against the dataset and writes results/*.json.
- 0305_cache_signals.pyCaches the model output, so extraction tests re-run in seconds instead of reprocessing 24 GB of video.
- 0408_windowed_eval.pyThe evaluation protocol: ten-second windows, one-second step.
- 0509_pos_baseline.pyRuns the POS baseline on identical face crops.
- 0610_compare_models.pyScores both methods under that single protocol.
- 0713_record_full.pyRecords the webcam sessions used for the motion work.
- 0814_static_vs_dynamic.pyFixed crop against per-frame face tracking, same recording.
- 0915_motion_protocol.pyThe controlled protocol: still, slow, fast, talking.
- 10export_dashboard.pyAssembles the one JSON file this site reads.
- Face detection
- Haar cascade (HC), first frame only
- Input size
- 128 × 128
- Pixel range
- 0-255 (no /255 normalisation, matching the toolbox)
- Clip length
- 128
- Window / step
- 10 s / 1 s
- Spectral estimator
- periodogram
Reading a heart rate off the signal
9 ways of turning a pulse waveform into one number, average error in BPM. Held-out means the 6 subjects the released weights were never trained on.
Scoring a method against itself proves nothing
Scoring a method against itself applied to the reference measures self-consistency, not accuracy - one method looked 18x better that way than against the oximeter's own readout. 'reference_error' is how wrong each method is at reading the ground-truth signal's own heart rate.
window median scores 0.11 the loose way and 2.02 against the device — the same method, looking 18× better.
| Method | vs reference | vs the oximeter | Inflation | Reference error |
|---|---|---|---|---|
| window median | 0.11 | 2.02 | 18.3× | 6.42 |
| window+harmonic median | 0.11 | 2.02 | 18.3× | 6.42 |
| harmonic support | 2.70 | 2.52 | 0.9× | 7.34 |
| zero-padded FFT | 2.70 | 2.52 | 0.9× | 7.34 |
| consensus | 1.02 | 2.55 | 2.5× | 6.29 |
| autocorrelation | 0.71 | 2.59 | 3.6× | 4.56 |
| peak counting | 1.44 | 3.16 | 2.2× | 5.75 |
| baseline (repo) | 1.46 | 3.23 | 2.2× | 7.47 |
| Welch | 0.42 | 3.42 | 8.1× | 6.47 |
All bars share one scale, so the columns compare directly. vs reference runs the same processing on the prediction and on the true pulse — it measures whether a method agrees with itself, which is why it looks so good. vs the oximeter compares against the device's own displayed heart rate, which none of this code touches. Reference error is how wrong each method is at reading the true pulse's own heart rate, with no model involved — a floor under the middle column.
What traceability does not establish
These numbers are reproducible. That does not make them general.
- Only 6 subjects are genuinely held out of the released checkpoint's training split; the other 9 measure memorisation, not generalisation.
- Half of the paper's 12-subject test split was unavailable, so the comparison with its reported 0.15 BPM is not like-for-like.
- On this webcam the noise floor is about 8 BPM while sitting perfectly still - twenty times the error achieved on the controlled UBFC recordings. Capture quality, not the model, is the current bottleneck.
- The motion protocol is one subject, one recording per condition.
- Ground truth is not always right: on two subjects the oximeter's own HR readout is wrong by more than 20 BPM, confirmed by two independent analyses of its waveform.