Skip to content

Commit

Permalink
Add band over censored volumes in executive summary carpet plots (Pen…
Browse files Browse the repository at this point in the history
  • Loading branch information
tsalo authored Mar 20, 2024
1 parent e7d63d2 commit f32f6dd
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 199 deletions.
16 changes: 8 additions & 8 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ jobs:
paths:
- .coverage.fmriprep_without_freesurfer
- store_artifacts:
path: /src/xcp_d/.circleci/out/test_fmriprep_without_freesurfer/xcp_d/
path: /src/xcp_d/.circleci/out/test_fmriprep_without_freesurfer/

ds001419_nifti:
<<: *dockersetup
Expand Down Expand Up @@ -186,7 +186,7 @@ jobs:
paths:
- .coverage.ds001419_nifti
- store_artifacts:
path: /src/xcp_d/.circleci/out/test_ds001419_nifti/xcp_d/
path: /src/xcp_d/.circleci/out/test_ds001419_nifti/

ds001419_cifti:
<<: *dockersetup
Expand Down Expand Up @@ -217,7 +217,7 @@ jobs:
paths:
- .coverage.ds001419_cifti
- store_artifacts:
path: /src/xcp_d/.circleci/out/test_ds001419_cifti/xcp_d/
path: /src/xcp_d/.circleci/out/test_ds001419_cifti/

ukbiobank:
<<: *dockersetup
Expand All @@ -235,7 +235,7 @@ jobs:
key: ukbiobank-08
- run: *runinstall
- run:
name: Run full xcp_d on nifti with freesurfer
name: Run full xcp_d on UK Biobank data
no_output_timeout: 1h
command: |
pytest -rP -o log_cli=true -m "ukbiobank" --cov-append --cov-report term-missing --cov=xcp_d --data_dir=/src/xcp_d/.circleci/data --output_dir=/src/xcp_d/.circleci/out --working_dir=/src/xcp_d/.circleci/work xcp_d
Expand All @@ -248,7 +248,7 @@ jobs:
paths:
- .coverage.ukbiobank
- store_artifacts:
path: /src/xcp_d/.circleci/out/test_ukbiobank/xcp_d/
path: /src/xcp_d/.circleci/out/test_ukbiobank/

nibabies:
<<: *dockersetup
Expand Down Expand Up @@ -279,7 +279,7 @@ jobs:
paths:
- .coverage.nibabies
- store_artifacts:
path: /src/xcp_d/.circleci/out/test_nibabies/xcp_d/
path: /src/xcp_d/.circleci/out/test_nibabies/

pnc_cifti:
<<: *dockersetup
Expand Down Expand Up @@ -310,7 +310,7 @@ jobs:
paths:
- .coverage.pnc_cifti
- store_artifacts:
path: /src/xcp_d/.circleci/out/test_pnc_cifti/xcp_d/
path: /src/xcp_d/.circleci/out/test_pnc_cifti/

pnc_cifti_t2wonly:
<<: *dockersetup
Expand Down Expand Up @@ -341,7 +341,7 @@ jobs:
paths:
- .coverage.pnc_cifti_t2wonly
- store_artifacts:
path: /src/xcp_d/.circleci/out/test_pnc_cifti_t2wonly/xcp_d/
path: /src/xcp_d/.circleci/out/test_pnc_cifti_t2wonly/

pytests:
<<: *dockersetup
Expand Down
2 changes: 1 addition & 1 deletion xcp_d/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ def build_workflow(opts, retval):
``multiprocessing.Process`` that allows fmriprep to enforce
a hard-limited memory-scope.
"""
from bids import BIDSLayout
from bids.layout import BIDSLayout
from nipype import config as ncfg
from nipype import logging as nlogging

Expand Down
44 changes: 25 additions & 19 deletions xcp_d/interfaces/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,18 @@ def _run_interface(self, runtime):
# The number of colors in the palette depends on whether there are random censors or not
palette = sns.color_palette("colorblind", 4 + censoring_df.shape[1])

fig, ax = plt.subplots(figsize=(16, 8))

time_array = np.arange(preproc_fd_timeseries.size) * self.inputs.TR

ax.plot(
time_array,
preproc_fd_timeseries,
label="Raw Framewise Displacement",
color=palette[0],
)
ax.axhline(self.inputs.fd_thresh, label="Outlier Threshold", color="gray", alpha=0.5)
with sns.axes_style("whitegrid"):
fig, ax = plt.subplots(figsize=(8, 4))

ax.plot(
time_array,
preproc_fd_timeseries,
label="Raw Framewise Displacement",
color=palette[0],
)
ax.axhline(self.inputs.fd_thresh, label="Outlier Threshold", color="salmon", alpha=0.5)

dummy_scans = self.inputs.dummy_scans
# This check is necessary, because init_prepare_confounds_wf connects dummy_scans from the
Expand Down Expand Up @@ -182,9 +183,9 @@ def _run_interface(self, runtime):
alpha=0.5,
)

ax.set_xlabel("Time (seconds)", fontsize=20)
ax.set_ylabel("Movement (millimeters)", fontsize=20)
ax.legend(fontsize=20)
ax.set_xlabel("Time (seconds)", fontsize=10)
ax.set_ylabel("Movement (millimeters)", fontsize=10)
ax.legend(fontsize=10)
fig.tight_layout()

self._results["out_file"] = fname_presuffix(
Expand Down Expand Up @@ -563,14 +564,19 @@ class _QCPlotsESInputSpec(BaseInterfaceInputSpec):
mandatory=True,
desc="TSV file with filtered motion parameters.",
)
temporal_mask = File(
exists=True,
mandatory=True,
desc="TSV file with temporal mask.",
)
TR = traits.Float(default_value=1, desc="Repetition time")
standardize = traits.Bool(
mandatory=True,
desc=(
"Whether to standardize the data or not. "
"If False, then the preferred DCAN version of the plot will be generated, "
"where the BOLD data are not rescaled, and the carpet plot has color limits of -600 "
"and 600. "
"where the BOLD data are not rescaled, and the carpet plot has color limits from "
"the 2.5th percentile to the 97.5th percentile. "
"If True, then the BOLD data will be z-scored and the color limits will be -2 and 2."
),
)
Expand Down Expand Up @@ -610,14 +616,14 @@ class QCPlotsES(SimpleInterface):
output_spec = _QCPlotsESOutputSpec

def _run_interface(self, runtime):
preprocessed_bold_figure = fname_presuffix(
preprocessed_figure = fname_presuffix(
"carpetplot_before_",
suffix="file.svg",
newpath=runtime.cwd,
use_ext=False,
)

denoised_bold_figure = fname_presuffix(
denoised_figure = fname_presuffix(
"carpetplot_after_",
suffix="file.svg",
newpath=runtime.cwd,
Expand All @@ -635,12 +641,12 @@ def _run_interface(self, runtime):

self._results["before_process"], self._results["after_process"] = plot_fmri_es(
preprocessed_bold=self.inputs.preprocessed_bold,
denoised_censored_bold=self.inputs.denoised_interpolated_bold,
denoised_interpolated_bold=self.inputs.denoised_interpolated_bold,
TR=self.inputs.TR,
filtered_motion=self.inputs.filtered_motion,
preprocessed_bold_figure=preprocessed_bold_figure,
denoised_bold_figure=denoised_bold_figure,
temporal_mask=self.inputs.temporal_mask,
preprocessed_figure=preprocessed_figure,
denoised_figure=denoised_figure,
standardize=self.inputs.standardize,
temporary_file_dir=runtime.cwd,
mask=mask_file,
Expand Down
28 changes: 19 additions & 9 deletions xcp_d/tests/test_utils_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import os

import numpy as np
import pandas as pd

from xcp_d.utils import plotting


Expand All @@ -10,25 +13,32 @@ def test_plot_fmri_es(ds001419_data, tmp_path_factory):
tmpdir = tmp_path_factory.mktemp("test_plot_fmri_es")

preprocessed_bold = ds001419_data["cifti_file"]
denoised_censored_bold = ds001419_data["cifti_file"]
denoised_interpolated_bold = ds001419_data["cifti_file"]

# Using unfiltered FD instead of calculating filtered version.
filtered_motion = ds001419_data["confounds_file"]
preprocessed_bold_figure = os.path.join(tmpdir, "unprocessed.svg")
denoised_bold_figure = os.path.join(tmpdir, "processed.svg")
preprocessed_figure = os.path.join(tmpdir, "unprocessed.svg")
denoised_figure = os.path.join(tmpdir, "processed.svg")
t_r = 2
n_volumes = pd.read_table(filtered_motion).shape[0]
tmask_arr = np.zeros(n_volumes, dtype=bool)
tmask_arr[:10] = True # flag first 10 volumes as bad
tmask_arr = tmask_arr.astype(int)
temporal_mask = os.path.join(tmpdir, "temporal_mask.tsv")
pd.DataFrame(columns=["framewise_displacement"], data=tmask_arr).to_csv(
temporal_mask, sep="\t", index=False
)

out_file1, out_file2 = plotting.plot_fmri_es(
preprocessed_bold=preprocessed_bold,
denoised_censored_bold=denoised_censored_bold,
denoised_interpolated_bold=denoised_interpolated_bold,
filtered_motion=filtered_motion,
preprocessed_bold_figure=preprocessed_bold_figure,
denoised_bold_figure=denoised_bold_figure,
preprocessed_figure=preprocessed_figure,
denoised_figure=denoised_figure,
TR=t_r,
standardize=False,
temporary_file_dir=tmpdir,
temporal_mask=temporal_mask,
)
assert os.path.isfile(out_file1)
assert os.path.isfile(out_file2)
Expand All @@ -38,14 +48,14 @@ def test_plot_fmri_es(ds001419_data, tmp_path_factory):

out_file1, out_file2 = plotting.plot_fmri_es(
preprocessed_bold=preprocessed_bold,
denoised_censored_bold=denoised_censored_bold,
denoised_interpolated_bold=denoised_interpolated_bold,
filtered_motion=filtered_motion,
preprocessed_bold_figure=preprocessed_bold_figure,
denoised_bold_figure=denoised_bold_figure,
preprocessed_figure=preprocessed_figure,
denoised_figure=denoised_figure,
TR=t_r,
standardize=True,
temporary_file_dir=tmpdir,
temporal_mask=temporal_mask,
)
assert os.path.isfile(out_file1)
assert os.path.isfile(out_file2)
2 changes: 1 addition & 1 deletion xcp_d/utils/bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import nibabel as nb
import yaml
from bids import BIDSLayout
from bids.layout import BIDSLayout
from nipype import logging
from packaging.version import Version

Expand Down
Loading

0 comments on commit f32f6dd

Please sign in to comment.