Product: Atlas IA (ai.atlas.app) Feature: VitalScan — camera-based estimation of physiological signals Engine version: ai/app/vitalscan.py (v0.2) Audience: App Store review, and anyone asking what these numbers are worth.
Validation status: not yet performed. Sections 1–5 describe what the software does and are verifiable against the source. Section 6 is the protocol for measuring accuracy and currently contains no results. Until it does, Atlas makes no accuracy claim, and the product states plainly in the app that these are educational estimates from a camera signal and not measurements from a medical device.
Atlas is not a medical device. VitalScan does not diagnose, does not screen for disease, and is not intended to inform any decision about health or treatment. Every result screen carries that statement above the figures, and the user accepts it before the first scan.
| Estimated from the camera | Never estimated from the camera |
|---|---|
| Heart rate (bpm) | Blood pressure |
| Respiratory rate (rpm) | Oxygen saturation (SpO₂) |
| Heart-rate variability, SDNN-like (ms) | |
| Stress index (%) |
Blood pressure and SpO₂ are not merely absent — their confidence floors are pinned at 101 on a 0–100 scale, so the publishing gate can never be satisfied. Estimating either from facial video is not something we consider defensible, and the configuration makes it unreachable rather than relying on anyone remembering.
VitalScan is remote photoplethysmography (rPPG): the volume of blood in facial capillaries changes with each heartbeat, and that changes how much light the skin reflects. The camera never needs to touch the user; the pulse is recovered from the colour signal.
Pulse extraction — POS (Plane-Orthogonal-to-Skin), Wang et al. 2017 *"Algorithmic Principles of Remote PPG", IEEE Transactions on Biomedical Engineering 64(7).*
Per region of interest, the mean R, G and B are taken per frame. In a sliding ~1.6 s window the RGB trace is temporally normalised and projected onto a plane orthogonal to the skin-tone direction, which cancels common-mode illumination change; the windows are overlap-added.
The zero-mean green channel is computed as a baseline candidate alongside POS, and the candidate with the cleaner pulse-band peak is the one used. The result is never worse than green-only, and gains POS's robustness where it helps.
Frequency estimation. The selected pulse is band-passed (3rd-order Butterworth, zero-phase) and its periodogram taken. The dominant in-band peak gives the value:
| Metric | Band | Physiological range |
|---|---|---|
| Heart rate | 0.7 – 4.0 Hz | 42 – 240 bpm |
| Respiratory rate | 0.1 – 0.5 Hz | 6 – 30 rpm |
Variability. SDNN-like: standard deviation of inter-peak intervals from the band-passed pulse, requiring at least four detected peaks. This is the least reliable of the four metrics and is treated as such.
Every metric carries a confidence of 0–100, and a value below its floor is not published — the API returns null and the app shows "not estimated" rather than a number nobody should trust.
Confidence combines two things:
Pulse concentration. Power at the fundamental peak plus its second harmonic, over total in-band power. A physiological pulse concentrates energy at f₀ *and* 2·f₀; broadband noise spreads flat and has no harmonic. A peak sitting in the band's lowest bins is down-weighted to 25%, because that is nearly always baseline drift leaking in from below rather than a real rhythm.
Signal quality, the fraction of five capture gates passed:
| Gate | Threshold |
|---|---|
| Lighting (luma) | 0.35 – 0.92 |
| Motion | ≤ 0.15 |
| Face coverage | ≥ 0.50 |
| Frame rate | ≥ 15 fps |
| Signal duration | ≥ 12 s |
Publishing floors, on the same 0–100 scale:
| Metric | Floor |
|---|---|
| Heart rate | 30 |
| Respiratory rate | 35 |
| HRV | 35 |
| Stress index | 30 |
| Blood pressure, SpO₂ | 101 — unreachable by construction |
All thresholds live in api/config/vitalscan.php and travel inside the request payload. The engine holds no clinical constants of its own.
The video never leaves the device. The frame processor reduces each frame to three mean RGB triplets — forehead, left cheek, right cheek — and only that time series is transmitted. No image, no frame, no landmark geometry is stored or sent. The stored record holds the derived metrics and their confidences.
Stated because a validation document that lists none is not credible.
rectangles in frame coordinates; a face that is off-centre, close or turned moves the intended forehead region onto hair or background. The web client uses MediaPipe face landmarks and does not have this limitation. Closing this gap on mobile is the single largest accuracy improvement available.
prevents measuring a wall; it does not confirm the regions sit where intended.
The camera compensates for exactly the brightness changes being measured.
resolution — and HRV especially — improve materially at 30 fps and 20–30 s.
fallback for darker skin under weak light. rPPG amplitude is itself lower on darker skin, so accuracy is expected to vary with skin tone. Quantifying that variation is a required part of §6, not an optional one.
No accuracy figures are claimed because none have been measured. This is the protocol to follow; the results tables are deliberately empty.
Reference standard. A cleared pulse oximeter or ECG chest strap recording simultaneously with each scan. Time-align to the scan window.
Participants. Minimum 30, spanning:
participants above 100 bpm (recorded after light exercise)
Conditions. Each participant scanned under: bright daylight, ordinary indoor light, and dim indoor light near the lower gate; still and with natural small movement. Scans at both 15 s and 30 s.
Metrics to report, per condition and per skin type
| Measure | Heart rate | Respiratory rate | HRV (SDNN) |
|---|---|---|---|
| Mean absolute error | *pending* | *pending* | *pending* |
| Bias (mean error) | *pending* | *pending* | *pending* |
| 95% limits of agreement (Bland–Altman) | *pending* | *pending* | *pending* |
| Proportion within ±5 bpm / ±2 rpm | *pending* | — | — |
| Rate of "not estimated" | *pending* | *pending* | *pending* |
The acceptance question is not only error. A scan that refuses to answer is a correct outcome; a scan that answers confidently and wrongly is not. Report the false-confidence rate — results published above the floor whose error exceeds the tolerance — separately, and treat it as the figure that governs whether floors need raising.
Until this table is filled: no accuracy claim appears in the app, in store copy, or in marketing. The wording stays "educational estimate", which is what the current evidence supports.
*Maintained alongside ai/app/vitalscan.py and api/config/vitalscan.php. If a threshold changes there, it changes here.*