|
21 | 21 | # https://www.nipreps.org/community/licensing/ |
22 | 22 | # |
23 | 23 | """Visualization tools.""" |
24 | | -import numpy as np |
25 | | -import nibabel as nb |
26 | | - |
27 | | -from nipype.utils.filemanip import fname_presuffix |
28 | | -from nipype.interfaces.base import ( |
29 | | - File, |
30 | | - BaseInterfaceInputSpec, |
31 | | - TraitedSpec, |
32 | | - SimpleInterface, |
33 | | - traits, |
34 | | - isdefined, |
35 | | -) |
36 | | -from niworkflows.utils.timeseries import _cifti_timeseries, _nifti_timeseries |
37 | | -from niworkflows.viz.plots import ( |
38 | | - fMRIPlot, |
39 | | - compcor_variance_plot, |
40 | | - confounds_correlation_plot, |
| 24 | +import warnings |
| 25 | +from nireports.interfaces import ( |
| 26 | + CompCorVariancePlot, |
| 27 | + ConfoundsCorrelationPlot, |
| 28 | + FMRISummary, |
41 | 29 | ) |
42 | 30 |
|
| 31 | +__all__ = ( |
| 32 | + "CompCorVariancePlot", |
| 33 | + "ConfoundsCorrelationPlot", |
| 34 | + "FMRISummary", |
| 35 | +) |
43 | 36 |
|
44 | | -class _FMRISummaryInputSpec(BaseInterfaceInputSpec): |
45 | | - in_func = File(exists=True, mandatory=True, desc="") |
46 | | - in_spikes_bg = File(exists=True, desc="") |
47 | | - fd = File(exists=True, desc="") |
48 | | - dvars = File(exists=True, desc="") |
49 | | - outliers = File(exists=True, desc="") |
50 | | - in_segm = File(exists=True, desc="") |
51 | | - tr = traits.Either(None, traits.Float, usedefault=True, desc="the TR") |
52 | | - fd_thres = traits.Float(0.2, usedefault=True, desc="") |
53 | | - drop_trs = traits.Int(0, usedefault=True, desc="dummy scans") |
54 | | - |
55 | | - |
56 | | -class _FMRISummaryOutputSpec(TraitedSpec): |
57 | | - out_file = File(exists=True, desc="written file path") |
58 | | - |
59 | | - |
60 | | -class FMRISummary(SimpleInterface): |
61 | | - """Prepare an fMRI summary plot for the report.""" |
62 | | - |
63 | | - input_spec = _FMRISummaryInputSpec |
64 | | - output_spec = _FMRISummaryOutputSpec |
65 | | - |
66 | | - def _run_interface(self, runtime): |
67 | | - import pandas as pd |
68 | | - |
69 | | - self._results["out_file"] = fname_presuffix( |
70 | | - self.inputs.in_func, |
71 | | - suffix="_fmriplot.svg", |
72 | | - use_ext=False, |
73 | | - newpath=runtime.cwd, |
74 | | - ) |
75 | | - |
76 | | - dataframe = pd.DataFrame({ |
77 | | - "outliers": np.loadtxt(self.inputs.outliers, usecols=[0]).tolist(), |
78 | | - # Pick non-standardize dvars (col 1) |
79 | | - # First timepoint is NaN (difference) |
80 | | - "DVARS": [np.nan] |
81 | | - + np.loadtxt(self.inputs.dvars, skiprows=1, usecols=[1]).tolist(), |
82 | | - # First timepoint is zero (reference volume) |
83 | | - "FD": [0.0] |
84 | | - + np.loadtxt(self.inputs.fd, skiprows=1, usecols=[0]).tolist(), |
85 | | - }) if ( |
86 | | - isdefined(self.inputs.outliers) |
87 | | - and isdefined(self.inputs.dvars) |
88 | | - and isdefined(self.inputs.fd) |
89 | | - ) else None |
90 | | - |
91 | | - input_data = nb.load(self.inputs.in_func) |
92 | | - seg_file = self.inputs.in_segm if isdefined(self.inputs.in_segm) else None |
93 | | - dataset, segments = ( |
94 | | - _cifti_timeseries(input_data) |
95 | | - if isinstance(input_data, nb.Cifti2Image) else |
96 | | - _nifti_timeseries(input_data, seg_file) |
97 | | - ) |
98 | | - |
99 | | - fig = fMRIPlot( |
100 | | - dataset, |
101 | | - segments=segments, |
102 | | - spikes_files=( |
103 | | - [self.inputs.in_spikes_bg] |
104 | | - if isdefined(self.inputs.in_spikes_bg) else None |
105 | | - ), |
106 | | - tr=( |
107 | | - self.inputs.tr if isdefined(self.inputs.tr) else |
108 | | - _get_tr(input_data) |
109 | | - ), |
110 | | - confounds=dataframe, |
111 | | - units={"outliers": "%", "FD": "mm"}, |
112 | | - vlines={"FD": [self.inputs.fd_thres]}, |
113 | | - nskip=self.inputs.drop_trs, |
114 | | - ).plot() |
115 | | - fig.savefig(self._results["out_file"], bbox_inches="tight") |
116 | | - return runtime |
117 | | - |
118 | | - |
119 | | -class _CompCorVariancePlotInputSpec(BaseInterfaceInputSpec): |
120 | | - metadata_files = traits.List( |
121 | | - File(exists=True), |
122 | | - mandatory=True, |
123 | | - desc="List of files containing component " "metadata", |
124 | | - ) |
125 | | - metadata_sources = traits.List( |
126 | | - traits.Str, |
127 | | - desc="List of names of decompositions " |
128 | | - "(e.g., aCompCor, tCompCor) yielding " |
129 | | - "the arguments in `metadata_files`", |
130 | | - ) |
131 | | - variance_thresholds = traits.Tuple( |
132 | | - traits.Float(0.5), |
133 | | - traits.Float(0.7), |
134 | | - traits.Float(0.9), |
135 | | - usedefault=True, |
136 | | - desc="Levels of explained variance to include in " "plot", |
137 | | - ) |
138 | | - out_file = traits.Either( |
139 | | - None, File, value=None, usedefault=True, desc="Path to save plot" |
140 | | - ) |
141 | | - |
142 | | - |
143 | | -class _CompCorVariancePlotOutputSpec(TraitedSpec): |
144 | | - out_file = File(exists=True, desc="Path to saved plot") |
145 | | - |
146 | | - |
147 | | -class CompCorVariancePlot(SimpleInterface): |
148 | | - """Plot the number of components necessary to explain the specified levels of variance.""" |
149 | | - |
150 | | - input_spec = _CompCorVariancePlotInputSpec |
151 | | - output_spec = _CompCorVariancePlotOutputSpec |
152 | | - |
153 | | - def _run_interface(self, runtime): |
154 | | - if self.inputs.out_file is None: |
155 | | - self._results["out_file"] = fname_presuffix( |
156 | | - self.inputs.metadata_files[0], |
157 | | - suffix="_compcor.svg", |
158 | | - use_ext=False, |
159 | | - newpath=runtime.cwd, |
160 | | - ) |
161 | | - else: |
162 | | - self._results["out_file"] = self.inputs.out_file |
163 | | - compcor_variance_plot( |
164 | | - metadata_files=self.inputs.metadata_files, |
165 | | - metadata_sources=self.inputs.metadata_sources, |
166 | | - output_file=self._results["out_file"], |
167 | | - varexp_thresh=self.inputs.variance_thresholds, |
168 | | - ) |
169 | | - return runtime |
170 | | - |
171 | | - |
172 | | -class _ConfoundsCorrelationPlotInputSpec(BaseInterfaceInputSpec): |
173 | | - confounds_file = File( |
174 | | - exists=True, mandatory=True, desc="File containing confound regressors" |
175 | | - ) |
176 | | - out_file = traits.Either( |
177 | | - None, File, value=None, usedefault=True, desc="Path to save plot" |
178 | | - ) |
179 | | - reference_column = traits.Str( |
180 | | - "global_signal", |
181 | | - usedefault=True, |
182 | | - desc="Column in the confound file for " |
183 | | - "which all correlation magnitudes " |
184 | | - "should be ranked and plotted", |
185 | | - ) |
186 | | - columns = traits.List( |
187 | | - traits.Str, |
188 | | - desc="Filter out all regressors not found in this list." |
189 | | - ) |
190 | | - max_dim = traits.Int( |
191 | | - 20, |
192 | | - usedefault=True, |
193 | | - desc="Maximum number of regressors to include in " |
194 | | - "plot. Regressors with highest magnitude of " |
195 | | - "correlation with `reference_column` will be " |
196 | | - "selected.", |
197 | | - ) |
198 | | - |
199 | | - |
200 | | -class _ConfoundsCorrelationPlotOutputSpec(TraitedSpec): |
201 | | - out_file = File(exists=True, desc="Path to saved plot") |
202 | | - |
203 | | - |
204 | | -class ConfoundsCorrelationPlot(SimpleInterface): |
205 | | - """Plot the correlation among confound regressors.""" |
206 | | - |
207 | | - input_spec = _ConfoundsCorrelationPlotInputSpec |
208 | | - output_spec = _ConfoundsCorrelationPlotOutputSpec |
209 | | - |
210 | | - def _run_interface(self, runtime): |
211 | | - if self.inputs.out_file is None: |
212 | | - self._results["out_file"] = fname_presuffix( |
213 | | - self.inputs.confounds_file, |
214 | | - suffix="_confoundCorrelation.svg", |
215 | | - use_ext=False, |
216 | | - newpath=runtime.cwd, |
217 | | - ) |
218 | | - else: |
219 | | - self._results["out_file"] = self.inputs.out_file |
220 | | - confounds_correlation_plot( |
221 | | - confounds_file=self.inputs.confounds_file, |
222 | | - columns=self.inputs.columns if isdefined(self.inputs.columns) else None, |
223 | | - max_dim=self.inputs.max_dim, |
224 | | - output_file=self._results["out_file"], |
225 | | - reference=self.inputs.reference_column, |
226 | | - ) |
227 | | - return runtime |
228 | | - |
229 | | - |
230 | | -def _get_tr(img): |
231 | | - """ |
232 | | - Attempt to extract repetition time from NIfTI/CIFTI header |
233 | | -
|
234 | | - Examples |
235 | | - -------- |
236 | | - >>> _get_tr(nb.load(Path(test_data) / |
237 | | - ... 'sub-ds205s03_task-functionallocalizer_run-01_bold_volreg.nii.gz')) |
238 | | - 2.2 |
239 | | - >>> _get_tr(nb.load(Path(test_data) / |
240 | | - ... 'sub-01_task-mixedgamblestask_run-02_space-fsLR_den-91k_bold.dtseries.nii')) |
241 | | - 2.0 |
242 | | -
|
243 | | - """ |
244 | | - |
245 | | - try: |
246 | | - return img.header.matrix.get_index_map(0).series_step |
247 | | - except AttributeError: |
248 | | - return img.header.get_zooms()[-1] |
249 | | - raise RuntimeError("Could not extract TR - unknown data structure type") |
| 37 | +warnings.warn("Please use nireports.interfaces", DeprecationWarning) |
0 commit comments