|
12 | 12 | ;; |
13 | 13 | ;; ### What is Camera PPG? |
14 | 14 | ;; |
15 | | -;; **Photoplethysmography (PPG)** is a technique for measuring blood volume changes in tissue, typically used to measure heart rate and blood oxygen levels. Traditional PPG sensors (like those in fitness watches) use dedicated infrared LEDs and photodetectors. |
| 15 | +;; **[Photoplethysmography (PPG)](https://en.wikipedia.org/wiki/Photoplethysmogram)** is a technique for measuring blood volume changes in tissue, typically used to measure heart rate and blood oxygen levels. Traditional PPG sensors (like those in fitness watches) use dedicated infrared LEDs and photodetectors. |
16 | 16 | ;; |
17 | | -;; **Camera PPG** (also called remote PPG or rPPG) is a fascinating alternative: it uses an ordinary smartphone camera to detect these same blood volume changes by analyzing subtle color variations in the skin. When you place your fingertip over the camera, each heartbeat causes a tiny change in blood volume that slightly alters the amount of light absorbed by the tissue. By recording video and analyzing the RGB color channels over time, we can extract vital signs like heart rate and SpO2 (blood oxygen saturation). |
| 17 | +;; **Camera PPG** (also called remote PPG or rPPG) is a fascinating alternative: it uses an ordinary smartphone camera to detect these same blood volume changes by analyzing subtle color variations in the skin. When you place your fingertip over the camera, each heartbeat causes a tiny change in blood volume that slightly alters the amount of light absorbed by the tissue. By recording video and analyzing the RGB color channels over time, we can extract vital signs like [heart rate](https://en.wikipedia.org/wiki/Heart_rate) and [SpO2](https://en.wikipedia.org/wiki/Oxygen_saturation_(medicine)) (blood oxygen saturation). |
18 | 18 | ;; |
19 | 19 | ;; ### The MTHS Dataset |
20 | 20 | ;; |
21 | 21 | ;; We're using the [MTHS dataset from the MEDVSE repository](https://github.com/MahdiFarvardin/MEDVSE), which contains smartphone camera PPG data from 60 subjects. For each subject, the dataset provides: |
22 | 22 | ;; |
23 | | -;; - **Signal data** (`signal_x.npy`): RGB time series sampled at 30 Hz from fingertip videos |
24 | | -;; - **Ground truth labels** (`label_x.npy`): Heart rate (bpm) and SpO2 (%) sampled at 1 Hz, measured with clinical-grade equipment |
| 23 | +;; - **Signal data** (`signal_x.npy`): RGB time series sampled at 30 [Hz](https://en.wikipedia.org/wiki/Hertz) from fingertip videos |
| 24 | +;; - **Ground truth labels** (`label_x.npy`): Heart rate ([bpm](https://en.wikipedia.org/wiki/Heart_rate)) and SpO2 (%) sampled at 1 Hz, measured with clinical-grade equipment |
25 | 25 | ;; |
26 | 26 | ;; ### What We'll Explore |
27 | 27 | ;; |
28 | | -;; In this notebook, we'll walk through a signal processing pipeline for analyzing Camera PPG data: |
| 28 | +;; In this notebook, we'll walk through a [signal processing](https://en.wikipedia.org/wiki/Signal_processing) pipeline for analyzing Camera PPG data: |
29 | 29 | ;; |
30 | | -;; 1. **Loading and visualizing** raw RGB signals from multiple subjects |
31 | | -;; 2. **Signal preprocessing**: removing DC offset, bandpass filtering to isolate heart rate frequencies (0.5-5 Hz, corresponding to 30-300 bpm), and standardization |
32 | | -;; 3. **Power spectrum analysis**: using windowed FFT to identify the dominant frequency (heart rate) |
| 30 | +;; 1. **Loading and visualizing** raw [RGB](https://en.wikipedia.org/wiki/RGB_color_model) signals from multiple subjects |
| 31 | +;; 2. **Signal preprocessing**: removing [DC offset](https://en.wikipedia.org/wiki/DC_bias), [bandpass filtering](https://en.wikipedia.org/wiki/Band-pass_filter) to isolate heart rate frequencies (0.5-5 Hz, corresponding to 30-300 bpm), and [standardization](https://en.wikipedia.org/wiki/Standard_score) |
| 32 | +;; 3. **Power spectrum analysis**: using windowed [FFT](https://en.wikipedia.org/wiki/Fast_Fourier_transform) to identify the dominant frequency (heart rate) |
33 | 33 | ;; |
34 | 34 | ;; This exploration demonstrates how Clojure's ecosystem—combining tablecloth for data wrangling, dtype-next for efficient numerical computation, jdsp for signal processing, and tableplot for visualization—provides powerful tools for biomedical signal analysis. |
35 | 35 |
|
|
92 | 92 |
|
93 | 93 | ;; ## Reading data |
94 | 94 | ;; |
95 | | -;; The MTHS dataset stores signals and labels as NumPy `.npy` files. Each subject has two files: |
| 95 | +;; The MTHS dataset stores signals and labels as [NumPy](https://en.wikipedia.org/wiki/NumPy) `.npy` files. Each subject has two files: |
96 | 96 | ;; |
97 | | -;; - `signal_X.npy`: RGB time series (3 channels × time samples) |
| 97 | +;; - `signal_X.npy`: RGB [time series](https://en.wikipedia.org/wiki/Time_series) (3 channels × time samples) |
98 | 98 | ;; - `label_X.npy`: Ground truth measurements |
99 | 99 | ;; |
100 | 100 | ;; We'll use libpython-clj to load these files via NumPy, then convert them to dtype-next structures for efficient processing in Clojure. |
|
133 | 133 | (dtype/shape (raw-data [:signal 23])) ; 30Hz signal → [n-samples, 3] array |
134 | 134 | (dtype/shape (raw-data [:label 23])) ; 1Hz labels → [n-samples, 2] array |
135 | 135 |
|
| 136 | +;; The [sampling rate](https://en.wikipedia.org/wiki/Sampling_(signal_processing)) is 30 Hz (30 samples per second) |
136 | 137 | (def sampling-rate 30) |
137 | 138 |
|
138 | 139 | ;; Let's create a helper function to convert a subject's raw signal into a tablecloth dataset |
|
152 | 153 | ;; ## Plotting |
153 | 154 | ;; |
154 | 155 | ;; Let's create a plotting function to visualize all three RGB channels over time. |
155 | | -;; In Camera PPG, different color channels can have different signal-to-noise ratios |
| 156 | +;; In Camera PPG, different color channels can have different [signal-to-noise ratios](https://en.wikipedia.org/wiki/Signal-to-noise_ratio) |
156 | 157 | ;; depending on skin tone and lighting conditions. Typically, the green channel is |
157 | | -;; strongest due to hemoglobin's absorption characteristics. |
| 158 | +;; strongest due to [hemoglobin](https://en.wikipedia.org/wiki/Hemoglobin)'s absorption characteristics. |
158 | 159 |
|
159 | 160 | (defn plot-signal [s] |
160 | 161 | (-> s |
|
176 | 177 | ;; Raw Camera PPG signals are noisy and contain artifacts. Before we can extract heart rate, |
177 | 178 | ;; we need to clean them up through a series of transformations: |
178 | 179 | ;; |
179 | | -;; 1. **DC removal (detrending)**: Removes the constant offset, leaving only the AC component |
| 180 | +;; 1. **[DC](https://en.wikipedia.org/wiki/Direct_current) removal ([detrending](https://en.wikipedia.org/wiki/Detrended_fluctuation_analysis))**: Removes the constant offset, leaving only the [AC component](https://en.wikipedia.org/wiki/Alternating_current) |
180 | 181 | ;; (the oscillating part due to heartbeats) |
181 | 182 | ;; |
182 | | -;; 2. **Bandpass filtering**: Keeps only frequencies in the 0.5-5 Hz range, which corresponds |
183 | | -;; to heart rates between 30-300 bpm. This removes high-frequency noise and low-frequency drift. |
| 183 | +;; 2. **[Bandpass filtering](https://en.wikipedia.org/wiki/Band-pass_filter)**: Keeps only frequencies in the 0.5-5 Hz range, which corresponds |
| 184 | +;; to heart rates between 30-300 bpm. This removes high-frequency [noise](https://en.wikipedia.org/wiki/Noise_(signal_processing)) and low-frequency drift. |
184 | 185 | ;; |
185 | | -;; 3. **Standardization**: Scales the signal to zero mean and unit variance, making it easier |
| 186 | +;; 3. **[Standardization](https://en.wikipedia.org/wiki/Standard_score)**: Scales the signal to zero [mean](https://en.wikipedia.org/wiki/Mean) and unit [variance](https://en.wikipedia.org/wiki/Variance), making it easier |
186 | 187 | ;; to compare across subjects and channels. |
187 | 188 | ;; |
188 | 189 | ;; Let's create a utility function to visualize how each transformation affects all subjects: |
|
220 | 221 |
|
221 | 222 | ;; **Bandpass Filter** |
222 | 223 | ;; |
223 | | -;; A 4th-order Butterworth bandpass filter that keeps only the frequencies associated with |
| 224 | +;; A 4th-order [Butterworth](https://en.wikipedia.org/wiki/Butterworth_filter) bandpass filter that keeps only the frequencies associated with |
224 | 225 | ;; normal heart rates (0.5-5 Hz = 30-300 bpm). This is a standard technique in PPG signal processing. |
225 | 226 |
|
226 | 227 | (defn bandpass-filter [signal {:keys [fs order low-cutoff high-cutoff]}] |
|
254 | 255 |
|
255 | 256 | ;; ## Power Spectrum Analysis |
256 | 257 | ;; |
257 | | -;; To extract the heart rate from our cleaned PPG signal, we'll use **frequency domain analysis**. |
| 258 | +;; To extract the heart rate from our cleaned PPG signal, we'll use **[frequency domain](https://en.wikipedia.org/wiki/Frequency_domain) analysis**. |
258 | 259 | ;; The idea is simple: a heartbeat creates a periodic oscillation in the signal, and we can find |
259 | | -;; the dominant frequency of that oscillation using the Fast Fourier Transform (FFT). |
| 260 | +;; the dominant frequency of that oscillation using the [Fast Fourier Transform (FFT)](https://en.wikipedia.org/wiki/Fast_Fourier_transform). |
260 | 261 | ;; |
261 | 262 | ;; ### Windowing and Overlap |
262 | 263 | ;; |
263 | | -;; Rather than analyzing the entire signal at once, we'll use **windowed analysis**: |
| 264 | +;; Rather than analyzing the entire signal at once, we'll use **[windowed analysis](https://en.wikipedia.org/wiki/Window_function)**: |
264 | 265 | ;; |
265 | 266 | ;; - Split the signal into overlapping windows (e.g., 10-second windows with 5% overlap) |
266 | | -;; - Apply a **Hanning window** to each segment to reduce spectral leakage |
| 267 | +;; - Apply a **[Hanning window](https://en.wikipedia.org/wiki/Hann_function)** to each segment to reduce [spectral leakage](https://en.wikipedia.org/wiki/Spectral_leakage) |
267 | 268 | ;; - Compute the FFT for each window |
268 | | -;; - The resulting power spectrum shows which frequencies are strongest |
| 269 | +;; - The resulting [power spectrum](https://en.wikipedia.org/wiki/Spectral_density) shows which frequencies are strongest |
269 | 270 | ;; |
270 | 271 | ;; The peak frequency in the power spectrum corresponds to the heart rate! |
271 | 272 | ;; |
|
0 commit comments