def compute(self): fname_data = self.fmri # # create temporary folder # sct.printv('\nCreate temporary folder...', self.param.verbose) # path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S/") # status, output = sct.run('mkdir '+path_tmp, self.param.verbose) # # motion correct the fmri data # # sct.printv('\nMotion correct the fMRI data...', self.param.verbose, 'normal') # path_fmri, fname_fmri, ext_fmri = sct.extract_fname(self.fmri) # fname_fmri_moco = fname_fmri # # print sct.slash_at_the_end(path_fmri) + fname_fmri # # sct.run('mcflirt -in ' + sct.slash_at_the_end(path_fmri, 1) + fname_fmri + ' -out ' + fname_fmri_moco) # compute tsnr sct.printv('\nCompute the tSNR...', self.param.verbose, 'normal') fname_data_mean = sct.add_suffix(fname_data, '_mean') sct.run('fslmaths ' + fname_data + ' -Tmean ' + fname_data_mean) fname_data_std = sct.add_suffix(fname_data, '_std') sct.run('fslmaths ' + fname_data + ' -Tstd ' + fname_data_std) fname_tsnr = sct.add_suffix(fname_data, '_tsnr') sct.run('fslmaths ' + fname_data_mean + ' -div ' + fname_data_std + ' ' + fname_tsnr) # Remove temp files sct.printv('\nRemove temporary files...', self.param.verbose, 'normal') sct.run('rm ' + fname_data_std) # to view results sct.printv('\nDone! To view results, type:', self.param.verbose, 'normal') sct.printv('fslview '+fname_tsnr+' &\n', self.param.verbose, 'info')
def _call_viewer_centerline(fname_in, interslice_gap=20.0): from spinalcordtoolbox.gui.base import AnatomicalParams from spinalcordtoolbox.gui.centerline import launch_centerline_dialog im_data = Image(fname_in) # Get the number of slice along the (IS) axis im_tmp = msct_image.change_orientation(im_data, 'RPI') _, _, nz, _, _, _, pz, _ = im_tmp.dim del im_tmp params = AnatomicalParams() # setting maximum number of points to a reasonable value params.num_points = np.ceil(nz * pz / interslice_gap) + 2 params.interval_in_mm = interslice_gap params.starting_slice = 'top' im_mask_viewer = msct_image.zeros_like(im_data) im_mask_viewer.absolutepath = sct.add_suffix(fname_in, '_viewer') controller = launch_centerline_dialog(im_data, im_mask_viewer, params) fname_labels_viewer = sct.add_suffix(fname_in, '_viewer') if not controller.saved: sct.log.error( 'The viewer has been closed before entering all manual points. Please try again.' ) sys.exit(1) # save labels controller.as_niftii(fname_labels_viewer) return fname_labels_viewer
def pad_im(fname_im, nx_full, ny_full, nz_full, xi, xf, yi, yf, zi, zf): fname_im_pad = sct.add_suffix(fname_im, "_pad") pad_xi = str(xi) pad_xf = str(nx_full - (xf + 1)) pad_yi = str(yi) pad_yf = str(ny_full - (yf + 1)) pad_zi = str(zi) pad_zf = str(nz_full - (zf + 1)) pad = ",".join([pad_xi, pad_xf, pad_yi, pad_yf, pad_zi, pad_zf]) if len(Image(fname_im).data.shape) == 5: status, output = sct.run("sct_image -i " + fname_im + " -mcs") s = "Created file(s):\n-->" output_fnames = output[output.find(s) + len(s) :].split("\n")[0].split("'") fname_comp_list = [output_fnames[i] for i in range(1, len(output_fnames), 2)] fname_comp_pad_list = [] for fname_comp in fname_comp_list: fname_comp_pad = sct.add_suffix(fname_comp, "_pad") sct.run("sct_image -i " + fname_comp + " -pad-asym " + pad + " -o " + fname_comp_pad) fname_comp_pad_list.append(fname_comp_pad) components = ",".join(fname_comp_pad_list) sct.run("sct_image -i " + components + " -omc -o " + fname_im_pad) sct.check_file_exist(fname_im_pad, verbose=1) else: sct.run("sct_image -i " + fname_im + " -pad-asym " + pad + " -o " + fname_im_pad) return fname_im_pad
def pad_im(fname_im, nx_full, ny_full, nz_full, xi, xf, yi, yf, zi, zf): fname_im_pad = sct.add_suffix(fname_im, '_pad') pad_xi = str(xi) pad_xf = str(nx_full - (xf + 1)) pad_yi = str(yi) pad_yf = str(ny_full - (yf + 1)) pad_zi = str(zi) pad_zf = str(nz_full - (zf + 1)) pad = ','.join([pad_xi, pad_xf, pad_yi, pad_yf, pad_zi, pad_zf]) if len(Image(fname_im).data.shape) == 5: status, output = sct.run('sct_image -i ' + fname_im + ' -mcs') s = 'Created file(s):\n-->' output_fnames = output[output.find(s) + len(s):].split('\n')[0].split("'") fname_comp_list = [ output_fnames[i] for i in range(1, len(output_fnames), 2) ] fname_comp_pad_list = [] for fname_comp in fname_comp_list: fname_comp_pad = sct.add_suffix(fname_comp, '_pad') sct.run('sct_image -i ' + fname_comp + ' -pad-asym ' + pad + ' -o ' + fname_comp_pad) fname_comp_pad_list.append(fname_comp_pad) components = ','.join(fname_comp_pad_list) sct.run('sct_image -i ' + components + ' -omc -o ' + fname_im_pad) sct.check_file_exist(fname_im_pad, verbose=1) else: sct.run('sct_image -i ' + fname_im + ' -pad-asym ' + pad + ' -o ' + fname_im_pad) return fname_im_pad
def reorient_data(self): for f in self.fname_metric_lst: os.rename(self.fname_metric_lst[f], add_suffix(''.join(extract_fname(self.param.fname_im)[1:]), '_2reorient')) im = Image(add_suffix(''.join(extract_fname(self.param.fname_im)[1:]), '_2reorient')) im = set_orientation(im, self.orientation_im) im.setFileName(self.fname_metric_lst[f]) im.save()
def segment(self): self.copy_data_to_tmp() # go to tmp directory os.chdir(self.tmp_dir) # load model self.model.load_model() self.target_im, self.info_preprocessing = pre_processing(self.param_seg.fname_im, self.param_seg.fname_seg, self.param_seg.fname_level, new_res=self.param_data.axial_res, square_size_size_mm=self.param_data.square_size_size_mm, denoising=self.param_data.denoising, verbose=self.param.verbose, rm_tmp=self.param.rm_tmp) printv('\nRegister target image to model data...', self.param.verbose, 'normal') # register target image to model dictionary space path_warp = self.register_target() if self.param_data.normalization: printv('\nNormalize intensity of target image...', self.param.verbose, 'normal') self.normalize_target() printv('\nProject target image into the model reduced space...', self.param.verbose, 'normal') self.project_target() printv('\nCompute similarities between target slices and model slices using model reduced space...', self.param.verbose, 'normal') list_dic_indexes_by_slice = self.compute_similarities() printv('\nLabel fusion of model slices most similar to target slices...', self.param.verbose, 'normal') self.label_fusion(list_dic_indexes_by_slice) printv('\nWarp back segmentation into image space...', self.param.verbose, 'normal') self.warp_back_seg(path_warp) printv('\nPost-processing...', self.param.verbose, 'normal') self.im_res_gmseg, self.im_res_wmseg = self.post_processing() if (self.param_seg.path_results != './') and (not os.path.exists('../' + self.param_seg.path_results)): # create output folder printv('\nCreate output folder ...', self.param.verbose, 'normal') os.chdir('..') os.mkdir(self.param_seg.path_results) os.chdir(self.tmp_dir) if self.param_seg.fname_manual_gmseg is not None: # compute validation metrics printv('\nCompute validation metrics...', self.param.verbose, 'normal') self.validation() if self.param_seg.ratio is not '0': printv('\nCompute GM/WM CSA ratio...', self.param.verbose, 'normal') self.compute_ratio() # go back to original directory os.chdir('..') printv('\nSave resulting GM and WM segmentations...', self.param.verbose, 'normal') self.fname_res_gmseg = self.param_seg.path_results + add_suffix(''.join(extract_fname(self.param_seg.fname_im)[1:]), '_gmseg') self.fname_res_wmseg = self.param_seg.path_results + add_suffix(''.join(extract_fname(self.param_seg.fname_im)[1:]), '_wmseg') self.im_res_gmseg.setFileName(self.fname_res_gmseg) self.im_res_wmseg.setFileName(self.fname_res_wmseg) self.im_res_gmseg.save() self.im_res_wmseg.save()
def segment(self): self.copy_data_to_tmp() # go to tmp directory curdir = os.getcwd() os.chdir(self.tmp_dir) # load model self.model.load_model() self.target_im, self.info_preprocessing = pre_processing(self.param_seg.fname_im, self.param_seg.fname_seg, self.param_seg.fname_level, new_res=self.param_data.axial_res, square_size_size_mm=self.param_data.square_size_size_mm, denoising=self.param_data.denoising, verbose=self.param.verbose, rm_tmp=self.param.rm_tmp) printv('\nRegister target image to model data...', self.param.verbose, 'normal') # register target image to model dictionary space path_warp = self.register_target() if self.param_data.normalization: printv('\nNormalize intensity of target image...', self.param.verbose, 'normal') self.normalize_target() printv('\nProject target image into the model reduced space...', self.param.verbose, 'normal') self.project_target() printv('\nCompute similarities between target slices and model slices using model reduced space...', self.param.verbose, 'normal') list_dic_indexes_by_slice = self.compute_similarities() printv('\nLabel fusion of model slices most similar to target slices...', self.param.verbose, 'normal') self.label_fusion(list_dic_indexes_by_slice) printv('\nWarp back segmentation into image space...', self.param.verbose, 'normal') self.warp_back_seg(path_warp) printv('\nPost-processing...', self.param.verbose, 'normal') self.im_res_gmseg, self.im_res_wmseg = self.post_processing() if (self.param_seg.path_results != './') and (not os.path.exists(os.path.join(curdir, self.param_seg.path_results))): # create output folder printv('\nCreate output folder ...', self.param.verbose, 'normal') os.chdir(curdir) os.mkdir(self.param_seg.path_results) os.chdir(self.tmp_dir) if self.param_seg.fname_manual_gmseg is not None: # compute validation metrics printv('\nCompute validation metrics...', self.param.verbose, 'normal') self.validation() # go back to original directory os.chdir(curdir) printv('\nSave resulting GM and WM segmentations...', self.param.verbose, 'normal') self.fname_res_gmseg = os.path.join(self.param_seg.path_results, add_suffix(''.join(extract_fname(self.param_seg.fname_im)[1:]), '_gmseg')) self.fname_res_wmseg = os.path.join(self.param_seg.path_results, add_suffix(''.join(extract_fname(self.param_seg.fname_im)[1:]), '_wmseg')) self.im_res_gmseg.absolutepath = self.fname_res_gmseg self.im_res_wmseg.absolutepath = self.fname_res_wmseg self.im_res_gmseg.save() self.im_res_wmseg.save()
def reorient_data(self): for f in self.fname_metric_lst: os.rename( self.fname_metric_lst[f], sct.add_suffix( ''.join(sct.extract_fname(self.param.fname_im)[1:]), '_2reorient')) im = Image(sct.add_suffix(''.join(sct.extract_fname(self.param.fname_im)[1:]), '_2reorient')) \ .change_orientation(self.orientation_im) \ .save(self.fname_metric_lst[f])
def compute(self): fname_data = self.fmri # # create temporary folder # sct.printv('\nCreate temporary folder...', self.param.verbose) # path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S/") # status, output = sct.run('mkdir '+path_tmp, self.param.verbose) # # motion correct the fmri data # # sct.printv('\nMotion correct the fMRI data...', self.param.verbose, 'normal') # path_fmri, fname_fmri, ext_fmri = sct.extract_fname(self.fmri) # fname_fmri_moco = fname_fmri # # print sct.slash_at_the_end(path_fmri) + fname_fmri # # sct.run('mcflirt -in ' + sct.slash_at_the_end(path_fmri, 1) + fname_fmri + ' -out ' + fname_fmri_moco) # compute tsnr sct.printv('\nCompute the tSNR...', self.param.verbose, 'normal') fname_data_mean = sct.add_suffix(fname_data, '_mean.nii') sct.run('sct_maths -i ' + fname_data + ' -o ' + fname_data_mean + ' -mean t') # if not average_data_across_dimension(fname_data, fname_data_mean, 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run('fslmaths ' + fname_data + ' -Tmean ' + fname_data_mean) fname_data_std = sct.add_suffix(fname_data, '_std.nii') sct.run('sct_maths -i ' + fname_data + ' -o ' + fname_data_std + ' -mean t') # if not average_data_across_dimension(fname_data, fname_data_std, 3, 1): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run('fslmaths ' + fname_data + ' -Tstd ' + fname_data_std) fname_tsnr = sct.add_suffix(fname_data, '_tsnr') from msct_image import Image nii_mean = Image(fname_data_mean) data_mean = nii_mean.data data_std = Image(fname_data_std).data data_tsnr = data_mean / data_std nii_tsnr = nii_mean nii_tsnr.data = data_tsnr nii_tsnr.setFileName(fname_tsnr) nii_tsnr.save() # sct.run('fslmaths ' + fname_data_mean + ' -div ' + fname_data_std + ' ' + fname_tsnr) # Remove temp files sct.printv('\nRemove temporary files...', self.param.verbose, 'normal') import os os.remove(fname_data_mean) os.remove(fname_data_std) # to view results sct.printv('\nDone! To view results, type:', self.param.verbose, 'normal') sct.printv('fslview ' + fname_tsnr + ' &\n', self.param.verbose, 'info')
def compute(self): fname_data = self.fmri # # create temporary folder # sct.printv('\nCreate temporary folder...', self.param.verbose) # path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S/") # status, output = sct.run('mkdir '+path_tmp, self.param.verbose) # # motion correct the fmri data # # sct.printv('\nMotion correct the fMRI data...', self.param.verbose, 'normal') # path_fmri, fname_fmri, ext_fmri = sct.extract_fname(self.fmri) # fname_fmri_moco = fname_fmri # # print sct.slash_at_the_end(path_fmri) + fname_fmri # # sct.run('mcflirt -in ' + sct.slash_at_the_end(path_fmri, 1) + fname_fmri + ' -out ' + fname_fmri_moco) # compute mean fname_data_mean = sct.add_suffix(fname_data, '_mean') sct_maths.main( args=['-i', fname_data, '-o', fname_data_mean, '-mean', 't']) # compute STD fname_data_std = sct.add_suffix(fname_data, '_std') sct_maths.main( args=['-i', fname_data, '-o', fname_data_std, '-std', 't']) # compute tSNR fname_tsnr = sct.add_suffix(fname_data, '_tsnr') from msct_image import Image nii_mean = Image(fname_data_mean) data_mean = nii_mean.data data_std = Image(fname_data_std).data data_tsnr = data_mean / data_std nii_tsnr = nii_mean nii_tsnr.data = data_tsnr nii_tsnr.setFileName(fname_tsnr) nii_tsnr.save() # Remove temp files sct.printv('\nRemove temporary files...', self.param.verbose, 'normal') import os os.remove(fname_data_mean) os.remove(fname_data_std) # to view results sct.printv('\nDone! To view results, type:', self.param.verbose, 'normal') sct.printv('fslview ' + fname_tsnr + ' &\n', self.param.verbose, 'info')
def compute(self): fname_data = self.fmri # # create temporary folder # sct.printv('\nCreate temporary folder...', self.param.verbose) # path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S/") # status, output = sct.run('mkdir '+path_tmp, self.param.verbose) # # motion correct the fmri data # # sct.printv('\nMotion correct the fMRI data...', self.param.verbose, 'normal') # path_fmri, fname_fmri, ext_fmri = sct.extract_fname(self.fmri) # fname_fmri_moco = fname_fmri # # print sct.slash_at_the_end(path_fmri) + fname_fmri # # sct.run('mcflirt -in ' + sct.slash_at_the_end(path_fmri, 1) + fname_fmri + ' -out ' + fname_fmri_moco) # compute tsnr sct.printv('\nCompute the tSNR...', self.param.verbose, 'normal') fname_data_mean = sct.add_suffix(fname_data, '_mean') sct.run('sct_maths -i '+fname_data+' -o '+fname_data_mean+' -mean t') # if not average_data_across_dimension(fname_data, fname_data_mean, 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run('fslmaths ' + fname_data + ' -Tmean ' + fname_data_mean) fname_data_std = sct.add_suffix(fname_data, '_std') sct.run('sct_maths -i '+fname_data+' -o '+fname_data_std+' -mean t') # if not average_data_across_dimension(fname_data, fname_data_std, 3, 1): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run('fslmaths ' + fname_data + ' -Tstd ' + fname_data_std) fname_tsnr = sct.add_suffix(fname_data, '_tsnr') from msct_image import Image nii_mean = Image(fname_data_mean) data_mean = nii_mean.data data_std = Image(fname_data_std).data data_tsnr = data_mean/data_std nii_tsnr = nii_mean nii_tsnr.data = data_tsnr nii_tsnr.setFileName(fname_tsnr) nii_tsnr.save() # sct.run('fslmaths ' + fname_data_mean + ' -div ' + fname_data_std + ' ' + fname_tsnr) # Remove temp files sct.printv('\nRemove temporary files...', self.param.verbose, 'normal') import os os.remove(fname_data_mean) os.remove(fname_data_std) # to view results sct.printv('\nDone! To view results, type:', self.param.verbose, 'normal') sct.printv('fslview '+fname_tsnr+' &\n', self.param.verbose, 'info')
def test_integrity(param_test): """ Test integrity of function """ fname_src = param_test.dict_args_with_path["-i"] fname_ref = param_test.dict_args_with_path["-d"] fname_dst = sct.add_suffix(os.path.basename(fname_src), "_reg") #fname_dst = "output.nii.gz" img_src = msct_image.Image(fname_src) img_ref = msct_image.Image(fname_ref) img_dst = msct_image.Image(fname_dst) if img_dst.orientation != img_ref.orientation: param_test.output += "\nImage has wrong orientation (%s -> %s)" \ % (img_ref.orientation, img_dst.orientation) param_test.status = 1 if len(img_src.data.shape) > 3: # Allowed failure for now return param_test if not (img_dst.data != 0).any(): param_test.output += "\nImage is garbage (all zeros)" param_test.status = 1 return param_test
def multicomponent_merge(fname_list): from numpy import zeros # WARNING: output multicomponent is not optimal yet, some issues may be related to the use of this function im_0 = Image(fname_list[0]) new_shape = list(im_0.data.shape) if len(new_shape) == 3: new_shape.append(1) new_shape.append(len(fname_list)) new_shape = tuple(new_shape) data_out = zeros(new_shape) for i, fname in enumerate(fname_list): im = Image(fname) dat = im.data if len(dat.shape) == 2: data_out[:, :, 0, 0, i] = dat.astype('float32') elif len(dat.shape) == 3: data_out[:, :, :, 0, i] = dat.astype('float32') elif len(dat.shape) == 4: data_out[:, :, :, :, i] = dat.astype('float32') del im del dat im_out = im_0.copy() im_out.data = data_out.astype('float32') im_out.hdr.set_intent('vector', (), '') im_out.absolutepath = sct.add_suffix(im_out.absolutepath, '_multicomponent') return im_out
def resample_file(fname_data, fname_out, new_size, new_size_type, interpolation, verbose): """This function will resample the specified input image file to the target size. Can deal with 2d, 3d or 4d image objects. :param fname_data: The input image filename. :param fname_out: The output image filename. :param new_size: The target size, i.e. 0.25x0.25 :param new_size_type: Unit of resample (mm, vox, factor) :param interpolation: The interpolation type :param verbose: verbosity level """ # Load data sct.printv('\nLoad data...', verbose) nii = nipy.load_image(fname_data) nii_r = resample_image(nii, new_size, new_size_type, interpolation, verbose) # build output file name if fname_out == '': fname_out = sct.add_suffix(fname_data, '_r') else: fname_out = fname_out # save data nipy.save_image(nii_r, fname_out) # to view results sct.display_viewer_syntax([fname_out], verbose=verbose) return nii_r
def load_manual_gmseg(list_slices_target, list_fname_manual_gmseg, tmp_dir, im_sc_seg_rpi, new_res, square_size_size_mm, for_model=False, fname_mask=None): if isinstance(list_fname_manual_gmseg, str): # consider fname_manual_gmseg as a list of file names to allow multiple manual GM segmentation list_fname_manual_gmseg = [list_fname_manual_gmseg] for fname_manual_gmseg in list_fname_manual_gmseg: os.chdir('..') shutil.copy(fname_manual_gmseg, tmp_dir) # change fname level to only file name (path = tmp dir now) path_gm, file_gm, ext_gm = extract_fname(fname_manual_gmseg) fname_manual_gmseg = file_gm + ext_gm os.chdir(tmp_dir) im_manual_gmseg = Image(fname_manual_gmseg) # reorient to RPI im_manual_gmseg = set_orientation(im_manual_gmseg, 'RPI') if fname_mask is not None: fname_gmseg_crop = add_suffix(im_manual_gmseg.absolutepath, '_pre_crop') crop_im = ImageCropper(input_file=im_manual_gmseg.absolutepath, output_file=fname_gmseg_crop, mask=fname_mask) im_manual_gmseg_crop = crop_im.crop() im_manual_gmseg = im_manual_gmseg_crop # assert gmseg has the right number of slices assert im_manual_gmseg.data.shape[2] == len( list_slices_target ), 'ERROR: the manual GM segmentation has not the same number of slices than the image.' # interpolate gm to reference image nz_gmseg, nx_gmseg, ny_gmseg, nt_gmseg, pz_gmseg, px_gmseg, py_gmseg, pt_gmseg = im_manual_gmseg.dim list_im_gm = interpolate_im_to_ref(im_manual_gmseg, im_sc_seg_rpi, new_res=new_res, sq_size_size_mm=square_size_size_mm, interpolation_mode=0) # load gm seg in list of slices n_poped = 0 for im_gm, slice_im in zip(list_im_gm, list_slices_target): if im_gm.data.max() == 0 and for_model: list_slices_target.pop(slice_im.id - n_poped) n_poped += 1 else: slice_im.gm_seg.append(im_gm.data) wm_slice = (slice_im.im > 0) - im_gm.data slice_im.wm_seg.append(wm_slice) return list_slices_target
def change_orientation(self, orientation, inverse=False, generate_path=False): """ Change orientation on image (in-place). :param orientation: orientation string (SCT "from" convention) :param inverse: if you think backwards, use this to specify that you actually want to transform *from* the specified orientation, not *to* it. :param generate_path: whether to create a derived path name from the original absolutepath (note: while it will generate a file suffix, don't expect the suffix but rather use the Image's absolutepath. If not set, the absolutepath is voided. """ if orientation is not None: change_orientation(self, orientation, self, inverse=inverse) if generate_path and self._path is not None: self._path = sct.add_suffix(self._path, "_{}".format(orientation.lower())) else: # safe option: remove path to avoid overwrites self._path = None return self
def compute(self): fname_data = self.fmri # open data nii_data = Image(fname_data) data = nii_data.data # compute mean data_mean = np.mean(data, 3) # compute STD data_std = np.std(data, 3, ddof=1) # compute TSNR data_tsnr = data_mean / data_std # save TSNR fname_tsnr = sct.add_suffix(fname_data, '_tsnr') nii_tsnr = nii_data nii_tsnr.data = data_tsnr nii_tsnr.setFileName(fname_tsnr) nii_tsnr.save(type='float32') # to view results sct.printv('\nDone! To view results, type:', self.param.verbose, 'normal') sct.printv('fslview ' + fname_tsnr + ' &\n', self.param.verbose, 'info')
def viewer_centerline(image_fname, interslice_gap, verbose): image_input_reoriented = Image(image_fname) nx, ny, nz, nt, px, py, pz, pt = image_input_reoriented.dim viewer = ClickViewerPropseg(image_input_reoriented) viewer.gap_inter_slice = int( interslice_gap / px) # px because the image is supposed to be SAL orientation viewer.number_of_slices = 0 viewer.calculate_list_slices() # start the viewer that ask the user to enter a few points along the spinal cord mask_points = viewer.start() if not mask_points and viewer.closed: mask_points = viewer.list_points_useful_notation if mask_points: # create the mask containing either the three-points or centerline mask for initialization mask_filename = sct.add_suffix(image_fname, "_mask_viewer") sct.run("sct_label_utils -i " + image_fname + " -create " + mask_points + " -o " + mask_filename, verbose=False) fname_output = mask_filename else: sct.printv( '\nERROR: the viewer has been closed before entering all manual points. Please try again.', 1, type='error') fname_output = None return fname_output
def func_rescale_header(fname_data, rescale_factor, verbose=0): """ Rescale the voxel dimension by modifying the NIFTI header qform. Write the output file in a temp folder. :param fname_data: :param rescale_factor: :return: fname_data_rescaled """ import nibabel as nib img = nib.load(fname_data) # get qform qform = img.header.get_qform() # multiply by scaling factor qform[0:3, 0:3] *= rescale_factor # generate a new nifti file header_rescaled = img.header.copy() header_rescaled.set_qform(qform) # the data are the same-- only the header changes img_rescaled = nib.nifti1.Nifti1Image(img.get_data(), None, header=header_rescaled) path_tmp = sct.tmp_create(basename="propseg", verbose=verbose) fname_data_rescaled = os.path.join( path_tmp, os.path.basename(sct.add_suffix(fname_data, "_rescaled"))) nib.save(img_rescaled, fname_data_rescaled) return fname_data_rescaled
def resample_file(fname_data, fname_out, new_size, new_size_type, interpolation, verbose): """This function will resample the specified input image file to the target size. :param fname_data: The input image filename. :param fname_out: The output image filename. :param new_size: The target size, i.e. 0.25x0.25 :param new_size_type: Unit of resample (mm, vox, factor) :param interpolation: The interpolation type :param verbose: verbosity level """ # Load data sct.printv('\nLoad data...', verbose) nii = nipy.load_image(fname_data) nii_r = resample_image(nii, new_size, new_size_type, interpolation, verbose) # build output file name if fname_out == '': fname_out = sct.add_suffix(fname_data, '_r') else: fname_out = fname_out # save data nipy.save_image(nii_r, fname_out) # to view results sct.printv('\nDone! To view results, type:', verbose) sct.printv('fslview ' + fname_out + ' &', verbose, 'info') return nii_r
def test_integrity(param_test): """ Test integrity of function """ dice_segmentation = float('nan') # extract name of output segmentation: data_seg.nii.gz file_seg = os.path.join(param_test.path_output, sct.add_suffix(param_test.file_input, '_seg')) # open output segmentation im_seg = Image(file_seg) # open ground truth im_seg_manual = Image(param_test.fname_gt) # compute dice coefficient between generated image and image from database dice_segmentation = compute_dice(im_seg, im_seg_manual, mode='3d', zboundaries=False) # display param_test.output += 'Computed dice: ' + str(dice_segmentation) param_test.output += '\nDice threshold (if computed Dice smaller: fail): ' + str( param_test.dice_threshold) if dice_segmentation < param_test.dice_threshold: param_test.status = 99 param_test.output += '\n--> FAILED' else: param_test.output += '\n--> PASSED' # update Panda structure param_test.results['dice'] = dice_segmentation return param_test
def apply_transfo(im_src, im_dest, warp, interp='spline', rm_tmp=True): # create tmp dir and go in it tmp_dir = sct.tmp_create() # copy warping field to tmp dir sct.copy(warp, tmp_dir) warp = ''.join(extract_fname(warp)[1:]) # go to tmp dir curdir = os.getcwd() os.chdir(tmp_dir) # save image and seg fname_src = 'src.nii.gz' im_src.save(fname_src) fname_dest = 'dest.nii.gz' im_dest.save(fname_dest) # apply warping field fname_src_reg = add_suffix(fname_src, '_reg') sct_apply_transfo.main( args=['-i', fname_src, '-d', fname_dest, '-w', warp, '-x', interp]) im_src_reg = Image(fname_src_reg) # get out of tmp dir os.chdir(curdir) if rm_tmp: # remove tmp dir sct.rmtree(tmp_dir) # return res image return im_src_reg
def test_integrity(param_test): """ Test integrity of function """ dice_segmentation = float('nan') # extract name of output segmentation: data_seg.nii.gz file_seg = os.path.join(param_test.path_output, sct.add_suffix(param_test.file_input, '_seg')) # open output segmentation im_seg = Image(file_seg) # open ground truth im_seg_manual = Image(param_test.fname_gt) # compute dice coefficient between generated image and image from database dice_segmentation = compute_dice(im_seg, im_seg_manual, mode='3d', zboundaries=False) # display param_test.output += 'Computed dice: '+str(dice_segmentation) param_test.output += '\nDice threshold (if computed Dice smaller: fail): '+str(param_test.dice_threshold) if dice_segmentation < param_test.dice_threshold: param_test.status = 99 param_test.output += '\n--> FAILED' else: param_test.output += '\n--> PASSED' # update Panda structure param_test.results['dice'] = dice_segmentation return param_test
def change_shape(self, shape, generate_path=False): """ Change data shape (in-place) :param generate_path: whether to create a derived path name from the original absolutepath (note: while it will generate a file suffix, don't expect the suffix but rather use the Image's absolutepath. If not set, the absolutepath is voided. This is mostly useful for adding/removing a fourth dimension, you probably don't want to use this function. """ if shape is not None: change_shape(self, shape, self) if generate_path and self._path is not None: self._path = sct.add_suffix( self._path, "_shape-{}".format("-".join([str(x) for x in shape]))) else: # safe option: remove path to avoid overwrites self._path = None return self
def register_data(im_src, im_dest, param_reg, path_copy_warp=None, rm_tmp=True): ''' Parameters ---------- im_src: class Image: source image im_dest: class Image: destination image param_reg: str: registration parameter path_copy_warp: path: path to copy the warping fields Returns: im_src_reg: class Image: source image registered on destination image ------- ''' # im_src and im_dest are already preprocessed (in theory: im_dest = mean_image) # binarize images to get seg im_src_seg = binarize(im_src, thr_min=1, thr_max=1) im_dest_seg = binarize(im_dest) # create tmp dir and go in it tmp_dir = tmp_create() os.chdir(tmp_dir) # save image and seg fname_src = 'src.nii.gz' im_src.setFileName(fname_src) im_src.save() fname_src_seg = 'src_seg.nii.gz' im_src_seg.setFileName(fname_src_seg) im_src_seg.save() fname_dest = 'dest.nii.gz' im_dest.setFileName(fname_dest) im_dest.save() fname_dest_seg = 'dest_seg.nii.gz' im_dest_seg.setFileName(fname_dest_seg) im_dest_seg.save() # do registration using param_reg sct_register_multimodal.main(args=['-i', fname_src, '-d', fname_dest, '-iseg', fname_src_seg, '-dseg', fname_dest_seg, '-param', param_reg]) # get registration result fname_src_reg = add_suffix(fname_src, '_reg') im_src_reg = Image(fname_src_reg) # get out of tmp dir os.chdir('..') # copy warping fields if path_copy_warp is not None and os.path.isdir(os.path.abspath(path_copy_warp)): path_copy_warp = os.path.abspath(path_copy_warp) file_src = extract_fname(fname_src)[1] file_dest = extract_fname(fname_dest)[1] fname_src2dest = 'warp_' + file_src +'2' + file_dest +'.nii.gz' fname_dest2src = 'warp_' + file_dest +'2' + file_src +'.nii.gz' shutil.copy(tmp_dir +'/' + fname_src2dest, path_copy_warp + '/') shutil.copy(tmp_dir + '/' + fname_dest2src, path_copy_warp + '/') if rm_tmp: # remove tmp dir shutil.rmtree(tmp_dir) # return res image return im_src_reg, fname_src2dest, fname_dest2src
def apply_transfo(im_src, im_dest, warp, interp='spline', rm_tmp=True): # create tmp dir and go in it tmp_dir = tmp_create() # copy warping field to tmp dir shutil.copy(warp, tmp_dir) warp = ''.join(extract_fname(warp)[1:]) # go to tmp dir os.chdir(tmp_dir) # save image and seg fname_src = 'src.nii.gz' im_src.setFileName(fname_src) im_src.save() fname_dest = 'dest.nii.gz' im_dest.setFileName(fname_dest) im_dest.save() # apply warping field fname_src_reg = add_suffix(fname_src, '_reg') sct_apply_transfo.main(args=['-i', fname_src, '-d', fname_dest, '-w', warp, '-x', interp]) im_src_reg = Image(fname_src_reg) # get out of tmp dir os.chdir('..') if rm_tmp: # remove tmp dir shutil.rmtree(tmp_dir) # return res image return im_src_reg
def resample_file(fname_data, fname_out, new_size, new_size_type, interpolation, verbose, fname_ref=None): """This function will resample the specified input image file to the target size. Can deal with 2d, 3d or 4d image objects. :param fname_data: The input image filename. :param fname_out: The output image filename. :param new_size: The target size, i.e. 0.25x0.25 :param new_size_type: Unit of resample (mm, vox, factor) :param interpolation: The interpolation type :param verbose: verbosity level :param fname_ref: Reference image to resample input image to """ # Load data logger.info('load data...') nii = nib.load(fname_data) if fname_ref is not None: nii_ref = nib.load(fname_ref) else: nii_ref = None nii_r = resample_nib(nii, new_size.split('x'), new_size_type, img_dest=nii_ref, interpolation=interpolation) # build output file name if fname_out == '': fname_out = sct.add_suffix(fname_data, '_r') else: fname_out = fname_out # save data nib.save(nii_r, fname_out) # to view results sct.display_viewer_syntax([fname_out], verbose=verbose) return nii_r
def main(fname_anat, fname_centerline, degree_poly, centerline_fitting, interp, remove_temp_files, verbose): # load input image im_anat = Image(fname_anat) nx, ny, nz, nt, px, py, pz, pt = im_anat.dim # re-oriente to RPI orientation_native = im_anat.orientation im_anat.change_orientation("RPI") # load centerline im_centerline = Image(fname_centerline).change_orientation("RPI") # smooth centerline and return fitted coordinates in voxel space x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline( im_centerline, algo_fitting=centerline_fitting, type_window='hanning', window_length=50, nurbs_pts_number=3000, phys_coordinates=False, verbose=verbose, all_slices=True) # compute translation for each slice, such that the flattened centerline is centered in the medial plane (R-L) and # avoid discontinuity in slices where there is no centerline (in which case, simply copy the translation of the # closest Z). # first, get zmin and zmax spanned by the centerline (i.e. with non-zero values) indz_centerline = np.where( [np.sum(im_centerline.data[:, :, iz]) for iz in range(nz)])[0] zmin, zmax = indz_centerline[0], indz_centerline[-1] # then, extend the centerline by copying values below zmin and above zmax x_centerline_extended = np.concatenate([ np.ones(zmin) * x_centerline_fit[0], x_centerline_fit, np.ones(nz - zmax) * x_centerline_fit[-1] ]) # loop across slices and apply translation im_anat_flattened = msct_image.change_type(im_anat, np.float32) # change type to float32 because of subsequent conversion (img_as_float). See #1790 for iz in range(nz): # compute translation along x (R-L) translation_x = x_centerline_extended[iz] - np.round(nx / 2.0) # apply transformation to 2D image with linear interpolation # tform = tf.SimilarityTransform(scale=1, rotation=0, translation=(translation_x, 0)) tform = transform.SimilarityTransform(translation=(0, translation_x)) # important to force input in float to skikit image, because it will output float values img = img_as_float(im_anat.data[:, :, iz]) img_reg = transform.warp(img, tform) im_anat_flattened.data[:, :, iz] = img_reg # img_as_uint(img_reg) # change back to native orientation im_anat_flattened.change_orientation(orientation_native) # save output fname_out = sct.add_suffix(fname_anat, '_flatten') im_anat_flattened.save(fname_out) sct.display_viewer_syntax([fname_anat, fname_out])
def savePredictions(predictions, path_output, list_images, segmentation_image_size): number_of_images = len(list_images) predictions = numpy.reshape(predictions, [number_of_images, segmentation_image_size, segmentation_image_size, NUM_LABELS]) predictions = predictions[:, :, :, 1] for i, pref in enumerate(predictions): im_pred = Image(pref) im_pred.setFileName(path_output+sct.add_suffix(list_images[i], '_pred')) im_pred.save()
def prepare(list_images): fname_images, orientation_images = [], [] for fname_im in list_images: from sct_image import orientation orientation_images.append(orientation(Image(fname_im), get=True, verbose=False)) path_fname, file_fname, ext_fname = sct.extract_fname(fname_im) reoriented_image_filename = 'tmp.' + sct.add_suffix(file_fname + ext_fname, "_SAL") sct.run('sct_image -i ' + fname_im + ' -o ' + reoriented_image_filename + ' -setorient SAL -v 0', verbose=False) fname_images.append(reoriented_image_filename) return fname_images, orientation_images
def compute_texture(self): offset = int(self.param_glcm.distance) printv('\nCompute texture metrics...', self.param.verbose, 'normal') # open image and re-orient it to RPI if needed im_tmp = Image(self.param.fname_im) if self.orientation_im != self.orientation_extraction: im_tmp = set_orientation(im_tmp, self.orientation_extraction) dct_metric = {} for m in self.metric_lst: im_2save = im_tmp.copy() im_2save.changeType(type='float64') im_2save.data *= 0 dct_metric[m] = im_2save # dct_metric[m] = Image(self.fname_metric_lst[m]) timer = Timer(number_of_iteration=len(self.dct_im_seg['im'])) timer.start() for im_z, seg_z, zz in zip(self.dct_im_seg['im'], self.dct_im_seg['seg'], range(len(self.dct_im_seg['im']))): for xx in range(im_z.shape[0]): for yy in range(im_z.shape[1]): if not seg_z[xx, yy]: continue if xx < offset or yy < offset: continue if xx > (im_z.shape[0] - offset - 1) or yy > (im_z.shape[1] - offset - 1): continue # to check if the whole glcm_window is in the axial_slice if False in np.unique(seg_z[xx - offset: xx + offset + 1, yy - offset: yy + offset + 1]): continue # to check if the whole glcm_window is in the mask of the axial_slice glcm_window = im_z[xx - offset: xx + offset + 1, yy - offset: yy + offset + 1] glcm_window = glcm_window.astype(np.uint8) dct_glcm = {} for a in self.param_glcm.angle.split(','): # compute the GLCM for self.param_glcm.distance and for each self.param_glcm.angle dct_glcm[a] = greycomatrix(glcm_window, [self.param_glcm.distance], [radians(int(a))], symmetric=self.param_glcm.symmetric, normed=self.param_glcm.normed) for m in self.metric_lst: # compute the GLCM property (m.split('_')[0]) of the voxel xx,yy,zz dct_metric[m].data[xx, yy, zz] = greycoprops(dct_glcm[m.split('_')[2]], m.split('_')[0])[0][0] timer.add_iteration() timer.stop() for m in self.metric_lst: fname_out = add_suffix(''.join(extract_fname(self.param.fname_im)[1:]), '_' + m) dct_metric[m].setFileName(fname_out) dct_metric[m].save() self.fname_metric_lst[m] = fname_out
def label_segmentation(fname_seg, list_disc_z, list_disc_value, verbose=1): """ Label segmentation image :param fname_seg: fname of the segmentation, no orientation expected :param list_disc_z: list of z that correspond to a disc :param list_disc_value: list of associated disc values :param verbose: :return: """ # open segmentation seg = Image(fname_seg) init_orientation = seg.orientation seg.change_orientation("RPI") dim = seg.dim ny = dim[1] nz = dim[2] # loop across z for iz in range(nz): # get index of the disc right above iz try: ind_above_iz = max( [i for i in range(len(list_disc_z)) if list_disc_z[i] > iz]) except ValueError: # if ind_above_iz is empty, attribute value 0 vertebral_level = 0 else: # assign vertebral level (add one because iz is BELOW the disk) vertebral_level = list_disc_value[ind_above_iz] + 1 # sct.printv(vertebral_level) # get voxels in mask ind_nonzero = np.nonzero(seg.data[:, :, iz]) seg.data[ind_nonzero[0], ind_nonzero[1], iz] = vertebral_level # if verbose == 2: # # move to OO. No time to finish... (JCA) # from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas # from matplotlib.figure import Figure # fig = Figure() # FigureCanvas(fig) # ax = fig.add_subplot(111) # ax.scatter(int(np.round(ny / 2)), iz, c=vertebral_level, vmin=min(list_disc_value), # vmax=max(list_disc_value), cmap='prism', marker='_', s=200) # # # TODO: the thing below crashes with the py3k move. Fix it when i have time... # import matplotlib # matplotlib.use('Agg') # import matplotlib.pyplot as plt # plt.figure(50) # plt.scatter(int(np.round(ny / 2)), iz, c=vertebral_level, vmin=min(list_disc_value), vmax=max(list_disc_value), cmap='prism', marker='_', s=200) # write file seg.change_orientation(init_orientation).save( sct.add_suffix(fname_seg, '_labeled'))
def project_labels_on_spinalcord(fname_label, fname_seg): """ Project labels orthogonally on the spinal cord centerline. The algorithm works by finding the smallest distance between each label and the spinal cord center of mass. :param fname_label: file name of labels :param fname_seg: file name of cord segmentation (could also be of centerline) :return: file name of projected labels """ # build output name fname_label_projected = sct.add_suffix(fname_label, "_projected") # open labels and segmentation im_label = Image(fname_label).change_orientation("RPI") im_seg = Image(fname_seg) native_orient = im_seg.orientation im_seg.change_orientation("RPI") # smooth centerline and return fitted coordinates in voxel space _, arr_ctl, _ = get_centerline(im_seg, algo_fitting='bspline') x_centerline_fit, y_centerline_fit, z_centerline = arr_ctl # convert pixel into physical coordinates centerline_xyz_transposed = \ [im_seg.transfo_pix2phys([[x_centerline_fit[i], y_centerline_fit[i], z_centerline[i]]])[0] for i in range(len(x_centerline_fit))] # transpose list centerline_phys_x = [i[0] for i in centerline_xyz_transposed] centerline_phys_y = [i[1] for i in centerline_xyz_transposed] centerline_phys_z = [i[2] for i in centerline_xyz_transposed] # get center of mass of label labels = im_label.getCoordinatesAveragedByValue() # initialize image of projected labels. Note that we use the space of the seg (not label). im_label_projected = msct_image.zeros_like(im_seg, dtype=np.uint8) # loop across label values for label in labels: # convert pixel into physical coordinates for the label label_phys_x, label_phys_y, label_phys_z = im_label.transfo_pix2phys([[label.x, label.y, label.z]])[0] # calculate distance between label and each point of the centerline distance_centerline = [np.linalg.norm([centerline_phys_x[i] - label_phys_x, centerline_phys_y[i] - label_phys_y, centerline_phys_z[i] - label_phys_z]) for i in range(len(x_centerline_fit))] # get the index corresponding to the min distance ind_min_distance = np.argmin(distance_centerline) # get centerline coordinate (in physical space) [min_phy_x, min_phy_y, min_phy_z] = [centerline_phys_x[ind_min_distance], centerline_phys_y[ind_min_distance], centerline_phys_z[ind_min_distance]] # convert coordinate to voxel space minx, miny, minz = im_seg.transfo_phys2pix([[min_phy_x, min_phy_y, min_phy_z]])[0] # use that index to assign projected label in the centerline im_label_projected.data[minx, miny, minz] = label.value # re-orient projected labels to native orientation and save im_label_projected.change_orientation(native_orient).save(fname_label_projected) return fname_label_projected
def pad_image(im, pad_x_i=0, pad_x_f=0, pad_y_i=0, pad_y_f=0, pad_z_i=0, pad_z_f=0): nx, ny, nz, nt, px, py, pz, pt = im.dim pad_x_i, pad_x_f, pad_y_i, pad_y_f, pad_z_i, pad_z_f = int(pad_x_i), int( pad_x_f), int(pad_y_i), int(pad_y_f), int(pad_z_i), int(pad_z_f) if len(im.data.shape) == 2: new_shape = list(im.data.shape) new_shape.append(1) im.data = im.data.reshape(new_shape) # initialize padded_data, with same type as im.data padded_data = np.zeros((nx + pad_x_i + pad_x_f, ny + pad_y_i + pad_y_f, nz + pad_z_i + pad_z_f), dtype=im.data.dtype) if pad_x_f == 0: pad_x_f = None elif pad_x_f > 0: pad_x_f *= -1 if pad_y_f == 0: pad_y_f = None elif pad_y_f > 0: pad_y_f *= -1 if pad_z_f == 0: pad_z_f = None elif pad_z_f > 0: pad_z_f *= -1 padded_data[pad_x_i:pad_x_f, pad_y_i:pad_y_f, pad_z_i:pad_z_f] = im.data im_out = im.copy() # TODO: Do not copy the Image(), because the dim field and hdr.get_data_shape() will not be updated properly. # better to just create a new Image() from scratch. im_out.data = padded_data # done after the call of the function im_out.absolutepath = sct.add_suffix(im_out.absolutepath, "_pad") # adapt the origin in the sform and qform matrix new_origin = np.dot(im_out.hdr.get_qform(), [-pad_x_i, -pad_y_i, -pad_z_i, 1]) im_out.hdr.structarr['qoffset_x'] = new_origin[0] im_out.hdr.structarr['qoffset_y'] = new_origin[1] im_out.hdr.structarr['qoffset_z'] = new_origin[2] im_out.hdr.structarr['srow_x'][-1] = new_origin[0] im_out.hdr.structarr['srow_y'][-1] = new_origin[1] im_out.hdr.structarr['srow_z'][-1] = new_origin[2] return im_out
def project_labels_on_spinalcord(fname_label, fname_seg, param_centerline): """ Project labels orthogonally on the spinal cord centerline. The algorithm works by finding the smallest distance between each label and the spinal cord center of mass. :param fname_label: file name of labels :param fname_seg: file name of cord segmentation (could also be of centerline) :return: file name of projected labels """ # build output name fname_label_projected = sct.add_suffix(fname_label, "_projected") # open labels and segmentation im_label = Image(fname_label).change_orientation("RPI") im_seg = Image(fname_seg) native_orient = im_seg.orientation im_seg.change_orientation("RPI") # smooth centerline and return fitted coordinates in voxel space _, arr_ctl, _, _ = get_centerline(im_seg, param_centerline) x_centerline_fit, y_centerline_fit, z_centerline = arr_ctl # convert pixel into physical coordinates centerline_xyz_transposed = \ [im_seg.transfo_pix2phys([[x_centerline_fit[i], y_centerline_fit[i], z_centerline[i]]])[0] for i in range(len(x_centerline_fit))] # transpose list centerline_phys_x = [i[0] for i in centerline_xyz_transposed] centerline_phys_y = [i[1] for i in centerline_xyz_transposed] centerline_phys_z = [i[2] for i in centerline_xyz_transposed] # get center of mass of label labels = im_label.getCoordinatesAveragedByValue() # initialize image of projected labels. Note that we use the space of the seg (not label). im_label_projected = msct_image.zeros_like(im_seg, dtype=np.uint8) # loop across label values for label in labels: # convert pixel into physical coordinates for the label label_phys_x, label_phys_y, label_phys_z = im_label.transfo_pix2phys([[label.x, label.y, label.z]])[0] # calculate distance between label and each point of the centerline distance_centerline = [np.linalg.norm([centerline_phys_x[i] - label_phys_x, centerline_phys_y[i] - label_phys_y, centerline_phys_z[i] - label_phys_z]) for i in range(len(x_centerline_fit))] # get the index corresponding to the min distance ind_min_distance = np.argmin(distance_centerline) # get centerline coordinate (in physical space) [min_phy_x, min_phy_y, min_phy_z] = [centerline_phys_x[ind_min_distance], centerline_phys_y[ind_min_distance], centerline_phys_z[ind_min_distance]] # convert coordinate to voxel space minx, miny, minz = im_seg.transfo_phys2pix([[min_phy_x, min_phy_y, min_phy_z]])[0] # use that index to assign projected label in the centerline im_label_projected.data[minx, miny, minz] = label.value # re-orient projected labels to native orientation and save im_label_projected.change_orientation(native_orient).save(fname_label_projected) return fname_label_projected
def __init__(self, im, v=1): sct.printv('Thinning ... ', v, 'normal') self.image = im self.image.data = bin_data(self.image.data) self.dim_im = len(self.image.data.shape) if self.dim_im == 2: self.thinned_image = msct_image.empty_like(self.image) self.thinned_image.data = self.zhang_suen(self.image.data) self.thinned_image.absolutepath = sct.add_suffix(self.image.absolutepath, "_thinned") elif self.dim_im == 3: if not self.image.orientation == 'IRP': sct.printv('-- changing orientation ...') self.image.change_orientation('IRP') thinned_data = np.asarray([self.zhang_suen(im_slice) for im_slice in self.image.data]) self.thinned_image = msct_image.empty_like(self.image) self.thinned_image.data = thinned_data self.thinned_image.absolutepath = sct.add_suffix(self.image.absolutepath, "_thinned")
def resample_image(fname, suffix='_resampled.nii.gz', binary=False, npx=0.3, npy=0.3, thr=0.0, interpolation='spline'): """ Resampling function: add a padding, resample, crop the padding :param fname: name of the image file to be resampled :param suffix: suffix added to the original fname after resampling :param binary: boolean, image is binary or not :param npx: new pixel size in the x direction :param npy: new pixel size in the y direction :param thr: if the image is binary, it will be thresholded at thr (default=0) after the resampling :param interpolation: type of interpolation used for the resampling :return: file name after resampling (or original fname if it was already in the correct resolution) """ im_in = Image(fname) orientation = im_in.orientation if orientation != 'RPI': fname = im_in.change_orientation(im_in, 'RPI', generate_path=True).save().absolutepath nx, ny, nz, nt, px, py, pz, pt = im_in.dim if np.round(px, 2) != np.round(npx, 2) or np.round(py, 2) != np.round(npy, 2): name_resample = sct.extract_fname(fname)[1] + suffix if binary: interpolation = 'nn' if nz == 1: # when data is 2d: we convert it to a 3d image in order to avoid conversion problem with 2d data # TODO: check if this above problem is still present (now that we are using nibabel instead of nipy) sct.run(['sct_image', '-i', ','.join([fname, fname]), '-concat', 'z', '-o', fname]) sct.run(['sct_resample', '-i', fname, '-mm', str(npx) + 'x' + str(npy) + 'x' + str(pz), '-o', name_resample, '-x', interpolation]) if nz == 1: # when input data was 2d: re-convert data 3d-->2d sct.run(['sct_image', '-i', name_resample, '-split', 'z']) im_split = Image(name_resample.split('.nii.gz')[0] + '_Z0000.nii.gz') im_split.save(name_resample) if binary: sct.run(['sct_maths', '-i', name_resample, '-bin', str(thr), '-o', name_resample]) if orientation != 'RPI': name_resample = Image(name_resample) \ .change_orientation(orientation, generate_path=True) \ .save() \ .absolutepath return name_resample else: if orientation != 'RPI': fname = sct.add_suffix(fname, "_RPI") im_in = msct_image.change_orientation(im_in, orientation).save(fname) sct.printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz') return fname
def main(args=None): parser = get_parser() param = Param() arguments = parser.parse(sys.argv[1:]) fname_src = arguments['-i'] fname_dst = arguments.get("-o", sct.add_suffix(fname_src, "_tsnr")) verbose = int(arguments['-v']) # call main function tsnr = Tsnr(param=param, fmri=fname_src, out=fname_dst) tsnr.compute()
def crop_im(fname_im, fname_mask): fname_im_crop = sct.add_suffix(fname_im, "_crop") status, output_crop = sct.run("sct_crop_image -i " + fname_im + " -m " + fname_mask + " -o " + fname_im_crop) output_list = output_crop.split("\n") xi, xf, yi, yf, zi, zf = 0, 0, 0, 0, 0, 0 for line in output_list: if "Dimension 0" in line: dim, i, xi, xf = line.split(" ") if "Dimension 1" in line: dim, i, yi, yf = line.split(" ") if "Dimension 2" in line: dim, i, zi, zf = line.split(" ") return fname_im_crop, int(xi), int(xf), int(yi), int(yf), int(zi), int(zf)
def crop_im(fname_im, fname_mask): fname_im_crop = sct.add_suffix(fname_im, '_crop') status, output_crop = sct.run(['sct_crop_image', '-i', fname_im, '-m', fname_mask, '-o', fname_im_crop]) output_list = output_crop.split('\n') xi, xf, yi, yf, zi, zf = 0, 0, 0, 0, 0, 0 for line in output_list: if 'Dimension 0' in line: dim, i, xi, xf = line.split(' ') if 'Dimension 1' in line: dim, i, yi, yf = line.split(' ') if 'Dimension 2' in line: dim, i, zi, zf = line.split(' ') return fname_im_crop, int(xi), int(xf), int(yi), int(yf), int(zi), int(zf)
def resample_image(fname, suffix='_resampled.nii.gz', binary=False, npx=0.3, npy=0.3, thr=0.0, interpolation='spline'): """ Resampling function: add a padding, resample, crop the padding :param fname: name of the image file to be resampled :param suffix: suffix added to the original fname after resampling :param binary: boolean, image is binary or not :param npx: new pixel size in the x direction :param npy: new pixel size in the y direction :param thr: if the image is binary, it will be thresholded at thr (default=0) after the resampling :param interpolation: type of interpolation used for the resampling :return: file name after resampling (or original fname if it was already in the correct resolution) """ im_in = Image(fname) orientation = im_in.orientation if orientation != 'RPI': fname = im_in.change_orientation(im_in, 'RPI', generate_path=True).save().absolutepath nx, ny, nz, nt, px, py, pz, pt = im_in.dim if np.round(px, 2) != np.round(npx, 2) or np.round(py, 2) != np.round(npy, 2): name_resample = sct.extract_fname(fname)[1] + suffix if binary: interpolation = 'nn' if nz == 1: # when data is 2d: we convert it to a 3d image in order to avoid nipy problem of conversion nifti-->nipy with 2d data sct.run(['sct_image', '-i', ','.join([fname, fname]), '-concat', 'z', '-o', fname]) sct.run(['sct_resample', '-i', fname, '-mm', str(npx) + 'x' + str(npy) + 'x' + str(pz), '-o', name_resample, '-x', interpolation]) if nz == 1: # when input data was 2d: re-convert data 3d-->2d sct.run(['sct_image', '-i', name_resample, '-split', 'z']) im_split = Image(name_resample.split('.nii.gz')[0] + '_Z0000.nii.gz') im_split.save(name_resample) if binary: sct.run(['sct_maths', '-i', name_resample, '-bin', str(thr), '-o', name_resample]) if orientation != 'RPI': name_resample = Image(name_resample) \ .change_orientation(orientation, generate_path=True) \ .save() \ .absolutepath return name_resample else: if orientation != 'RPI': fname = sct.add_suffix(fname, "_RPI") im_in = msct_image.change_orientation(im_in, orientation).save(fname) sct.printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz') return fname
def change_type(self, dtype, generate_path=False): """ Change data type on image. Note: the image path is voided. """ if dtype is not None: change_type(self, dtype, self) if generate_path and self._path is not None: self._path = sct.add_suffix(self._path, "_{}".format(dtype.name)) else: # safe option: remove path to avoid overwrites self._path = None return self
def compute_texture(self): offset = int(self.param_glcm.distance) sct.printv('\nCompute texture metrics...', self.param.verbose, 'normal') # open image and re-orient it to RPI if needed im_tmp = Image(self.param.fname_im) if self.orientation_im != self.orientation_extraction: im_tmp.change_orientation(self.orientation_extraction) dct_metric = {} for m in self.metric_lst: im_2save = msct_image.zeros_like(im_tmp, dtype='float64') dct_metric[m] = im_2save # dct_metric[m] = Image(self.fname_metric_lst[m]) with tqdm.tqdm() as pbar: for im_z, seg_z, zz in zip(self.dct_im_seg['im'], self.dct_im_seg['seg'], range(len(self.dct_im_seg['im']))): for xx in range(im_z.shape[0]): for yy in range(im_z.shape[1]): if not seg_z[xx, yy]: continue if xx < offset or yy < offset: continue if xx > (im_z.shape[0] - offset - 1) or yy > (im_z.shape[1] - offset - 1): continue # to check if the whole glcm_window is in the axial_slice if False in np.unique(seg_z[xx - offset: xx + offset + 1, yy - offset: yy + offset + 1]): continue # to check if the whole glcm_window is in the mask of the axial_slice glcm_window = im_z[xx - offset: xx + offset + 1, yy - offset: yy + offset + 1] glcm_window = glcm_window.astype(np.uint8) dct_glcm = {} for a in self.param_glcm.angle.split(','): # compute the GLCM for self.param_glcm.distance and for each self.param_glcm.angle dct_glcm[a] = greycomatrix(glcm_window, [self.param_glcm.distance], [np.radians(int(a))], symmetric=self.param_glcm.symmetric, normed=self.param_glcm.normed) for m in self.metric_lst: # compute the GLCM property (m.split('_')[0]) of the voxel xx,yy,zz dct_metric[m].data[xx, yy, zz] = greycoprops(dct_glcm[m.split('_')[2]], m.split('_')[0])[0][0] pbar.set_postfix(pos="{}/{}".format(zz, len(self.dct_im_seg["im"]))) pbar.update(1) for m in self.metric_lst: fname_out = sct.add_suffix(''.join(sct.extract_fname(self.param.fname_im)[1:]), '_' + m) dct_metric[m].save(fname_out) self.fname_metric_lst[m] = fname_out
def load_manual_gmseg(list_slices_target, list_fname_manual_gmseg, tmp_dir, im_sc_seg_rpi, new_res, square_size_size_mm, for_model=False, fname_mask=None): if isinstance(list_fname_manual_gmseg, str): # consider fname_manual_gmseg as a list of file names to allow multiple manual GM segmentation list_fname_manual_gmseg = [list_fname_manual_gmseg] curdir = os.getcwd() for fname_manual_gmseg in list_fname_manual_gmseg: sct.copy(fname_manual_gmseg, tmp_dir) # change fname level to only file name (path = tmp dir now) path_gm, file_gm, ext_gm = extract_fname(fname_manual_gmseg) fname_manual_gmseg = file_gm + ext_gm os.chdir(tmp_dir) im_manual_gmseg = Image(fname_manual_gmseg).change_orientation("RPI") if fname_mask is not None: fname_gmseg_crop = add_suffix(im_manual_gmseg.absolutepath, '_pre_crop') crop_im = ImageCropper(input_file=im_manual_gmseg.absolutepath, output_file=fname_gmseg_crop, mask=fname_mask) im_manual_gmseg_crop = crop_im.crop() im_manual_gmseg = im_manual_gmseg_crop # assert gmseg has the right number of slices assert im_manual_gmseg.data.shape[2] == len(list_slices_target), 'ERROR: the manual GM segmentation has not the same number of slices than the image.' # interpolate gm to reference image nz_gmseg, nx_gmseg, ny_gmseg, nt_gmseg, pz_gmseg, px_gmseg, py_gmseg, pt_gmseg = im_manual_gmseg.dim list_im_gm = interpolate_im_to_ref(im_manual_gmseg, im_sc_seg_rpi, new_res=new_res, sq_size_size_mm=square_size_size_mm, interpolation_mode=0) # load gm seg in list of slices n_poped = 0 for im_gm, slice_im in zip(list_im_gm, list_slices_target): if im_gm.data.max() == 0 and for_model: list_slices_target.pop(slice_im.id - n_poped) n_poped += 1 else: slice_im.gm_seg.append(im_gm.data) wm_slice = (slice_im.im > 0) - im_gm.data slice_im.wm_seg.append(wm_slice) os.chdir(curdir) return list_slices_target
def pad_image(im, pad_x_i=0, pad_x_f=0, pad_y_i=0, pad_y_f=0, pad_z_i=0, pad_z_f=0): nx, ny, nz, nt, px, py, pz, pt = im.dim pad_x_i, pad_x_f, pad_y_i, pad_y_f, pad_z_i, pad_z_f = int(pad_x_i), int(pad_x_f), int(pad_y_i), int(pad_y_f), int(pad_z_i), int(pad_z_f) if len(im.data.shape) == 2: new_shape = list(im.data.shape) new_shape.append(1) im.data = im.data.reshape(new_shape) # initialize padded_data, with same type as im.data padded_data = np.zeros((nx + pad_x_i + pad_x_f, ny + pad_y_i + pad_y_f, nz + pad_z_i + pad_z_f), dtype=im.data.dtype) if pad_x_f == 0: pad_x_f = None elif pad_x_f > 0: pad_x_f *= -1 if pad_y_f == 0: pad_y_f = None elif pad_y_f > 0: pad_y_f *= -1 if pad_z_f == 0: pad_z_f = None elif pad_z_f > 0: pad_z_f *= -1 padded_data[pad_x_i:pad_x_f, pad_y_i:pad_y_f, pad_z_i:pad_z_f] = im.data im_out = im.copy() # TODO: Do not copy the Image(), because the dim field and hdr.get_data_shape() will not be updated properly. # better to just create a new Image() from scratch. im_out.data = padded_data # done after the call of the function im_out.absolutepath = sct.add_suffix(im_out.absolutepath, "_pad") # adapt the origin in the sform and qform matrix new_origin = np.dot(im_out.hdr.get_qform(), [-pad_x_i, -pad_y_i, -pad_z_i, 1]) im_out.hdr.structarr['qoffset_x'] = new_origin[0] im_out.hdr.structarr['qoffset_y'] = new_origin[1] im_out.hdr.structarr['qoffset_z'] = new_origin[2] im_out.hdr.structarr['srow_x'][-1] = new_origin[0] im_out.hdr.structarr['srow_y'][-1] = new_origin[1] im_out.hdr.structarr['srow_z'][-1] = new_origin[2] return im_out
def main(fname_anat, fname_centerline, verbose): """ Main function :param fname_anat: :param fname_centerline: :param verbose: :return: """ # load input images im_anat = Image(fname_anat) im_centerline = Image(fname_centerline) # flatten sagittal im_anat_flattened = flatten_sagittal(im_anat, im_centerline, verbose) # save output fname_out = sct.add_suffix(fname_anat, '_flatten') im_anat_flattened.save(fname_out) sct.display_viewer_syntax([fname_anat, fname_out])
def run_main(): parser = get_parser() arguments = parser.parse(sys.argv[1:]) input_filename = arguments["-i"] try: output_filename = arguments["-o"] except KeyError: output_filename = sct.add_suffix(input_filename, '_gmseg') use_tta = "-t" in arguments model_name = arguments["-m"] threshold = arguments['-thr'] verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level if threshold > 1.0 or threshold < 0.0: raise RuntimeError("Threshold should be between 0.0 and 1.0.") # Threshold zero means no thresholding if threshold == 0.0: threshold = None from spinalcordtoolbox.deepseg_gm import deepseg_gm deepseg_gm.check_backend() out_fname = deepseg_gm.segment_file(input_filename, output_filename, model_name, threshold, int(verbose), use_tta) path_qc = arguments.get("-qc", None) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) if path_qc is not None: generate_qc(fname_in1=input_filename, fname_seg=out_fname, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_deepseg_gm') sct.display_viewer_syntax([input_filename, format(out_fname)], colormaps=['gray', 'red'], opacities=['1', '0.7'], verbose=verbose)
def func_rescale_header(fname_data, rescale_factor, verbose=0): """ Rescale the voxel dimension by modifying the NIFTI header qform. Write the output file in a temp folder. :param fname_data: :param rescale_factor: :return: fname_data_rescaled """ import nibabel as nib img = nib.load(fname_data) # get qform qform = img.header.get_qform() # multiply by scaling factor qform[0:3, 0:3] *= rescale_factor # generate a new nifti file header_rescaled = img.header.copy() header_rescaled.set_qform(qform) # the data are the same-- only the header changes img_rescaled = nib.nifti1.Nifti1Image(img.get_data(), None, header=header_rescaled) path_tmp = sct.tmp_create(basename="propseg", verbose=verbose) fname_data_rescaled = os.path.join(path_tmp, os.path.basename(sct.add_suffix(fname_data, "_rescaled"))) nib.save(img_rescaled, fname_data_rescaled) return fname_data_rescaled
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True): if fname_grid is None: from numpy import zeros tmp_dir = sct.tmp_create() im_warp = Image(fname_warp) os.chdir(tmp_dir) assert len(im_warp.data.shape) == 5, "ERROR: Warping field does bot have 5 dimensions..." nx, ny, nz, nt, ndimwarp = im_warp.data.shape # nx, ny, nz, nt, px, py, pz, pt = im_warp.dim # This does not work because dimensions of a warping field are not correctly read : it would be 1,1,1,1,1,1,1,1 sq = zeros((step, step)) sq[step - 1] = 1 sq[:, step - 1] = 1 dat = zeros((nx, ny, nz)) for i in range(0, dat.shape[0], step): for j in range(0, dat.shape[1], step): for k in range(dat.shape[2]): if dat[i : i + step, j : j + step, k].shape == (step, step): dat[i : i + step, j : j + step, k] = sq fname_grid = "grid_" + str(step) + ".nii.gz" im_grid = Image(param=dat) grid_hdr = im_warp.hdr im_grid.hdr = grid_hdr im_grid.setFileName(fname_grid) im_grid.save() fname_grid_resample = sct.add_suffix(fname_grid, "_resample") sct.run("sct_resample -i " + fname_grid + " -f 3x3x1 -x nn -o " + fname_grid_resample) fname_grid = tmp_dir + fname_grid_resample os.chdir("..") path_warp, file_warp, ext_warp = sct.extract_fname(fname_warp) grid_warped = path_warp + "grid_warped_gm" + ext_warp sct.run("sct_apply_transfo -i " + fname_grid + " -d " + fname_grid + " -w " + fname_warp + " -o " + grid_warped) if rm_tmp: sct.run("rm -rf " + tmp_dir, error_exit="warning") return grid_warped
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True): if fname_grid is None: from numpy import zeros tmp_dir = tmp_create() im_warp = Image(fname_warp) status, out = run('fslhd '+fname_warp) from os import chdir chdir(tmp_dir) dim1 = 'dim1 ' dim2 = 'dim2 ' dim3 = 'dim3 ' nx = int(out[out.find(dim1):][len(dim1):out[out.find(dim1):].find('\n')]) ny = int(out[out.find(dim2):][len(dim2):out[out.find(dim2):].find('\n')]) nz = int(out[out.find(dim3):][len(dim3):out[out.find(dim3):].find('\n')]) sq = zeros((step, step)) sq[step-1] = 1 sq[:, step-1] = 1 dat = zeros((nx, ny, nz)) for i in range(0, dat.shape[0], step): for j in range(0, dat.shape[1], step): for k in range(dat.shape[2]): if dat[i:i+step, j:j+step, k].shape == (step, step): dat[i:i+step, j:j+step, k] = sq fname_grid = 'grid_'+str(step)+'.nii.gz' im_grid = Image(param=dat) grid_hdr = im_warp.hdr im_grid.hdr = grid_hdr im_grid.setFileName(fname_grid) im_grid.save() fname_grid_resample = add_suffix(fname_grid, '_resample') run('sct_resample -i '+fname_grid+' -f 3x3x1 -x nn -o '+fname_grid_resample) fname_grid = tmp_dir+fname_grid_resample chdir('..') path_warp, file_warp, ext_warp = extract_fname(fname_warp) grid_warped = path_warp+extract_fname(fname_grid)[1]+'_'+file_warp+ext_warp run('sct_apply_transfo -i '+fname_grid+' -d '+fname_grid+' -w '+fname_warp+' -o '+grid_warped) if rm_tmp: run('rm -rf '+tmp_dir, error_exit='warning')
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True): if fname_grid is None: from numpy import zeros tmp_dir = sct.tmp_create() im_warp = Image(fname_warp) status, out = sct.run(['fslhd', fname_warp]) curdir = os.getcwd() os.chdir(tmp_dir) dim1 = 'dim1 ' dim2 = 'dim2 ' dim3 = 'dim3 ' nx = int(out[out.find(dim1):][len(dim1):out[out.find(dim1):].find('\n')]) ny = int(out[out.find(dim2):][len(dim2):out[out.find(dim2):].find('\n')]) nz = int(out[out.find(dim3):][len(dim3):out[out.find(dim3):].find('\n')]) sq = zeros((step, step)) sq[step - 1] = 1 sq[:, step - 1] = 1 dat = zeros((nx, ny, nz)) for i in range(0, dat.shape[0], step): for j in range(0, dat.shape[1], step): for k in range(dat.shape[2]): if dat[i:i + step, j:j + step, k].shape == (step, step): dat[i:i + step, j:j + step, k] = sq fname_grid = 'grid_' + str(step) + '.nii.gz' im_grid = Image(param=dat) grid_hdr = im_warp.hdr im_grid.hdr = grid_hdr im_grid.absolutepath = fname_grid im_grid.save() fname_grid_resample = sct.add_suffix(fname_grid, '_resample') sct.run(['sct_resample', '-i', fname_grid, '-f', '3x3x1', '-x', 'nn', '-o', fname_grid_resample]) fname_grid = os.path.join(tmp_dir, fname_grid_resample) os.chdir(curdir) path_warp, file_warp, ext_warp = sct.extract_fname(fname_warp) grid_warped = os.path.join(path_warp, sct.extract_fname(fname_grid)[1] + '_' + file_warp + ext_warp) sct.run(['sct_apply_transfo', '-i', fname_grid, '-d', fname_grid, '-w', fname_warp, '-o', grid_warped]) if rm_tmp: sct.rmtree(tmp_dir)
def test_integrity(param_test): """ Test integrity of function """ # initializations distance_detection = float('nan') # extract name of output centerline: data_centerline_optic.nii.gz file_pmj = os.path.join(param_test.path_output, sct.add_suffix(param_test.file_input, '_pmj')) # open output segmentation im_pmj = Image(file_pmj) # open ground truth im_pmj_manual = Image(param_test.fname_gt) # compute Euclidean distance between predicted and GT PMJ label x_true, y_true, z_true = np.where(im_pmj_manual.data == 50) x_pred, y_pred, z_pred = np.where(im_pmj.data == 50) x_true, y_true, z_true = im_pmj_manual.transfo_pix2phys([[x_true[0], y_true[0], z_true[0]]])[0] x_pred, y_pred, z_pred = im_pmj.transfo_pix2phys([[x_pred[0], y_pred[0], z_pred[0]]])[0] distance_detection = math.sqrt(((x_true - x_pred))**2 + ((y_true - y_pred))**2 + ((z_true - z_pred))**2) param_test.output += 'Computed distance: ' + str(distance_detection) param_test.output += 'Distance threshold (if computed Distance higher: fail): ' + str(param_test.dist_threshold) if distance_detection > param_test.dist_threshold: param_test.status = 99 param_test.output += '--> FAILED' else: param_test.output += '--> PASSED' # update Panda structure param_test.results['distance_detection'] = distance_detection return param_test
def test_integrity(param_test): """ Test integrity of function """ # open ground truth im_seg_manual = Image(param_test.fname_gt).change_orientation("RPI") # Compute center of mass of the SC seg on each axial slice. center_of_mass_x_y_z_lst = [[int(center_of_mass(im_seg_manual.data[:, :, zz])[0]), int(center_of_mass(im_seg_manual.data[:, :, zz])[1]), zz] for zz in range(im_seg_manual.dim[2])] im_ctr_manual = msct_image.zeros_like(im_seg_manual) for x_y_z in center_of_mass_x_y_z_lst: im_ctr_manual.data[x_y_z[0], x_y_z[1], x_y_z[2]] = 1 # open output segmentation path_in, file_in, _ = sct.extract_fname(param_test.file_input) file_ctr = os.path.join(param_test.path_data, 't2s', sct.add_suffix(param_test.file_input, '_centerline')) im_ctr = Image(file_ctr).change_orientation("RPI") # compute MSE between generated ctr and ctr from database mse_detection = compute_mse(im_ctr, im_ctr_manual) param_test.output += 'Computed MSE: ' + str(mse_detection) param_test.output += 'MSE threshold (if computed MSE higher: fail): ' + str(param_test.mse_threshold) if mse_detection > param_test.mse_threshold: param_test.status = 99 param_test.output += '--> FAILED' else: param_test.output += '--> PASSED' # update Panda structure param_test.results['mse_detection'] = mse_detection return param_test
def change_shape(self, shape, generate_path=False): """ Change data shape (in-place) :param generate_path: whether to create a derived path name from the original absolutepath (note: while it will generate a file suffix, don't expect the suffix but rather use the Image's absolutepath. If not set, the absolutepath is voided. This is mostly useful for adding/removing a fourth dimension, you probably don't want to use this function. """ if shape is not None: change_shape(self, shape, self) if generate_path and self._path is not None: self._path = sct.add_suffix(self._path, "_shape-{}".format("-".join([str(x) for x in shape]))) else: # safe option: remove path to avoid overwrites self._path = None return self
def multicomponent_split(im): """ Convert composite image (e.g., ITK warping field, 5dim) into several 3d volumes. Replaces "c3d -mcs warp_comp.nii -oo warp_vecx.nii warp_vecy.nii warp_vecz.nii" :param im: :return: """ data = im.data assert len(data.shape) == 5 data_out = [] for i in range(data.shape[-1]): dat_out = data[:, :, :, :, i] ''' while dat_out.shape[-1] == 1: dat_out = reshape(dat_out, dat_out.shape[:-1]) ''' data_out.append(dat_out) # .astype('float32')) im_out = [im.copy() for j in range(len(data_out))] for i, im in enumerate(im_out): im.data = data_out[i] im.hdr.set_intent('vector', (), '') im.absolutepath = sct.add_suffix(im.absolutepath, "_{}".format(i)) return im_out
def split_data(im_in, dim, squeeze_data=True): """ Split data :param im_in: input image. :param dim: dimension: 0, 1, 2, 3. :return: list of split images """ dim_list = ['x', 'y', 'z', 't'] # Parse file name # Open first file. data = im_in.data # in case input volume is 3d and dim=t, create new axis if dim + 1 > len(np.shape(data)): data = data[..., np.newaxis] # in case splitting along the last dim, make sure to remove the last dim to avoid singleton if dim + 1 == len(np.shape(data)): if squeeze_data: do_reshape = True else: do_reshape = False else: do_reshape = False # Split data into list data_split = np.array_split(data, data.shape[dim], dim) # Write each file im_out_list = [] for idx_img, dat in enumerate(data_split): im_out = msct_image.empty_like(im_in) if do_reshape: im_out.data = dat.reshape(tuple([ x for (idx_shape, x) in enumerate(data.shape) if idx_shape != dim])) else: im_out.data = dat im_out.absolutepath = sct.add_suffix(im_in.absolutepath, "_{}{}".format(dim_list[dim].upper(), str(idx_img).zfill(4))) im_out_list.append(im_out) return im_out_list