Skip to content

Commit 3147e01

Browse files
authored
[ENH] Use different masking thresholds for denoising and classification (#736)
* Use two different adaptive masks. masksum_denoise: Threshold of 1, used for denoising and optimal combination. masksum_clf: Threshold of 3, used for decomposition and component selection. * Fix long line. * Derive classifier masks directly from denoising ones. * Use denoising mask for component figures. * Swap full and limited T2*/S0 maps. Now that the full maps are used throughout the pipeline, they are the primary T2*/S0 outputs. The limited maps are not used for anything, and are only written out if the workflow is run in verbose mode. * Apply the same logic to t2smap. * Fix tests. * Fix documentation about full vs. limited maps. * Fix bad merge. * Log updated masking procedure. * Fix!
1 parent 7e1cf2c commit 3147e01

File tree

9 files changed

+113
-88
lines changed

9 files changed

+113
-88
lines changed

docs/approach.rst

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,9 @@ this voxel), so the line is fit to all available data.
108108
``tedana`` actually performs and uses two sets of :math:`T_{2}^*`/:math:`S_0` model fits.
109109
In one case, ``tedana`` estimates :math:`T_{2}^*` and :math:`S_0` for voxels with good signal in at
110110
least two echoes.
111-
The resulting "limited" :math:`T_{2}^*` and :math:`S_0` maps are used throughout
112-
most of the pipeline.
113111
In the other case, ``tedana`` estimates :math:`T_{2}^*` and :math:`S_0` for voxels
114112
with good data in only one echo as well, but uses the first two echoes for those voxels.
115-
The resulting "full" :math:`T_{2}^*` and :math:`S_0` maps are used to generate the
116-
optimally combined data.
113+
The resulting "full" :math:`T_{2}^*` and :math:`S_0` maps are used throughout the rest of the pipeline.
117114

118115
.. image:: /_static/a05_loglinear_regression.png
119116

docs/outputs.rst

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ Outputs of the tedana workflow
1313
Filename Content
1414
================================================ =====================================================
1515
dataset_description.json Top-level metadata for the workflow.
16-
T2starmap.nii.gz Limited estimated T2* 3D map.
16+
T2starmap.nii.gz Full estimated T2* 3D map.
1717
Values are in seconds.
1818
The difference between the limited and full maps
1919
is that, for voxels affected by dropout where
20-
only one echo contains good data, the full map
21-
uses the single echo's value while the limited
22-
map has a NaN.
23-
S0map.nii.gz Limited S0 3D map.
20+
only one echo contains good data, the full map uses
21+
the T2* estimate from the first two echoes, while the
22+
limited map has a NaN.
23+
S0map.nii.gz Full S0 3D map.
2424
The difference between the limited and full maps
2525
is that, for voxels affected by dropout where
26-
only one echo contains good data, the full map
27-
uses the single echo's value while the limited
28-
map has a NaN.
26+
only one echo contains good data, the full map uses
27+
the S0 estimate from the first two echoes, while the
28+
limited map has a NaN.
2929
desc-optcom_bold.nii.gz Optimally combined time series.
3030
desc-optcomDenoised_bold.nii.gz Denoised optimally combined time series. Recommended
3131
dataset for analysis.
@@ -79,15 +79,19 @@ If ``verbose`` is set to True:
7979
============================================================== =====================================================
8080
Filename Content
8181
============================================================== =====================================================
82-
desc-full_T2starmap.nii.gz Full T2* map/time series.
82+
desc-limited_T2starmap.nii.gz Limited T2* map/time series.
8383
Values are in seconds.
84-
The difference between the limited and full maps is
85-
that, for voxels affected by dropout where only one
86-
echo contains good data, the full map uses the
87-
single echo's value while the limited map has a NaN.
88-
Only used for optimal combination.
89-
desc-full_S0map.nii.gz Full S0 map/time series. Only used for optimal
90-
combination.
84+
The difference between the limited and full maps
85+
is that, for voxels affected by dropout where
86+
only one echo contains good data, the full map uses
87+
the S0 estimate from the first two echoes, while the
88+
limited map has a NaN.
89+
desc-limited_S0map.nii.gz Limited S0 map/time series.
90+
The difference between the limited and full maps
91+
is that, for voxels affected by dropout where
92+
only one echo contains good data, the full map uses
93+
the S0 estimate from the first two echoes, while the
94+
limited map has a NaN.
9195
echo-[echo]_desc-[PCA|ICA]_components.nii.gz Echo-wise PCA/ICA component weight maps.
9296
echo-[echo]_desc-[PCA|ICA]R2ModelPredictions_components.nii.gz Component- and voxel-wise R2-model predictions,
9397
separated by echo.

tedana/resources/config/outputs.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
"bidsv1.5.0": "desc-adaptiveGoodSignal_mask"
55
},
66
"t2star img": {
7-
"orig": "t2sv",
7+
"orig": "t2svG",
88
"bidsv1.5.0": "T2starmap"
99
},
1010
"s0 img": {
11-
"orig": "s0v",
11+
"orig": "s0vG",
1212
"bidsv1.5.0": "S0map"
1313
},
1414
"combined img": {
@@ -47,13 +47,13 @@
4747
"orig": "lowk_ts_OC",
4848
"bidsv1.5.0": "desc-optcomRejected_bold"
4949
},
50-
"full t2star img": {
51-
"orig": "t2svG",
52-
"bidsv1.5.0": "desc-full_T2starmap"
50+
"limited t2star img": {
51+
"orig": "t2sv",
52+
"bidsv1.5.0": "desc-limited_T2starmap"
5353
},
54-
"full s0 img": {
55-
"orig": "s0vG",
56-
"bidsv1.5.0": "desc-full_S0map"
54+
"limited s0 img": {
55+
"orig": "s0v",
56+
"bidsv1.5.0": "desc-limited_S0map"
5757
},
5858
"whitened img": {
5959
"orig": "ts_OC_whitened",

tedana/tests/data/fiu_four_echo_outputs.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ desc-PCA_mixing.tsv
2020
desc-PCA_stat-z_components.nii.gz
2121
desc-T1likeEffect_min.nii.gz
2222
desc-adaptiveGoodSignal_mask.nii.gz
23-
desc-full_S0map.nii.gz
24-
desc-full_T2starmap.nii.gz
2523
desc-globalSignal_map.nii.gz
2624
desc-globalSignal_timeseries.tsv
25+
desc-limited_S0map.nii.gz
26+
desc-limited_T2starmap.nii.gz
2727
desc-optcomAcceptedMIRDenoised_bold.nii.gz
2828
desc-optcomAccepted_bold.nii.gz
2929
desc-optcomDenoised_bold.nii.gz
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
dataset_description.json
2-
desc-full_S0map.nii.gz
3-
desc-full_T2starmap.nii.gz
2+
desc-limited_S0map.nii.gz
3+
desc-limited_T2starmap.nii.gz
44
desc-optcom_bold.nii.gz
55
S0map.nii.gz
66
T2starmap.nii.gz

tedana/tests/data/nih_five_echo_outputs_verbose.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ desc-PCA_metrics.tsv
1818
desc-PCA_mixing.tsv
1919
desc-PCA_stat-z_components.nii.gz
2020
desc-adaptiveGoodSignal_mask.nii.gz
21-
desc-full_S0map.nii.gz
22-
desc-full_T2starmap.nii.gz
21+
desc-limited_S0map.nii.gz
22+
desc-limited_T2starmap.nii.gz
2323
desc-optcomAccepted_bold.nii.gz
2424
desc-optcomDenoised_bold.nii.gz
2525
desc-optcomPCAReduced_bold.nii.gz

tedana/tests/test_t2smap.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ def test_basic_t2smap1(self):
3131
assert len(img.shape) == 3
3232
img = nib.load(op.join(out_dir, 'S0map.nii.gz'))
3333
assert len(img.shape) == 3
34-
img = nib.load(op.join(out_dir, 'desc-full_T2starmap.nii.gz'))
34+
img = nib.load(op.join(out_dir, 'desc-limited_T2starmap.nii.gz'))
3535
assert len(img.shape) == 3
36-
img = nib.load(op.join(out_dir, 'desc-full_S0map.nii.gz'))
36+
img = nib.load(op.join(out_dir, 'desc-limited_S0map.nii.gz'))
3737
assert len(img.shape) == 3
3838
img = nib.load(op.join(out_dir, 'desc-optcom_bold.nii.gz'))
3939
assert len(img.shape) == 4
@@ -57,9 +57,9 @@ def test_basic_t2smap2(self):
5757
assert len(img.shape) == 4
5858
img = nib.load(op.join(out_dir, 'S0map.nii.gz'))
5959
assert len(img.shape) == 4
60-
img = nib.load(op.join(out_dir, 'desc-full_T2starmap.nii.gz'))
60+
img = nib.load(op.join(out_dir, 'desc-limited_T2starmap.nii.gz'))
6161
assert len(img.shape) == 4
62-
img = nib.load(op.join(out_dir, 'desc-full_S0map.nii.gz'))
62+
img = nib.load(op.join(out_dir, 'desc-limited_S0map.nii.gz'))
6363
assert len(img.shape) == 4
6464
img = nib.load(op.join(out_dir, 'desc-optcom_bold.nii.gz'))
6565
assert len(img.shape) == 4
@@ -83,9 +83,9 @@ def test_basic_t2smap3(self):
8383
assert len(img.shape) == 3
8484
img = nib.load(op.join(out_dir, 'S0map.nii.gz'))
8585
assert len(img.shape) == 3
86-
img = nib.load(op.join(out_dir, 'desc-full_T2starmap.nii.gz'))
86+
img = nib.load(op.join(out_dir, 'desc-limited_T2starmap.nii.gz'))
8787
assert len(img.shape) == 3
88-
img = nib.load(op.join(out_dir, 'desc-full_S0map.nii.gz'))
88+
img = nib.load(op.join(out_dir, 'desc-limited_S0map.nii.gz'))
8989
assert len(img.shape) == 3
9090
img = nib.load(op.join(out_dir, 'desc-optcom_bold.nii.gz'))
9191
assert len(img.shape) == 4
@@ -109,9 +109,9 @@ def test_basic_t2smap4(self):
109109
assert len(img.shape) == 4
110110
img = nib.load(op.join(out_dir, 'S0map.nii.gz'))
111111
assert len(img.shape) == 4
112-
img = nib.load(op.join(out_dir, 'desc-full_T2starmap.nii.gz'))
112+
img = nib.load(op.join(out_dir, 'desc-limited_T2starmap.nii.gz'))
113113
assert len(img.shape) == 4
114-
img = nib.load(op.join(out_dir, 'desc-full_S0map.nii.gz'))
114+
img = nib.load(op.join(out_dir, 'desc-limited_S0map.nii.gz'))
115115
assert len(img.shape) == 4
116116
img = nib.load(op.join(out_dir, 'desc-optcom_bold.nii.gz'))
117117
assert len(img.shape) == 4
@@ -135,9 +135,9 @@ def test_t2smap_cli(self):
135135
assert len(img.shape) == 3
136136
img = nib.load(op.join(out_dir, 'S0map.nii.gz'))
137137
assert len(img.shape) == 3
138-
img = nib.load(op.join(out_dir, 'desc-full_T2starmap.nii.gz'))
138+
img = nib.load(op.join(out_dir, 'desc-limited_T2starmap.nii.gz'))
139139
assert len(img.shape) == 3
140-
img = nib.load(op.join(out_dir, 'desc-full_S0map.nii.gz'))
140+
img = nib.load(op.join(out_dir, 'desc-limited_S0map.nii.gz'))
141141
assert len(img.shape) == 3
142142
img = nib.load(op.join(out_dir, 'desc-optcom_bold.nii.gz'))
143143
assert len(img.shape) == 4

tedana/workflows/t2smap.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -171,21 +171,27 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None,
171171
-----
172172
This workflow writes out several files, which are described below:
173173
174-
========================== =================================================
174+
============================= =================================================
175175
Filename Content
176-
========================== =================================================
177-
T2starmap.nii.gz Limited estimated T2* 3D map or 4D timeseries.
176+
============================= =================================================
177+
T2starmap.nii.gz Estimated T2* 3D map or 4D timeseries.
178178
Will be a 3D map if ``fitmode`` is 'all' and a
179179
4D timeseries if it is 'ts'.
180-
S0map.nii.gz Limited S0 3D map or 4D timeseries.
181-
desc-full_T2starmap.nii.gz Full T2* map/timeseries. The difference between
180+
S0map.nii.gz S0 3D map or 4D timeseries.
181+
desc-limited_T2starmap.nii.gz Limited T2* map/timeseries. The difference between
182182
the limited and full maps is that, for voxels
183183
affected by dropout where only one echo contains
184-
good data, the full map uses the single echo's
185-
value while the limited map has a NaN.
186-
desc-full_S0map.nii.gz Full S0 map/timeseries.
184+
good data, the full map uses the T2* estimate
185+
from the first two echos, while the limited map
186+
will have a NaN.
187+
desc-limited_S0map.nii.gz Limited S0 map/timeseries. The difference between
188+
the limited and full maps is that, for voxels
189+
affected by dropout where only one echo contains
190+
good data, the full map uses the S0 estimate
191+
from the first two echos, while the limited map
192+
will have a NaN.
187193
desc-optcom_bold.nii.gz Optimally combined timeseries.
188-
========================== =================================================
194+
============================= =================================================
189195
"""
190196
out_dir = op.abspath(out_dir)
191197
if not op.isdir(out_dir):
@@ -234,36 +240,36 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None,
234240

235241
# set a hard cap for the T2* map/timeseries
236242
# anything that is 10x higher than the 99.5 %ile will be reset to 99.5 %ile
237-
cap_t2s = stats.scoreatpercentile(t2s_limited.flatten(), 99.5,
243+
cap_t2s = stats.scoreatpercentile(t2s_full.flatten(), 99.5,
238244
interpolation_method='lower')
239245
cap_t2s_sec = utils.millisec2sec(cap_t2s * 10.)
240246
LGR.debug('Setting cap on T2* map at {:.5f}s'.format(cap_t2s_sec))
241-
t2s_limited[t2s_limited > cap_t2s * 10] = cap_t2s
247+
t2s_full[t2s_full > cap_t2s * 10] = cap_t2s
242248

243249
LGR.info('Computing optimal combination')
244250
# optimally combine data
245251
OCcatd = combine.make_optcom(catd, tes, masksum, t2s=t2s_full,
246252
combmode=combmode)
247253

248254
# clean up numerical errors
249-
for arr in (OCcatd, s0_limited, t2s_limited):
255+
for arr in (OCcatd, s0_full, t2s_full):
250256
np.nan_to_num(arr, copy=False)
251257

252-
s0_limited[s0_limited < 0] = 0
253-
t2s_limited[t2s_limited < 0] = 0
258+
s0_full[s0_full < 0] = 0
259+
t2s_full[t2s_full < 0] = 0
254260

255261
io_generator.save_file(
256-
utils.millisec2sec(t2s_limited),
262+
utils.millisec2sec(t2s_full),
257263
't2star img',
258264
)
259-
io_generator.save_file(s0_limited, 's0 img')
265+
io_generator.save_file(s0_full, 's0 img')
260266
io_generator.save_file(
261-
utils.millisec2sec(t2s_full),
262-
'full t2star img',
267+
utils.millisec2sec(t2s_limited),
268+
'limited t2star img',
263269
)
264270
io_generator.save_file(
265-
s0_full,
266-
'full s0 img',
271+
s0_limited,
272+
'limited s0 img',
267273
)
268274
io_generator.save_file(OCcatd, 'combined img')
269275

0 commit comments

Comments
 (0)