@@ -68,27 +68,50 @@ def init_anat_preproc_wf(
6868 return wf
6969
7070
71- def init_anat_csf_norm_wf (name = 'anat_csf_norm_wf ' ) -> LiterateWorkflow :
71+ def init_csf_norm_wf (name : str = 'csf_norm_wf ' ) -> LiterateWorkflow :
7272 """Replace low intensity voxels within the CSF mask with the median value."""
7373
7474 workflow = LiterateWorkflow (name = name )
75- inputnode = niu .IdentityInterface (fields = ['anat_preproc' , 'anat_dseg' ], name = 'inputnode' )
75+ workflow .__desc__ = (
76+ 'The CSF mask was used to normalize the anatomical template by the median of voxels '
77+ 'within the mask.'
78+ )
79+ inputnode = niu .IdentityInterface (fields = ['anat_preproc' , 'anat_tpms' ], name = 'inputnode' )
7680 outputnode = niu .IdentityInterface (fields = ['anat_preproc' ], name = 'outputnode' )
7781
78- applymask = pe .Node (ApplyMask (), name = 'applymask' )
82+ # select CSF from BIDS-ordered list (GM, WM, CSF)
83+ select_csf = pe .Node (niu .Select (index = 2 ), name = 'select_csf' )
84+ norm_csf = pe .Node (niu .Function (function = _normalize_roi ), name = 'norm_csf' )
7985
80- norm2median = pe .Node (niu .Function (function = _normalize_csf ), name = 'norm2median' )
81- # 1. mask brain with CSF mask
82- # fslmaths input.nii.gz -mas aseg_label-CSF_mask.nii.gz input_CSF.nii.gz
83- # 2. get median intensity of nonzero voxels in mask
84- # fslstats input_CSF.nii.gz -P 50
85- # 3. normalize CSF-masked T2w to the median
86- # fslmaths input_CSF.nii.gz -bin -mul <median intensity from (> median_CSF.nii.gz
87- # 4. make the modified T2w, setting voxel intensity to be the max between the original T2w's,
88- # and the normalized mask from (3)'s:
89- # fslmaths input.nii.gz -max median_CSF.nii.gz input_floorCSF.nii.gz
86+ workflow .connect ([
87+ (inputnode , select_csf , [('anat_tpms' , 'in_list' )]),
88+ (select_csf , norm_csf , [('out' , 'mask_file' )]),
89+ (inputnode , norm_csf , [('anat_preproc' , 'in_file' )]),
90+ (norm_csf , outputnode , [('out' , 'anat_preproc' )]),
91+ ]) # fmt:skip
9092
9193 return workflow
9294
9395
94- def _normalize_csf (in_file ): ...
96+ def _normalize_roi (in_file , mask_file , threshold = 0.2 , out_file = None ):
97+ """Normalize low intensity voxels that fall within a given mask."""
98+ import nibabel as nb
99+ import numpy as np
100+
101+ img = nb .load (in_file )
102+ img_data = img .get_fdata ()
103+ mask_img = nb .load (mask_file )
104+ # binary mask
105+ bin_mask = mask_img .get_fdata () > threshold
106+ mask_data = bin_mask * img_data
107+
108+ median = np .median (mask_data [mask_data > 0 ])
109+ normed_data = np .maximum (img_data , bin_mask * median )
110+
111+ oimg = img .__class__ (normed_data , img .affine , img .header )
112+ if not out_file :
113+ from nipype .utils .filemanip import fname_presuffix
114+
115+ out_file = fname_presuffix (in_file , suffix = 'normed' )
116+ oimg .to_filename (out_file )
117+ return out_file
0 commit comments