-
-
Comparison unlocked after capture conditions are confirmed (invented sample scores)
-
Not comparable yet: the capture check names the difference and how to retake
-
Comparable pair: left-versus-right cheek contrast against its noise floor
-
YouCam's own scores move with the light: per-concern wobble and a score judge
Inspiration
Skin-analysis scores move with the window, the distance and the angle of the head. A consumer who sees "texture +5" may credit a serum when the real change was the light. We measured how much: with no treatment at all, YouCam's own texture score moved +29.9 and moisture +29.2 between front-lit and backlit photos of the same face.
What it does
SkinTrace measures the capture conditions of two selfies from the pixels before anything is compared, then either reports a change or says exactly how to retake.
- Capture check (local): MediaPipe face landmarks and OpenCV give face scale and position, head roll/yaw/pitch, face brightness, light direction and sharpness. If any of these differs by more than its measured noise floor (MDC95, from same-condition repeats), the pair is "Not comparable yet", with retake advice, e.g. "Hold the phone at the same distance as the first photo".
- Cheek-contrast verdict: if the pair is comparable, it reports left-cheek minus right-cheek redness (L*a*b* a*) against its own noise floor. A shift of both cheeks together is shown separately, because lighting, colour balance or a real change could all cause it, and this check cannot tell those apart.
- YouCam score wobble: with consent and a key, a pair that passes the local check is scored by YouCam's Skin Analysis (15 SD concerns). Each concern's change is judged against YouCam's measured wobble: "within noise", "beyond noise", or "beyond noise, but light alone moved this score the same way". A pair the local check refuses spends no YouCam units.
/judge-scoresdoes the same for scores you type in.
How we built it
Python standard library server, MediaPipe FaceMesh, OpenCV and NumPy for the measurement engine (engine/), and the YouCam API flow: register file, upload to the presigned host (only that allow-listed host; the API key is never forwarded), skin-analysis task, poll. We compare raw_score, not the adjusted ui_score. 58 automated tests (no photos, no network), plus a Playwright browser check of the measured flow.
Evidence (one person, one room; see spike/ in the repo)
- 36 window-front vs window-behind pairs, skin unchanged: a naive redness comparison shows a "change" beyond the noise floor in 36/36. The capture check refuses 34/36, and the left-versus-right cheek contrast exceeds its floor in 2/36.
- Synthetic validation: a known redness added in pixels to one cheek of real photos is flagged on that cheek and never on the other; 2 false alarms in 120 pairs.
- YouCam: 12 face crops (6 front-lit, 6 backlit, no treatment) from the consenting owner; 10 scored, 2 rejected as too dark. Exploratory thresholds, with selection bias from the rejected photos.
Challenges
The presigned upload goes to a separate storage host, which we allow-list exactly. YouCam results include summary items without raw_score, which our parser now skips (with a test). Real-photo validation of a true positive change is still missing: our rubbed-cheek photos were too turned and cut off, and the gate correctly refused them.
Limits
These are cosmetic colour readings, not diagnoses or medical advice. A measured change cannot show that a product caused it. Thresholds come from one person's repeats. No photos or per-photo numbers are in the repository, and photos are processed in memory and not saved.
What's next
Validate on more people and lighting set-ups, and test real positive changes (e.g. concealer on one cheek).
Log in or sign up for Devpost to join the conversation.