|
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