def extend_mask12(target, out_dir, b_value, mask=None, n_iter=200): command = f'N4BiasFieldCorrection -d 3 -i {target} -b {str(b_value)} -s 4 -c [{str(n_iter)}x{str(n_iter)}x{str(n_iter)},1e-4] -w {out_dir}/mask12.mnc -x {out_dir}/null_mask.mnc -o {out_dir}/corrected_lower.mnc' rc = run_command(command) command = f'ThresholdImage 3 {out_dir}/corrected_lower.mnc {out_dir}/otsu_weight.mnc Otsu 4' rc = run_command(command) otsu_img = sitk.ReadImage(f'{out_dir}/otsu_weight.mnc', sitk.sitkUInt8) otsu_array = sitk.GetArrayFromImage(otsu_img) if mask is not None: resampled_mask_img = sitk.ReadImage(mask, sitk.sitkUInt8) resampled_mask_array = sitk.GetArrayFromImage(resampled_mask_img) otsu_array = otsu_array * resampled_mask_array lower_mask = (otsu_array == 1.0) + (otsu_array == 2.0) def extend_mask(mask, lower_mask): otsu_img = sitk.ReadImage(mask, sitk.sitkUInt8) otsu_array = sitk.GetArrayFromImage(otsu_img) combined_mask = (otsu_array + lower_mask) > 0 mask_img = sitk.GetImageFromArray(combined_mask.astype('uint8'), isVector=False) mask_img.CopyInformation(otsu_img) sitk.WriteImage(mask_img, mask) extend_mask(f'{out_dir}/mask12.mnc', lower_mask) extend_mask(f'{out_dir}/mask123.mnc', lower_mask) extend_mask(f'{out_dir}/mask1234.mnc', lower_mask)
def seed_corr(bold_file, mask_file, seed): import os from rabies.utils import run_command brain_mask = np.asarray(nb.load(mask_file).dataobj) volume_indices = brain_mask.astype(bool) timeseries_array = np.asarray(nb.load(bold_file).dataobj) sub_timeseries = np.zeros( [timeseries_array.shape[3], volume_indices.sum()]) for t in range(timeseries_array.shape[3]): sub_timeseries[t, :] = timeseries_array[:, :, :, t][volume_indices] resampled = os.path.abspath('resampled.nii.gz') command = f'antsApplyTransforms -i {seed} -r {mask_file} -o {resampled} -n GenericLabel' rc = run_command(command) roi_mask = np.asarray( nb.load(resampled).dataobj)[volume_indices].astype(bool) # extract the voxel timeseries within the mask, and take the mean ROI timeseries seed_timeseries = sub_timeseries[:, roi_mask].mean(axis=1) corrs = vcorrcoef(sub_timeseries.T, seed_timeseries) corrs[np.isnan(corrs)] = 0 return corrs
def otsu_bias_cor(target, otsu_ref, out_dir, b_value, mask=None, n_iter=200): command = f'ImageMath 3 {out_dir}/null_mask.mnc ThresholdAtMean {otsu_ref} 0' rc = run_command(command) command = f'ThresholdImage 3 {otsu_ref} {out_dir}/otsu_weight.mnc Otsu 4' rc = run_command(command) otsu_img = sitk.ReadImage(f'{out_dir}/otsu_weight.mnc', sitk.sitkUInt8) otsu_array = sitk.GetArrayFromImage(otsu_img) if mask is not None: resampled_mask_img = sitk.ReadImage(mask, sitk.sitkUInt8) resampled_mask_array = sitk.GetArrayFromImage(resampled_mask_img) otsu_array = otsu_array * resampled_mask_array combined_mask = (otsu_array == 1.0) + (otsu_array == 2.0) mask_img = sitk.GetImageFromArray(combined_mask.astype('uint8'), isVector=False) mask_img.CopyInformation(otsu_img) sitk.WriteImage(mask_img, f'{out_dir}/mask12.mnc') combined_mask = (otsu_array == 1.0) + (otsu_array == 2.0) + (otsu_array == 3.0) mask_img = sitk.GetImageFromArray(combined_mask.astype('uint8'), isVector=False) mask_img.CopyInformation(otsu_img) sitk.WriteImage(mask_img, f'{out_dir}/mask123.mnc') combined_mask = (otsu_array == 1.0) + (otsu_array == 2.0) + ( otsu_array == 3.0) + (otsu_array == 4.0) mask_img = sitk.GetImageFromArray(combined_mask.astype('uint8'), isVector=False) mask_img.CopyInformation(otsu_img) sitk.WriteImage(mask_img, f'{out_dir}/mask1234.mnc') extend_mask12(target, out_dir, b_value=b_value, mask=mask, n_iter=200) command = f'N4BiasFieldCorrection -v -d 3 -i {target} -b {str(b_value)} -s 4 -c [{str(n_iter)}x{str(n_iter)}x{str(n_iter)},1e-4] -w {out_dir}/mask12.mnc -x {out_dir}/null_mask.mnc -o {out_dir}/corrected1_.mnc' rc = run_command(command) #command = 'N4BiasFieldCorrection -v -d 3 -i corrected1_.mnc -b %s -s 4 -c [%sx%sx%s,1e-4] -w mask123.mnc -x null_mask.mnc -o corrected2_.mnc' % (str(b_value), str(n_iter),str(n_iter),str(n_iter),) #rc = run_command(command) command = f'N4BiasFieldCorrection -v -d 3 -i {out_dir}/corrected1_.mnc -b {str(b_value)} -s 4 -c [{str(n_iter)}x{str(n_iter)}x{str(n_iter)},1e-4] -w {out_dir}/mask1234.mnc -x {out_dir}/null_mask.mnc -o {out_dir}/multistage_corrected.mnc' rc = run_command(command)
def _run_interface(self, runtime): filename_template = pathlib.Path( self.inputs.name_source).name.rsplit(".nii")[0] script_path = 'plot_overlap.sh' os.makedirs(self.inputs.out_dir, exist_ok=True) out_name = self.inputs.out_dir+'/' + \ filename_template+'_registration.png' command = f'{script_path} {self.inputs.moving} {self.inputs.fixed} {out_name}' rc = run_command(command) setattr(self, 'out_png', out_name) return runtime
def run_group_ICA(bold_file_list, mask_file, dim, random_seed): import os import pandas as pd # create a filelist.txt file_path = os.path.abspath('filelist.txt') from rabies.utils import flatten_list merged = flatten_list(list(bold_file_list)) df = pd.DataFrame(data=merged) df.to_csv(file_path, header=False, sep=',', index=False) from rabies.utils import run_command out_dir = os.path.abspath('group_melodic.ica') command = f'melodic -i {file_path} -m {mask_file} -o {out_dir} -d {dim} --report --seed={str(random_seed)}' rc = run_command(command) IC_file = out_dir + '/melodic_IC.nii.gz' return out_dir, IC_file
def run_antsRegistration(reg_method, brain_extraction=False, moving_image='NULL', moving_mask='NULL', fixed_image='NULL', fixed_mask='NULL', rabies_data_type=8): import os import pathlib # Better path manipulation filename_split = pathlib.Path(moving_image).name.rsplit(".nii") from rabies.preprocess_pkg.registration import define_reg_script reg_call = define_reg_script(reg_method) if reg_method == 'Rigid' or reg_method == 'Affine' or reg_method == 'SyN': if brain_extraction: reg_call += " --mask-extract --keep-mask-after-extract" command = f"{reg_call} --moving-mask {moving_mask} --fixed-mask {fixed_mask} --resampled-output {filename_split[0]}_output_warped_image.nii.gz {moving_image} {fixed_image} {filename_split[0]}_output_" else: command = f'{reg_call} {moving_image} {moving_mask} {fixed_image} {fixed_mask} {filename_split[0]}' from rabies.utils import run_command rc = run_command(command) cwd = os.getcwd() warped_image = f'{cwd}/{filename_split[0]}_output_warped_image.nii.gz' affine = f'{cwd}/{filename_split[0]}_output_0GenericAffine.mat' warp = f'{cwd}/{filename_split[0]}_output_1Warp.nii.gz' inverse_warp = f'{cwd}/{filename_split[0]}_output_1InverseWarp.nii.gz' if not os.path.isfile(warped_image) or not os.path.isfile(affine): raise ValueError( 'REGISTRATION ERROR: OUTPUT FILES MISSING. Make sure the provided registration script runs properly.' ) if not os.path.isfile(warp) or not os.path.isfile(inverse_warp): from nipype import logging log = logging.getLogger('nipype.workflow') log.debug( 'No non-linear warp files as output. Assumes linear registration.') warp = 'NULL' inverse_warp = 'NULL' import SimpleITK as sitk sitk.WriteImage(sitk.ReadImage(warped_image, rabies_data_type), warped_image) return [affine, warp, inverse_warp, warped_image]
def otsu_scaling(image_file): img = sitk.ReadImage(image_file) array = sitk.GetArrayFromImage(img) # select a smart vmax for the image display to enhance contrast command = f'ThresholdImage 3 {image_file} otsu_weight.nii.gz Otsu 4' rc = run_command(command) # clip off the background mask = sitk.GetArrayFromImage(sitk.ReadImage('otsu_weight.nii.gz')) voxel_subset = array[mask > 1.0] # select a maximal value which encompasses 90% of the voxels in the mask voxel_subset.sort() vmax = voxel_subset[int(len(voxel_subset) * 0.9)] scaled = array / vmax scaled_img = sitk.GetImageFromArray(scaled, isVector=False) scaled_img.CopyInformation(img) return scaled_img
def resample_4D(input_4d, ref_file): import os import pathlib # Better path manipulation import SimpleITK as sitk from rabies.utils import run_command, split_volumes, Merge, copyInfo_3DImage rabies_data_type = sitk.sitkFloat32 # check if the IC_file has the same dimensions as bold_file img_array = sitk.GetArrayFromImage(sitk.ReadImage(ref_file))[0, :, :, :] image_3d = copyInfo_3DImage( sitk.GetImageFromArray(img_array, isVector=False), sitk.ReadImage(ref_file)) new_ref = 'temp_ref.nii.gz' sitk.WriteImage(image_3d, 'temp_ref.nii.gz') filename_split = pathlib.Path(input_4d).name.rsplit(".nii") # Splitting into list of single volumes [split_volumes_files, num_volumes] = split_volumes(input_4d, "split_", rabies_data_type) resampled_volumes = [] for x in range(0, num_volumes): resampled_vol_fname = os.path.abspath("resampled_volume" + str(x) + ".nii.gz") resampled_volumes.append(resampled_vol_fname) command = f'antsApplyTransforms -i {split_volumes_files[x]} -n BSpline[5] -r {new_ref} -o {resampled_vol_fname}' rc = run_command(command) # change image to specified data type sitk.WriteImage(sitk.ReadImage(resampled_vol_fname, rabies_data_type), resampled_vol_fname) out = Merge(in_files=resampled_volumes, header_source=input_4d, rabies_data_type=rabies_data_type, clip_negative=False).run() return out.outputs.out_file
def resample_mask(in_file, ref_file): transforms = [] inverses = [] # resampling the reference image to the dimension of the EPI from rabies.utils import run_command import pathlib # Better path manipulation filename_split = pathlib.Path( in_file).name.rsplit(".nii") out_file = os.path.abspath(filename_split[0])+'_resampled.nii.gz' # tranforms is a list of transform files, set in order of call within antsApplyTransforms transform_string = "" for transform, inverse in zip(transforms, inverses): if transform == 'NULL': continue elif bool(inverse): transform_string += f"-t [{transform},1] " else: transform_string += f"-t {transform} " command = f'antsApplyTransforms -i {in_file} {transform_string}-n GenericLabel -r {ref_file} -o {out_file}' rc = run_command(command) return out_file
def otsu_bias_cor(target, otsu_ref, out_name, b_value, mask=None, n_iter=200): import SimpleITK as sitk from rabies.utils import run_command command = 'ImageMath 3 null_mask.nii.gz ThresholdAtMean %s 0' % (otsu_ref) rc = run_command(command) command = 'ThresholdImage 3 %s otsu_weight.nii.gz Otsu 4' % (otsu_ref) rc = run_command(command) otsu_img = sitk.ReadImage('otsu_weight.nii.gz', sitk.sitkUInt8) otsu_array = sitk.GetArrayFromImage(otsu_img) if mask is not None: resampled_mask_img = sitk.ReadImage(mask, sitk.sitkUInt8) resampled_mask_array = sitk.GetArrayFromImage(resampled_mask_img) otsu_array = otsu_array * resampled_mask_array combined_mask = (otsu_array == 1.0) + (otsu_array == 2.0) mask_img = sitk.GetImageFromArray(combined_mask.astype('uint8'), isVector=False) mask_img.CopyInformation(otsu_img) sitk.WriteImage(mask_img, 'mask12.nii.gz') combined_mask = (otsu_array == 3.0) + (otsu_array == 4.0) mask_img = sitk.GetImageFromArray(combined_mask.astype('uint8'), isVector=False) mask_img.CopyInformation(otsu_img) sitk.WriteImage(mask_img, 'mask34.nii.gz') combined_mask = (otsu_array == 1.0) + (otsu_array == 2.0) + (otsu_array == 3.0) mask_img = sitk.GetImageFromArray(combined_mask.astype('uint8'), isVector=False) mask_img.CopyInformation(otsu_img) sitk.WriteImage(mask_img, 'mask123.nii.gz') combined_mask = (otsu_array == 2.0) + (otsu_array == 3.0) + (otsu_array == 4.0) mask_img = sitk.GetImageFromArray(combined_mask.astype('uint8'), isVector=False) mask_img.CopyInformation(otsu_img) sitk.WriteImage(mask_img, 'mask234.nii.gz') combined_mask = (otsu_array == 1.0) + (otsu_array == 2.0) + ( otsu_array == 3.0) + (otsu_array == 4.0) mask_img = sitk.GetImageFromArray(combined_mask.astype('uint8'), isVector=False) mask_img.CopyInformation(otsu_img) sitk.WriteImage(mask_img, 'mask1234.nii.gz') command = 'N4BiasFieldCorrection -d 3 -i %s -b %s -s 1 -c [%sx%sx%s,1e-4] -w mask12.nii.gz -x null_mask.nii.gz -o corrected1.nii.gz' % ( target, str(b_value), str(n_iter), str(n_iter), str(n_iter), ) rc = run_command(command) command = 'N4BiasFieldCorrection -d 3 -i corrected1.nii.gz -b %s -s 1 -c [%sx%sx%s,1e-4] -w mask34.nii.gz -x null_mask.nii.gz -o corrected2.nii.gz' % ( str(b_value), str(n_iter), str(n_iter), str(n_iter), ) rc = run_command(command) command = 'N4BiasFieldCorrection -d 3 -i corrected2.nii.gz -b %s -s 1 -c [%sx%sx%s,1e-4] -w mask123.nii.gz -x null_mask.nii.gz -o corrected3.nii.gz' % ( str(b_value), str(n_iter), str(n_iter), str(n_iter), ) rc = run_command(command) command = 'N4BiasFieldCorrection -d 3 -i corrected3.nii.gz -b %s -s 1 -c [%sx%sx%s,1e-4] -w mask234.nii.gz -x null_mask.nii.gz -o corrected4.nii.gz' % ( str(b_value), str(n_iter), str(n_iter), str(n_iter), ) rc = run_command(command) command = 'N4BiasFieldCorrection -d 3 -i corrected4.nii.gz -b %s -s 1 -c [%sx%sx%s,1e-4] -w mask1234.nii.gz -x null_mask.nii.gz -o %s' % ( str(b_value), str(n_iter), str(n_iter), str(n_iter), out_name, ) rc = run_command(command)
def _run_interface(self, runtime): import os import numpy as np import SimpleITK as sitk import pathlib # Better path manipulation filename_split = pathlib.Path( self.inputs.name_source).name.rsplit(".nii") from rabies.utils import run_command, resample_image_spacing from rabies.preprocess_pkg.registration import run_antsRegistration cwd = os.getcwd() resampled = '%s/%s_resampled.nii.gz' % (cwd, filename_split[0]) resampled_mask = '%s/%s_resampled_mask.nii.gz' % (cwd, filename_split[0]) biascor_EPI = '%s/%s_bias_cor.nii.gz' % ( cwd, filename_split[0], ) # resample to isotropic resolution based on lowest dimension input_ref_EPI_img = sitk.ReadImage(self.inputs.input_ref_EPI, self.inputs.rabies_data_type) dim = input_ref_EPI_img.GetSpacing() low_dim = np.asarray(dim).min() #sitk.WriteImage(resample_image_spacing( # input_ref_EPI_img, (low_dim, low_dim, low_dim)), resampled) # the -b will be rounded up to the nearest multiple of 10 of the image largest dimension largest_dim = (np.array(input_ref_EPI_img.GetSize()) * np.array(input_ref_EPI_img.GetSpacing())).max() b_value = int(np.ceil(largest_dim / 10) * 10) bias_cor_input = self.inputs.input_ref_EPI otsu_bias_cor(target=bias_cor_input, otsu_ref=bias_cor_input, out_name=cwd + '/corrected_iter1.nii.gz', b_value=b_value) otsu_bias_cor(target=bias_cor_input, otsu_ref=cwd + '/corrected_iter1.nii.gz', out_name=cwd + '/corrected_iter2.nii.gz', b_value=b_value) [affine, warp, inverse_warp, warped_image ] = run_antsRegistration(reg_method='Rigid', moving_image=cwd + '/corrected_iter2.nii.gz', fixed_image=self.inputs.anat, fixed_mask=self.inputs.anat_mask) command = 'antsApplyTransforms -d 3 -i %s -t [%s,1] -r %s -o %s -n GenericLabel' % ( self.inputs.anat_mask, affine, cwd + '/corrected_iter2.nii.gz', resampled_mask) rc = run_command(command) otsu_bias_cor(target=bias_cor_input, otsu_ref=cwd + '/corrected_iter2.nii.gz', out_name=cwd + '/final_otsu.nii.gz', b_value=b_value, mask=resampled_mask) # resample to anatomical image resolution dim = sitk.ReadImage(self.inputs.anat, self.inputs.rabies_data_type).GetSpacing() low_dim = np.asarray(dim).min() sitk.WriteImage( resample_image_spacing( sitk.ReadImage(cwd + '/final_otsu.nii.gz', self.inputs.rabies_data_type), (low_dim, low_dim, low_dim)), biascor_EPI) sitk.WriteImage( sitk.ReadImage(cwd + '/corrected_iter2.nii.gz', self.inputs.rabies_data_type), cwd + '/corrected_iter2.nii.gz') sitk.WriteImage( sitk.ReadImage(biascor_EPI, self.inputs.rabies_data_type), biascor_EPI) sitk.WriteImage( sitk.ReadImage(warped_image, self.inputs.rabies_data_type), warped_image) sitk.WriteImage( sitk.ReadImage(resampled_mask, self.inputs.rabies_data_type), resampled_mask) setattr(self, 'init_denoise', cwd + '/corrected_iter2.nii.gz') setattr(self, 'corrected_EPI', biascor_EPI) setattr(self, 'warped_EPI', warped_image) setattr(self, 'denoise_mask', resampled_mask) return runtime
def _run_interface(self, runtime): import os import numpy as np import SimpleITK as sitk from rabies.utils import resample_image_spacing, run_command import pathlib # Better path manipulation filename_split = pathlib.Path( self.inputs.name_source).name.rsplit(".nii") cwd = os.getcwd() # resample the anatomical image to the resolution of the provided template target_img = sitk.ReadImage(self.inputs.target_img, self.inputs.rabies_data_type) if not target_img.GetDimension() == 3: raise ValueError( f"Input image {self.inputs.target_img} is not 3-dimensional.") anat_dim = target_img.GetSpacing() template_image = sitk.ReadImage(self.inputs.anat_ref, self.inputs.rabies_data_type) template_dim = template_image.GetSpacing() if not (np.array(anat_dim) == np.array(template_dim)).sum() == 3: from nipype import logging log = logging.getLogger('nipype.workflow') log.debug( 'Anat image will be resampled to the template resolution.') resampled = resample_image_spacing(target_img, template_dim) target_img = f'{cwd}/{filename_split[0]}_resampled.nii.gz' sitk.WriteImage(resampled, target_img) else: target_img = self.inputs.target_img if self.inputs.inho_cor_method == 'disable': # outputs correspond to the inputs corrected = target_img init_denoise = corrected resampled_mask = self.inputs.anat_mask elif self.inputs.inho_cor_method in [ 'Rigid', 'Affine', 'SyN', 'no_reg' ]: corrected = f'{cwd}/{filename_split[0]}_inho_cor.nii.gz' if self.inputs.image_type == 'EPI': processing_script = 'EPI-preprocessing.sh' elif self.inputs.image_type == 'structural': processing_script = 'structural-preprocessing.sh' else: raise ValueError( f"Image type must be 'EPI' or 'structural', {self.inputs.image_type}" ) command = f'{processing_script} {target_img} {corrected} {self.inputs.anat_ref} {self.inputs.anat_mask} {self.inputs.inho_cor_method} {self.inputs.multistage_otsu} {str(self.inputs.otsu_threshold)}' rc = run_command(command) resampled_mask = corrected.split('.nii.gz')[0] + '_mask.nii.gz' init_denoise = corrected.split( '.nii.gz')[0] + '_init_denoise.nii.gz' elif self.inputs.inho_cor_method in ['N4_reg']: if self.inputs.image_type == 'EPI': bias_correction = OtsuEPIBiasCorrection( input_ref_EPI=target_img, anat=self.inputs.anat_ref, anat_mask=self.inputs.anat_mask, name_source=self.inputs.name_source, rabies_data_type=self.inputs.rabies_data_type) out = bias_correction.run() corrected = out.outputs.corrected_EPI resampled_mask = out.outputs.denoise_mask init_denoise = out.outputs.init_denoise elif self.inputs.image_type == 'structural': corrected = f'{cwd}/{filename_split[0]}_inho_cor.nii.gz' input_anat = target_img command = 'ImageMath 3 null_mask.nii.gz ThresholdAtMean %s 0' % ( input_anat) rc = run_command(command) command = 'ImageMath 3 thresh_mask.nii.gz ThresholdAtMean %s 1.2' % ( input_anat) rc = run_command(command) command = 'N4BiasFieldCorrection -d 3 -s 4 -i %s -b [20] -c [200x200x200,0.0] -w thresh_mask.nii.gz -x null_mask.nii.gz -o N4.nii.gz' % ( input_anat) rc = run_command(command) command = 'DenoiseImage -d 3 -i N4.nii.gz -o denoise.nii.gz' rc = run_command(command) from rabies.preprocess_pkg.registration import run_antsRegistration [affine, warp, inverse_warp, warped_image ] = run_antsRegistration(reg_method='Affine', moving_image=input_anat, fixed_image=self.inputs.anat_ref, fixed_mask=self.inputs.anat_mask) command = 'antsApplyTransforms -d 3 -i %s -t [%s,1] -r %s -o resampled_mask.nii.gz -n GenericLabel' % ( self.inputs.anat_mask, affine, input_anat) rc = run_command(command) command = 'N4BiasFieldCorrection -d 3 -s 2 -i %s -b [20] -c [200x200x200x200,0.0] -w resampled_mask.nii.gz -r 1 -x null_mask.nii.gz -o N4.nii.gz' % ( input_anat) rc = run_command(command) command = 'DenoiseImage -d 3 -i N4.nii.gz -o %s' % (corrected) rc = run_command(command) # resample image to specified data format sitk.WriteImage( sitk.ReadImage(corrected, self.inputs.rabies_data_type), corrected) init_denoise = cwd + '/denoise.nii.gz' resampled_mask = cwd + '/resampled_mask.nii.gz' else: raise ValueError( f"Image type must be 'EPI' or 'structural', {self.inputs.image_type}" ) else: raise ValueError("Wrong inho_cor_method.") # resample image to specified data format sitk.WriteImage( sitk.ReadImage(corrected, self.inputs.rabies_data_type), corrected) setattr(self, 'corrected', corrected) setattr(self, 'init_denoise', init_denoise) setattr(self, 'denoise_mask', resampled_mask) return runtime
def _run_interface(self, runtime): from nipype import logging log = logging.getLogger('nipype.workflow') in_nii = sitk.ReadImage(self.inputs.in_file, self.inputs.rabies_data_type) if not in_nii.GetDimension()==4: raise ValueError(f"Input image {self.inputs.in_file} is not 4-dimensional.") data_slice = sitk.GetArrayFromImage(in_nii)[:50, :, :, :] n_volumes_to_discard = _get_vols_to_discard(in_nii) filename_split = pathlib.Path(self.inputs.in_file).name.rsplit(".nii") out_ref_fname = os.path.abspath( f'{filename_split[0]}_bold_ref.nii.gz') if (not n_volumes_to_discard == 0) and self.inputs.detect_dummy: log.info("Detected "+str(n_volumes_to_discard) + " dummy scans. Taking the median of these volumes as reference EPI.") median_image_data = np.median( data_slice[:n_volumes_to_discard, :, :, :], axis=0) out_bold_file = os.path.abspath( f'{filename_split[0]}_cropped_dummy.nii.gz') img_array = sitk.GetArrayFromImage( in_nii)[n_volumes_to_discard:, :, :, :] image_4d = copyInfo_4DImage(sitk.GetImageFromArray( img_array, isVector=False), in_nii, in_nii) sitk.WriteImage(image_4d, out_bold_file) else: out_bold_file = self.inputs.in_file n_volumes_to_discard = 0 if self.inputs.detect_dummy: log.info( "Detected no dummy scans. Generating the ref EPI based on multiple volumes.") # if no dummy scans, will generate a median from a subset of max 100 # slices of the time series if in_nii.GetSize()[-1] > 100: slice_fname = os.path.abspath("slice.nii.gz") image_4d = copyInfo_4DImage(sitk.GetImageFromArray( data_slice[20:100, :, :, :], isVector=False), in_nii, in_nii) sitk.WriteImage(image_4d, slice_fname) median_fname = os.path.abspath("median.nii.gz") image_3d = copyInfo_3DImage(sitk.GetImageFromArray( np.median(data_slice[20:100, :, :, :], axis=0), isVector=False), in_nii) sitk.WriteImage(image_3d, median_fname) else: slice_fname = self.inputs.in_file median_fname = os.path.abspath("median.nii.gz") image_3d = copyInfo_3DImage(sitk.GetImageFromArray( np.median(data_slice, axis=0), isVector=False), in_nii) sitk.WriteImage(image_3d, median_fname) # First iteration to generate reference image. res = antsMotionCorr(in_file=slice_fname, ref_file=median_fname, prebuilt_option=self.inputs.HMC_option, transform_type='Rigid', second=False, rabies_data_type=self.inputs.rabies_data_type).run() median = np.median(sitk.GetArrayFromImage(sitk.ReadImage( res.outputs.mc_corrected_bold, self.inputs.rabies_data_type)), axis=0) tmp_median_fname = os.path.abspath("tmp_median.nii.gz") image_3d = copyInfo_3DImage( sitk.GetImageFromArray(median, isVector=False), in_nii) sitk.WriteImage(image_3d, tmp_median_fname) # Second iteration to generate reference image. res = antsMotionCorr(in_file=slice_fname, ref_file=tmp_median_fname, prebuilt_option=self.inputs.HMC_option, transform_type='Rigid', second=True, rabies_data_type=self.inputs.rabies_data_type).run() # evaluate a trimmed mean instead of a median, trimming the 5% extreme values from scipy import stats median_image_data = stats.trim_mean(sitk.GetArrayFromImage(sitk.ReadImage( res.outputs.mc_corrected_bold, self.inputs.rabies_data_type)), 0.05, axis=0) # median_image_data is a 3D array of the median image, so creates a new nii image # saves it image_3d = copyInfo_3DImage(sitk.GetImageFromArray( median_image_data, isVector=False), in_nii) sitk.WriteImage(image_3d, out_ref_fname) # denoise the resulting reference image through non-local mean denoising # Denoising reference image. command = f'DenoiseImage -d 3 -i {out_ref_fname} -o {out_ref_fname}' rc = run_command(command) setattr(self, 'ref_image', out_ref_fname) setattr(self, 'bold_file', out_bold_file) return runtime
def slice_timing_correction(in_file, tr='auto', tpattern='alt', rabies_data_type=8): ''' This functions applies slice-timing correction on the anterior-posterior slice acquisition direction. The input image, assumed to be in RAS orientation (accoring to nibabel; note that the nibabel reading of RAS corresponds to LPI for AFNI). The A and S dimensions will be swapped to apply AFNI's 3dTshift STC with a quintic fitting function, which can only be applied to the Z dimension of the data matrix. The corrected image is then re-created with proper axes and the corrected timeseries. **Inputs** in_file BOLD series NIfTI file in RAS orientation. ignore Number of non-steady-state volumes detected at beginning of ``bold_file`` tr TR of the BOLD image. tpattern Input to AFNI's 3dTshift -tpattern option, which specifies the directionality of slice acquisition, or whether it is sequential or interleaved. **Outputs** out_file Slice-timing corrected BOLD series NIfTI file ''' import os import SimpleITK as sitk import numpy as np if tpattern == "alt": tpattern = 'alt-z' elif tpattern == "seq": tpattern = 'seq-z' else: raise ValueError('Invalid --tpattern provided.') img = sitk.ReadImage(in_file, rabies_data_type) if tr == 'auto': tr = str(img.GetSpacing()[3]) + 's' else: tr = str(tr) + 's' # get image data img_array = sitk.GetArrayFromImage(img) shape = img_array.shape new_array = np.zeros([shape[0], shape[2], shape[1], shape[3]]) for i in range(shape[2]): new_array[:, i, :, :] = img_array[:, :, i, :] image_out = sitk.GetImageFromArray(new_array, isVector=False) sitk.WriteImage(image_out, 'STC_temp.nii.gz') command = f'3dTshift -quintic -prefix temp_tshift.nii.gz -tpattern {tpattern} -TR {tr} STC_temp.nii.gz' from rabies.utils import run_command rc = run_command(command) tshift_img = sitk.ReadImage('temp_tshift.nii.gz', rabies_data_type) tshift_array = sitk.GetArrayFromImage(tshift_img) new_array = np.zeros(shape) for i in range(shape[2]): new_array[:, :, i, :] = tshift_array[:, i, :, :] image_out = sitk.GetImageFromArray(new_array, isVector=False) from rabies.utils import copyInfo_4DImage image_out = copyInfo_4DImage(image_out, img, img) import pathlib # Better path manipulation filename_split = pathlib.Path(in_file).name.rsplit(".nii") out_file = os.path.abspath(filename_split[0] + '_tshift.nii.gz') sitk.WriteImage(image_out, out_file) return out_file
def _run_interface(self, runtime): import os import pandas as pd import pathlib import multiprocessing cwd = os.getcwd() template_folder = self.inputs.output_folder command = f'mkdir -p {template_folder}' rc = run_command(command) merged = flatten_list(list(self.inputs.moving_image_list)) # create a csv file of the input image list csv_path = cwd + '/commonspace_input_files.csv' df = pd.DataFrame(data=merged) df.to_csv(csv_path, header=False, sep=',', index=False) if self.inputs.masking: merged_masks = flatten_list(list(self.inputs.moving_mask_list)) mask_csv_path = cwd + '/commonspace_input_masks.csv' df = pd.DataFrame(data=merged_masks) df.to_csv(mask_csv_path, header=False, sep=',', index=False) masks = f'--masks {mask_csv_path}' else: merged_masks = ['NULL'] masks = '' unbiased_mask = 'NULL' if len(merged) == 1: from nipype import logging log = logging.getLogger('nipype.workflow') log.info( "Only a single scan was provided as input for commonspace registration. Commonspace registration " "won't be run, and the output template will be the input scan." ) # create an identity transform as a surrogate for the commonspace transforms import SimpleITK as sitk dimension = 3 identity = sitk.Transform(dimension, sitk.sitkIdentity) file = merged[0] mask = merged_masks[0] filename_template = pathlib.Path(file).name.rsplit(".nii")[0] transform_file = template_folder + filename_template + '_identity.mat' sitk.WriteTransform(identity, transform_file) setattr(self, 'unbiased_template', file) setattr(self, 'unbiased_mask', mask) setattr(self, 'affine_list', [transform_file]) setattr(self, 'warp_list', [transform_file]) setattr(self, 'inverse_warp_list', [transform_file]) setattr(self, 'warped_image_list', [file]) return runtime # convert nipype plugin spec to match QBATCH plugin = self.inputs.cluster_type if plugin == 'MultiProc' or plugin == 'Linear': cluster_type = 'local' num_threads = multiprocessing.cpu_count() elif plugin == 'SGE' or plugin == 'SGEGraph': cluster_type = 'sge' num_threads = 1 elif plugin == 'PBS': cluster_type = 'PBS' num_threads = 1 elif plugin == 'SLURM' or plugin == 'SLURMGraph': cluster_type = 'slurm' num_threads = 1 else: raise ValueError( "Plugin option must correspond to one of 'local', 'sge', 'pbs' or 'slurm'" ) command = f'QBATCH_SYSTEM={cluster_type} QBATCH_CORES={num_threads} modelbuild.sh \ --float --average-type median --gradient-step 0.25 --iterations 2 --starting-target {self.inputs.template_anat} --stages rigid,affine,nlin \ --output-dir {template_folder} --sharpen-type unsharp --block --debug {masks} {csv_path}' rc = run_command(command) unbiased_template = template_folder + \ '/nlin/1/average/template_sharpen_shapeupdate.nii.gz' # verify that all outputs are present if not os.path.isfile(unbiased_template): raise ValueError(unbiased_template + " doesn't exists.") if self.inputs.masking: unbiased_mask = template_folder + \ '/nlin/1/average/mask_shapeupdate.nii.gz' # verify that all outputs are present if not os.path.isfile(unbiased_mask): raise ValueError(unbiased_mask + " doesn't exists.") affine_list = [] warp_list = [] inverse_warp_list = [] warped_image_list = [] i = 0 for file in merged: file = str(file) filename_template = pathlib.Path(file).name.rsplit(".nii")[0] native_to_unbiased_inverse_warp = f'{template_folder}/nlin/1/transforms/{filename_template}_1InverseWarp.nii.gz' if not os.path.isfile(native_to_unbiased_inverse_warp): raise ValueError(native_to_unbiased_inverse_warp + " file doesn't exists.") native_to_unbiased_warp = f'{template_folder}/nlin/1/transforms/{filename_template}_1Warp.nii.gz' if not os.path.isfile(native_to_unbiased_warp): raise ValueError(native_to_unbiased_warp + " file doesn't exists.") native_to_unbiased_affine = f'{template_folder}/nlin/1/transforms/{filename_template}_0GenericAffine.mat' if not os.path.isfile(native_to_unbiased_affine): raise ValueError(native_to_unbiased_affine + " file doesn't exists.") warped_image = f'{template_folder}/nlin/1/resample/{filename_template}.nii.gz' if not os.path.isfile(warped_image): raise ValueError(warped_image + " file doesn't exists.") inverse_warp_list.append(native_to_unbiased_inverse_warp) warp_list.append(native_to_unbiased_warp) affine_list.append(native_to_unbiased_affine) warped_image_list.append(warped_image) i += 1 setattr(self, 'unbiased_template', unbiased_template) setattr(self, 'unbiased_mask', unbiased_mask) setattr(self, 'affine_list', affine_list) setattr(self, 'warp_list', warp_list) setattr(self, 'inverse_warp_list', inverse_warp_list) setattr(self, 'warped_image_list', warped_image_list) return runtime
def _run_interface(self, runtime): import numpy as np import os from rabies.utils import run_command import pathlib # Better path manipulation filename_split = pathlib.Path(self.inputs.bold).name.rsplit(".nii") # generate a .nii file representing the positioning or framewise displacement for each voxel within the brain_mask # first the voxelwise positioning map command = f'antsMotionCorrStats -m {self.inputs.movpar_file} -o {filename_split[0]}_pos_file.csv -x {self.inputs.brain_mask} \ -d {self.inputs.bold} -s {filename_split[0]}_pos_voxelwise.nii.gz' rc = run_command(command) pos_voxelwise = os.path.abspath(f"{filename_split[0]}_pos_file.nii.gz") # then the voxelwise framewise displacement map command = f'antsMotionCorrStats -m {self.inputs.movpar_file} -o {filename_split[0]}_FD_file.csv -x {self.inputs.brain_mask} \ -d {self.inputs.bold} -s {filename_split[0]}_FD_voxelwise.nii.gz -f 1' rc = run_command(command) FD_csv = os.path.abspath(f"{filename_split[0]}_FD_file.csv") FD_voxelwise = os.path.abspath(f"{filename_split[0]}_FD_file.nii.gz") confounds = [] csv_columns = [] WM_signal = extract_mask_trace(self.inputs.bold, self.inputs.WM_mask) confounds.append(WM_signal) csv_columns += ['WM_signal'] CSF_signal = extract_mask_trace(self.inputs.bold, self.inputs.CSF_mask) confounds.append(CSF_signal) csv_columns += ['CSF_signal'] vascular_signal = extract_mask_trace(self.inputs.bold, self.inputs.vascular_mask) confounds.append(vascular_signal) csv_columns += ['vascular_signal'] [aCompCor, num_comp ] = compute_aCompCor(self.inputs.bold, self.inputs.WM_mask, self.inputs.CSF_mask, method=self.inputs.aCompCor_method, rabies_data_type=self.inputs.rabies_data_type) for param in range(aCompCor.shape[1]): confounds.append(aCompCor[:, param]) comp_column = [] for comp in range(num_comp): comp_column.append('aCompCor' + str(comp + 1)) csv_columns += comp_column global_signal = extract_mask_trace(self.inputs.bold, self.inputs.brain_mask) confounds.append(global_signal) csv_columns += ['global_signal'] motion_24 = motion_24_params(self.inputs.movpar_file) for param in range(motion_24.shape[1]): confounds.append(motion_24[:, param]) csv_columns += [ 'mov1', 'mov2', 'mov3', 'rot1', 'rot2', 'rot3', 'mov1_der', 'mov2_der', 'mov3_der', 'rot1_der', 'rot2_der', 'rot3_der', 'mov1^2', 'mov2^2', 'mov3^2', 'rot1^2', 'rot2^2', 'rot3^2', 'mov1_der^2', 'mov2_der^2', 'mov3_der^2', 'rot1_der^2', 'rot2_der^2', 'rot3_der^2' ] confounds_csv = write_confound_csv(np.transpose(np.asarray(confounds)), csv_columns, filename_split[0]) setattr(self, 'FD_csv', FD_csv) setattr(self, 'FD_voxelwise', FD_voxelwise) setattr(self, 'pos_voxelwise', pos_voxelwise) setattr(self, 'confounds_csv', confounds_csv) return runtime