Circadify

Face-Glow Overlay

Draw the thermal face-mesh glow on the live camera with circadify.ui.render_face_glow, then build your own scan UI around it.

circadify.ui ships the camera / face-glow pipeline — the thermal face-mesh glow that makes a Circadify scan recognizable — and nothing else of the UI. You render the glow on each captured frame with render_face_glow, get the frames from the on_frame callback, and build whatever window, sidebar, or web view you want around it.

This mirrors how the Web and iOS SDKs expose their glow as a presentation layer over a headless core — except in Python you own the surrounding UI, so the glow drops cleanly into a desktop window, a Streamlit app, a Jupyter notebook, or a web stream. A complete example window (camera + glow + a vitals/quality/progress sidebar) lives in demo_gui.py in the Python SDK test app.

render_face_glow(...)

Draw the thermal glow onto a frame, in place, then display it however you like:

import cv2
from circadify import CircadifyClient
from circadify.ui import render_face_glow
 
def on_frame(ev):
    bgr = cv2.cvtColor(ev.rgb, cv2.COLOR_RGB2BGR)   # on_frame gives RGB
    render_face_glow(bgr, ev.landmarks, t=ev.collected / 30.0,
                     scanning=(ev.phase.value == "capturing"))
    cv2.imshow("scan", bgr)
    cv2.waitKey(1)
 
client = CircadifyClient(api_key="ck_live_your_key_here", on_frame=on_frame)
result = client.measure_vitals()
print(result.heart_rate, result.confidence)
python
ParameterTypeDefaultDescription
frame_bgrnumpy.ndarrayRequired. A BGR uint8 [H, W, 3] image (OpenCV-native). on_frame gives RGB — convert first with cv2.cvtColor(ev.rgb, cv2.COLOR_RGB2BGR).
landmarksSequence[(x, y)] | NoneRequired. Per-vertex pixel coordinates aligned to frame_bgr (from ev.landmarks; ≥468 points). Scale them if you resized the frame. None/too few → no-op.
tfloat0.0Time in seconds — drives the forehead→cheek sweep animation.
scanningboolTrueTrue during capture (brighter); False for the calmer idle look.
themeGlowThemeTHERMALThe thermal color ramp. See Recoloring.

Returns the same frame_bgr array with the glow composited in. When no face is detected (landmarks is None or has fewer than 468 points) the frame is returned unchanged.

Note

render_face_glow is a pure render helper — no window, no global state. It works anywhere you can show a NumPy image: a cv2 window, Streamlit st.image, a Jupyter cell, an MJPEG/JPEG web stream, or a Qt QImage.

What the glow looks like

  • The full 468-point / 880-triangle MediaPipe face mesh.
  • A 4-stop thermal ramp (amber → orange → red core) that sweeps region-by-region across the forehead and cheeks while capturing.
  • Face-bbox-normalised, so it looks the same whether the face fills the frame or not.

It faithfully reproduces the Web SDK's heat-glow shader.

Recoloring

Pass a GlowTheme to change the ramp (colors are BGR, OpenCV-native):

from circadify.ui import GlowTheme, render_face_glow
 
# A cool cyan ramp instead of the default thermal one (BGR tuples).
cool = GlowTheme(glow_core=(255, 230, 80), glow_hot=(255, 210, 80),
                 glow_warm=(230, 180, 70), glow_cool=(200, 150, 60))
render_face_glow(bgr, ev.landmarks, t=t, theme=cool)
python

GlowTheme holds the four ramp stops glow_core / glow_hot / glow_warm / glow_cool. The default instance is THERMAL.

Build a full window

The SDK deliberately doesn't ship a window or sidebar — you own those. The test app's demo_gui.py is a complete, copy-pasteable example: it runs measure_vitals on a worker thread, composites the camera + render_face_glow + a vitals/quality/progress sidebar with OpenCV on the main thread, and handles cancellation. Use it as a starting point for a desktop tool, or adapt the on_frame pattern above for a web/Streamlit/Qt front end.

Requirements

Caution

render_face_glow needs the [camera] extra (pip install "circadify[camera]", for OpenCV). It is a pure draw call — no display required — so it also runs headless (server, notebook, web backend); just show the returned frame however your stack does. Only the live camera capture (measure_vitals) needs an actual webcam.

Next Steps

  • Camera & Input — webcam, video files, and your own frames feeding the same engine.
  • Methods — the on_frame callback and the rest of CircadifyClient.
  • Configuration — callbacks and capture options.