Hung Manh Pham1, Matthew Yiwen Ho1, Yiming Zhang1, Dimitris Spathis2,3, Aaqib Saeed4, Dong Ma1,2*
1School of Computing and Information Systems, Singapore Management University, Singapore
2Department of Computer Science and Technology, University of Cambridge, UK
3Google, UK
4Department of Industrial Design, Eindhoven University of Technology, The Netherlands
Photoplethysmography (PPG) is a widely used non-invasive technique for monitoring cardiovascular health and various physiological parameters on consumer and medical devices. While motion artifacts are well-known challenges in dynamic settings, suboptimal skin-sensor contact in sedentary conditions - an important issue often overlooked in existing literature - can distort PPG signal morphology, leading to the loss or shift of essential waveform features and therefore degrading sensing performance. In this work, we propose a deep learning-based framework that transforms contact pressure-distorted PPG signals into ones with the ideal morphology, known as CP-PPG. CP-PPG incorporates a well-crafted data processing pipeline and an adversarially trained deep generative model, together with a custom PPG-aware loss function. We validated CP-PPG through comprehensive evaluations, including 1) morphology transformation performance, 2) downstream physiological monitoring performance on public datasets, and 3) in-the-wild performance, which together demonstrate substantial and consistent improvements in signal fidelity.
CP-PPG/
├── configs/
│ ├── config.yml # Main configuration file
│ └── seed.py # Reproducibility settings (seed=42)
├── src/
│ ├── models/
│ │ ├── cpppg.py # Generator and Discriminator architectures
│ │ └── model_utils.py # PPG blocks (Gated CNN, SE block, etc.)
│ ├── trainer/
│ │ ├── engine.py # Standard training loop
│ │ └── adverarial_engine.py # Adversarial (GAN) training loop
│ ├── dataloader/
│ │ └── dataset.py # PPGDataset and data loaders
│ ├── metrics/
│ │ ├── losses.py # Custom PPG-aware loss, Hinge loss, GenLoss
│ │ └── metrics.py # Signal comparison metrics
│ ├── utils/
│ │ ├── utils.py # General utilities, config loading, plotting
│ │ ├── preprocess.py # Signal processing (filtering, windowing)
│ │ ├── prepare.py # Data handler for train/val/test splits
│ │ ├── enrichment.py # Data augmentation transforms
│ │ ├── feature.py # PPG feature extraction
│ │ └── classification.py # Waveform classification utilities
│ └── experiments/
│ └── tools/
│ ├── train.py # Training entry point
│ ├── test.py # Inference and evaluation
│ └── deploy.py # Flask API server
├── pipeline.ipynb # End-to-end walkthrough notebook
├── Dockerfile # Container deployment
├── requirements.txt # Python dependencies
└── checkpoints/ # Saved model weights
conda create -n ppg python=3.11
conda activate ppg
pip install -r requirements.txtWe use the WF-PPG dataset introduced in WF-PPG. This contains simultaneous recordings of distorted (input) and ideal (reference) wrist PPG signals across multiple subjects under varying skin-sensor contact pressures. Download and place the preprocessed data in json format under data/.
{
"<subject_id>.csv": {
"in_windows": [[float, ...], ...],
"ref_windows": [[float, ...], ...],
},
...
}CP-PPG uses Comet ML for experiment tracking. To enable it:
- Create a Comet account and get your API key.
- Save the key to
configs/experiment_apikey.txt.
python src/experiments/tools/train.pyNote that the training supports different settings controlled by configs/config.yml. Subsequently, a pre-trained CP-PPG checkpoint can be found here.
Run inference on a test set and print quantitative metrics:
python src/experiments/tools/test.pyThis evaluates the trained model on the test set, comparing the enhanced CP-PPG signals to the ideal morphology using metrics such as MSE, Pearson correlation, and a custom PPG similarity score.
python src/experiments/tools/deploy.pyThis starts a Flask server on port 8080 with the following endpoints:
| Endpoint | Method | Description |
|---|---|---|
/enhance |
POST | Transforms a distorted PPG signal into ideal morphology |
/preprocess |
POST | Normalizes a raw PPG signal |
Docker
docker build -t cpppg .
docker run -p 8080:8080 cpppgFor a step-by-step walkthrough covering data exploration, augmentation, model definition, training, and deployment, see pipeline.ipynb.
@article{pham2025reliable,
title={Reliable wrist PPG monitoring by mitigating poor skin sensor contact},
author={Pham, Hung Manh and Ho, Matthew Yiwen and Zhang, Yiming and Spathis, Dimitris and Saeed, Aaqib and Ma, Dong},
journal={Scientific Reports},
year={2025},
publisher={Nature Publishing Group UK London}
}
@article{ho2025wf,
title={WF-PPG: A wrist-finger dual-channel dataset for studying the impact of contact pressure on PPG morphology},
author={Ho, Matthew Yiwen and Pham, Hung Manh and Saeed, Aaqib and Ma, Dong},
journal={Scientific Data},
volume={12},
number={1},
pages={200},
year={2025},
publisher={Nature Publishing Group UK London}
}
This research was supported by the Singapore Ministry of Education (MOE) Academic Research Fund (AcRF) Tier 2 grant (Grant ID: T2EP20124-0046).
