def get_Tmean(in_file_path): with tempfile.TemporaryDirectory() as tmpdirname: # tMean_path = f'{tmpdirname}/tMean.nii.gz' tMean_path = 'tMean.nii.gz' mean_image = MeanImage(in_file=in_file_path, dimension='T', out_file=tMean_path) mean_image.run() mean_image = nib.load(tMean_path).dataobj return mean_image
def get_f_means(self): data_selection_func = self.data_selection.loc[ self.data_selection.datatype == 'func'] for _, elem in tqdm(data_selection_func.iterrows(), total=len(data_selection_func), postfix='Tmean'): in_path = elem.path out_dir = self.tmean_dir / f'sub-{elem.subject}' / f'ses-{elem.session}' / elem.datatype out_dir.mkdir(parents=True, exist_ok=True) out_path = out_dir / elem.path.split('/')[-1] mean_image = MeanImage(in_file=in_path, dimension='T', out_file=out_path) mean_image.run() self.data_selection.loc[self.data_selection.path == elem.path, 'tmean_path'] = out_path
st = map(float, f) print st realigner.inputs.slice_times = st else: # ascend alternate every 2nd slice, starting at 2nd slice realigner.inputs.slice_times = 'asc_alt_2_1' realigner.inputs.slice_info = 2 realigner.run() # Step#3 get T-mean of rs image after realignment fslmaths = MeanImage() fslmaths.inputs.in_file = 'corr_rest_roi.nii.gz' fslmaths.inputs.out_file = 'mean_corr_rest_roi.nii.gz' fslmaths.inputs.dimension = 'T' fslmaths.run() # Step#4 get binary mask & skull stripped imag img_StMoco = os.path.abspath('corr_rest_roi.nii.gz') btr = fsl.BET() btr.inputs.in_file = img_StMoco btr.inputs.mask = True btr.run() # Step#5 tsnr calculation on realigned image tsnr = misc.TSNR() tsnr.inputs.in_file = 'corr_rest_roi.nii.gz' tsnr.inputs.mean_file = 'corr_rest_roi_tsnr_mean.nii.gz' tsnr.inputs.tsnr_file = 'corr_rest_roi_tsnr.nii.gz' tsnr.inputs.stddev_file = 'corr_rest_roi_tsnr_stddev.nii.gz'
def gnl_correction(input, file_bash, file_coeff, python3_env, python2_env, path_output, cleanup=True): """ The purpose of the following function is to correct for gradient nonlinearities. A corrected file is written using spline interpolation. The function needs FSL to be included in the search path. Inputs: *input: filename of input image. *file_bash: filename of bash script which calls the gradient unwarping toolbox. *file_coeff: filename of siemens coefficient file. *python3_env: name of python3 virtual environment. *python2_env: name of python2 virtual environment. *path_output: path where output is written. *cleanup: delete intermediate files. created by Daniel Haenelt Date created: 10-01-2020 Last modified: 10-01-2020 """ import os import shutil as sh import numpy as np import nibabel as nb from nipype.interfaces.fsl import ConvertWarp, Merge from nipype.interfaces.fsl.maths import MeanImage from nipype.interfaces.fsl.preprocess import ApplyWarp from lib.io.get_filename import get_filename from lib.cmap.generate_coordinate_mapping import generate_coordinate_mapping # get fileparts path, name, ext = get_filename(input) # make subfolders path_grad = os.path.join(path_output, "grad") if not os.path.exists(path_grad): os.makedirs(path_grad) # parse arguments file_output = os.path.join(path_output, name + "_gnlcorr" + ext) file_warp = os.path.join(path_grad, "warp.nii.gz") file_jacobian = os.path.join(path_grad, "warp_jacobian.nii.gz") # run gradient unwarp os.system("bash " + file_bash + \ " " + python3_env + \ " " + python2_env + \ " " + path_grad + \ " " + input + \ " trilinear.nii.gz" + \ " " + file_coeff) # now create an appropriate warpfield output (relative convention) convertwarp = ConvertWarp() convertwarp.inputs.reference = os.path.join(path_grad, "trilinear.nii.gz") convertwarp.inputs.warp1 = os.path.join(path_grad, "fullWarp_abs.nii.gz") convertwarp.inputs.abswarp = True convertwarp.inputs.out_relwarp = True convertwarp.inputs.out_file = file_warp convertwarp.inputs.args = "--jacobian=" + file_jacobian convertwarp.run() # convertwarp's jacobian output has 8 frames, each combination of one-sided differences, so average them calcmean = MeanImage() calcmean.inputs.in_file = file_jacobian calcmean.inputs.dimension = "T" calcmean.inputs.out_file = file_jacobian calcmean.run() # apply warp to first volume applywarp = ApplyWarp() applywarp.inputs.in_file = input applywarp.inputs.ref_file = input applywarp.inputs.relwarp = True applywarp.inputs.field_file = file_warp applywarp.inputs.output_type = "NIFTI" applywarp.inputs.out_file = file_output applywarp.inputs.interp = "spline" applywarp.run() # normalise warped output image to initial intensity range data_img = nb.load(input) data_array = data_img.get_fdata() max_data = np.max(data_array) min_data = np.min(data_array) data_img = nb.load(file_output) data_array = data_img.get_fdata() data_array[data_array < min_data] = 0 data_array[data_array > max_data] = max_data output = nb.Nifti1Image(data_array, data_img.affine, data_img.header) nb.save(output, file_output) # calculate gradient deviations os.system("calc_grad_perc_dev" + \ " --fullwarp=" + file_warp + \ " -o " + os.path.join(path_grad,"grad_dev")) # merge directions merger = Merge() merger.inputs.in_files = [ os.path.join(path_grad, "grad_dev_x.nii.gz"), os.path.join(path_grad, "grad_dev_y.nii.gz"), os.path.join(path_grad, "grad_dev_z.nii.gz") ] merger.inputs.dimension = 't' merger.inputs.merged_file = os.path.join(path_grad, "grad_dev.nii.gz") merger.run() # convert from % deviation to absolute data_img = nb.load(os.path.join(path_grad, "grad_dev.nii.gz")) data_array = data_img.get_fdata() data_array = data_array / 100 output = nb.Nifti1Image(data_array, data_img.affine, data_img.header) nb.save(output, os.path.join(path_grad, "grad_dev.nii.gz")) # warp coordinate mapping generate_coordinate_mapping(input, 0, path_grad, suffix="gnl", time=False, write_output=True) applywarp = ApplyWarp() applywarp.inputs.in_file = os.path.join(path_grad, "cmap_gnl.nii") applywarp.inputs.ref_file = input applywarp.inputs.relwarp = True applywarp.inputs.field_file = file_warp applywarp.inputs.out_file = os.path.join(path_grad, "cmap_gnl.nii") applywarp.inputs.interp = "trilinear" applywarp.inputs.output_type = "NIFTI" applywarp.run() # clean intermediate files if cleanup: sh.rmtree(path_grad, ignore_errors=True)
def predict_mask(in_file: str, masking_config_path=None, input_type: str = 'anat'): """ The image is first resampled into the resolution of the template space, which has a voxel size of 0.2 × 0.2 × 0.2. This is done with the Resample command from the FSL library which is an analysis tool for FMRI, MRI and DTI brain imaging data. Then, the image is preprocessed using the preprocessing methods of the model class. The predictions of the model are reconstructed to a 3D mask via the command Nifit1Image from nibabel. This is done using the same affine space as the input image. The latter is then reshaped into the original shape inverting the preprocessing step, either with the opencv resize method or by cropping. Additionally, the binary mask is resampled into its original affine space, before being multiplied with the brain image to extract the ROI. Parameters ---------- in_file : str path to the file that is to be masked masking_config_path : str path to the masking config. The masking config is a json file. All parameters have default values that will be set in the "get_masking_opts" method. The masking config may contain following parameters (if any of them is not given in the config, the default value will be taken from mlebe/masking/config/default_schema.json): model_folder_path: str The path to the pretrained model. If not set the default mlebe model will be selected. use_cuda: bool boolean indicating if cuda will be used for the masking visualisation_path: str if set, the masking predictions will be saved to this destination. crop_values: if set, the input bids images will be cropped with given values in the x-y dimensions. bias_field_correction: dict If set, the input image will be bias corrected before given as input to the model. The parameter of the bias correction can be given as a dictionary. The default values can be found in the default_schema.json config. input_type : str either 'func' for CDV or BOLD contrast or 'anat' for T2 contrast Returns ------- resampled_mask_path : str path to the mask nii_path_masked : str path to the masked image """ import os from os import path from pathlib import Path import ants import nibabel as nib import numpy as np import pandas as pd from ants.registration import resample_image from nipype.interfaces.fsl.maths import MeanImage from mlebe import log from mlebe.masking.utils import get_mask, get_mlebe_models, get_biascorrect_opts_defaults from mlebe.masking.utils import remove_outliers, get_masking_opts, crop_bids_image, \ reconstruct_image, pad_to_shape, get_model_config log.info( f'Starting masking of {in_file} with config {masking_config_path}.') masking_opts = get_masking_opts(masking_config_path, input_type) if masking_opts['masked_dir']: masked_dir = masking_opts['masked_dir'] df_selection = pd.read_csv(f'{masked_dir}/data_selection.csv') df_selection = df_selection.loc[df_selection.path.str.endswith( in_file)] nii_path_masked = df_selection.masked_path.item() resampled_mask_path = df_selection.mask_path.item() assert nii_path_masked, f'nii_path_masked not found for {in_file}' assert resampled_mask_path, f'nii_path_masked not found for {resampled_mask_path}' assert Path(nii_path_masked).exists( ), f'nii_path_masked {nii_path_masked} does not exist.' assert Path(resampled_mask_path).exists( ), f'resampled_mask_path {resampled_mask_path} does not exist.' return nii_path_masked, [resampled_mask_path], resampled_mask_path if 'model_folder_path' not in masking_opts or not masking_opts[ 'model_folder_path']: # if no model_folder_path is given in the config, the default models are selected. masking_opts['model_folder_path'] = get_mlebe_models(input_type) model_config = get_model_config(masking_opts) input = in_file if input_type == 'func': tMean_path = 'tMean_.nii.gz' mean_image = MeanImage(in_file=input, dimension='T', out_file=tMean_path) mean_image.run() # command = 'fslmaths {a} -Tmean {b}'.format(a=input, b=tMean_path) # log.info(f'Executing command "{command}"') # os.system(command) assert Path(tMean_path).exists() input = tMean_path resampled_path = 'resampled_input.nii.gz' resampled_nii_path = path.abspath(path.expanduser(resampled_path)) if masking_opts['testing']: resampled_nii = resample_image(ants.image_read(str(input)), (0.2, 0.2, 0.2), False) nib.save(resampled_nii, resampled_nii_path) else: resample_cmd = 'ResampleImage 3 {input} '.format( input=input) + resampled_nii_path + ' 0.2x0.2x0.2' os.system(resample_cmd) log.info(f'Resample image with "{resample_cmd}"') if 'crop_values' in masking_opts and masking_opts['crop_values']: crop_bids_image(resampled_nii_path, masking_opts['crop_values']) """ Bias correction """ if 'bias_field_correction' in masking_opts and masking_opts[ 'bias_field_correction']: bias_correction_config = get_biascorrect_opts_defaults(masking_opts) bias_corrected_path = path.abspath( path.expanduser('corrected_input.nii.gz')) if masking_opts['testing']: convergence_args = bias_correction_config['convergence'].strip( '][').split(', ') iters = [int(elem) for elem in convergence_args[0].split('x')] tol = float(convergence_args[1]) bias_corrected = ants.n4_bias_field_correction( ants.image_read(resampled_nii_path), bias_correction_config['bspline_fitting'], convergence={ 'iters': iters, 'tol': tol }, shrink_factor=bias_correction_config['shrink_factor']) nib.save(bias_corrected, bias_corrected_path) else: command = 'N4BiasFieldCorrection --bspline-fitting {} -d 3 --input-image {} --convergence {} --output {} ' \ '--shrink-factor {}'.format( bias_correction_config['bspline_fitting'], resampled_nii_path, bias_correction_config['convergence'], bias_corrected_path, bias_correction_config['shrink_factor']) os.system(command) log.info(f'Apply bias correction with "{command}"') else: bias_corrected_path = resampled_nii_path image = nib.load(bias_corrected_path) in_file_data = image.get_data() """ Getting the mask """ ori_shape = np.moveaxis(in_file_data, 2, 0).shape in_file_data, mask_pred, network_input = get_mask( model_config, in_file_data, ori_shape, use_cuda=masking_opts['use_cuda']) mask_pred = remove_outliers(mask_pred) if 'visualisation_path' in masking_opts and masking_opts[ 'visualisation_path']: log.info(f'visualisation_path is {masking_opts["visualisation_path"]}') save_visualisation(masking_opts, in_file, network_input, mask_pred) """ Reconstruct to original image size """ resized = reconstruct_image(ori_shape, mask_pred) resized_path = 'resized_mask.nii.gz' resized_path = path.abspath(path.expanduser(resized_path)) resized_mask = nib.Nifti1Image(resized, image.affine, image.header) nib.save(resized_mask, resized_path) # get voxel sizes from input input_image = nib.load(input) input_img_affine = input_image.affine voxel_sizes = nib.affines.voxel_sizes(input_img_affine) resampled_mask_path = 'resampled_mask.nii.gz' resampled_mask_path = path.abspath(path.expanduser(resampled_mask_path)) if masking_opts['testing']: resized_mask = ants.image_read(resized_path) resampled_mask_data = resample_image( resized_mask, (voxel_sizes[0], voxel_sizes[1], voxel_sizes[2]), False, 1) else: resample_cmd = 'ResampleImage 3 {input} '.format( input=resized_path ) + ' ' + resampled_mask_path + ' {x}x{y}x{z} '.format( x=voxel_sizes[0], y=voxel_sizes[1], z=voxel_sizes[2]) + ' 0 1' log.info(f'Resample image with "{resample_cmd}"') os.system(resample_cmd) resampled_mask = nib.load(resampled_mask_path) resampled_mask_data = resampled_mask.get_data() input_image_data = input_image.get_data() if resampled_mask_data.shape != input_image_data.shape: resampled_mask_data = pad_to_shape(resampled_mask_data, input_image_data) if masking_opts['testing']: nib.save(resampled_mask_data, resampled_mask_path) resampled_mask_data = resampled_mask_data.numpy() else: nib.save( nib.Nifti1Image(resampled_mask_data, input_image.affine, input_image.header), resampled_mask_path) """ Masking of the input image """ log.info('Masking the input image with the generated mask.') masked_image = np.multiply(resampled_mask_data, input_image_data).astype( 'float32' ) # nibabel gives a non-helpful error if trying to save data that has dtype float64 nii_path_masked = 'masked_output.nii.gz' nii_path_masked = path.abspath(path.expanduser(nii_path_masked)) masked_image = nib.Nifti1Image(masked_image, input_image.affine, input_image.header) nib.save(masked_image, nii_path_masked) log.info(f'Masking of input image {in_file} finished successfully.') # f/s_biascorrect takes a list as input for the mask while biascorrect takes directly the path return nii_path_masked, [resampled_mask_path], resampled_mask_path
# merge all gray matter probability map across subjects gm_merged = os.path.join(outdir, 'gm_prob_merged.nii.gz') merger = Merge() merger.inputs.in_files = gm_file_list merger.inputs.dimension = 't' merger.inputs.output_type = 'NIFTI_GZ' merger.inputs.merged_file = gm_merged merger.run() # get the average of the merged map gm_merged_ave = gm_merged[:-7] + '_mean.nii.gz' print('...average GM map: ', gm_merged_ave) tmean = MeanImage() tmean.inputs.in_file = gm_merged tmean.inputs.dimension = 'T' tmean.inputs.output_type = 'NIFTI_GZ' tmean.inputs.out_file = gm_merged_ave tmean.run() # binarize the group level gray matter map gm_mask = gm_merged_ave[:-7] + '_mask_060.nii.gz' binarize = MathsCommand() binarize.inputs.args = '-thr 0.60 -bin' binarize.inputs.in_file = gm_merged_ave binarize.inputs.out_file = gm_mask binarize.run()