def get_avg_probability(self, mask_img, struct_idx): ''' Calculates the average probability of the atlas voxels that belong to mask_img. Parameters ---------- mask_img: nib.Nifti1Image struct_idx: int Index of the atlas' structure of interest. Returns ------- float number of the resulting average probability ''' mask_img = nifti2nipy(mask_img) mask_vol = mask_img.get_data() if self.image.shape[3] <= struct_idx: return 0 prob_img = nifti2nipy(self.image) prob_vol = prob_img[:, :, :, struct_idx] total = 0. mask_sum = np.sum(mask_vol) masked_probs = self._get_roi_mask_intersect(mask_img, struct_idx) return masked_probs/mask_sum if mask_sum > 0 else 0
def _resample(self, image, p_resample, type_img, image_ref=None): """ Resample at a fixed resolution to make sure the cord always appears with similar scale, regardless of the native resolution of the image. Assumes SAL orientation. :param image: Image() to resample :param p_resample: float: Resampling resolution in mm :param type_img: {'im', 'seg'}: If im, interpolate using spline. If seg, interpolate using linear then binarize. :param image_ref: Destination Image() to resample image to. :return: """ dict_interp = {'im': 'spline', 'seg': 'linear'} # Create nibabel object nii = Nifti1Image(image.data, image.hdr.get_best_affine()) img = nifti2nipy(nii) # If no reference image is provided, resample to specified resolution if image_ref is None: # Resample to px x p_resample x p_resample mm (orientation is SAL by convention in QC module) img_r = resample_nipy(img, new_size=str(image.dim[4]) + 'x' + str(p_resample) + 'x' + str(p_resample), new_size_type='mm', interpolation=dict_interp[type_img]) # Otherwise, resampling to the space of the reference image else: # Create nibabel object for reference image nii_ref = Nifti1Image(image_ref.data, image_ref.hdr.get_best_affine()) img_ref = nifti2nipy(nii_ref) img_r = resample_nipy(img, img_dest=img_ref, interpolation=dict_interp[type_img]) # If resampled image is a segmentation, binarize using threshold at 0.5 if type_img == 'seg': img_r_data = (img_r.get_data() > 0.5) * 1 else: img_r_data = img_r.get_data() nii_r = nipy2nifti(img_r) # Create Image objects image_r = Image(img_r_data, hdr=nii_r.header, dim=nii_r.header.get_data_shape()). \ change_orientation(image.orientation) return image_r
def _resample(self, image, p_resample, type, image_ref=None): """ Resample at a fixed resolution to make sure the cord always appears with similar scale, regardless of the native resolution of the image. Assumes SAL orientation. :param image: Image() to resample :param p_resample: float: Resampling resolution in mm :param type: {'im', 'seg'}: If im, interpolate using spline. If seg, interpolate using linear then binarize. :param image_ref: Destination Image() to resample image to. :return: """ # If no reference image is provided, create nipy object and resample using resample_nipy() if image_ref is None: dict_interp = {'im': 'spline', 'seg': 'linear'} # Create nibabel object nii = Nifti1Image(image.data, image.hdr.get_best_affine()) img = nifti2nipy(nii) # Resample to px x p_resample x p_resample mm (orientation is SAL by convention in QC module) img_r = resample_nipy(img, new_size=str(image.dim[4]) + 'x' + str(p_resample) + 'x' + str(p_resample), new_size_type='mm', interpolation=dict_interp[type]) # If segmentation, binarize using threshold at 0.5 if type == 'seg': img_r_data = (img_r.get_data() > 0.5) * 1 else: img_r_data = img_r.get_data() nii_r = nipy2nifti(img_r) # Create Image objects image_r = Image(img_r_data, hdr=nii_r.header, dim=nii_r.header.get_data_shape()). \ change_orientation(image.orientation) # If resampling to reference image, use Image() built-in resampling function to ref image else: dict_interp = {'im': 3, 'seg': 0} image_r = image.interpolate_from_image(image_ref, interpolation_mode=dict_interp[type], border='nearest') return image_r
def _get_roi_mask_intersect(self, mask_img, struct_idx): ''' Parameters ---------- mask_img: nib.Nifti1Image or nipy Image struct_idx: int Returns ------- The total sum of ''' mask_vol = np.array(mask_img.get_data()) if self.image.shape[3] <= struct_idx: return 0 lab_img = nifti2nipy(self.image) lab_vol = lab_img[:, :, :, struct_idx] masked_probs = 0.0 mask_cm = get_3D_coordmap(mask_img) lab_cm = get_3D_coordmap(lab_img) for idx in np.argwhere(mask_vol): voxc = mm_to_voxcoord(lab_cm, *voxcoord_to_mm(mask_cm, *idx)) prob = 100 if lab_vol[tuple(voxc)] == struct_idx else 0 masked_probs += prob * mask_vol[tuple(idx)] return masked_probs
def get_roi_overlap(self, mask_img, struct_idx): """ Calculates the percentage overlap of mask_img and the ROI given by struct_idx Parameters ---------- mask_img: nib.Nifti1Image struct_idx: int Index of the atlas' structure of interest. bin_prob: boolean True if it is to binarise ROI probability map, False otherwise Returns ------- float number of the resulting ROI overlap percentage """ mask_img = nifti2nipy(mask_img) #mask_vol = mask_img.get_data() if self.image.shape[3] <= struct_idx: return 0 log.info('Using ' + self.image.get_filename()) prob_vol = self.image.get_data()[:, :, :, struct_idx] prob_sum = np.sum(prob_vol) masked_probs = self._get_roi_mask_intersect(mask_img, struct_idx) return masked_probs/prob_sum if prob_sum > 0 else 0
def get_avg_probability(self, mask_img, struct_idx): """ Calculates the average probability of the atlas voxels that belong to mask_img. Parameters ---------- mask_img: nib.Nifti1Image struct_idx: int Index of the atlas' structure of interest. Returns ------- float number of the resulting average probability """ mask_img = nifti2nipy(mask_img) mask_vol = mask_img.get_data() if self.image.shape[3] <= struct_idx: return 0 log.info('Using ' + self.image.get_filename()) #lab_vol = self.image.get_data() mask_sum = np.sum(mask_vol) masked_probs = self._get_roi_mask_intersect(mask_img, struct_idx) return masked_probs/mask_sum if mask_sum > 0 else 0
def _get_roi_mask_intersect(self, mask_img, struct_idx): """ Parameters ---------- mask_img: nib.Nifti1Image or nipy Image struct_idx: int Returns ------- The total sum of """ mask_vol = np.array(mask_img.get_data()) if self.image.shape[3] <= struct_idx: return 0 lab_img = nifti2nipy(self.image) lab_vol = lab_img[:, :, :, struct_idx] masked_probs = 0.0 mask_cm = get_3D_coordmap(mask_img) lab_cm = get_3D_coordmap(lab_img) for idx in np.argwhere(mask_vol): voxc = mm_to_voxcoord(lab_cm, *voxcoord_to_mm(mask_cm, *idx)) prob = 100 if lab_vol[tuple(voxc)] == struct_idx else 0 masked_probs += prob * mask_vol[tuple(idx)] return masked_probs
def get_roi_overlap(self, mask_img, struct_idx): ''' Calculates the percentage overlap of mask_img and the ROI given by struct_idx Parameters ---------- mask_img: nib.Nifti1Image struct_idx: int Index of the atlas' structure of interest. bin_prob: boolean True if it is to binarise ROI probability map, False otherwise Returns ------- float number of the resulting ROI overlap percentage ''' mask_img = nifti2nipy(mask_img) mask_vol = mask_img.get_data() if self.image.shape[3] <= struct_idx: return 0 prob_vol = self.image.get_data()[:, :, :, struct_idx] prob_sum = np.sum(prob_vol) masked_probs = self._get_roi_mask_intersect(mask_img, struct_idx) return masked_probs/prob_sum if prob_sum > 0 else 0
def nifti2occiput(nif): """Convert Nifti image to occiput ImageND image. Args: nif (Nifti): Nifti image. Returns: ImageND: occiput image. """ nip = nifti2nipy(nif) return nipy2occiput(nip)
def fake_4dimage_nipy(): """ :return: an empty 4-d nipy Image """ nx, ny, nz, nt = 9, 9, 9, 3 # image dimension data = np.zeros((nx, ny, nz, nt), dtype=np.int8) data[4, 4, 4, 0] = 1. affine = np.eye(4) # Create nibabel object nii = nib.nifti1.Nifti1Image(data, affine) # return nipy object return nifti2nipy(nii)
def fake_3dimage_nipy_big(): """ :return: an empty 3-d nipy Image """ nx, ny, nz = 29, 39, 19 # image dimension data = np.zeros((nx, ny, nz), dtype=np.int8) data[14, 19, 9] = 1. affine = np.eye(4) # Create nibabel object nii = nib.nifti1.Nifti1Image(data, affine) # return nipy object return nifti2nipy(nii)
def segment_file(input_filename, output_filename, model_name, threshold, verbosity): """Segment a volume file. :param input_filename: the input filename. :param output_filename: the output filename. :param model_name: the name of model to use. :param threshold: threshold to apply in predictions (if None, no threshold will be applied) :param verbosity: the verbosity level. :return: the output filename. """ nii_original = nipy.load_image(input_filename) pixdim = nii_original.header["pixdim"][3] target_resample = "0.25x0.25x{:.5f}".format(pixdim) nii_resampled = nipy_resample.resample_image(nii_original, target_resample, 'mm', 'linear', verbosity) if (nii_resampled.shape[0] < 200) \ or (nii_resampled.shape[1] < 200): raise RuntimeError("Image too small ({}, {})".format( nii_resampled.shape[0], nii_resampled.shape[1])) nii_resampled = nipy2nifti(nii_resampled) pred_slices = segment_volume(nii_resampled, model_name, threshold) original_res = "{:.5f}x{:.5f}x{:.5f}".format( nii_original.header["pixdim"][1], nii_original.header["pixdim"][2], nii_original.header["pixdim"][3]) volume_affine = nii_resampled.affine volume_header = nii_resampled.header nii_segmentation = nib.Nifti1Image(pred_slices, volume_affine, volume_header) nii_segmentation = nifti2nipy(nii_segmentation) nii_resampled_original = nipy_resample.resample_image(nii_segmentation, original_res, 'mm', 'linear', verbosity) res_data = nii_resampled_original.get_data() # Threshold after resampling, only if specified if threshold is not None: res_data = threshold_predictions(res_data, 0.5) nipy.save_image(nii_resampled_original, output_filename) return output_filename
def motion_correction_nipy(in_file, out_path, mc_alg, extra_params={}): """ an attempt at motion correction using NiPy package. inputs: in_file: Full path to the resting-state scan. out_path: Full path to the (to be) output file. mc_alg: can be either 'nipy_spacerealign' or 'nipy_spacetimerealign' extra_params: extra parameters to SpaceRealign, SpaceTimeRealign, estimate return: the motion corrected image """ alg_dict = { 'nipy_spacerealign': (SpaceRealign, {}), 'nipy_spacetimerealign': (SpaceTimeRealign, { 'tr': 2, 'slice_times': 'asc_alt_2', 'slice_info': 2 }) } # format: {'function_name':(function, kwargs), ...} # processing starts here if type(in_file) in nib.all_image_classes: I = nifti2nipy(in_file) # assume Nifti1Image else: I = load_image(in_file) print 'source image loaded. ' # initialize the registration algorithm reg = AllFeatures(alg_dict[mc_alg][0], extra_params).run(I, **alg_dict[mc_alg][1]) # reg = alg_dict[mc_alg][0](I, **alg_dict[mc_alg][1]) # SpaceTimeRealign(I, tr=2, ...) print 'motion correction algorithm established. ' print 'estimating...' if USE_CACHE: mem = Memory("func_preproc_cache_2") mem.cache(AllFeatures(reg.estimate, extra_params).run)(refscan=None) # mem.cache(reg.estimate)(refscan=None) else: AllFeatures(reg.estimate, extra_params).run(refscan=None) # reg.estimate(refscan=None) print 'estimation complete. Writing to file...' result = reg.resample(0) if out_path: save_image(result, out_path) return nipy2nifti(result)
def segment_file(input_filename, output_filename, model_name, threshold, verbosity, use_tta): """Segment a volume file. :param input_filename: the input filename. :param output_filename: the output filename. :param model_name: the name of model to use. :param threshold: threshold to apply in predictions (if None, no threshold will be applied) :param verbosity: the verbosity level. :param use_tta: whether it should use TTA (test-time augmentation) or not. :return: the output filename. """ nii_original = nipy.load_image(input_filename) pixdim = nii_original.header["pixdim"][3] target_resample = "0.25x0.25x{:.5f}".format(pixdim) nii_resampled = resampling.resample_nipy(nii_original, new_size=target_resample, new_size_type='mm', interpolation='linear', verbose=verbosity) nii_resampled = nipy2nifti(nii_resampled) pred_slices = segment_volume(nii_resampled, model_name, threshold, use_tta) original_res = "{:.5f}x{:.5f}x{:.5f}".format( nii_original.header["pixdim"][1], nii_original.header["pixdim"][2], nii_original.header["pixdim"][3]) volume_affine = nii_resampled.affine volume_header = nii_resampled.header nii_segmentation = nib.Nifti1Image(pred_slices, volume_affine, volume_header) nii_segmentation = nifti2nipy(nii_segmentation) nii_resampled_original = resampling.resample_nipy(nii_segmentation, new_size=original_res, new_size_type='mm', interpolation='linear', verbose=verbosity) res_data = nii_resampled_original.get_data() # Threshold after resampling, only if specified if threshold is not None: res_data = threshold_predictions(res_data, 0.5) nipy.save_image(nii_resampled_original, output_filename) return output_filename
def get_3D_coordmap(img): ''' Gets a 3D CoordinateMap from img. Parameters ---------- img: nib.Nifti1Image or nipy Image Returns ------- nipy.core.reference.coordinate_map.CoordinateMap ''' if isinstance(img, nib.Nifti1Image): img = nifti2nipy(img) if img.ndim == 4: from nipy.core.reference.coordinate_map import drop_io_dim cm = drop_io_dim(img.coordmap, 3) else: cm = img.coordmap return cm
def resample_nipy(img, new_size=None, new_size_type=None, img_dest=None, interpolation='linear', verbose=1): """Resample a nipy image object based on a specified resampling factor. Can deal with 2d, 3d or 4d image objects. :param img: nipy Image. :param new_size: list of float: Resampling factor, final dimension or resolution, depending on new_size_type. TODO: implement as list. :param new_size_type: {'vox', 'factor', 'mm'}: Feature used for resampling. Examples: new_size=[128, 128, 90], new_size_type='vox' --> Resampling to a dimension of 128x128x90 voxels new_size=[2, 2, 2], new_size_type='factor' --> 2x isotropic upsampling new_size=[1, 1, 5], new_size_type='mm' --> Resampling to a resolution of 1x1x5 mm :param img_dest: Destination nipy Image to resample the input image to. In this case, new_size and new_size_type are ignored :param interpolation: {'nn', 'linear', 'spline'}. The interpolation type :return: The resampled nipy Image. """ # TODO: deal with 4d (and other dim) data # set interpolation method dict_interp = {'nn': 0, 'linear': 1, 'spline': 2} if img_dest is None: # Get dimensions of data p = img.header.get_zooms() shape = img.header.get_data_shape() # parse input argument new_size = new_size.split('x') if img.ndim == 4: new_size += [ '1' ] # needed because the code below is general, i.e., does not assume 3d input and uses img.shape # assert len(shape) == len(new_size) # compute new shape based on specific resampling method if new_size_type == 'vox': shape_r = tuple([int(new_size[i]) for i in range(img.ndim)]) elif new_size_type == 'factor': if len(new_size) == 1: # isotropic resampling new_size = tuple([new_size[0] for i in range(img.ndim)]) # compute new shape as: shape_r = shape * f shape_r = tuple([ int(np.round(shape[i] * float(new_size[i]))) for i in range(img.ndim) ]) elif new_size_type == 'mm': if len(new_size) == 1: # isotropic resampling new_size = tuple([new_size[0] for i in range(img.ndim)]) # compute new shape as: shape_r = shape * (p_r / p) shape_r = tuple([ int(np.round(shape[i] * float(p[i]) / float(new_size[i]))) for i in range(img.ndim) ]) else: sct.log.error('new_size_type is not recognized.') # Generate 3d affine transformation: R affine = img.affine[:4, :4] affine[3, :] = np.array([ 0, 0, 0, 1 ]) # satisfy to nifti convention. Otherwise it grabs the temporal sct.log.debug('Affine matrix: \n' + str(affine), verbose) R = np.eye(4) for i in range(3): R[i, i] = img.shape[i] / float(shape_r[i]) affine_r = np.dot(affine, R) reference = (shape_r, affine_r) else: # If reference is provided reference = img_dest R = None if img.ndim == 3: img_r = n_resample(img, transform=R, reference=reference, mov_voxel_coords=True, ref_voxel_coords=True, dtype='double', interp_order=dict_interp[interpolation], mode='nearest') elif img.ndim == 4: # TODO: Cover img_dest with 4D volumes # Import here instead of top of the file because this is an isolated case and nibabel takes time to import import nibabel as nib from nipy.io.nifti_ref import nifti2nipy, nipy2nifti data4d = np.zeros(shape_r) # Loop across 4th dimension and resample each 3d volume for it in range(img.shape[3]): # Create dummy 3d nipy image nii_tmp = nib.nifti1.Nifti1Image(img.get_data()[..., it], affine) img3d_r = n_resample(nifti2nipy(nii_tmp), transform=R, reference=(shape_r[:-1], affine_r), mov_voxel_coords=True, ref_voxel_coords=True, dtype='double', interp_order=dict_interp[interpolation], mode='nearest') data4d[..., it] = img3d_r.get_data() # Create 4d nipy Image nii4d = nib.nifti1.Nifti1Image(data4d, affine_r) # Convert to nipy object img_r = nifti2nipy(nii4d) # print "HOLA!!!" # print img.coordmap # print img_r.coordmap return img_r
def dummy_segmentation(size_arr=(256, 256, 256), pixdim=(1, 1, 1), dtype=np.float64, orientation='LPI', shape='rectangle', angle_RL=0, angle_AP=0, angle_IS=0, radius_RL=5.0, radius_AP=3.0, zeroslice=[], debug=False): """Create a dummy Image with a ellipse or ones running from top to bottom in the 3rd dimension, and rotate the image to make sure that compute_csa and compute_shape properly estimate the centerline angle. :param size_arr: tuple: (nx, ny, nz) :param pixdim: tuple: (px, py, pz) :param dtype: Numpy dtype. :param orientation: Orientation of the image. Default: LPI :param shape: {'rectangle', 'ellipse'} :param angle_RL: int: angle around RL axis (in deg) :param angle_AP: int: angle around AP axis (in deg) :param angle_IS: int: angle around IS axis (in deg) :param radius_RL: float: 1st radius. With a, b = 50.0, 30.0 (in mm), theoretical CSA of ellipse is 4712.4 :param radius_AP: float: 2nd radius :param zeroslice: list int: zero all slices listed in this param :param debug: Write temp files for debug :return: img: Image object """ # Initialization padding = 15 # Padding size (isotropic) to avoid edge effect during rotation # Create a 3d array, with dimensions corresponding to x: RL, y: AP, z: IS nx, ny, nz = [int(size_arr[i] * pixdim[i]) for i in range(3)] data = np.random.random((nx, ny, nz)) * 0. xx, yy = np.mgrid[:nx, :ny] # loop across slices and add object for iz in range(nz): if shape == 'rectangle': # theoretical CSA: (a*2+1)(b*2+1) data[:, :, iz] = ((abs(xx - nx / 2) <= radius_RL) & (abs(yy - ny / 2) <= radius_AP)) * 1 if shape == 'ellipse': data[:, :, iz] = (((xx - nx / 2) / radius_RL) ** 2 + ((yy - ny / 2) / radius_AP) ** 2 <= 1) * 1 # Pad to avoid edge effect during rotation data = np.pad(data, padding, 'reflect') # ROTATION ABOUT IS AXIS # rotate (in deg), and re-grid using linear interpolation data_rotIS = rotate(data, angle_IS, resize=False, center=None, order=1, mode='constant', cval=0, clip=False, preserve_range=False) # ROTATION ABOUT RL AXIS # Swap x-z axes (to make a rotation within y-z plane, because rotate will apply rotation on the first 2 dims) data_rotIS_swap = data_rotIS.swapaxes(0, 2) # rotate (in deg), and re-grid using linear interpolation data_rotIS_swap_rotRL = rotate(data_rotIS_swap, angle_RL, resize=False, center=None, order=1, mode='constant', cval=0, clip=False, preserve_range=False) # swap back data_rotIS_rotRL = data_rotIS_swap_rotRL.swapaxes(0, 2) # ROTATION ABOUT AP AXIS # Swap y-z axes (to make a rotation within x-z plane) data_rotIS_rotRL_swap = data_rotIS_rotRL.swapaxes(1, 2) # rotate (in deg), and re-grid using linear interpolation data_rotIS_rotRL_swap_rotAP = rotate(data_rotIS_rotRL_swap, angle_AP, resize=False, center=None, order=1, mode='constant', cval=0, clip=False, preserve_range=False) # swap back data_rot = data_rotIS_rotRL_swap_rotAP.swapaxes(1, 2) # Crop image (to remove padding) data_rot_crop = data_rot[padding:nx+padding, padding:ny+padding, padding:nz+padding] # Zero specified slices if zeroslice is not []: data_rot_crop[:, :, zeroslice] = 0 # Create nibabel object xform = np.eye(4) for i in range(3): xform[i][i] = 1 # in [mm] nii = nib.nifti1.Nifti1Image(data_rot_crop.astype('float32'), xform) # Create nipy object and resample to desired resolution nii_nipy = nifti2nipy(nii) nii_nipy_r = resample_nipy(nii_nipy, new_size='x'.join([str(i) for i in pixdim]), new_size_type='mm', interpolation='linear', dtype=dtype) nii_r = nipy2nifti(nii_nipy_r) # Create Image object. Default orientation is LPI. # For debugging add .save() at the end of the command below img = Image(nii_r.get_data(), hdr=nii_r.header, dim=nii_r.header.get_data_shape()) # Update orientation img.change_orientation(orientation) if debug: img.save('tmp_dummy_seg_'+datetime.now().strftime("%Y%m%d%H%M%S%f")+'.nii.gz') return img
def dummy_segmentation(size_arr=(256, 256, 256), pixdim=(1, 1, 1), dtype=np.float64, orientation='LPI', shape='rectangle', angle_RL=0, angle_AP=0, angle_IS=0, radius_RL=5.0, radius_AP=3.0, zeroslice=[], debug=False): """Create a dummy Image with a ellipse or ones running from top to bottom in the 3rd dimension, and rotate the image to make sure that compute_csa and compute_shape properly estimate the centerline angle. :param size_arr: tuple: (nx, ny, nz) :param pixdim: tuple: (px, py, pz) :param dtype: Numpy dtype. :param orientation: Orientation of the image. Default: LPI :param shape: {'rectangle', 'ellipse'} :param angle_RL: int: angle around RL axis (in deg) :param angle_AP: int: angle around AP axis (in deg) :param angle_IS: int: angle around IS axis (in deg) :param radius_RL: float: 1st radius. With a, b = 50.0, 30.0 (in mm), theoretical CSA of ellipse is 4712.4 :param radius_AP: float: 2nd radius :param zeroslice: list int: zero all slices listed in this param :param debug: Write temp files for debug :return: img: Image object """ # Initialization padding = 15 # Padding size (isotropic) to avoid edge effect during rotation # Create a 3d array, with dimensions corresponding to x: RL, y: AP, z: IS nx, ny, nz = [int(size_arr[i] * pixdim[i]) for i in range(3)] data = np.random.random((nx, ny, nz)) * 0. xx, yy = np.mgrid[:nx, :ny] # loop across slices and add object for iz in range(nz): if shape == 'rectangle': # theoretical CSA: (a*2+1)(b*2+1) data[:, :, iz] = ((abs(xx - nx / 2) <= radius_RL) & (abs(yy - ny / 2) <= radius_AP)) * 1 if shape == 'ellipse': data[:, :, iz] = (((xx - nx / 2) / radius_RL)**2 + ((yy - ny / 2) / radius_AP)**2 <= 1) * 1 # Pad to avoid edge effect during rotation data = np.pad(data, padding, 'reflect') # ROTATION ABOUT IS AXIS # rotate (in deg), and re-grid using linear interpolation data_rotIS = rotate(data, angle_IS, resize=False, center=None, order=1, mode='constant', cval=0, clip=False, preserve_range=False) # ROTATION ABOUT RL AXIS # Swap x-z axes (to make a rotation within y-z plane, because rotate will apply rotation on the first 2 dims) data_rotIS_swap = data_rotIS.swapaxes(0, 2) # rotate (in deg), and re-grid using linear interpolation data_rotIS_swap_rotRL = rotate(data_rotIS_swap, angle_RL, resize=False, center=None, order=1, mode='constant', cval=0, clip=False, preserve_range=False) # swap back data_rotIS_rotRL = data_rotIS_swap_rotRL.swapaxes(0, 2) # ROTATION ABOUT AP AXIS # Swap y-z axes (to make a rotation within x-z plane) data_rotIS_rotRL_swap = data_rotIS_rotRL.swapaxes(1, 2) # rotate (in deg), and re-grid using linear interpolation data_rotIS_rotRL_swap_rotAP = rotate(data_rotIS_rotRL_swap, angle_AP, resize=False, center=None, order=1, mode='constant', cval=0, clip=False, preserve_range=False) # swap back data_rot = data_rotIS_rotRL_swap_rotAP.swapaxes(1, 2) # Crop image (to remove padding) data_rot_crop = data_rot[padding:nx + padding, padding:ny + padding, padding:nz + padding] # Zero specified slices if zeroslice is not []: data_rot_crop[:, :, zeroslice] = 0 # Create nibabel object xform = np.eye(4) for i in range(3): xform[i][i] = 1 # in [mm] nii = nib.nifti1.Nifti1Image(data_rot_crop.astype('float32'), xform) # Create nipy object and resample to desired resolution nii_nipy = nifti2nipy(nii) nii_nipy_r = resample_nipy(nii_nipy, new_size='x'.join([str(i) for i in pixdim]), new_size_type='mm', interpolation='linear', dtype=dtype) nii_r = nipy2nifti(nii_nipy_r) # Create Image object. Default orientation is LPI. # For debugging add .save() at the end of the command below img = Image(nii_r.get_data(), hdr=nii_r.header, dim=nii_r.header.get_data_shape()) # Update orientation img.change_orientation(orientation) if debug: img.save('tmp_dummy_seg_' + datetime.now().strftime("%Y%m%d%H%M%S%f") + '.nii.gz') return img