Repository navigation
Releases: tharindu326/physiotrack
Release list
v3.0.0
3.0.0 (2026-09-25)
⚠ BREAKING CHANGES
- face: Video(face_orientation=...) is replaced by
Video(face_stages=[...]); FrameResult.face_orientation (list of dicts) is replaced
by FrameResult.faces (a face Result); Detection.Face is removed in favour of
pt.Face; detectors expose a public task attribute instead of _task;
Models.Pose3D.FaceOrientation moved to Models.Face.Orientation;
FaceOrientation.predict(source, bboxes) is now predict(source, faces), it no
longer takes verbose/**kwargs, and its head-pose values change with the new
crop; custom rppg_roi objects must accept skin_mask(frame, boxes=None). - pose: WholeBody keypoints 40-49 and 59-70 swap their left/right names
(face_left_eyebrow_* <-> face_right_eyebrow_, face_left_eye_ <-> face_right_eye_*).
Features
- face: face analysis as core face stages, face signals and Video support (b358381)
Bug Fixes
v2.0.0
2.0.0 (2026-07-26)
⚠ BREAKING CHANGES
- six public API changes, all from #11.
Pose3D.predict(json_path, vid_path, ...)no longer exists. It now takes
keypoints in memory -- an array, aVideoResults, or frame records -- and
returns aPose3DResultinstead of a(frames_data, poses)tuple. For the
previous file-based workflow usePose3D.predict_json(json_path, vid_path),
which still returns the records alongside the result.FaceOrientation.predict_batch(frames, bboxes_list)removed. Pass a list to
predict()instead; it returns one result per frame, as every other predictor
already did.JointAnglePlotter.attach_panels()and.attach_canvas()removed. Use
attach_to_frame(), which every other panel already used; the joint and ROM
grids are now rendered as one canvas.RealTimePlotter.plot()renamed topush(). It appends a sample, whereas
plot()renders an existing result everywhere else in the library.PoseCanonicalizer.transform_to_back_view(),
.transform_to_left_side_view()and.transform_to_right_side_view()removed.
Useto_canonical_geometric(poses, view=...)orcanonicalize_pose(...).Models.Pose3D.Canonicalizer.Viewremoved. A canonical viewpoint is a
parameter, not a checkpoint, so it is now the top-levelCanonicalViewenum.
Weight cache location also changed: checkpoints are no longer written inside the
installed package. Run physiotrack.migrate_weight_cache() once to move an
existing cache instead of re-downloading it.
Bug Fixes
v1.1.0
v1.0.0
1.0.0 (2026-07-05)
⚠ BREAKING CHANGES
- signals: physiotrack.signals.ppg.Filtering has been removed. Use the
equivalent free functions in physiotrack.signals.filters (bandpass_filter,
zero_mean_std_norm, bandpass_firwin, detrend_advanced).
Bug Fixes
- signals: correct DSP bugs, unify filtering, add validated test suite (5ca0066)
v0.5.0
v0.4.0
v0.3.0
Physiotrack v0.3.0
This release adds face-part parsing to the segmentation subsystem and tidies the public API
so everything is reached through clean, hierarchical entry points.
✨ Added
- Face parsing via
Segmentation.Face(SegFace). Dense face-part segmentation into 19
CelebAMask-HQ classes (skin, eyes, brows, nose, lips, hair, ears, glasses, hat, earring,
necklace, neck, cloth, ...). It runs on face crops and, likePoseauto-detecting people,
auto-detects faces with a YOLO face detector when no boxes are passed:Registered asfrom physiotrack import Segmentation result = Segmentation.Face().predict(image) # auto-detects faces seg_map = result.seg_map # (H, W) face-part class map labels = result.names # class id -> label overlay = result.plot() # 19-class palette overlay
Models.Segmentation.SegFace.Face.swinb_celeba_512. Only the inference code is
vendored (Swin-Base/CelebA model + transformer/utils), under the upstream MIT license. - New public exports so examples and user code never reach into internal module paths:
physiotrack:apply_3dpcnet_transform,reverse_3dpcnet_transform.physiotrack.pose:COCO_WHOLEBODY_NAMES,HUMAN26M_NAMES.physiotrack.signals: motion helpers (keypoint sequences, centroids, resampling, feature
extraction) alongside the existing filters / normalization / rPPG / plotting exports.
Result.paletteso palette-based segmentation maps (e.g. face parsing) overlay with the
correct per-class colours.- examples/face_parsing.py — runnable SegFace example (auto-detect and explicit-box modes).
- Demo video linked from the README.
♻️ Changed
- Cleaner, hierarchical public API. README's "The Unified API" now includes a calling-hierarchy
diagram showing the flat entry points, the model registry, theResultfamily, and the public
subsystems (signals/pose/face). - Examples import only from public entry points.
evaluate_pose.pyandmotion.pyno longer
reach intophysiotrack.modules.*or deep file paths; they import fromphysiotrack,
physiotrack.pose, andphysiotrack.signals. - README architecture diagram, feature table, model registry, and project layout updated to
include face parsing.
⚠️ Breaking
None.
🔁 Upgrading
No code changes required for existing usage. To use the new face parser:
from physiotrack import Segmentation
result = Segmentation.Face(device=0).predict(image)📝 Notes
- The SegFace checkpoint (
segface_swinb_celeba_512.pt, slimmed to inference-only weights) is in
the HuggingFace weights repo and must be pushed for the registry download to work. - All released canonicalizer/face-parsing checkpoints download on first use via the
Models
registry; no weights are bundled in the package.
Full documentation: see the README. License: GPL-3.0.
v0.2.0
Physiotrack v0.2.0
This release adds new pose-canonicalization models and substantially simplifies how 3DPCNet checkpoints are configured and loaded, alongside a full README overhaul.
✨ Added
- Two new pose-canonicalization checkpoints, trained directly on TotalCapture with 48 augmented camera viewpoints (the earlier
S2/S3models were trained on MMFi and evaluated cross-dataset on TotalCapture):Models.Pose3D.Canonicalizer.Models._3DPCNetTC48_byCam— camera-disjoint split (test cams 41–48)Models.Pose3D.Canonicalizer.Models._3DPCNetTC48_byAction— action-disjoint split (test action rom3)- These cut rotation error by roughly an order of magnitude (≈4.2° → 0.45°) and PA-MPJPE by ~10 mm versus the cross-dataset MMFi models. See the canonicalization comparison table in the README.
- README: a detailed Mermaid architecture diagram of the whole library, a canonicalization model-comparison table, a table of contents, a project-layout map, and collapsible per-subsystem usage sections.
♻️ Changed
- Single bundled inference config for all 3DPCNet models. Every canonicalizer checkpoint shares one network architecture, so inference now reads a single config shipped inside the package (
physiotrack/modules/_3DCPNet/configs/inference_config.yaml). No per-model YAML is downloaded or required. - Model downloads are leaner. The canonicalizer now downloads only the
.pthweights from Hugging Face; configuration is no longer fetched over the network. First-run setup is simpler and has one less failure mode.
⚠️ Removed / Breaking
Models.Pose3D.Canonicalizer.Configsenum removed. It only pointed at the per-model YAMLs that are no longer used. If you referenced it directly, drop the reference — the canonicalizer resolves its config automatically. Model selection (Models.Pose3D.Canonicalizer.Models.*) and views (...Canonicalizer.View.*) are unchanged.
🔁 Upgrading
No code changes are required for typical usage:
from physiotrack import canonicalize_pose, Models
canonical = canonicalize_pose(
poses_3d,
model=Models.Pose3D.Canonicalizer.Models._3DPCNetTC48_byCam, # new, recommended
view="front",
)If you were passing a downloaded per-model config path manually, you can stop — omit config_path and the bundled config is used. The GEOMETRIC (training-free) path is unaffected.
📝 Notes
- The four canonicalizer checkpoints on Hugging Face follow the
best_model_3DPCNet*.pthnaming convention; per-model.yamlfiles have been retired. - All architecture/inference code is unchanged for existing models — the new TC48 checkpoints use the same network as the
S2/S3HybridGCNTransformer models and load with the existing code.
Full documentation: see the README. License: GPL-3.0.
v0.1.0
The first public release of Physiotrack, an open-source Python toolkit for contactless human understanding — turning RGB / RGB-D / thermal video into interpretable physiological and behavioral signals. Developed at the Center for Machine Vision and Signal Processing (CMVS), University of Oulu.
Philosophy: send meaning, not pixels — extract human-state features so downstream systems can reason about states instead of raw video.
⚠️ This is an early0.xrelease. The library is under active development and the public API may change before1.0.
✨ Highlights
- One unified API across every image predictor: configure the model, call
.predict(img)(ormodel(img)), read a structuredResult, and draw withresult.plot(). - Composable subsystems that work standalone or as an end-to-end pipeline via the
Videoorchestrator. - Automatic model management — weights resolve through the
Modelsregistry and download on first use (Ultralytics + Hugging Face).
📦 What's included
| Subsystem | Capability | Backends |
|---|---|---|
| Detection | Multi-person / object boxes | YOLO11, RT-DETR, VR-specific |
| Tracking | Persistent IDs, occlusion-robust | OC-SORT, ByteTrack, StrongSORT, BoostTrack |
| Pose 2D | 17 (COCO) or 133 (whole-body) keypoints | ViTPose, Sapiens, YOLO11-Pose |
| Pose 3D | Lift 2D keypoints to 3D over time | MotionBERT, DDHPose |
| Canonicalization | Viewpoint-invariant 3D pose alignment | 3DPCNet, geometric |
| Segmentation | Pixel-level instance masks | YOLO-Seg, Sapiens, VR-Head |
| Depth | Monocular dense depth | Depth-Anything-V2 (s/b/l) |
| Face | Detection + 3D head orientation | YOLO-Face, 6DRepNet360, CMVS-FO-VR |
| Signals | rPPG (HR/RR) + motion features | POS, CHROM, LGI, OMIT + filters |
| Views | Bird's-eye floor map, ego-video & depth overlays | — |
Inputs: monocular RGB, RGB-D + infrared (Azure Kinect), depth, and thermal video; live cameras, files, or single images.
🚀 Installation
git clone https://github.com/tharindu326/physiotrack.git
cd physiotrack
pip install -e .Install the PyTorch build matching your platform/CUDA first (requirements.txt pins a CUDA 12.8 build, torch==2.9.1). Model weights are not bundled and download automatically on first use.
🧑💻 Quick start
import cv2
import physiotrack as pt
frame = cv2.imread("frame.png")
det = pt.Detection.Person(conf=0.25, device=0)
result = det.predict(frame)
cv2.imwrite("out.png", result.plot())
pose = pt.Pose.Person()
people = pose.predict(frame)
wrist = people[0].keypoints.by_name("left_wrist")
print(wrist.x, wrist.y, wrist.confidence)End-to-end video pipeline:
from physiotrack import Video, Detection, Pose, Tracker, TrackerConfig, Models
Video(
source="input.mp4",
detector=Detection.Person(),
pose=Pose.Custom(model=Models.Pose.ViTPose.WholeBody.b_wholebody),
tracker=Tracker(config=TrackerConfig(tracker="ocsort", classes=[0])),
output_dir="output",
).run(output_video="out.mp4", output_json="out.json")⚠️ Known limitations
- API is pre-
1.0and subject to change. - A CUDA GPU is recommended for real-time use; CPU works but is slower.
- Model weights are downloaded on demand and are not part of the package.
- Some subsystems (e.g. parts of
signals) are still evolving.
📚 Citation
If you use Physiotrack in your research, please cite the relevant papers listed in the README.
🙏 Acknowledgments
Supported by the University of Oulu and the Research Council of Finland through the 6G Flagship Programme (346208), Profi5 HiDyn (326291), and Profi7 Hybrid Intelligence (352788). Computational resources provided by CSC (IT Center for Science, Finland).
Full documentation: see the README. License: GPL-3.0.