"""
File: app/deepbeat_data_visualization.py
Project: 22HLT01 QUMPHY
Contact: nando.hegemann@ptb.de
Gitlab: https://gitlab.com/qumphy
Description: Create images of samples from DeepBeat dataset.
"""
import os
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from tqdm.auto import tqdm
import qumphy # local import
[docs]
def normalize_signals(signals: np.ndarray) -> np.ndarray:
"""Normalize signal data to [0, 1].
Parameters
----------
signals : np.ndarray
Signal array.
Returns
-------
:
Normalized signal array.
"""
shift = np.min(signals, axis=1).reshape(-1, 1)
scale = 1 / (np.max(signals, axis=1).reshape(-1, 1) - shift)
return scale * (signals - shift)
[docs]
def plot_signal_grid(
labels: np.ndarray,
signals: np.ndarray,
subject_ids: np.ndarray,
tag: str,
save_path: str,
dpi: int = 300,
) -> None:
"""Save a 6x4 grid of randomly sampled PPG signals colored by AF label.
Parameters
----------
labels : np.ndarray
Binary AF labels (0/1) for each sample.
signals : np.ndarray
PPG time series.
subject_ids : np.ndarray
Subject IDs.
tag : str
Filename stem and figure title suffix.
save_path : str
Directory the ``<tag>.png`` image is written to.
dpi : int, optional
Resolution of image, by default 300.
"""
cmap = matplotlib.cm.get_cmap("Spectral_r", 2)
cmin = 0
cmax = 1
scale = 1 / (cmax - cmin)
n_rows, n_cols = 6, 4
idxs = np.random.randint(labels.shape[0], size=(n_rows, n_cols))
# create plot
layout = [[f"{i}-{j}" for j in range(n_cols)] + ["cmap1"] for i in range(n_rows)]
for i in range(n_rows):
for j in range(n_cols):
if i <= 1 and j >= n_cols - 2:
layout[i][j] = "big"
fig, ax = plt.subplot_mosaic(
layout, constrained_layout=True, figsize=(5 * (n_cols + 1), 2 * n_rows)
)
for i in range(n_rows):
for j in range(n_cols):
if i > 1 or j < n_cols - 2:
label = labels[idxs[i, j]]
signal = signals[idxs[i, j]]
sid = [subject_ids[idxs[i, j]]]
color = cmap(scale * (label - cmin))
ax[f"{i}-{j}"].plot(signal, color=color, lw=1)
ax[f"{i}-{j}"].set_axis_off()
ax[f"{i}-{j}"].set_title(f"subject id: {sid}")
label = labels[idxs[0, n_cols - 1]]
signal = signals[idxs[0, n_cols - 1]]
sid = subject_ids[idxs[0, n_cols - 1]]
color = cmap(scale * (label - cmin))
ax["big"].plot(signal, color=color, lw=2)
ax["big"].set_axis_off()
ax["big"].set_title(f"subject id: {sid}")
norm = matplotlib.colors.Normalize(vmin=-0.5, vmax=1.5)
ax["cmap1"].set_axis_off()
fig.colorbar(
matplotlib.cm.ScalarMappable(norm=norm, cmap=cmap),
ax=ax["cmap1"],
label="label (AF)",
pad=-0.7,
ticks=[0, 1],
)
plt.suptitle(
f"DeepBeat formatted -- {tag}",
fontsize=30,
)
plt.savefig(save_path + tag + ".png", dpi=dpi)
plt.close()
[docs]
def main() -> None:
"""Render one PNG grid per DeepBeat split into ``../img/deepbeat/``."""
# load data
data_path = "../data/deepbeat/"
save_path = "../img/deepbeat/"
os.makedirs(save_path, exist_ok=True)
file_ids = (
["test"]
+ [f"validate{j+1:02d}" for j in range(3)]
+ [f"train{j+1:02d}" for j in range(20)]
)
for fid in tqdm(file_ids):
labels, signals, subject_ids, _ = qumphy.data.deepbeat.load(fid, data_path)
signals = normalize_signals(signals)
# add samples of training data to tensorboard
plot_signal_grid(
labels, signals, subject_ids, tag=fid, save_path=save_path, dpi=300
)
del labels, signals, subject_ids
if __name__ == "__main__":
main()