Skip to content

Commit 7b7aee9

Browse files
authored
Using lateral_ventricles_mask as the csf_mask when segmentation is off (#1656)
2 parents 2fb2eba + bc09278 commit 7b7aee9

File tree

3 files changed

+90
-50
lines changed

3 files changed

+90
-50
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
- `fslmaths`
1919
- `mri_vol2vol`
2020
- `recon-all`
21+
- Added ability to use lateral ventricles mask in place of Cerebrospinal Fluid mask when when Segmentation is Off, specifically for the Rodent pipeline, but works on any dataset when Segmentation is Off.
2122

2223
### Changed
2324
- In a given pipeline configuration, segmentation probability maps and binary tissue masks are warped to template space, and those warped masks are included in the output directory
@@ -32,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3233
- Fixed [bug](https://github.com/FCP-INDI/C-PAC/issues/1638) in which working connectivity matrix filepaths were generated incorrectly, preventing generating matrices depending on container bindings
3334
- Fixed broken links in README
3435
- Fixed [bug](https://github.com/FCP-INDI/C-PAC/issues/1575) in which anatomical-only configurations required functional data directories
36+
- Fixed [bug] (https://github.com/FCP-INDI/C-PAC/issues/1532) in which nuisance regressors runs when Segmentation is Off by using Lateral Ventricles Mask as the CSF mask.
3537

3638
## [1.8.2] - 2021-12-02
3739

CPAC/nuisance/nuisance.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ def gather_nuisance(functional_file_path,
461461
def create_regressor_workflow(nuisance_selectors,
462462
use_ants,
463463
ventricle_mask_exist,
464+
csf_mask_exist,
464465
all_bold=False,
465466
name='nuisance_regressors'):
466467
"""
@@ -1129,6 +1130,7 @@ def create_regressor_workflow(nuisance_selectors,
11291130
pipeline_resource_pool,
11301131
tissue_regressor_descriptor,
11311132
regressor_selector,
1133+
csf_mask_exist,
11321134
use_ants=use_ants,
11331135
ventricle_mask_exist=ventricle_mask_exist,
11341136
all_bold=all_bold
@@ -2176,9 +2178,13 @@ def nuisance_regressors_generation(wf, cfg, strat_pool, pipe_num, opt=None):
21762178
use_ants = reg_tool == 'ants'
21772179

21782180
ventricle = strat_pool.check_rpool('lateral-ventricles-mask')
2181+
csf_mask = strat_pool.check_rpool(["label-CSF_desc-eroded_mask",
2182+
"label-CSF_desc-preproc_mask",
2183+
"label-CSF_mask"])
21792184

21802185
regressors = create_regressor_workflow(opt, use_ants,
21812186
ventricle_mask_exist=ventricle,
2187+
csf_mask_exist = csf_mask,
21822188
name='nuisance_regressors_'
21832189
f'{opt["Name"]}_{pipe_num}')
21842190

@@ -2578,10 +2584,14 @@ def nuisance_regressors_generation_EPItemplate(wf, cfg, strat_pool, pipe_num, op
25782584

25792585
use_ants = reg_tool == 'ants'
25802586
ventricle = strat_pool.check_rpool('lateral-ventricles-mask')
2587+
csf_mask = strat_pool.check_rpool(["space-bold_label-CSF_desc-eroded_mask",
2588+
"space-bold_label-CSF_desc-preproc_mask",
2589+
"space-bold_label-CSF_mask"])
25812590

25822591
regressors = create_regressor_workflow(opt, use_ants,
25832592
ventricle_mask_exist=ventricle,
25842593
all_bold=True,
2594+
csf_mask_exist = csf_mask,
25852595
name='nuisance_regressors_'
25862596
f'{opt["Name"]}_{pipe_num}')
25872597

CPAC/nuisance/utils/__init__.py

Lines changed: 78 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77
import nipype.interfaces.utility as util
88
from CPAC.pipeline import nipype_pipeline_engine as pe
99
from nipype.interfaces import afni
10+
from nipype import logging
1011

1112
from CPAC.nuisance.utils.compcor import calc_compcor_components
1213
from CPAC.nuisance.utils.crc import encode as crc_encode
1314
from CPAC.utils.interfaces.fsl import Merge as fslMerge
1415
from CPAC.utils.interfaces.function import Function
1516
from CPAC.registration.utils import check_transforms, generate_inverse_transform_flags
1617

18+
logger = logging.getLogger('nipype.workflow')
19+
1720

1821
def find_offending_time_points(fd_j_file_path=None, fd_p_file_path=None, dvars_file_path=None,
1922
fd_j_threshold=None, fd_p_threshold=None, dvars_threshold=None,
@@ -271,6 +274,7 @@ def generate_summarize_tissue_mask(nuisance_wf,
271274
pipeline_resource_pool,
272275
regressor_descriptor,
273276
regressor_selector,
277+
csf_mask_exist,
274278
use_ants=True,
275279
ventricle_mask_exist=True,
276280
all_bold=False):
@@ -322,46 +326,46 @@ def generate_summarize_tissue_mask(nuisance_wf,
322326

323327
if all_bold:
324328
pass
325-
326-
mask_to_epi = pe.Node(interface=fsl.FLIRT(),
327-
name='{}_flirt'
328-
.format(node_mask_key),
329-
mem_gb=3.63,
330-
mem_x=(3767129957844731 / 1208925819614629174706176,
331-
'in_file'))
332-
333-
mask_to_epi.inputs.interp = 'nearestneighbour'
334-
335-
if regressor_selector['extraction_resolution'] == "Functional":
336-
# apply anat2func matrix
337-
mask_to_epi.inputs.apply_xfm = True
338-
mask_to_epi.inputs.output_type = 'NIFTI_GZ'
339-
nuisance_wf.connect(*(
340-
pipeline_resource_pool['Functional_mean'] +
341-
(mask_to_epi, 'reference')
342-
))
343-
nuisance_wf.connect(*(
344-
pipeline_resource_pool['Transformations']['anat_to_func_linear_xfm'] +
345-
(mask_to_epi, 'in_matrix_file')
346-
))
347329

348-
else:
349-
resolution = regressor_selector['extraction_resolution']
350-
mask_to_epi.inputs.apply_isoxfm = resolution
330+
if csf_mask_exist:
331+
mask_to_epi = pe.Node(interface=fsl.FLIRT(),
332+
name='{}_flirt'.format(node_mask_key),
333+
mem_gb=3.63,
334+
mem_x=(3767129957844731 / 1208925819614629174706176,
335+
'in_file'))
336+
337+
mask_to_epi.inputs.interp = 'nearestneighbour'
338+
339+
if regressor_selector['extraction_resolution'] == "Functional":
340+
# apply anat2func matrix
341+
mask_to_epi.inputs.apply_xfm = True
342+
mask_to_epi.inputs.output_type = 'NIFTI_GZ'
343+
nuisance_wf.connect(*(
344+
pipeline_resource_pool['Functional_mean'] +
345+
(mask_to_epi, 'reference')
346+
))
347+
nuisance_wf.connect(*(
348+
pipeline_resource_pool['Transformations']['anat_to_func_linear_xfm'] +
349+
(mask_to_epi, 'in_matrix_file')
350+
))
351351

352-
nuisance_wf.connect(*(
353-
pipeline_resource_pool['Anatomical_{}mm'
352+
else:
353+
resolution = regressor_selector['extraction_resolution']
354+
mask_to_epi.inputs.apply_isoxfm = resolution
355+
356+
nuisance_wf.connect(*(
357+
pipeline_resource_pool['Anatomical_{}mm'
354358
.format(resolution)] +
355-
(mask_to_epi, 'reference')
356-
))
359+
(mask_to_epi, 'reference')
360+
))
357361

358-
nuisance_wf.connect(*(
359-
pipeline_resource_pool[prev_mask_key] +
360-
(mask_to_epi, 'in_file')
361-
))
362+
nuisance_wf.connect(*(
363+
pipeline_resource_pool[prev_mask_key] +
364+
(mask_to_epi, 'in_file')
365+
))
362366

363-
pipeline_resource_pool[mask_key] = \
364-
(mask_to_epi, 'out_file')
367+
pipeline_resource_pool[mask_key] = \
368+
(mask_to_epi, 'out_file')
365369

366370
if full_mask_key.startswith('CerebrospinalFluid'):
367371
pipeline_resource_pool = generate_summarize_tissue_mask_ventricles_masking(
@@ -370,6 +374,7 @@ def generate_summarize_tissue_mask(nuisance_wf,
370374
regressor_descriptor,
371375
regressor_selector,
372376
node_mask_key,
377+
csf_mask_exist,
373378
use_ants,
374379
ventricle_mask_exist
375380
)
@@ -389,27 +394,28 @@ def generate_summarize_tissue_mask(nuisance_wf,
389394
pipeline_resource_pool[mask_key] = \
390395
(erode_mask_node, 'out_file')
391396

392-
return pipeline_resource_pool, full_mask_key
397+
return pipeline_resource_pool, full_mask_key
393398

394399

395400
def generate_summarize_tissue_mask_ventricles_masking(nuisance_wf,
396401
pipeline_resource_pool,
397402
regressor_descriptor,
398403
regressor_selector,
399404
mask_key,
405+
csf_mask_exist,
400406
use_ants=True,
401407
ventricle_mask_exist=True):
402408

409+
if csf_mask_exist == False:
410+
logger.warning('Segmentation is Off, - therefore will be using '
411+
'lateral_ventricle_mask as CerebrospinalFluid_mask.')
412+
403413
# Mask CSF with Ventricles
404414
if '{}_Unmasked'.format(mask_key) not in pipeline_resource_pool:
405415

406-
# reduce CSF mask to the lateral ventricles
407-
mask_csf_with_lat_ven = pe.Node(interface=afni.Calc(outputtype='NIFTI_GZ'), name='{}_Ventricles'.format(mask_key))
408-
mask_csf_with_lat_ven.inputs.expr = 'a*b'
409-
mask_csf_with_lat_ven.inputs.out_file = 'csf_lat_ven_mask.nii.gz'
410-
411-
if ventricle_mask_exist :
416+
if ventricle_mask_exist:
412417
ventricles_key = 'VentriclesToAnat'
418+
413419
if 'resolution' in regressor_descriptor:
414420
ventricles_key += '_{}'.format(regressor_descriptor['resolution'])
415421

@@ -444,7 +450,20 @@ def generate_summarize_tissue_mask_ventricles_masking(nuisance_wf,
444450
nuisance_wf.connect(collect_linear_transforms, 'out', lat_ven_mni_to_anat, 'transforms')
445451

446452
nuisance_wf.connect(*(pipeline_resource_pool['Ventricles'] + (lat_ven_mni_to_anat, 'input_image')))
447-
nuisance_wf.connect(*(pipeline_resource_pool[mask_key] + (lat_ven_mni_to_anat, 'reference_image')))
453+
resolution = regressor_selector['extraction_resolution']
454+
455+
if csf_mask_exist:
456+
nuisance_wf.connect(*(
457+
pipeline_resource_pool[mask_key] +
458+
(lat_ven_mni_to_anat, 'reference_image')))
459+
elif resolution == 'Functional':
460+
nuisance_wf.connect(*(
461+
pipeline_resource_pool['Functional_mean'] +
462+
(lat_ven_mni_to_anat, 'reference_image')))
463+
else:
464+
nuisance_wf.connect(*(
465+
pipeline_resource_pool['Anatomical_{}mm'.format(resolution)] +
466+
(lat_ven_mni_to_anat, 'reference_image')))
448467

449468
pipeline_resource_pool[ventricles_key] = (lat_ven_mni_to_anat, 'output_image')
450469

@@ -460,17 +479,26 @@ def generate_summarize_tissue_mask_ventricles_masking(nuisance_wf,
460479

461480
pipeline_resource_pool[ventricles_key] = (lat_ven_mni_to_anat, 'out_file')
462481

463-
nuisance_wf.connect(*(pipeline_resource_pool[ventricles_key] + (mask_csf_with_lat_ven, 'in_file_a')))
464-
nuisance_wf.connect(*(pipeline_resource_pool[mask_key] + (mask_csf_with_lat_ven, 'in_file_b')))
482+
if csf_mask_exist:
483+
# reduce CSF mask to the lateral ventricles
484+
mask_csf_with_lat_ven = pe.Node(interface=afni.Calc(outputtype='NIFTI_GZ'),
485+
name='{}_Ventricles'.format(mask_key))
486+
mask_csf_with_lat_ven.inputs.expr = 'a*b'
487+
mask_csf_with_lat_ven.inputs.out_file = 'csf_lat_ven_mask.nii.gz'
488+
489+
nuisance_wf.connect(*(pipeline_resource_pool[ventricles_key] + (mask_csf_with_lat_ven, 'in_file_a')))
490+
nuisance_wf.connect(*(pipeline_resource_pool[mask_key] + (mask_csf_with_lat_ven, 'in_file_b')))
465491

466-
pipeline_resource_pool['{}_Unmasked'.format(mask_key)] = pipeline_resource_pool[mask_key]
467-
pipeline_resource_pool[mask_key] = (mask_csf_with_lat_ven, 'out_file')
468-
else :
469-
pipeline_resource_pool['{}_Unmasked'.format(mask_key)] = pipeline_resource_pool[mask_key]
492+
pipeline_resource_pool['{}_Unmasked'.format(mask_key)] = pipeline_resource_pool[mask_key]
493+
pipeline_resource_pool[mask_key] = (mask_csf_with_lat_ven, 'out_file')
470494

495+
else:
496+
pipeline_resource_pool[mask_key] = pipeline_resource_pool[ventricles_key]
497+
471498
return pipeline_resource_pool
472499

473500

501+
474502
class NuisanceRegressor(object):
475503

476504
def __init__(self, selector):
@@ -579,7 +607,7 @@ def encode(selector):
579607
res = "%.2gmm" % s['extraction_resolution']
580608
if s.get('erode_mask'):
581609
res += 'E'
582-
pieces += [res]
610+
pieces += [res]
583611

584612
pieces += [NuisanceRegressor._summary_params(s)]
585613
pieces += [NuisanceRegressor._derivative_params(s)]

0 commit comments

Comments
 (0)