def compute_ratio(self): type_ratio = self.param_seg.ratio tmp_dir_ratio = 'tmp_ratio/' os.mkdir(tmp_dir_ratio) os.chdir(tmp_dir_ratio) fname_gmseg = self.im_res_gmseg.absolutepath fname_wmseg = self.im_res_wmseg.absolutepath self.im_res_gmseg.save() self.im_res_wmseg.save() if self.im_res_gmseg.orientation is not 'RPI': im_res_gmseg = set_orientation(self.im_res_gmseg, 'RPI') im_res_wmseg = set_orientation(self.im_res_wmseg, 'RPI') fname_gmseg = im_res_gmseg.absolutepath fname_wmseg = im_res_wmseg.absolutepath sct_process_segmentation.main(['-i', fname_gmseg, '-p', 'csa', '-ofolder', 'gm_csa', '-no-angle', '1']) sct_process_segmentation.main(['-i', fname_wmseg, '-p', 'csa', '-ofolder', 'wm_csa', '-no-angle', '1']) gm_csa = open('gm_csa/csa_per_slice.txt', 'r') wm_csa = open('wm_csa/csa_per_slice.txt', 'r') gm_csa_lines = gm_csa.readlines() wm_csa_lines = wm_csa.readlines() gm_csa.close() wm_csa.close() fname_ratio = 'ratio_by_' + type_ratio + '.txt' file_ratio = open(fname_ratio, 'w') file_ratio.write(type_ratio + ', ratio GM/WM CSA\n') csa_gm_wm_by_level = {0: [], 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: [], 13: [], 14: [], 15: [], 16: [], 17: [], 18: [], 19: [], 20: [], 21: [], 22: [], 23: [], 24: []} for gm_line, wm_line in zip(gm_csa_lines[1:], wm_csa_lines[1:]): i, gm_area, gm_angle = gm_line.split(',') j, wm_area, wm_angle = wm_line.split(',') assert i == j if type_ratio == 'level': level_slice = int(self.target_im[int(i)].level) csa_gm_wm_by_level[level_slice].append((float(gm_area), float(wm_area))) else: file_ratio.write(i + ', ' + str(float(gm_area) / float(wm_area)) + '\n') if type_ratio == 'level': for l, gm_wm_list in sorted(csa_gm_wm_by_level.items()): if str(gm_wm_list) != '[]': csa_gm_list = [] csa_wm_list = [] for gm, wm in gm_wm_list: csa_gm_list.append(gm) csa_wm_list.append(wm) csa_gm = np.mean(csa_gm_list) csa_wm = np.mean(csa_wm_list) file_ratio.write(str(l) + ', ' + str(csa_gm / csa_wm) + '\n') file_ratio.close() shutil.copy(fname_ratio, '../../' + self.param_seg.path_results + '/' + fname_ratio) os.chdir('..')
def extract_slices(self): # open image and re-orient it to RPI if needed im, seg = Image(self.param.fname_im), Image(self.param.fname_seg) if self.orientation_im != self.orientation_extraction: im, seg = set_orientation(im, self.orientation_extraction), set_orientation(seg, self.orientation_extraction) # extract axial slices in self.dct_im_seg self.dct_im_seg['im'], self.dct_im_seg['seg'] = [im.data[:, :, z] for z in range(im.dim[2])], [seg.data[:, :, z] for z in range(im.dim[2])]
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 = get_orientation_3d(im_in) if orientation != 'RPI': im_in = set_orientation(im_in, 'RPI') im_in.save() fname = im_in.absolutepath nx, ny, nz, nt, px, py, pz, pt = im_in.dim if round(px, 2) != round(npx, 2) or round(py, 2) != round(npy, 2): name_resample = sct.extract_fname(fname)[1] + suffix if binary: interpolation = 'nn' sct.run('sct_resample -i ' + fname + ' -mm ' + str(npx) + 'x' + str(npy) + 'x' + str(pz) + ' -o ' + name_resample + ' -x ' + interpolation) if binary: # sct.run('sct_maths -i ' + name_resample + ' -thr ' + str(thr) + ' -o ' + name_resample) sct.run('sct_maths -i ' + name_resample + ' -bin ' + str(thr) + ' -o ' + name_resample) if orientation != 'RPI': im_resample = Image(name_resample) im_resample = set_orientation(im_resample, orientation) im_resample.save() name_resample = im_resample.absolutepath return name_resample else: if orientation != 'RPI': im_in = set_orientation(im_in, orientation) im_in.save() fname = im_in.absolutepath sct.printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz') return fname
def get_im_from_list(self, data): im = Image(data) # set pix dimension im.hdr.structarr['pixdim'][1] = self.param_data.axial_res im.hdr.structarr['pixdim'][2] = self.param_data.axial_res # set the correct orientation im.setFileName('im_to_orient.nii.gz') im.save() im = set_orientation(im, 'IRP') im = set_orientation(im, 'PIL', data_inversion=True) return im
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 = get_orientation(im_in) if orientation != 'RPI': im_in = set_orientation(im_in, 'RPI') im_in.save() fname = im_in.absolutepath nx, ny, nz, nt, px, py, pz, pt = im_in.dim if round(px, 2) != round(npx, 2) or round(py, 2) != 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.setFileName(name_resample) im_split.save() if binary: sct.run(['sct_maths', '-i', name_resample, '-bin', str(thr), '-o', name_resample]) if orientation != 'RPI': im_resample = Image(name_resample) im_resample = set_orientation(im_resample, orientation) im_resample.save() name_resample = im_resample.absolutepath return name_resample else: if orientation != 'RPI': im_in = set_orientation(im_in, orientation) im_in.save() fname = im_in.absolutepath sct.printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz') return fname
def orient2pir(self): """Orient input data to PIR orientation.""" if self.orientation_im != 'PIR': # open image and re-orient it to PIR if needed im_tmp = Image(self.fname_im) set_orientation(im_tmp, 'PIR', fname_out=''.join( sct.extract_fname(self.fname_im)[1:])) if self.fname_seg is not None: set_orientation(Image(self.fname_seg), 'PIR', fname_out=''.join( sct.extract_fname(self.fname_seg)[1:]))
def post_processing(self): square_mask = Image(self.preprocessed.square_mask) tmp_res_names = [] for res_im in [self.gm_seg.res_wm_seg, self.gm_seg.res_gm_seg, self.gm_seg.corrected_wm_seg]: res_im_original_space = inverse_square_crop(res_im, square_mask) res_im_original_space.save() res_im_original_space = set_orientation(res_im_original_space, self.preprocessed.original_orientation) res_im_original_space.save() res_fname_original_space = res_im_original_space.file_name ext = res_im_original_space.ext # crop from the same pad size output_crop = res_fname_original_space+'_crop' sct.run('sct_crop_image -i '+res_fname_original_space+ext+' -dim 0,1,2 -start '+self.preprocessed.pad[0]+','+self.preprocessed.pad[1]+','+self.preprocessed.pad[2]+' -end -'+self.preprocessed.pad[0]+',-'+self.preprocessed.pad[1]+',-'+self.preprocessed.pad[2]+' -o '+output_crop+ext) res_fname_original_space = output_crop target_path, target_name, target_ext = sct.extract_fname(self.target_fname) res_name = target_name + res_im.file_name[len(sct.extract_fname(self.preprocessed.processed_target)[1]):] + '.nii.gz' if self.seg_param.res_type == 'binary': bin = True else: bin = False old_res_name = resample_image(res_fname_original_space+ext, npx=self.preprocessed.original_px, npy=self.preprocessed.original_py, binary=bin) if self.seg_param.res_type == 'prob': # sct.run('fslmaths ' + old_res_name + ' -thr 0.05 ' + old_res_name) sct.run('sct_maths -i ' + old_res_name + ' -thr 0.05 -o ' + old_res_name) sct.run('cp ' + old_res_name + ' '+res_name) tmp_res_names.append(res_name) self.res_names['wm_seg'] = tmp_res_names[0] self.res_names['gm_seg'] = tmp_res_names[1] self.res_names['corrected_wm_seg'] = tmp_res_names[2]
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 = Image(param=self.zhang_suen(self.image.data), absolutepath=self.image.path + self.image.file_name + '_thinned' + self.image.ext, hdr=self.image.hdr) elif self.dim_im == 3: if not self.image.orientation == 'IRP': from sct_image import set_orientation print '-- changing orientation ...' self.image = set_orientation(self.image, 'IRP') thinned_data = np.asarray( [self.zhang_suen(im_slice) for im_slice in self.image.data]) self.thinned_image = Image(param=thinned_data, absolutepath=self.image.path + self.image.file_name + '_thinned' + self.image.ext, hdr=self.image.hdr)
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 generate_mask_pmj(self): """Output the PMJ mask.""" if self.pa_coord != -1: # If PMJ has been detected im = Image(''.join(extract_fname( self.fname_im)[1:])) # image in PIR orientation im_mask = im.copy() im_mask.data *= 0 # empty mask im_mask.data[self.pa_coord, self.is_coord, self.rl_coord] = 50 # voxel with value = 50 if self.quality_control: # output QC image self.save_qc(im.data[:, :, self.rl_coord], [self.pa_coord, self.is_coord]) im_mask.setFileName(self.fname_out) im_mask = set_orientation( im_mask, self.orientation_im, fname_out=self.fname_out) # reorient data x_pmj, y_pmj, z_pmj = np.where(im_mask.data == 50) printv('\tx_pmj = ' + str(x_pmj[0]), self.verbose, 'info') printv('\ty_pmj = ' + str(y_pmj[0]), self.verbose, 'info') printv('\tz_pmj = ' + str(z_pmj[0]), self.verbose, 'info') im_mask.save()
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 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 = get_orientation_3d(im_in) if orientation != 'RPI': im_in = set_orientation(im_in, 'RPI') im_in.save() fname = im_in.absolutepath nx, ny, nz, nt, px, py, pz, pt = im_in.dim if round(px, 2) != round(npx, 2) or round(py, 2) != round(npy, 2): name_resample = sct.extract_fname(fname)[1] + suffix if binary: interpolation = 'nn' sct.run('sct_resample -i '+fname+' -mm '+str(npx)+'x'+str(npy)+'x'+str(pz)+' -o '+name_resample+' -x '+interpolation) if binary: # sct.run('sct_maths -i ' + name_resample + ' -thr ' + str(thr) + ' -o ' + name_resample) sct.run('sct_maths -i ' + name_resample + ' -bin ' + str(thr) + ' -o ' + name_resample) if orientation != 'RPI': im_resample = Image(name_resample) im_resample = set_orientation(im_resample, orientation) im_resample.save() name_resample = im_resample.absolutepath return name_resample else: if orientation != 'RPI': im_in = set_orientation(im_in, orientation) im_in.save() fname = im_in.absolutepath sct.printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz') return fname
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 compute_length(fname_segmentation, remove_temp_files, verbose = 0): from math import sqrt # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S") + '_'+str(randint(1, 1000000)), 1) sct.run('mkdir '+path_tmp, verbose) # copy files into tmp folder sct.run('cp '+fname_segmentation+' '+path_tmp) # go to tmp folder os.chdir(path_tmp) # Change orientation of the input centerline into RPI sct.printv('\nOrient centerline to RPI orientation...', param.verbose) im_seg = Image(file_data+ext_data) fname_segmentation_orient = 'segmentation_rpi' + ext_data im_seg_orient = set_orientation(im_seg, 'RPI') im_seg_orient.setFileName(fname_segmentation_orient) im_seg_orient.save() # Get dimension sct.printv('\nGet dimensions...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = im_seg_orient.dim sct.printv('.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz), param.verbose) sct.printv('.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm', param.verbose) # smooth segmentation/centerline #x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv,y_centerline_deriv,z_centerline_deriv = smooth_centerline(fname_segmentation_orient, param, 'hanning', 1) x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv,y_centerline_deriv,z_centerline_deriv = smooth_centerline(fname_segmentation_orient, type_window='hanning', window_length=80, algo_fitting='hanning', verbose = verbose) # compute length of centerline result_length = 0.0 for i in range(len(x_centerline_fit)-1): result_length += sqrt(((x_centerline_fit[i+1]-x_centerline_fit[i])*px)**2+((y_centerline_fit[i+1]-y_centerline_fit[i])*py)**2+((z_centerline[i+1]-z_centerline[i])*pz)**2) return result_length
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): 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') # 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 interpolate_im_to_ref(im_input, im_input_sc, new_res=0.3, sq_size_size_mm=22.5, interpolation_mode=3): nx, ny, nz, nt, px, py, pz, pt = im_input.dim im_input_sc = im_input_sc.copy() im_input= im_input.copy() # keep only spacing and origin in qform to avoid rotation issues input_qform = im_input.hdr.get_qform() for i in range(4): for j in range(4): if i!=j and j!=3: input_qform[i, j] = 0 im_input.hdr.set_qform(input_qform) im_input.hdr.set_sform(input_qform) im_input_sc.hdr = im_input.hdr sq_size = int(sq_size_size_mm/new_res) # create a reference image : square of ones im_ref = Image(np.ones((sq_size, sq_size, 1), dtype=np.int), dim=(sq_size, sq_size, 1, 0, new_res, new_res, pz, 0), orientation='RPI') # copy input qform matrix to reference image im_ref.hdr.set_qform(im_input.hdr.get_qform()) im_ref.hdr.set_sform(im_input.hdr.get_sform()) # set correct header to reference image im_ref.hdr.set_data_shape((sq_size, sq_size, 1)) im_ref.hdr.set_zooms((new_res, new_res, pz)) # save image to set orientation to RPI (not properly done at the creation of the image) fname_ref = 'im_ref.nii.gz' im_ref.setFileName(fname_ref) im_ref.save() im_ref = set_orientation(im_ref, 'RPI', fname_out=fname_ref) # set header origin to zero to get physical coordinates of the center of the square im_ref.hdr.as_analyze_map()['qoffset_x'] = 0 im_ref.hdr.as_analyze_map()['qoffset_y'] = 0 im_ref.hdr.as_analyze_map()['qoffset_z'] = 0 im_ref.hdr.set_sform(im_ref.hdr.get_qform()) im_ref.hdr.set_qform(im_ref.hdr.get_qform()) [[x_square_center_phys, y_square_center_phys, z_square_center_phys]] = im_ref.transfo_pix2phys(coordi=[[int(sq_size / 2), int(sq_size / 2), 0]]) list_interpolate_images = [] # iterate on z dimension of input image for iz in range(nz): # copy reference image: one reference image per slice im_ref_slice_iz = im_ref.copy() # get center of mass of SC for slice iz x_seg, y_seg = (im_input_sc.data[:, :, iz] > 0).nonzero() x_center, y_center = np.mean(x_seg), np.mean(y_seg) [[x_center_phys, y_center_phys, z_center_phys]] = im_input_sc.transfo_pix2phys(coordi=[[x_center, y_center, iz]]) # center reference image on SC for slice iz im_ref_slice_iz.hdr.as_analyze_map()['qoffset_x'] = x_center_phys - x_square_center_phys im_ref_slice_iz.hdr.as_analyze_map()['qoffset_y'] = y_center_phys - y_square_center_phys im_ref_slice_iz.hdr.as_analyze_map()['qoffset_z'] = z_center_phys im_ref_slice_iz.hdr.set_sform(im_ref_slice_iz.hdr.get_qform()) im_ref_slice_iz.hdr.set_qform(im_ref_slice_iz.hdr.get_qform()) # interpolate input image to reference image im_input_interpolate_iz = im_input.interpolate_from_image(im_ref_slice_iz, interpolation_mode=interpolation_mode, border='nearest') # reshape data to 2D if needed if len(im_input_interpolate_iz.data.shape) == 3: im_input_interpolate_iz.data = im_input_interpolate_iz.data.reshape(im_input_interpolate_iz.data.shape[:-1]) # add slice to list list_interpolate_images.append(im_input_interpolate_iz) return list_interpolate_images
def compute_properties_along_centerline(fname_seg_image, property_list, fname_disks_image=None, smooth_factor=5.0, interpolation_mode=0, remove_temp_files=1, verbose=1): # Check list of properties # If diameters is in the list, compute major and minor axis length and check orientation compute_diameters = False property_list_local = list(property_list) if 'diameters' in property_list_local: compute_diameters = True property_list_local.remove('diameters') property_list_local.append('major_axis_length') property_list_local.append('minor_axis_length') property_list_local.append('orientation') # TODO: make sure fname_segmentation and fname_disks are in the same space # create temporary folder and copying data sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end( 'tmp.' + time.strftime("%y%m%d%H%M%S") + '_' + str(randint(1, 1000000)), 1) sct.run('mkdir ' + path_tmp, verbose) sct.run('cp ' + fname_seg_image + ' ' + path_tmp) if fname_disks_image is not None: sct.run('cp ' + fname_disks_image + ' ' + path_tmp) # go to tmp folder os.chdir(path_tmp) fname_segmentation = os.path.abspath(fname_seg_image) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # Change orientation of the input centerline into RPI sct.printv('\nOrient centerline to RPI orientation...', verbose) im_seg = Image(file_data + ext_data) fname_segmentation_orient = 'segmentation_rpi' + ext_data image = set_orientation(im_seg, 'RPI') image.setFileName(fname_segmentation_orient) image.save() # Initiating some variables nx, ny, nz, nt, px, py, pz, pt = image.dim resolution = 0.5 properties = {key: [] for key in property_list_local} properties['incremental_length'] = [] properties['distance_from_C1'] = [] properties['vertebral_level'] = [] properties['z_slice'] = [] # compute the spinal cord centerline based on the spinal cord segmentation number_of_points = 5 * nz x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline( fname_segmentation_orient, algo_fitting='nurbs', verbose=verbose, nurbs_pts_number=number_of_points, all_slices=False, phys_coordinates=True, remove_outliers=True) centerline = Centerline(x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv) # Compute vertebral distribution along centerline based on position of intervertebral disks if fname_disks_image is not None: fname_disks = os.path.abspath(fname_disks_image) path_data, file_data, ext_data = sct.extract_fname(fname_disks) im_disks = Image(file_data + ext_data) fname_disks_orient = 'disks_rpi' + ext_data image_disks = set_orientation(im_disks, 'RPI') image_disks.setFileName(fname_disks_orient) image_disks.save() image_disks = Image(fname_disks_orient) coord = image_disks.getNonZeroCoordinates(sorting='z', reverse_coord=True) coord_physical = [] for c in coord: c_p = image_disks.transfo_pix2phys([[c.x, c.y, c.z]])[0] c_p.append(c.value) coord_physical.append(c_p) centerline.compute_vertebral_distribution(coord_physical) sct.printv('Computing spinal cord shape along the spinal cord...') timer_properties = sct.Timer( number_of_iteration=centerline.number_of_points) timer_properties.start() # Extracting patches perpendicular to the spinal cord and computing spinal cord shape for index in range(centerline.number_of_points): # value_out = -5.0 value_out = 0.0 current_patch = centerline.extract_perpendicular_square( image, index, resolution=resolution, interpolation_mode=interpolation_mode, border='constant', cval=value_out) # check for pixels close to the spinal cord segmentation that are out of the image from skimage.morphology import dilation patch_zero = np.copy(current_patch) patch_zero[patch_zero == value_out] = 0.0 patch_borders = dilation(patch_zero) - patch_zero """ if np.count_nonzero(patch_borders + current_patch == value_out + 1.0) != 0: c = image.transfo_phys2pix([centerline.points[index]])[0] print 'WARNING: no patch for slice', c[2] timer_properties.add_iteration() continue """ sc_properties = properties2d(patch_zero, [resolution, resolution]) if sc_properties is not None: properties['incremental_length'].append( centerline.incremental_length[index]) if fname_disks_image is not None: properties['distance_from_C1'].append( centerline.dist_points[index]) properties['vertebral_level'].append( centerline.l_points[index]) properties['z_slice'].append( image.transfo_phys2pix([centerline.points[index]])[0][2]) for property_name in property_list_local: properties[property_name].append(sc_properties[property_name]) else: c = image.transfo_phys2pix([centerline.points[index]])[0] print 'WARNING: no properties for slice', c[2] timer_properties.add_iteration() timer_properties.stop() # Adding centerline to the properties for later use properties['centerline'] = centerline # We assume that the major axis is in the right-left direction # this script checks the orientation of the spinal cord and invert axis if necessary to make sure the major axis is right-left if compute_diameters: diameter_major = properties['major_axis_length'] diameter_minor = properties['minor_axis_length'] orientation = properties['orientation'] for i, orientation_item in enumerate(orientation): if -45.0 < orientation_item < 45.0: continue else: temp = diameter_minor[i] properties['minor_axis_length'][i] = diameter_major[i] properties['major_axis_length'][i] = temp properties['RL_diameter'] = properties['major_axis_length'] properties['AP_diameter'] = properties['minor_axis_length'] del properties['major_axis_length'] del properties['minor_axis_length'] # smooth the spinal cord shape with a gaussian kernel if required # TODO: not all properties can be smoothed if smooth_factor != 0.0: # smooth_factor is in mm import scipy window = scipy.signal.hann(smooth_factor / np.mean(centerline.progressive_length)) for property_name in property_list_local: properties[property_name] = scipy.signal.convolve( properties[property_name], window, mode='same') / np.sum(window) if compute_diameters: property_list_local.remove('major_axis_length') property_list_local.remove('minor_axis_length') property_list_local.append('RL_diameter') property_list_local.append('AP_diameter') property_list = property_list_local # Display properties on the referential space. Requires intervertebral disks if verbose == 2: x_increment = 'distance_from_C1' if fname_disks_image is None: x_increment = 'incremental_length' # Display the image and plot all contours found fig, axes = plt.subplots(len(property_list_local), sharex=True, sharey=False) for k, property_name in enumerate(property_list_local): axes[k].plot(properties[x_increment], properties[property_name]) axes[k].set_ylabel(property_name) if fname_disks_image is not None: properties[ 'distance_disk_from_C1'] = centerline.distance_from_C1label # distance between each disk and C1 (or first disk) xlabel_disks = [ centerline.convert_vertlabel2disklabel[label] for label in properties['distance_disk_from_C1'] ] xtick_disks = [ properties['distance_disk_from_C1'][label] for label in properties['distance_disk_from_C1'] ] plt.xticks(xtick_disks, xlabel_disks, rotation=30) else: axes[-1].set_xlabel('Position along the spinal cord (in mm)') plt.show() # Removing temporary folder os.chdir('..') shutil.rmtree(path_tmp, ignore_errors=True) return property_list, properties
def main(args=None): # Initialization # fname_anat = '' # fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) # remove_temp_files = param.remove_temp_files # verbose = param.verbose start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-r' in arguments: remove_temp_files = int(arguments['-r']) if '-v' in arguments: verbose = int(arguments['-v']) # Display arguments print '\nCheck input arguments...' print ' Volume to smooth .................. ' + fname_anat print ' Centerline ........................ ' + fname_centerline print ' Sigma (mm) ........................ '+str(sigma) print ' Verbose ........................... '+str(verbose) # Check that input is 3D: from msct_image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv('WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i '+fname_anat+' -split t -o '+fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname(fname_centerline) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('cp '+fname_anat+' '+path_tmp+'anat'+ext_anat, verbose) sct.run('cp '+fname_centerline+' '+path_tmp+'centerline'+ext_centerline, verbose) # go to tmp folder os.chdir(path_tmp) # convert to nii format convert('anat'+ext_anat, 'anat.nii') convert('centerline'+ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI print '\nOrient input volume to RPI orientation...' fname_anat_rpi = set_orientation('anat.nii', 'RPI', filename=True) move(fname_anat_rpi, 'anat_rpi.nii') # Change orientation of the input image into RPI print '\nOrient centerline to RPI orientation...' fname_centerline_rpi = set_orientation('centerline.nii', 'RPI', filename=True) move(fname_centerline_rpi, 'centerline_rpi.nii') # Straighten the spinal cord # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'): # if they exist, copy them into current folder sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning') shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz') shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz') shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz') # apply straightening sct.run('sct_apply_transfo -i anat_rpi.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o anat_rpi_straight.nii -x spline', verbose) else: sct.run('sct_straighten_spinalcord -i anat_rpi.nii -s centerline_rpi.nii -qc 0 -x spline', verbose) # Smooth the straightened image along z print '\nSmooth the straightened image along z...' sct.run('sct_maths -i anat_rpi_straight.nii -smooth 0,0,'+str(sigma)+' -o anat_rpi_straight_smooth.nii', verbose) # Apply the reversed warping field to get back the curved spinal cord print '\nApply the reversed warping field to get back the curved spinal cord...' sct.run('sct_apply_transfo -i anat_rpi_straight_smooth.nii -o anat_rpi_straight_smooth_curved.nii -d anat.nii -w warp_straight2curve.nii.gz -x spline', verbose) # replace zeroed voxels by original image (issue #937) sct.printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.setFileName('anat_rpi_straight_smooth_curved_nonzero.nii') nii_smooth.save() # come back to parent folder os.chdir('..') # Generate output file print '\nGenerate output file...' sct.generate_output_file(path_tmp+'/anat_rpi_straight_smooth_curved_nonzero.nii', file_anat+'_smooth'+ext_anat) # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp) # Display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s\n' # to view results sct.printv('Done! To view results, type:', verbose) sct.printv('fslview '+file_anat+' '+file_anat+'_smooth &\n', verbose, 'info')
def interpolate_im_to_ref(im_input, im_input_sc, new_res=0.3, sq_size_size_mm=22.5, interpolation_mode=3): nx, ny, nz, nt, px, py, pz, pt = im_input.dim im_input_sc = im_input_sc.copy() im_input = im_input.copy() # keep only spacing and origin in qform to avoid rotation issues input_qform = im_input.hdr.get_qform() for i in range(4): for j in range(4): if i != j and j != 3: input_qform[i, j] = 0 im_input.hdr.set_qform(input_qform) im_input.hdr.set_sform(input_qform) im_input_sc.hdr = im_input.hdr sq_size = int(sq_size_size_mm / new_res) # create a reference image : square of ones im_ref = Image(np.ones((sq_size, sq_size, 1), dtype=np.int), dim=(sq_size, sq_size, 1, 0, new_res, new_res, pz, 0), orientation='RPI') # copy input qform matrix to reference image im_ref.hdr.set_qform(im_input.hdr.get_qform()) im_ref.hdr.set_sform(im_input.hdr.get_sform()) # set correct header to reference image im_ref.hdr.set_data_shape((sq_size, sq_size, 1)) im_ref.hdr.set_zooms((new_res, new_res, pz)) # save image to set orientation to RPI (not properly done at the creation of the image) fname_ref = 'im_ref.nii.gz' im_ref.setFileName(fname_ref) im_ref.save() im_ref = set_orientation(im_ref, 'RPI', fname_out=fname_ref) # set header origin to zero to get physical coordinates of the center of the square im_ref.hdr.as_analyze_map()['qoffset_x'] = 0 im_ref.hdr.as_analyze_map()['qoffset_y'] = 0 im_ref.hdr.as_analyze_map()['qoffset_z'] = 0 im_ref.hdr.set_sform(im_ref.hdr.get_qform()) im_ref.hdr.set_qform(im_ref.hdr.get_qform()) [[x_square_center_phys, y_square_center_phys, z_square_center_phys]] = im_ref.transfo_pix2phys( coordi=[[int(sq_size / 2), int(sq_size / 2), 0]]) list_interpolate_images = [] # iterate on z dimension of input image for iz in range(nz): # copy reference image: one reference image per slice im_ref_slice_iz = im_ref.copy() # get center of mass of SC for slice iz x_seg, y_seg = (im_input_sc.data[:, :, iz] > 0).nonzero() x_center, y_center = np.mean(x_seg), np.mean(y_seg) [[x_center_phys, y_center_phys, z_center_phys] ] = im_input_sc.transfo_pix2phys(coordi=[[x_center, y_center, iz]]) # center reference image on SC for slice iz im_ref_slice_iz.hdr.as_analyze_map( )['qoffset_x'] = x_center_phys - x_square_center_phys im_ref_slice_iz.hdr.as_analyze_map( )['qoffset_y'] = y_center_phys - y_square_center_phys im_ref_slice_iz.hdr.as_analyze_map()['qoffset_z'] = z_center_phys im_ref_slice_iz.hdr.set_sform(im_ref_slice_iz.hdr.get_qform()) im_ref_slice_iz.hdr.set_qform(im_ref_slice_iz.hdr.get_qform()) # interpolate input image to reference image im_input_interpolate_iz = im_input.interpolate_from_image( im_ref_slice_iz, interpolation_mode=interpolation_mode, border='nearest') # reshape data to 2D if needed if len(im_input_interpolate_iz.data.shape) == 3: im_input_interpolate_iz.data = im_input_interpolate_iz.data.reshape( im_input_interpolate_iz.data.shape[:-1]) # add slice to list list_interpolate_images.append(im_input_interpolate_iz) return list_interpolate_images
def post_processing(self): # DO INTERPOLATION BACK TO ORIGINAL IMAGE # get original SC segmentation oriented in RPI im_sc_seg_original_rpi = self.info_preprocessing['im_sc_seg_rpi'].copy() nx_ref, ny_ref, nz_ref, nt_ref, px_ref, py_ref, pz_ref, pt_ref = im_sc_seg_original_rpi.dim # create res GM seg image im_res_gmseg = im_sc_seg_original_rpi.copy() im_res_gmseg.data = np.zeros(im_res_gmseg.data.shape) printv(' Interpolate result back into original space...', self.param.verbose, 'normal') for iz, im_iz_preprocessed in enumerate(self.info_preprocessing['interpolated_images']): # im gmseg for slice iz im_gmseg = im_iz_preprocessed.copy() im_gmseg.data = np.zeros(im_gmseg.data.shape) im_gmseg.data = self.target_im[iz].gm_seg im_res_slice, im_res_tot = (im_gmseg, im_res_gmseg) # get reference image for this slice # (use only one slice to accelerate interpolation) im_ref = im_sc_seg_original_rpi.copy() im_ref.data = im_ref.data[:, :, iz] im_ref.dim = (nx_ref, ny_ref, 1, nt_ref, px_ref, py_ref, pz_ref, pt_ref) # correct reference header for this slice [[x_0_ref, y_0_ref, z_0_ref]] = im_ref.transfo_pix2phys(coordi=[[0, 0, iz]]) im_ref.hdr.as_analyze_map()['qoffset_x'] = x_0_ref im_ref.hdr.as_analyze_map()['qoffset_y'] = y_0_ref im_ref.hdr.as_analyze_map()['qoffset_z'] = z_0_ref im_ref.hdr.set_sform(im_ref.hdr.get_qform()) im_ref.hdr.set_qform(im_ref.hdr.get_qform()) # set im_res_slice header with im_sc_seg_original_rpi origin im_res_slice.hdr.as_analyze_map()['qoffset_x'] = x_0_ref im_res_slice.hdr.as_analyze_map()['qoffset_y'] = y_0_ref im_res_slice.hdr.as_analyze_map()['qoffset_z'] = z_0_ref im_res_slice.hdr.set_sform(im_res_slice.hdr.get_qform()) im_res_slice.hdr.set_qform(im_res_slice.hdr.get_qform()) # get physical coordinates of center of sc x_seg, y_seg = (im_sc_seg_original_rpi.data[:, :, iz] > 0).nonzero() x_center, y_center = np.mean(x_seg), np.mean(y_seg) [[x_center_phys, y_center_phys, z_center_phys]] = im_sc_seg_original_rpi.transfo_pix2phys(coordi=[[x_center, y_center, iz]]) # get physical coordinates of center of square WITH im_res_slice WITH SAME ORIGIN AS im_sc_seg_original_rpi sq_size_pix = int(self.param_data.square_size_size_mm / self.param_data.axial_res) [[x_square_center_phys, y_square_center_phys, z_square_center_phys]] = im_res_slice.transfo_pix2phys( coordi=[[int(sq_size_pix / 2), int(sq_size_pix / 2), 0]]) # set im_res_slice header by adding center of SC and center of square (in the correct space) to origin im_res_slice.hdr.as_analyze_map()['qoffset_x'] += x_center_phys - x_square_center_phys im_res_slice.hdr.as_analyze_map()['qoffset_y'] += y_center_phys - y_square_center_phys im_res_slice.hdr.as_analyze_map()['qoffset_z'] += z_center_phys im_res_slice.hdr.set_sform(im_res_slice.hdr.get_qform()) im_res_slice.hdr.set_qform(im_res_slice.hdr.get_qform()) # reshape data im_res_slice.data = im_res_slice.data.reshape((sq_size_pix, sq_size_pix, 1)) # interpolate to reference image interp = 1 im_res_slice_interp = im_res_slice.interpolate_from_image(im_ref, interpolation_mode=interp, border='nearest') # set correct slice of total image with this slice if len(im_res_slice_interp.data.shape) == 3: shape_x, shape_y, shape_z = im_res_slice_interp.data.shape im_res_slice_interp.data = im_res_slice_interp.data.reshape((shape_x, shape_y)) im_res_tot.data[:, :, iz] = im_res_slice_interp.data if self.param_seg.type_seg == 'bin': # binarize GM seg data_gm = im_res_gmseg.data data_gm[data_gm >= self.param_seg.thr_bin] = 1 data_gm[data_gm < self.param_seg.thr_bin] = 0 im_res_gmseg.data = data_gm # create res WM seg image from GM and SC im_res_wmseg = im_sc_seg_original_rpi.copy() im_res_wmseg.data = im_res_wmseg.data - im_res_gmseg.data # Put res back in original orientation printv(' Reorient resulting segmentations to native orientation...', self.param.verbose, 'normal') fname_gmseg = 'res_gmseg.nii.gz' fname_wmseg = 'res_wmseg.nii.gz' im_res_gmseg.setFileName(fname_gmseg) im_res_gmseg.save() im_res_wmseg.setFileName(fname_wmseg) im_res_wmseg.save() im_res_gmseg = set_orientation(im_res_gmseg, self.info_preprocessing['orientation']) im_res_wmseg = set_orientation(im_res_wmseg, self.info_preprocessing['orientation']) return im_res_gmseg, im_res_wmseg
def compute_ratio(self): type_ratio = self.param_seg.ratio tmp_dir_ratio = 'tmp_ratio/' os.mkdir(tmp_dir_ratio) os.chdir(tmp_dir_ratio) fname_gmseg = self.im_res_gmseg.absolutepath fname_wmseg = self.im_res_wmseg.absolutepath self.im_res_gmseg.save() self.im_res_wmseg.save() if self.im_res_gmseg.orientation is not 'RPI': im_res_gmseg = set_orientation(self.im_res_gmseg, 'RPI') im_res_wmseg = set_orientation(self.im_res_wmseg, 'RPI') fname_gmseg = im_res_gmseg.absolutepath fname_wmseg = im_res_wmseg.absolutepath #sct_process_segmentation.main(['-i', fname_gmseg, '-p', 'csa', '-ofolder', 'gm_csa']) run('sct_process_segmentation -i ' + fname_gmseg + ' -p csa -ofolder gm_csa') #sct_process_segmentation.main(['-i', fname_wmseg, '-p', 'csa', '-ofolder', 'wm_csa']) run('sct_process_segmentation -i ' + fname_wmseg + ' -p csa -ofolder wm_csa') gm_csa = open('gm_csa/csa_per_slice.txt', 'r') wm_csa = open('wm_csa/csa_per_slice.txt', 'r') gm_csa_lines = gm_csa.readlines() wm_csa_lines = wm_csa.readlines() gm_csa.close() wm_csa.close() fname_ratio = 'ratio_by_'+type_ratio+'.txt' file_ratio = open(fname_ratio, 'w') file_ratio.write(type_ratio + ', ratio GM/WM CSA\n') csa_gm_wm_by_level = {0: [], 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: [], 13: [], 14: [], 15: [], 16: [], 17: [], 18: [], 19: [], 20: [], 21: [], 22: [], 23: [], 24: []} for gm_line, wm_line in zip(gm_csa_lines[1:], wm_csa_lines[1:]): i, gm_area, gm_angle = gm_line.split(',') j, wm_area, wm_angle = wm_line.split(',') assert i == j if type_ratio == 'level': level_slice = int(self.target_im[int(i)].level) csa_gm_wm_by_level[level_slice].append((float(gm_area), float(wm_area))) else: file_ratio.write(i + ', ' + str(float(gm_area) / float(wm_area)) + '\n') if type_ratio == 'level': for l, gm_wm_list in sorted(csa_gm_wm_by_level.items()): if str(gm_wm_list) != '[]': csa_gm_list = [] csa_wm_list = [] for gm, wm in gm_wm_list: csa_gm_list.append(gm) csa_wm_list.append(wm) csa_gm = np.mean(csa_gm_list) csa_wm = np.mean(csa_wm_list) file_ratio.write(str(l) + ', ' + str(csa_gm / csa_wm) + '\n') file_ratio.close() shutil.copy(fname_ratio, '../../'+self.param_seg.path_results+'/'+fname_ratio) os.chdir('..')
def __init__(self, im1, im2=None, param=None): self.im1 = im1 self.im2 = im2 self.dim_im = len(self.im1.data.shape) self.dim_pix = 0 self.distances = None self.res = '' self.param = param self.dist1_distribution = None self.dist2_distribution = None if self.dim_im == 3: self.orientation1 = self.im1.orientation if self.orientation1 != 'IRP': self.im1 = set_orientation(self.im1, 'IRP') if self.im2 is not None: self.orientation2 = self.im2.orientation if self.orientation2 != 'IRP': self.im2 = set_orientation(self.im2, 'IRP') if self.param.thinning: self.thinning1 = Thinning(self.im1, self.param.verbose) self.thinning1.thinned_image.save() if self.im2 is not None: self.thinning2 = Thinning(self.im2, self.param.verbose) self.thinning2.thinned_image.save() if self.dim_im == 2 and self.im2 is not None: self.compute_dist_2im_2d() if self.dim_im == 3: if self.im2 is None: self.compute_dist_1im_3d() else: self.compute_dist_2im_3d() if self.dim_im == 2 and self.distances is not None: self.dist1_distribution = self.distances.min_distances_1[ np.nonzero(self.distances.min_distances_1)] self.dist2_distribution = self.distances.min_distances_2[ np.nonzero(self.distances.min_distances_2)] if self.dim_im == 3: self.dist1_distribution = [] self.dist2_distribution = [] for d in self.distances: self.dist1_distribution.append(d.min_distances_1[np.nonzero( d.min_distances_1)]) self.dist2_distribution.append(d.min_distances_2[np.nonzero( d.min_distances_2)]) self.res = 'Hausdorff\'s distance - First relative Hausdorff\'s distance median - Second relative Hausdorff\'s distance median(all in mm)\n' for i, d in enumerate(self.distances): med1 = np.median(self.dist1_distribution[i]) med2 = np.median(self.dist2_distribution[i]) if self.im2 is None: self.res += 'Slice ' + str(i) + ' - slice ' + str( i + 1) + ': ' + str( d.H * self.dim_pix) + ' - ' + str( med1 * self.dim_pix) + ' - ' + str( med2 * self.dim_pix) + ' \n' else: self.res += 'Slice ' + str(i) + ': ' + str( d.H * self.dim_pix) + ' - ' + str( med1 * self.dim_pix) + ' - ' + str( med2 * self.dim_pix) + ' \n' sct.printv( '-----------------------------------------------------------------------------\n' + self.res, self.param.verbose, 'normal') if self.param.verbose == 2: self.show_results()
def __init__(self, im1, im2=None, param=None): self.im1 = im1 self.im2 = im2 self.dim_im = len(self.im1.data.shape) self.dim_pix = 0 self.distances = None self.res = '' self.param = param self.dist1_distribution = None self.dist2_distribution = None if self.dim_im == 3: self.orientation1 = self.im1.orientation if self.orientation1 != 'IRP': self.im1 = set_orientation(self.im1, 'IRP') if self.im2 is not None: self.orientation2 = self.im2.orientation if self.orientation2 != 'IRP': self.im2 = set_orientation(self.im2, 'IRP') if self.param.thinning: self.thinning1 = Thinning(self.im1, self.param.verbose) self.thinning1.thinned_image.save() if self.im2 is not None: self.thinning2 = Thinning(self.im2, self.param.verbose) self.thinning2.thinned_image.save() if self.dim_im == 2 and self.im2 is not None: self.compute_dist_2im_2d() if self.dim_im == 3: if self.im2 is None: self.compute_dist_1im_3d() else: self.compute_dist_2im_3d() if self.dim_im == 2 and self.distances is not None: self.dist1_distribution = self.distances.min_distances_1[np.nonzero(self.distances.min_distances_1)] self.dist2_distribution = self.distances.min_distances_2[np.nonzero(self.distances.min_distances_2)] if self.dim_im == 3: self.dist1_distribution = [] self.dist2_distribution = [] for d in self.distances: self.dist1_distribution.append(d.min_distances_1[np.nonzero(d.min_distances_1)]) self.dist2_distribution.append(d.min_distances_2[np.nonzero(d.min_distances_2)]) self.res = 'Hausdorff\'s distance - First relative Hausdorff\'s distance median - Second relative Hausdorff\'s distance median(all in mm)\n' for i, d in enumerate(self.distances): med1 = np.median(self.dist1_distribution[i]) med2 = np.median(self.dist2_distribution[i]) if self.im2 is None: self.res += 'Slice ' + str(i) + ' - slice ' + str(i+1) + ': ' + str(d.H*self.dim_pix) + ' - ' + str(med1*self.dim_pix) + ' - ' + str(med2*self.dim_pix) + ' \n' else: self.res += 'Slice ' + str(i) + ': ' + str(d.H*self.dim_pix) + ' - ' + str(med1*self.dim_pix) + ' - ' + str(med2*self.dim_pix) + ' \n' sct.printv('-----------------------------------------------------------------------------\n' + self.res, self.param.verbose, 'normal') if self.param.verbose == 2: self.show_results()
def main(fname_anat, fname_centerline, degree_poly, centerline_fitting, interp, remove_temp_files, verbose): # extract path of the script path_script = os.path.dirname(__file__)+'/' # Parameters for debug mode if param.debug == 1: print '\n*** WARNING: DEBUG MODE ON ***\n' status, path_sct_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') fname_anat = path_sct_data+'/t2/t2.nii.gz' fname_centerline = path_sct_data+'/t2/t2_seg.nii.gz' # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) # Display arguments print '\nCheck input arguments...' print ' Input volume ...................... '+fname_anat print ' Centerline ........................ '+fname_centerline print '' # Get input image orientation im_anat = Image(fname_anat) input_image_orientation = get_orientation(im_anat) # Reorient input data into RL PA IS orientation im_centerline = Image(fname_centerline) im_anat_orient = set_orientation(im_anat, 'RPI') im_anat_orient.setFileName('tmp.anat_orient.nii') im_centerline_orient = set_orientation(im_centerline, 'RPI') im_centerline_orient.setFileName('tmp.centerline_orient.nii') # Open centerline #========================================================================================== print '\nGet dimensions of input centerline...' nx, ny, nz, nt, px, py, pz, pt = im_centerline_orient.dim print '.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz) print '.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm' print '\nOpen centerline volume...' data = im_centerline_orient.data X, Y, Z = (data>0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # loop across z and associate x,y coordinate with the point having maximum intensity x_centerline = [0 for iz in range(min_z_index, max_z_index+1, 1)] y_centerline = [0 for iz in range(min_z_index, max_z_index+1, 1)] z_centerline = [iz for iz in range(min_z_index, max_z_index+1, 1)] # Two possible scenario: # 1. the centerline is probabilistic: each slices contains voxels with the probability of containing the centerline [0:...:1] # We only take the maximum value of the image to aproximate the centerline. # 2. The centerline/segmentation image contains many pixels per slice with values {0,1}. # We take all the points and approximate the centerline on all these points. X, Y, Z = ((data<1)*(data>0)).nonzero() # X is empty if binary image if (len(X) > 0): # Scenario 1 for iz in range(min_z_index, max_z_index+1, 1): x_centerline[iz-min_z_index], y_centerline[iz-min_z_index] = numpy.unravel_index(data[:,:,iz].argmax(), data[:,:,iz].shape) else: # Scenario 2 for iz in range(min_z_index, max_z_index+1, 1): x_seg, y_seg = (data[:,:,iz]>0).nonzero() if len(x_seg) > 0: x_centerline[iz-min_z_index] = numpy.mean(x_seg) y_centerline[iz-min_z_index] = numpy.mean(y_seg) # TODO: find a way to do the previous loop with this, which is more neat: # [numpy.unravel_index(data[:,:,iz].argmax(), data[:,:,iz].shape) for iz in range(0,nz,1)] # clear variable del data # Fit the centerline points with the kind of curve given as argument of the script and return the new smoothed coordinates if centerline_fitting == 'nurbs': try: x_centerline_fit, y_centerline_fit = b_spline_centerline(x_centerline,y_centerline,z_centerline) except ValueError: print "splines fitting doesn't work, trying with polynomial fitting...\n" x_centerline_fit, y_centerline_fit = polynome_centerline(x_centerline,y_centerline,z_centerline) elif centerline_fitting == 'polynome': x_centerline_fit, y_centerline_fit = polynome_centerline(x_centerline,y_centerline,z_centerline) #========================================================================================== # Split input volume print '\nSplit input volume...' im_anat_orient_split_list = split_data(im_anat_orient, 2) file_anat_split = [] for im in im_anat_orient_split_list: file_anat_split.append(im.absolutepath) im.save() # initialize variables file_mat_inv_cumul = ['tmp.mat_inv_cumul_Z'+str(z).zfill(4) for z in range(0,nz,1)] z_init = min_z_index displacement_max_z_index = x_centerline_fit[z_init-min_z_index]-x_centerline_fit[max_z_index-min_z_index] # write centerline as text file print '\nGenerate fitted transformation matrices...' file_mat_inv_cumul_fit = ['tmp.mat_inv_cumul_fit_Z'+str(z).zfill(4) for z in range(0,nz,1)] for iz in range(min_z_index, max_z_index+1, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') if (x_centerline[iz-min_z_index] == 0 and y_centerline[iz-min_z_index] == 0): displacement = 0 else: displacement = x_centerline_fit[z_init-min_z_index]-x_centerline_fit[iz-min_z_index] fid.write('%i %i %i %f\n' %(1, 0, 0, displacement) ) fid.write('%i %i %i %f\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # we complete the displacement matrix in z direction for iz in range(0, min_z_index, 1): fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, 0) ) fid.write('%i %i %i %f\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() for iz in range(max_z_index+1, nz, 1): fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, displacement_max_z_index) ) fid.write('%i %i %i %f\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # apply transformations to data print '\nApply fitted transformation matrices...' file_anat_split_fit = ['tmp.anat_orient_fit_Z'+str(z).zfill(4) for z in range(0,nz,1)] for iz in range(0, nz, 1): # forward cumulative transformation to data sct.run(fsloutput+'flirt -in '+file_anat_split[iz]+' -ref '+file_anat_split[iz]+' -applyxfm -init '+file_mat_inv_cumul_fit[iz]+' -out '+file_anat_split_fit[iz]+' -interp '+interp) # Merge into 4D volume print '\nMerge into 4D volume...' from glob import glob im_to_concat_list = [Image(fname) for fname in glob('tmp.anat_orient_fit_Z*.nii')] im_concat_out = concat_data(im_to_concat_list, 2) im_concat_out.setFileName('tmp.anat_orient_fit.nii') im_concat_out.save() # sct.run(fsloutput+'fslmerge -z tmp.anat_orient_fit tmp.anat_orient_fit_z*') # Reorient data as it was before print '\nReorient data back into native orientation...' fname_anat_fit_orient = set_orientation(im_concat_out.absolutepath, input_image_orientation, filename=True) move(fname_anat_fit_orient, 'tmp.anat_orient_fit_reorient.nii') # Generate output file (in current folder) print '\nGenerate output file (in current folder)...' sct.generate_output_file('tmp.anat_orient_fit_reorient.nii', file_anat+'_flatten'+ext_anat) # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm -rf tmp.*') # to view results print '\nDone! To view results, type:' print 'fslview '+file_anat+ext_anat+' '+file_anat+'_flatten'+ext_anat+' &\n'
def main(args=None): # Initialization # fname_anat = '' # fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) # remove_temp_files = param.remove_temp_files # verbose = param.verbose start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-r' in arguments: remove_temp_files = int(arguments['-r']) if '-v' in arguments: verbose = int(arguments['-v']) # Display arguments sct.printv('\nCheck input arguments...') sct.printv(' Volume to smooth .................. ' + fname_anat) sct.printv(' Centerline ........................ ' + fname_centerline) sct.printv(' Sigma (mm) ........................ ' + str(sigma)) sct.printv(' Verbose ........................... ' + str(verbose)) # Check that input is 3D: from msct_image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv('WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i ' + fname_anat + ' -split t -o ' + fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname(fname_centerline) path_tmp = sct.tmp_create(basename="smooth_spinalcord", verbose=verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.copy(fname_anat, os.path.join(path_tmp, "anat" + ext_anat)) sct.copy(fname_centerline, os.path.join(path_tmp, "centerline" + ext_centerline)) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # convert to nii format convert('anat' + ext_anat, 'anat.nii') convert('centerline' + ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI sct.printv('\nOrient input volume to RPI orientation...') fname_anat_rpi = set_orientation('anat.nii', 'RPI', filename=True) shutil.move(fname_anat_rpi, 'anat_rpi.nii') # Change orientation of the input image into RPI sct.printv('\nOrient centerline to RPI orientation...') fname_centerline_rpi = set_orientation('centerline.nii', 'RPI', filename=True) shutil.move(fname_centerline_rpi, 'centerline_rpi.nii') # Straighten the spinal cord # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) cache_sig = sct.cache_signature( input_files=["anat_rpi.nii", "centerline_rpi.nii"], input_params={"x": "spline"}, ) cachefile = os.path.join(curdir, "straightening.cache") if sct.cache_valid(cachefile, cache_sig) and os.path.isfile(os.path.join(curdir, 'warp_curve2straight.nii.gz')) and os.path.isfile(os.path.join(curdir, 'warp_straight2curve.nii.gz')) and os.path.isfile(os.path.join(curdir, 'straight_ref.nii.gz')): # if they exist, copy them into current folder sct.printv('Reusing existing warping field which seems to be valid', verbose, 'warning') sct.copy(os.path.join(curdir, 'warp_curve2straight.nii.gz'), 'warp_curve2straight.nii.gz') sct.copy(os.path.join(curdir, 'warp_straight2curve.nii.gz'), 'warp_straight2curve.nii.gz') sct.copy(os.path.join(curdir, 'straight_ref.nii.gz'), 'straight_ref.nii.gz') # apply straightening sct.run(['sct_apply_transfo', '-i', 'anat_rpi.nii', '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', 'anat_rpi_straight.nii', '-x', 'spline'], verbose) else: sct.run(['sct_straighten_spinalcord', '-i', 'anat_rpi.nii', '-s', 'centerline_rpi.nii', '-x', 'spline'], verbose) sct.cache_save(cachefile, cache_sig) # Smooth the straightened image along z sct.printv('\nSmooth the straightened image along z...') sct.run(['sct_maths', '-i', 'anat_rpi_straight.nii', '-smooth', '0,0,' + str(sigma), '-o', 'anat_rpi_straight_smooth.nii'], verbose) # Apply the reversed warping field to get back the curved spinal cord sct.printv('\nApply the reversed warping field to get back the curved spinal cord...') sct.run(['sct_apply_transfo', '-i', 'anat_rpi_straight_smooth.nii', '-o', 'anat_rpi_straight_smooth_curved.nii', '-d', 'anat.nii', '-w', 'warp_straight2curve.nii.gz', '-x', 'spline'], verbose) # replace zeroed voxels by original image (issue #937) sct.printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.setFileName('anat_rpi_straight_smooth_curved_nonzero.nii') nii_smooth.save() # come back os.chdir(curdir) # Generate output file sct.printv('\nGenerate output file...') sct.generate_output_file(os.path.join(path_tmp, "anat_rpi_straight_smooth_curved_nonzero.nii"), file_anat + '_smooth' + ext_anat) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...') sct.rmtree(path_tmp) # Display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's\n') sct.display_viewer_syntax([file_anat, file_anat + '_smooth'], verbose=verbose)
def main(): # initialization fname_mask = '' # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_data = arguments['-i'] fname_mask = arguments['-m'] vert_label_fname = arguments["-vertfile"] vert_levels = arguments["-vert"] slices_of_interest = arguments["-z"] method = arguments["-method"] verbose = int(arguments['-v']) # Check if data are in RPI input_im = Image(fname_data) input_orient = get_orientation_3d(input_im) # If orientation is not RPI, change to RPI if input_orient != 'RPI': sct.printv('\nCreate temporary folder to change the orientation of the NIFTI files into RPI...', verbose) path_tmp = sct.tmp_create() # change orientation and load data sct.printv('\nChange input image orientation and load it...', verbose) input_im_rpi = set_orientation(input_im, 'RPI', fname_out=path_tmp+'input_RPI.nii') input_data = input_im_rpi.data # Do the same for the mask sct.printv('\nChange mask orientation and load it...', verbose) mask_im_rpi = set_orientation(Image(fname_mask), 'RPI', fname_out=path_tmp+'mask_RPI.nii') mask_data = mask_im_rpi.data # Do the same for vertebral labeling if present if vert_levels != 'None': sct.printv('\nChange vertebral labeling file orientation and load it...', verbose) vert_label_im_rpi = set_orientation(Image(vert_label_fname), 'RPI', fname_out=path_tmp+'vert_labeling_RPI.nii') vert_labeling_data = vert_label_im_rpi.data # Remove the temporary folder used to change the NIFTI files orientation into RPI sct.printv('\nRemove the temporary folder...', verbose) rmdir(path_tmp) else: # Load data sct.printv('\nLoad data...', verbose) input_data = input_im.data mask_data = Image(fname_mask).data if vert_levels != 'None': vert_labeling_data = Image(vert_label_fname).data sct.printv('\tDone.', verbose) # Get slices corresponding to vertebral levels if vert_levels != 'None': from sct_extract_metric import get_slices_matching_with_vertebral_levels slices_of_interest, actual_vert_levels, warning_vert_levels = get_slices_matching_with_vertebral_levels(mask_data, vert_levels, vert_labeling_data, verbose) # Remove slices that were not selected if slices_of_interest == 'None': slices_of_interest = '0:'+str(mask_data.shape[2]-1) slices_boundary = slices_of_interest.split(':') slices_of_interest_list = range(int(slices_boundary[0]), int(slices_boundary[1])+1) # Crop input_data = input_data[:, :, slices_of_interest_list, :] mask_data = mask_data[:, :, slices_of_interest_list] # Get signal and noise indexes_roi = np.where(mask_data == 1) if method == 'mult': signal = np.mean(input_data[indexes_roi]) std_input_temporal = np.std(input_data, 3) noise = np.mean(std_input_temporal[indexes_roi]) elif method == 'diff': data_1 = input_data[:, :, :, 0] data_2 = input_data[:, :, :, 1] signal = np.mean(np.add(b0_1[indexes_roi], b0_2[indexes_roi])) noise = np.sqrt(2)*np.std(np.subtract(b0_1[indexes_roi], b0_2[indexes_roi])) elif method == 'background': sct.printv('ERROR: Sorry, method is not implemented yet.', 1, 'error') elif method == 'nema': sct.printv('ERROR: Sorry, method is not implemented yet.', 1, 'error') # compute SNR SNR = signal/noise # Display result sct.printv('\nSNR_'+method+' = '+str(SNR)+'\n', type='info')
def pre_processing(fname_target, fname_sc_seg, fname_level=None, fname_manual_gmseg=None, new_res=0.3, square_size_size_mm=22.5, denoising=True, verbose=1, rm_tmp=True, for_model=False): printv('\nPre-process data...', verbose, 'normal') tmp_dir = sct.tmp_create() sct.copy(fname_target, tmp_dir) fname_target = ''.join(extract_fname(fname_target)[1:]) sct.copy(fname_sc_seg, tmp_dir) fname_sc_seg = ''.join(extract_fname(fname_sc_seg)[1:]) curdir = os.getcwd() os.chdir(tmp_dir) original_info = { 'orientation': None, 'im_sc_seg_rpi': None, 'interpolated_images': [] } im_target = Image(fname_target).copy() im_sc_seg = Image(fname_sc_seg).copy() # get original orientation printv(' Reorient...', verbose, 'normal') original_info['orientation'] = im_target.orientation # assert images are in the same orientation assert im_target.orientation == im_sc_seg.orientation, "ERROR: the image to segment and it's SC segmentation are not in the same orientation" im_target_rpi = set_orientation(im_target, 'RPI') im_sc_seg_rpi = set_orientation(im_sc_seg, 'RPI') original_info['im_sc_seg_rpi'] = im_sc_seg_rpi.copy( ) # target image in RPI will be used to post-process segmentations # denoise using P. Coupe non local means algorithm (see [Manjon et al. JMRI 2010]) implemented in dipy if denoising: printv(' Denoise...', verbose, 'normal') # crop image before denoising to fasten denoising nx, ny, nz, nt, px, py, pz, pt = im_target_rpi.dim size_x, size_y = (square_size_size_mm + 1) / px, (square_size_size_mm + 1) / py size = int(math.ceil(max(size_x, size_y))) # create mask fname_mask = 'mask_pre_crop.nii.gz' sct_create_mask.main([ '-i', im_target_rpi.absolutepath, '-p', 'centerline,' + im_sc_seg_rpi.absolutepath, '-f', 'box', '-size', str(size), '-o', fname_mask ]) # crop image fname_target_crop = add_suffix(im_target_rpi.absolutepath, '_pre_crop') crop_im = ImageCropper(input_file=im_target_rpi.absolutepath, output_file=fname_target_crop, mask=fname_mask) im_target_rpi_crop = crop_im.crop() # crop segmentation fname_sc_seg_crop = add_suffix(im_sc_seg_rpi.absolutepath, '_pre_crop') crop_sc_seg = ImageCropper(input_file=im_sc_seg_rpi.absolutepath, output_file=fname_sc_seg_crop, mask=fname_mask) im_sc_seg_rpi_crop = crop_sc_seg.crop() # denoising from sct_maths import denoise_nlmeans block_radius = 3 block_radius = int( im_target_rpi_crop.data.shape[2] / 2) if im_target_rpi_crop.data.shape[2] < (block_radius * 2) else block_radius patch_radius = block_radius - 1 data_denoised = denoise_nlmeans(im_target_rpi_crop.data, block_radius=block_radius, patch_radius=patch_radius) im_target_rpi_crop.data = data_denoised im_target_rpi = im_target_rpi_crop im_sc_seg_rpi = im_sc_seg_rpi_crop else: fname_mask = None # interpolate image to reference square image (resample and square crop centered on SC) printv(' Interpolate data to the model space...', verbose, 'normal') list_im_slices = interpolate_im_to_ref(im_target_rpi, im_sc_seg_rpi, new_res=new_res, sq_size_size_mm=square_size_size_mm) original_info[ 'interpolated_images'] = list_im_slices # list of images (not Slice() objects) printv(' Mask data using the spinal cord segmentation...', verbose, 'normal') list_sc_seg_slices = interpolate_im_to_ref( im_sc_seg_rpi, im_sc_seg_rpi, new_res=new_res, sq_size_size_mm=square_size_size_mm, interpolation_mode=1) for i in range(len(list_im_slices)): # list_im_slices[i].data[list_sc_seg_slices[i].data == 0] = 0 list_sc_seg_slices[i] = binarize(list_sc_seg_slices[i], thr_min=0.5, thr_max=1) list_im_slices[ i].data = list_im_slices[i].data * list_sc_seg_slices[i].data printv(' Split along rostro-caudal direction...', verbose, 'normal') list_slices_target = [ Slice(slice_id=i, im=im_slice.data, gm_seg=[], wm_seg=[]) for i, im_slice in enumerate(list_im_slices) ] # load vertebral levels if fname_level is not None: printv(' Load vertebral levels...', verbose, 'normal') # copy level file to tmp dir os.chdir(curdir) sct.copy(fname_level, tmp_dir) os.chdir(tmp_dir) # change fname level to only file name (path = tmp dir now) fname_level = ''.join(extract_fname(fname_level)[1:]) # load levels list_slices_target = load_level(list_slices_target, fname_level) os.chdir(curdir) # load manual gmseg if there is one (model data) if fname_manual_gmseg is not None: printv('\n\tLoad manual GM segmentation(s) ...', verbose, 'normal') list_slices_target = load_manual_gmseg(list_slices_target, fname_manual_gmseg, tmp_dir, im_sc_seg_rpi, new_res, square_size_size_mm, for_model=for_model, fname_mask=fname_mask) if rm_tmp: # remove tmp folder sct.rmtree(tmp_dir) return list_slices_target, original_info
def _orient(self, fname, orientation): im = Image(fname) im = set_orientation(im, orientation, fname_out=fname)
def compute_csa(fname_segmentation, verbose, remove_temp_files, step, smoothing_param, figure_fit, file_csa_volume, slices, vert_levels, fname_vertebral_labeling='', algo_fitting = 'hanning', type_window = 'hanning', window_length = 80): # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S") + '_'+str(randint(1, 1000000)), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('sct_convert -i '+fname_segmentation+' -o '+path_tmp+'segmentation.nii.gz', verbose) # go to tmp folder os.chdir(path_tmp) # Change orientation of the input segmentation into RPI sct.printv('\nChange orientation to RPI...', verbose) sct.run('sct_image -i segmentation.nii.gz -setorient RPI -o segmentation_RPI.nii.gz', verbose) # Open segmentation volume sct.printv('\nOpen segmentation volume...', verbose) im_seg = Image('segmentation_RPI.nii.gz') data_seg = im_seg.data # hdr_seg = im_seg.hdr # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_seg.dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # # Extract min and max index in Z direction X, Y, Z = (data_seg > 0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline('segmentation_RPI.nii.gz', algo_fitting=algo_fitting, type_window=type_window, window_length=window_length, verbose=verbose) z_centerline_scaled = [x*pz for x in z_centerline] # Compute CSA sct.printv('\nCompute CSA...', verbose) # Empty arrays in which CSA for each z slice will be stored csa = np.zeros(max_z_index-min_z_index+1) for iz in xrange(min_z_index, max_z_index+1): # compute the vector normal to the plane normal = normalize(np.array([x_centerline_deriv[iz-min_z_index], y_centerline_deriv[iz-min_z_index], z_centerline_deriv[iz-min_z_index]])) # compute the angle between the normal vector of the plane and the vector z angle = np.arccos(np.dot(normal, [0, 0, 1])) # compute the number of voxels, assuming the segmentation is coded for partial volume effect between 0 and 1. number_voxels = np.sum(data_seg[:, :, iz]) # compute CSA, by scaling with voxel size (in mm) and adjusting for oblique plane csa[iz-min_z_index] = number_voxels * px * py * np.cos(angle) sct.printv('\nSmooth CSA across slices...', verbose) if smoothing_param: from msct_smooth import smoothing_window sct.printv('.. Hanning window: '+str(smoothing_param)+' mm', verbose) csa_smooth = smoothing_window(csa, window_len=smoothing_param/pz, window='hanning', verbose=0) # display figure if verbose == 2: import matplotlib.pyplot as plt plt.figure() pltx, = plt.plot(z_centerline_scaled, csa, 'bo') pltx_fit, = plt.plot(z_centerline_scaled, csa_smooth, 'r', linewidth=2) plt.title("Cross-sectional area (CSA)") plt.xlabel('z (mm)') plt.ylabel('CSA (mm^2)') plt.legend([pltx, pltx_fit], ['Raw', 'Smoothed']) plt.show() # update variable csa = csa_smooth else: sct.printv('.. No smoothing!', verbose) # Create output text file sct.printv('\nWrite text file...', verbose) file_results = open('csa.txt', 'w') for i in range(min_z_index, max_z_index+1): file_results.write(str(int(i)) + ',' + str(csa[i-min_z_index])+'\n') # Display results sct.printv('z='+str(i-min_z_index)+': '+str(csa[i-min_z_index])+' mm^2', verbose, 'bold') file_results.close() # output volume of csa values sct.printv('\nCreate volume of CSA values...', verbose) data_csa = data_seg.astype(np.float32, copy=False) # loop across slices for iz in range(min_z_index, max_z_index+1): # retrieve seg pixels x_seg, y_seg = (data_csa[:, :, iz] > 0).nonzero() seg = [[x_seg[i],y_seg[i]] for i in range(0, len(x_seg))] # loop across pixels in segmentation for i in seg: # replace value with csa value data_csa[i[0], i[1], iz] = csa[iz-min_z_index] # replace data im_seg.data = data_csa # set original orientation # TODO: FIND ANOTHER WAY!! # im_seg.change_orientation(orientation) --> DOES NOT WORK! # set file name -- use .gz because faster to write im_seg.setFileName('csa_volume_RPI.nii.gz') im_seg.changeType('float32') # save volume im_seg.save() # get orientation of the input data im_seg_original = Image('segmentation.nii.gz') orientation = im_seg_original.orientation sct.run('sct_image -i csa_volume_RPI.nii.gz -setorient '+orientation+' -o '+file_csa_volume) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) copyfile(path_tmp+'csa.txt', path_data+param.fname_csa) # sct.generate_output_file(path_tmp+'csa.txt', path_data+param.fname_csa) # extension already included in param.fname_csa sct.generate_output_file(path_tmp+file_csa_volume, path_data+file_csa_volume) # extension already included in name_output # average csa across vertebral levels or slices if asked (flag -z or -l) if slices or vert_levels: from sct_extract_metric import save_metrics warning = '' if vert_levels and not fname_vertebral_labeling: sct.printv('\nERROR: Vertebral labeling file is missing. See usage.\n', 1, 'error') elif vert_levels and fname_vertebral_labeling: # from sct_extract_metric import get_slices_matching_with_vertebral_levels sct.printv('\tSelected vertebral levels... '+vert_levels) # convert the vertebral labeling file to RPI orientation im_vertebral_labeling = set_orientation(Image(fname_vertebral_labeling), 'RPI', fname_out=path_tmp+'vertebral_labeling_RPI.nii') # get the slices corresponding to the vertebral levels # slices, vert_levels_list, warning = get_slices_matching_with_vertebral_levels(data_seg, vert_levels, im_vertebral_labeling.data, 1) slices, vert_levels_list, warning = get_slices_matching_with_vertebral_levels_based_centerline(vert_levels, im_vertebral_labeling.data, x_centerline_fit, y_centerline_fit, z_centerline) elif not vert_levels: vert_levels_list = [] sct.printv('Average CSA across slices...', type='info') # parse the selected slices slices_lim = slices.strip().split(':') slices_list = range(int(slices_lim[0]), int(slices_lim[1])+1) CSA_for_selected_slices = [] # Read the file csa.txt and get the CSA for the selected slices with open(path_data+param.fname_csa) as openfile: for line in openfile: line_split = line.strip().split(',') if int(line_split[0]) in slices_list: CSA_for_selected_slices.append(float(line_split[1])) # average the CSA mean_CSA = np.mean(np.asarray(CSA_for_selected_slices)) std_CSA = np.std(np.asarray(CSA_for_selected_slices)) sct.printv('Mean CSA: '+str(mean_CSA)+' +/- '+str(std_CSA)+' mm^2', type='info') # write result into output file save_metrics([0], [file_data], slices, [mean_CSA], [std_CSA], path_data + 'csa_mean.txt', path_data+file_csa_volume, 'nb_voxels x px x py x cos(theta) slice-by-slice (in mm^3)', '', actual_vert=vert_levels_list, warning_vert_levels=warning) # compute volume between the selected slices sct.printv('Compute the volume in between the selected slices...', type='info') nb_vox = np.sum(data_seg[:, :, slices_list]) volume = nb_vox*px*py*pz sct.printv('Volume in between the selected slices: '+str(volume)+' mm^3', type='info') # write result into output file save_metrics([0], [file_data], slices, [volume], [np.nan], path_data + 'volume.txt', path_data+file_data, 'nb_voxels x px x py x pz (in mm^3)', '', actual_vert=vert_levels_list, warning_vert_levels=warning) # Remove temporary files if remove_temp_files: sct.printv('\nRemove temporary files...') sct.run('rm -rf '+path_tmp, error_exit='warning')
def pre_processing(fname_target, fname_sc_seg, fname_level=None, fname_manual_gmseg=None, new_res=0.3, square_size_size_mm=22.5, denoising=True, verbose=1, rm_tmp=True, for_model=False): printv('\nPre-process data...', verbose, 'normal') tmp_dir = 'tmp_preprocessing_' + time.strftime("%y%m%d%H%M%S") + '_' + str(random.randint(1, 1000000)) + '/' if not os.path.exists(tmp_dir): os.mkdir(tmp_dir) shutil.copy(fname_target, tmp_dir) fname_target = ''.join(extract_fname(fname_target)[1:]) shutil.copy(fname_sc_seg, tmp_dir) fname_sc_seg = ''.join(extract_fname(fname_sc_seg)[1:]) os.chdir(tmp_dir) original_info = {'orientation': None, 'im_sc_seg_rpi': None, 'interpolated_images': []} im_target = Image(fname_target).copy() im_sc_seg = Image(fname_sc_seg).copy() # get original orientation printv(' Reorient...', verbose, 'normal') original_info['orientation'] = im_target.orientation # assert images are in the same orientation assert im_target.orientation == im_sc_seg.orientation, "ERROR: the image to segment and it's SC segmentation are not in the same orientation" im_target_rpi = set_orientation(im_target, 'RPI') im_sc_seg_rpi = set_orientation(im_sc_seg, 'RPI') original_info['im_sc_seg_rpi'] = im_sc_seg_rpi.copy() # target image in RPI will be used to post-process segmentations # interpolate image to reference square image (resample and square crop centered on SC) printv(' Interpolate data to the model space...', verbose, 'normal') list_im_slices = interpolate_im_to_ref(im_target_rpi, im_sc_seg_rpi, new_res=new_res, sq_size_size_mm=square_size_size_mm) original_info['interpolated_images'] = list_im_slices # list of images (not Slice() objects) # denoise using P. Coupe non local means algorithm (see [Manjon et al. JMRI 2010]) implemented in dipy if denoising: printv(' Denoise...', verbose, 'normal') from sct_maths import denoise_nlmeans data = np.asarray([im.data for im in list_im_slices]) data_denoised = denoise_nlmeans(data, block_radius = int(len(list_im_slices)/2)) for i in range(len(list_im_slices)): list_im_slices[i].data = data_denoised[i] printv(' Mask data using the spinal cord segmentation...', verbose, 'normal') list_sc_seg_slices = interpolate_im_to_ref(im_sc_seg_rpi, im_sc_seg_rpi, new_res=new_res, sq_size_size_mm=square_size_size_mm, interpolation_mode=1) for i in range(len(list_im_slices)): # list_im_slices[i].data[list_sc_seg_slices[i].data == 0] = 0 list_sc_seg_slices[i] = binarize(list_sc_seg_slices[i], thr_min=0.5, thr_max=1) list_im_slices[i].data = list_im_slices[i].data * list_sc_seg_slices[i].data printv(' Split along rostro-caudal direction...', verbose, 'normal') list_slices_target = [Slice(slice_id=i, im=im_slice.data, gm_seg=[], wm_seg=[]) for i, im_slice in enumerate(list_im_slices)] # load vertebral levels if fname_level is not None: printv(' Load vertebral levels...', verbose, 'normal') # copy level file to tmp dir os.chdir('..') shutil.copy(fname_level, tmp_dir) os.chdir(tmp_dir) # change fname level to only file name (path = tmp dir now) fname_level = ''.join(extract_fname(fname_level)[1:]) # load levels list_slices_target = load_level(list_slices_target, fname_level) # load manual gmseg if there is one (model data) if fname_manual_gmseg is not None: printv('\n\tLoad manual GM segmentation(s) ...', verbose, 'normal') list_slices_target = load_manual_gmseg(list_slices_target, fname_manual_gmseg, tmp_dir, im_sc_seg_rpi, new_res, square_size_size_mm, for_model=for_model) os.chdir('..') if rm_tmp: # remove tmp folder shutil.rmtree(tmp_dir) return list_slices_target, original_info
def deep_segmentation_spinalcord(fname_image, contrast_type, output_folder, ctr_algo='cnn', brain_bool=True, kernel_size='2d', remove_temp_files=1, verbose=1): """Pipeline.""" path_script = os.path.dirname(__file__) path_sct = os.path.dirname(path_script) # create temporary folder with intermediate results sct.log.info("Creating temporary folder...") file_fname = os.path.basename(fname_image) tmp_folder = sct.TempFolder() tmp_folder_path = tmp_folder.get_path() fname_image_tmp = tmp_folder.copy_from(fname_image) tmp_folder.chdir() # orientation of the image, should be RPI sct.log.info("Reorient the image to RPI, if necessary...") fname_orient = sct.add_suffix(file_fname, '_RPI') im_2orient = Image(file_fname) original_orientation = im_2orient.orientation if original_orientation != 'RPI': im_orient = set_orientation(im_2orient, 'RPI') im_orient.setFileName(fname_orient) im_orient.save() else: im_orient = im_2orient sct.copy(fname_image_tmp, fname_orient) # resampling RPI image sct.log.info("Resample the image to 0.5 mm isotropic resolution...") fname_res = sct.add_suffix(fname_orient, '_resampled') im_2res = im_orient input_resolution = im_2res.dim[4:7] new_resolution = 'x'.join(['0.5', '0.5', str(input_resolution[2])]) spinalcordtoolbox.resample.nipy_resample.resample_file(fname_orient, fname_res, new_resolution, 'mm', 'linear', verbose=0) # find the spinal cord centerline - execute OptiC binary sct.log.info("Finding the spinal cord centerline...") if ctr_algo == 'svm': # run optic on a heatmap computed by a trained SVM+HoG algorithm optic_models_fname = os.path.join(path_sct, 'data', 'optic_models', '{}_model'.format(contrast_type)) _, centerline_filename = optic.detect_centerline( image_fname=fname_res, contrast_type=contrast_type, optic_models_path=optic_models_fname, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, output_roi=False, verbose=0) elif ctr_algo == 'cnn': # CNN parameters dct_patch_ctr = { 't2': { 'size': (80, 80), 'mean': 51.1417, 'std': 57.4408 }, 't2s': { 'size': (80, 80), 'mean': 68.8591, 'std': 71.4659 }, 't1': { 'size': (80, 80), 'mean': 55.7359, 'std': 64.3149 }, 'dwi': { 'size': (80, 80), 'mean': 55.744, 'std': 45.003 } } dct_params_ctr = { 't2': { 'features': 16, 'dilation_layers': 2 }, 't2s': { 'features': 8, 'dilation_layers': 3 }, 't1': { 'features': 24, 'dilation_layers': 3 }, 'dwi': { 'features': 8, 'dilation_layers': 2 } } # load model ctr_model_fname = os.path.join(path_sct, 'data', 'deepseg_sc_models', '{}_ctr.h5'.format(contrast_type)) ctr_model = nn_architecture_ctr( height=dct_patch_ctr[contrast_type]['size'][0], width=dct_patch_ctr[contrast_type]['size'][1], channels=1, classes=1, features=dct_params_ctr[contrast_type]['features'], depth=2, temperature=1.0, padding='same', batchnorm=True, dropout=0.0, dilation_layers=dct_params_ctr[contrast_type]['dilation_layers']) ctr_model.load_weights(ctr_model_fname) # compute the heatmap fname_heatmap = sct.add_suffix(fname_res, "_heatmap") img_filename = ''.join(sct.extract_fname(fname_heatmap)[:2]) fname_heatmap_nii = img_filename + '.nii' z_max = heatmap(filename_in=fname_res, filename_out=fname_heatmap_nii, model=ctr_model, patch_shape=dct_patch_ctr[contrast_type]['size'], mean_train=dct_patch_ctr[contrast_type]['mean'], std_train=dct_patch_ctr[contrast_type]['std'], brain_bool=brain_bool) # run optic on the heatmap centerline_filename = sct.add_suffix(fname_heatmap, "_ctr") heatmap2optic(fname_heatmap=fname_heatmap_nii, lambda_value=7 if contrast_type == 't2s' else 1, fname_out=centerline_filename, z_max=z_max if brain_bool else None) # crop image around the spinal cord centerline sct.log.info("Cropping the image around the spinal cord...") fname_crop = sct.add_suffix(fname_res, '_crop') crop_size = 64 if kernel_size == '2d' else 96 X_CROP_LST, Y_CROP_LST = crop_image_around_centerline( filename_in=fname_res, filename_ctr=centerline_filename, filename_out=fname_crop, crop_size=crop_size) # normalize the intensity of the images sct.log.info("Normalizing the intensity...") fname_norm = sct.add_suffix(fname_crop, '_norm') apply_intensity_normalization(img_path=fname_crop, fname_out=fname_norm) if kernel_size == '2d': # segment data using 2D convolutions sct.log.info( "Segmenting the spinal cord using deep learning on 2D patches...") segmentation_model_fname = os.path.join( path_sct, 'data', 'deepseg_sc_models', '{}_sc.h5'.format(contrast_type)) fname_seg_crop = sct.add_suffix(fname_norm, '_seg') seg_crop_data = segment_2d(model_fname=segmentation_model_fname, contrast_type=contrast_type, input_size=(crop_size, crop_size), fname_in=fname_norm, fname_out=fname_seg_crop) elif kernel_size == '3d': # resample to 0.5mm isotropic fname_res3d = sct.add_suffix(fname_norm, '_resampled3d') spinalcordtoolbox.resample.nipy_resample.resample_file(fname_norm, fname_res3d, '0.5x0.5x0.5', 'mm', 'linear', verbose=0) # segment data using 3D convolutions sct.log.info( "Segmenting the spinal cord using deep learning on 3D patches...") segmentation_model_fname = os.path.join( path_sct, 'data', 'deepseg_sc_models', '{}_sc_3D.h5'.format(contrast_type)) fname_seg_crop_res = sct.add_suffix(fname_res3d, '_seg') segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, fname_in=fname_res3d, fname_out=fname_seg_crop_res) # resample to the initial pz resolution fname_seg_res2d = sct.add_suffix(fname_seg_crop_res, '_resampled2d') initial_2d_resolution = 'x'.join( ['0.5', '0.5', str(input_resolution[2])]) spinalcordtoolbox.resample.nipy_resample.resample_file( fname_seg_crop_res, fname_seg_res2d, initial_2d_resolution, 'mm', 'linear', verbose=0) seg_crop_data = Image(fname_seg_res2d).data # reconstruct the segmentation from the crop data sct.log.info("Reassembling the image...") fname_seg_res_RPI = sct.add_suffix(file_fname, '_res_RPI_seg') uncrop_image(fname_ref=fname_res, fname_out=fname_seg_res_RPI, data_crop=seg_crop_data, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST) # resample to initial resolution sct.log.info( "Resampling the segmentation to the original image resolution...") fname_seg_RPI = sct.add_suffix(file_fname, '_RPI_seg') initial_resolution = 'x'.join([ str(input_resolution[0]), str(input_resolution[1]), str(input_resolution[2]) ]) spinalcordtoolbox.resample.nipy_resample.resample_file(fname_seg_res_RPI, fname_seg_RPI, initial_resolution, 'mm', 'linear', verbose=0) # binarize the resampled image to remove interpolation effects sct.log.info( "Binarizing the segmentation to avoid interpolation effects...") thr = '0.0001' if contrast_type in ['t1', 'dwi'] else '0.5' sct.run( ['sct_maths', '-i', fname_seg_RPI, '-bin', thr, '-o', fname_seg_RPI], verbose=0) # post processing step to z_regularized post_processing_volume_wise(fname_in=fname_seg_RPI) # reorient to initial orientation sct.log.info( "Reorienting the segmentation to the original image orientation...") fname_seg = sct.add_suffix(file_fname, '_seg') if original_orientation != 'RPI': im_seg_orient = set_orientation(Image(fname_seg_RPI), original_orientation) im_seg_orient.setFileName(fname_seg) im_seg_orient.save() else: sct.copy(fname_seg_RPI, fname_seg) tmp_folder.chdir_undo() # copy image from temporary folder into output folder sct.copy(os.path.join(tmp_folder_path, fname_seg), output_folder) # remove temporary files if remove_temp_files: sct.log.info("Remove temporary files...") tmp_folder.cleanup() return os.path.join(output_folder, fname_seg)
def main(): # Initialization fname_anat = '' fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) remove_temp_files = param.remove_temp_files verbose = param.verbose start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-r' in arguments: remove_temp_files = int(arguments['-r']) if '-v' in arguments: verbose = int(arguments['-v']) # Display arguments print '\nCheck input arguments...' print ' Volume to smooth .................. ' + fname_anat print ' Centerline ........................ ' + fname_centerline print ' FWHM .............................. '+str(sigma) print ' Verbose ........................... '+str(verbose) # Check that input is 3D: from msct_image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv('WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i '+fname_anat+' -split t -o '+fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname(fname_centerline) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('cp '+fname_anat+' '+path_tmp+'anat'+ext_anat, verbose) sct.run('cp '+fname_centerline+' '+path_tmp+'centerline'+ext_centerline, verbose) # go to tmp folder os.chdir(path_tmp) # convert to nii format convert('anat'+ext_anat, 'anat.nii') convert('centerline'+ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI print '\nOrient input volume to RPI orientation...' fname_anat_rpi = set_orientation('anat.nii', 'RPI', filename=True) move(fname_anat_rpi, 'anat_rpi.nii') # Change orientation of the input image into RPI print '\nOrient centerline to RPI orientation...' fname_centerline_rpi = set_orientation('centerline.nii', 'RPI', filename=True) move(fname_centerline_rpi, 'centerline_rpi.nii') # ## new # # ### Make sure that centerline file does not have halls # file_c = load('centerline_rpi.nii') # data_c = file_c.get_data() # hdr_c = file_c.get_header() # # data_temp = copy(data_c) # data_temp *= 0 # data_output = copy(data_c) # data_output *= 0 # nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension('centerline_rpi.nii') # # ## Change seg to centerline if it is a segmentation # sct.printv('\nChange segmentation to centerline if it is a centerline...\n') # z_centerline = [iz for iz in range(0, nz, 1) if data_c[:,:,iz].any() ] # nz_nonz = len(z_centerline) # if nz_nonz==0 : # print '\nERROR: Centerline is empty' # sys.exit() # x_centerline = [0 for iz in range(0, nz_nonz, 1)] # y_centerline = [0 for iz in range(0, nz_nonz, 1)] # #print("z_centerline", z_centerline,nz_nonz,len(x_centerline)) # print '\nGet center of mass of the centerline ...' # for iz in xrange(len(z_centerline)): # x_centerline[iz], y_centerline[iz] = ndimage.measurements.center_of_mass(array(data_c[:,:,z_centerline[iz]])) # data_temp[x_centerline[iz], y_centerline[iz], z_centerline[iz]] = 1 # # ## Complete centerline # sct.printv('\nComplete the halls of the centerline if there are any...\n') # X,Y,Z = data_temp.nonzero() # # x_centerline_extended = [0 for i in range(0, nz, 1)] # y_centerline_extended = [0 for i in range(0, nz, 1)] # for iz in range(len(Z)): # x_centerline_extended[Z[iz]] = X[iz] # y_centerline_extended[Z[iz]] = Y[iz] # # X_centerline_extended = nonzero(x_centerline_extended) # X_centerline_extended = transpose(X_centerline_extended) # Y_centerline_extended = nonzero(y_centerline_extended) # Y_centerline_extended = transpose(Y_centerline_extended) # # # initialization: we set the extrem values to avoid edge effects # x_centerline_extended[0] = x_centerline_extended[X_centerline_extended[0]] # x_centerline_extended[-1] = x_centerline_extended[X_centerline_extended[-1]] # y_centerline_extended[0] = y_centerline_extended[Y_centerline_extended[0]] # y_centerline_extended[-1] = y_centerline_extended[Y_centerline_extended[-1]] # # # Add two rows to the vector X_means_smooth_extended: # # one before as means_smooth_extended[0] is now diff from 0 # # one after as means_smooth_extended[-1] is now diff from 0 # X_centerline_extended = append(X_centerline_extended, len(x_centerline_extended)-1) # X_centerline_extended = insert(X_centerline_extended, 0, 0) # Y_centerline_extended = append(Y_centerline_extended, len(y_centerline_extended)-1) # Y_centerline_extended = insert(Y_centerline_extended, 0, 0) # # #recurrence # count_zeros_x=0 # count_zeros_y=0 # for i in range(1,nz-1): # if x_centerline_extended[i]==0: # x_centerline_extended[i] = 0.5*(x_centerline_extended[X_centerline_extended[i-1-count_zeros_x]] + x_centerline_extended[X_centerline_extended[i-count_zeros_x]]) # count_zeros_x += 1 # if y_centerline_extended[i]==0: # y_centerline_extended[i] = 0.5*(y_centerline_extended[Y_centerline_extended[i-1-count_zeros_y]] + y_centerline_extended[Y_centerline_extended[i-count_zeros_y]]) # count_zeros_y += 1 # # # Save image centerline completed to be used after # sct.printv('\nSave image completed: centerline_rpi_completed.nii...\n') # for i in range(nz): # data_output[x_centerline_extended[i],y_centerline_extended[i],i] = 1 # img = Nifti1Image(data_output, None, hdr_c) # save(img, 'centerline_rpi_completed.nii') # # #end new # Straighten the spinal cord print '\nStraighten the spinal cord...' sct.run('sct_straighten_spinalcord -i anat_rpi.nii -s centerline_rpi.nii -qc 0 -x spline -v '+str(verbose)) # Smooth the straightened image along z print '\nSmooth the straightened image along z...' sct.run('sct_maths -i anat_rpi_straight.nii -smooth 0,0,'+str(sigma)+' -o anat_rpi_straight_smooth.nii', verbose) # Apply the reversed warping field to get back the curved spinal cord print '\nApply the reversed warping field to get back the curved spinal cord...' sct.run('sct_apply_transfo -i anat_rpi_straight_smooth.nii -o anat_rpi_straight_smooth_curved.nii -d anat.nii -w warp_straight2curve.nii.gz -x spline', verbose) # come back to parent folder os.chdir('..') # Generate output file print '\nGenerate output file...' sct.generate_output_file(path_tmp+'/anat_rpi_straight_smooth_curved.nii', file_anat+'_smooth'+ext_anat) # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp) # Display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s\n' # to view results sct.printv('Done! To view results, type:', verbose) sct.printv('fslview '+file_anat+' '+file_anat+'_smooth &\n', verbose, 'info')
def process(self): # preprocessing os.chdir(self.tmp_dir) im_target = Image(self.original_target) im_sc_seg = Image(self.original_sc_seg) self.original_header = im_target.hdr self.original_orientation = im_target.orientation index_x = self.original_orientation.find('R') if 'R' in self.original_orientation else self.original_orientation.find('L') index_y = self.original_orientation.find('P') if 'P' in self.original_orientation else self.original_orientation.find('A') index_z = self.original_orientation.find('I') if 'I' in self.original_orientation else self.original_orientation.find('S') # resampling of the images nx, ny, nz, nt, px, py, pz, pt = im_target.dim pix_dim = [px, py, pz] self.original_px = pix_dim[index_x] self.original_py = pix_dim[index_y] if round(self.original_px, 2) != self.resample_to or round(self.original_py, 2) != self.resample_to: self.t2star = resample_image(self.original_target, npx=self.resample_to, npy=self.resample_to) self.sc_seg = resample_image(self.original_sc_seg, binary=True, npx=self.resample_to, npy=self.resample_to) # denoising (optional) im_target = Image(self.t2star) if self.denoising: from sct_maths import denoise_ornlm im_target.data = denoise_ornlm(im_target.data) im_target.save() self.t2star = im_target.file_name + im_target.ext box_size = int(22.5/self.resample_to) # Pad in case the spinal cord is too close to the edges pad_size = box_size/2 + 2 self.pad = [str(pad_size)]*3 self.pad[index_z] = str(0) t2star_pad = sct.add_suffix(self.t2star, '_pad') sc_seg_pad = sct.add_suffix(self.sc_seg, '_pad') sct.run('sct_image -i '+self.t2star+' -pad '+self.pad[0]+','+self.pad[1]+','+self.pad[2]+' -o '+t2star_pad) sct.run('sct_image -i '+self.sc_seg+' -pad '+self.pad[0]+','+self.pad[1]+','+self.pad[2]+' -o '+sc_seg_pad) self.t2star = t2star_pad self.sc_seg = sc_seg_pad # put data in RPI t2star_rpi = sct.add_suffix(self.t2star, '_RPI') sc_seg_rpi = sct.add_suffix(self.sc_seg, '_RPI') sct.run('sct_image -i '+self.t2star+' -setorient RPI -o '+t2star_rpi) sct.run('sct_image -i '+self.sc_seg+' -setorient RPI -o '+sc_seg_rpi) self.t2star = t2star_rpi self.sc_seg = sc_seg_rpi self.square_mask, self.processed_target = crop_t2_star(self.t2star, self.sc_seg, box_size=box_size) if self.t2 is not None: self.fname_level = compute_level_file(self.t2star, self.sc_seg, self.t2, self.t2_seg, self.t2_landmarks) elif self.fname_level is not None: level_orientation = get_orientation(self.fname_level, filename=True) if level_orientation != 'IRP': self.fname_level = set_orientation(self.fname_level, 'IRP', filename=True) os.chdir('..')
def post_processing(self): ## DO INTERPOLATION BACK TO ORIGINAL IMAGE # get original SC segmentation oriented in RPI im_sc_seg_original_rpi = self.info_preprocessing['im_sc_seg_rpi'].copy() nx_ref, ny_ref, nz_ref, nt_ref, px_ref, py_ref, pz_ref, pt_ref = im_sc_seg_original_rpi.dim # create res GM seg image im_res_gmseg = im_sc_seg_original_rpi.copy() im_res_gmseg.data = np.zeros(im_res_gmseg.data.shape) # create res WM seg image im_res_wmseg = im_sc_seg_original_rpi.copy() im_res_wmseg.data = np.zeros(im_res_wmseg.data.shape) printv(' Interpolate result back into original space...', self.param.verbose, 'normal') for iz, im_iz_preprocessed in enumerate(self.info_preprocessing['interpolated_images']): # im gmseg for slice iz im_gmseg = im_iz_preprocessed.copy() im_gmseg.data = np.zeros(im_gmseg.data.shape) im_gmseg.data = self.target_im[iz].gm_seg # im wmseg for slice iz im_wmseg = im_iz_preprocessed.copy() im_wmseg.data = np.zeros(im_wmseg.data.shape) im_wmseg.data = self.target_im[iz].wm_seg for im_res_slice, im_res_tot in [(im_gmseg, im_res_gmseg), (im_wmseg, im_res_wmseg)]: # get reference image for this slice # (use only one slice to accelerate interpolation) im_ref = im_sc_seg_original_rpi.copy() im_ref.data = im_ref.data[:, :, iz] im_ref.dim = (nx_ref, ny_ref, 1, nt_ref, px_ref, py_ref, pz_ref, pt_ref) # correct reference header for this slice [[x_0_ref, y_0_ref, z_0_ref]] = im_ref.transfo_pix2phys(coordi=[[0, 0, iz]]) im_ref.hdr.as_analyze_map()['qoffset_x'] = x_0_ref im_ref.hdr.as_analyze_map()['qoffset_y'] = y_0_ref im_ref.hdr.as_analyze_map()['qoffset_z'] = z_0_ref im_ref.hdr.set_sform(im_ref.hdr.get_qform()) im_ref.hdr.set_qform(im_ref.hdr.get_qform()) # set im_res_slice header with im_sc_seg_original_rpi origin im_res_slice.hdr.as_analyze_map()['qoffset_x'] = x_0_ref im_res_slice.hdr.as_analyze_map()['qoffset_y'] = y_0_ref im_res_slice.hdr.as_analyze_map()['qoffset_z'] = z_0_ref im_res_slice.hdr.set_sform(im_res_slice.hdr.get_qform()) im_res_slice.hdr.set_qform(im_res_slice.hdr.get_qform()) # get physical coordinates of center of sc x_seg, y_seg = (im_sc_seg_original_rpi.data[:, :, iz] > 0).nonzero() x_center, y_center = np.mean(x_seg), np.mean(y_seg) [[x_center_phys, y_center_phys, z_center_phys]] = im_sc_seg_original_rpi.transfo_pix2phys(coordi=[[x_center, y_center, iz]]) # get physical coordinates of center of square WITH im_res_slice WITH SAME ORIGIN AS im_sc_seg_original_rpi sq_size_pix = int(self.param_data.square_size_size_mm / self.param_data.axial_res) [[x_square_center_phys, y_square_center_phys, z_square_center_phys]] = im_res_slice.transfo_pix2phys( coordi=[[int(sq_size_pix / 2), int(sq_size_pix / 2), 0]]) # set im_res_slice header by adding center of SC and center of square (in the correct space) to origin im_res_slice.hdr.as_analyze_map()['qoffset_x'] += x_center_phys - x_square_center_phys im_res_slice.hdr.as_analyze_map()['qoffset_y'] += y_center_phys - y_square_center_phys im_res_slice.hdr.as_analyze_map()['qoffset_z'] += z_center_phys im_res_slice.hdr.set_sform(im_res_slice.hdr.get_qform()) im_res_slice.hdr.set_qform(im_res_slice.hdr.get_qform()) # reshape data im_res_slice.data = im_res_slice.data.reshape((sq_size_pix, sq_size_pix, 1)) # interpolate to reference image interp = 0 if self.param_seg.type_seg == 'bin' else 1 im_res_slice_interp = im_res_slice.interpolate_from_image(im_ref, interpolation_mode=interp, border='nearest') # set correct slice of total image with this slice if len(im_res_slice_interp.data.shape) == 3: shape_x, shape_y, shape_z = im_res_slice_interp.data.shape im_res_slice_interp.data = im_res_slice_interp.data.reshape((shape_x, shape_y)) im_res_tot.data[:, :, iz] = im_res_slice_interp.data printv(' Reorient resulting segmentations to native orientation...', self.param.verbose, 'normal') ## PUT RES BACK IN ORIGINAL ORIENTATION im_res_gmseg.setFileName('res_gmseg.nii.gz') im_res_gmseg.save() im_res_gmseg = set_orientation(im_res_gmseg, self.info_preprocessing['orientation']) im_res_wmseg.setFileName('res_wmseg.nii.gz') im_res_wmseg.save() im_res_wmseg = set_orientation(im_res_wmseg, self.info_preprocessing['orientation']) return im_res_gmseg, im_res_wmseg
def main(fname_anat, fname_centerline, degree_poly, centerline_fitting, interp, remove_temp_files, verbose): # extract path of the script path_script = os.path.dirname(__file__) + '/' # Parameters for debug mode if param.debug == 1: print '\n*** WARNING: DEBUG MODE ON ***\n' status, path_sct_data = commands.getstatusoutput( 'echo $SCT_TESTING_DATA_DIR') fname_anat = path_sct_data + '/t2/t2.nii.gz' fname_centerline = path_sct_data + '/t2/t2_seg.nii.gz' # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) # Display arguments print '\nCheck input arguments...' print ' Input volume ...................... ' + fname_anat print ' Centerline ........................ ' + fname_centerline print '' # Get input image orientation im_anat = Image(fname_anat) input_image_orientation = get_orientation_3d(im_anat) # Reorient input data into RL PA IS orientation im_centerline = Image(fname_centerline) im_anat_orient = set_orientation(im_anat, 'RPI') im_anat_orient.setFileName('tmp.anat_orient.nii') im_centerline_orient = set_orientation(im_centerline, 'RPI') im_centerline_orient.setFileName('tmp.centerline_orient.nii') # Open centerline #========================================================================================== print '\nGet dimensions of input centerline...' nx, ny, nz, nt, px, py, pz, pt = im_centerline_orient.dim print '.. matrix size: ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) print '.. voxel size: ' + str(px) + 'mm x ' + str(py) + 'mm x ' + str( pz) + 'mm' print '\nOpen centerline volume...' data = im_centerline_orient.data X, Y, Z = (data > 0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # loop across z and associate x,y coordinate with the point having maximum intensity x_centerline = [0 for iz in range(min_z_index, max_z_index + 1, 1)] y_centerline = [0 for iz in range(min_z_index, max_z_index + 1, 1)] z_centerline = [iz for iz in range(min_z_index, max_z_index + 1, 1)] # Two possible scenario: # 1. the centerline is probabilistic: each slices contains voxels with the probability of containing the centerline [0:...:1] # We only take the maximum value of the image to aproximate the centerline. # 2. The centerline/segmentation image contains many pixels per slice with values {0,1}. # We take all the points and approximate the centerline on all these points. X, Y, Z = ((data < 1) * (data > 0)).nonzero() # X is empty if binary image if (len(X) > 0): # Scenario 1 for iz in range(min_z_index, max_z_index + 1, 1): x_centerline[iz - min_z_index], y_centerline[ iz - min_z_index] = numpy.unravel_index( data[:, :, iz].argmax(), data[:, :, iz].shape) else: # Scenario 2 for iz in range(min_z_index, max_z_index + 1, 1): x_seg, y_seg = (data[:, :, iz] > 0).nonzero() if len(x_seg) > 0: x_centerline[iz - min_z_index] = numpy.mean(x_seg) y_centerline[iz - min_z_index] = numpy.mean(y_seg) # TODO: find a way to do the previous loop with this, which is more neat: # [numpy.unravel_index(data[:,:,iz].argmax(), data[:,:,iz].shape) for iz in range(0,nz,1)] # clear variable del data # Fit the centerline points with the kind of curve given as argument of the script and return the new smoothed coordinates if centerline_fitting == 'nurbs': try: x_centerline_fit, y_centerline_fit = b_spline_centerline( x_centerline, y_centerline, z_centerline) except ValueError: print "splines fitting doesn't work, trying with polynomial fitting...\n" x_centerline_fit, y_centerline_fit = polynome_centerline( x_centerline, y_centerline, z_centerline) elif centerline_fitting == 'polynome': x_centerline_fit, y_centerline_fit = polynome_centerline( x_centerline, y_centerline, z_centerline) #========================================================================================== # Split input volume print '\nSplit input volume...' im_anat_orient_split_list = split_data(im_anat_orient, 2) file_anat_split = [] for im in im_anat_orient_split_list: file_anat_split.append(im.absolutepath) im.save() # initialize variables file_mat_inv_cumul = [ 'tmp.mat_inv_cumul_Z' + str(z).zfill(4) for z in range(0, nz, 1) ] z_init = min_z_index displacement_max_z_index = x_centerline_fit[ z_init - min_z_index] - x_centerline_fit[max_z_index - min_z_index] # write centerline as text file print '\nGenerate fitted transformation matrices...' file_mat_inv_cumul_fit = [ 'tmp.mat_inv_cumul_fit_Z' + str(z).zfill(4) for z in range(0, nz, 1) ] for iz in range(min_z_index, max_z_index + 1, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') if (x_centerline[iz - min_z_index] == 0 and y_centerline[iz - min_z_index] == 0): displacement = 0 else: displacement = x_centerline_fit[ z_init - min_z_index] - x_centerline_fit[iz - min_z_index] fid.write('%i %i %i %f\n' % (1, 0, 0, displacement)) fid.write('%i %i %i %f\n' % (0, 1, 0, 0)) fid.write('%i %i %i %i\n' % (0, 0, 1, 0)) fid.write('%i %i %i %i\n' % (0, 0, 0, 1)) fid.close() # we complete the displacement matrix in z direction for iz in range(0, min_z_index, 1): fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' % (1, 0, 0, 0)) fid.write('%i %i %i %f\n' % (0, 1, 0, 0)) fid.write('%i %i %i %i\n' % (0, 0, 1, 0)) fid.write('%i %i %i %i\n' % (0, 0, 0, 1)) fid.close() for iz in range(max_z_index + 1, nz, 1): fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' % (1, 0, 0, displacement_max_z_index)) fid.write('%i %i %i %f\n' % (0, 1, 0, 0)) fid.write('%i %i %i %i\n' % (0, 0, 1, 0)) fid.write('%i %i %i %i\n' % (0, 0, 0, 1)) fid.close() # apply transformations to data print '\nApply fitted transformation matrices...' file_anat_split_fit = [ 'tmp.anat_orient_fit_Z' + str(z).zfill(4) for z in range(0, nz, 1) ] for iz in range(0, nz, 1): # forward cumulative transformation to data sct.run(fsloutput + 'flirt -in ' + file_anat_split[iz] + ' -ref ' + file_anat_split[iz] + ' -applyxfm -init ' + file_mat_inv_cumul_fit[iz] + ' -out ' + file_anat_split_fit[iz] + ' -interp ' + interp) # Merge into 4D volume print '\nMerge into 4D volume...' from glob import glob im_to_concat_list = [ Image(fname) for fname in glob('tmp.anat_orient_fit_Z*.nii') ] im_concat_out = concat_data(im_to_concat_list, 2) im_concat_out.setFileName('tmp.anat_orient_fit.nii') im_concat_out.save() # sct.run(fsloutput+'fslmerge -z tmp.anat_orient_fit tmp.anat_orient_fit_z*') # Reorient data as it was before print '\nReorient data back into native orientation...' fname_anat_fit_orient = set_orientation(im_concat_out.absolutepath, input_image_orientation, filename=True) move(fname_anat_fit_orient, 'tmp.anat_orient_fit_reorient.nii') # Generate output file (in current folder) print '\nGenerate output file (in current folder)...' sct.generate_output_file('tmp.anat_orient_fit_reorient.nii', file_anat + '_flatten' + ext_anat) # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm -rf tmp.*') # to view results print '\nDone! To view results, type:' print 'fslview ' + file_anat + ext_anat + ' ' + file_anat + '_flatten' + ext_anat + ' &\n'
def main(args=None): # Initialization # fname_anat = '' # fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) # remove_temp_files = param.remove_temp_files # verbose = param.verbose start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-r' in arguments: remove_temp_files = int(arguments['-r']) if '-v' in arguments: verbose = int(arguments['-v']) # Display arguments sct.printv('\nCheck input arguments...') sct.printv(' Volume to smooth .................. ' + fname_anat) sct.printv(' Centerline ........................ ' + fname_centerline) sct.printv(' Sigma (mm) ........................ ' + str(sigma)) sct.printv(' Verbose ........................... ' + str(verbose)) # Check that input is 3D: from msct_image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv( 'WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i ' + fname_anat + ' -split t -o ' + fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname( fname_centerline) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir ' + path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('cp ' + fname_anat + ' ' + path_tmp + 'anat' + ext_anat, verbose) sct.run( 'cp ' + fname_centerline + ' ' + path_tmp + 'centerline' + ext_centerline, verbose) # go to tmp folder os.chdir(path_tmp) # convert to nii format convert('anat' + ext_anat, 'anat.nii') convert('centerline' + ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI sct.printv('\nOrient input volume to RPI orientation...') fname_anat_rpi = set_orientation('anat.nii', 'RPI', filename=True) move(fname_anat_rpi, 'anat_rpi.nii') # Change orientation of the input image into RPI sct.printv('\nOrient centerline to RPI orientation...') fname_centerline_rpi = set_orientation('centerline.nii', 'RPI', filename=True) move(fname_centerline_rpi, 'centerline_rpi.nii') # Straighten the spinal cord # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile( '../warp_straight2curve.nii.gz') and os.path.isfile( '../straight_ref.nii.gz'): # if they exist, copy them into current folder sct.printv( 'WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning') shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz') shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz') shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz') # apply straightening sct.run( 'sct_apply_transfo -i anat_rpi.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o anat_rpi_straight.nii -x spline', verbose) else: sct.run( 'sct_straighten_spinalcord -i anat_rpi.nii -s centerline_rpi.nii -qc 0 -x spline', verbose) # Smooth the straightened image along z sct.printv('\nSmooth the straightened image along z...') sct.run( 'sct_maths -i anat_rpi_straight.nii -smooth 0,0,' + str(sigma) + ' -o anat_rpi_straight_smooth.nii', verbose) # Apply the reversed warping field to get back the curved spinal cord sct.printv( '\nApply the reversed warping field to get back the curved spinal cord...' ) sct.run( 'sct_apply_transfo -i anat_rpi_straight_smooth.nii -o anat_rpi_straight_smooth_curved.nii -d anat.nii -w warp_straight2curve.nii.gz -x spline', verbose) # replace zeroed voxels by original image (issue #937) sct.printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.setFileName('anat_rpi_straight_smooth_curved_nonzero.nii') nii_smooth.save() # come back to parent folder os.chdir('..') # Generate output file sct.printv('\nGenerate output file...') sct.generate_output_file( path_tmp + '/anat_rpi_straight_smooth_curved_nonzero.nii', file_anat + '_smooth' + ext_anat) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...') sct.run('rm -rf ' + path_tmp) # Display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's\n') # to view results sct.printv('Done! To view results, type:', verbose) sct.printv('fslview ' + file_anat + ' ' + file_anat + '_smooth &\n', verbose, 'info')
def get_centerline_from_point(input_image, point_file, gap=4, gaussian_kernel=4, remove_tmp_files=1): # Initialization fname_anat = input_image fname_point = point_file slice_gap = gap remove_tmp_files = remove_tmp_files gaussian_kernel = gaussian_kernel start_time = time() verbose = 1 # get path of the toolbox status, path_sct = commands.getstatusoutput("echo $SCT_DIR") path_sct = sct.slash_at_the_end(path_sct, 1) # Parameters for debug mode if param.debug == 1: sct.printv("\n*** WARNING: DEBUG MODE ON ***\n\t\t\tCurrent working directory: " + os.getcwd(), "warning") status, path_sct_testing_data = commands.getstatusoutput("echo $SCT_TESTING_DATA_DIR") fname_anat = path_sct_testing_data + "/t2/t2.nii.gz" fname_point = path_sct_testing_data + "/t2/t2_centerline_init.nii.gz" slice_gap = 5 # check existence of input files sct.check_file_exist(fname_anat) sct.check_file_exist(fname_point) # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_point, file_point, ext_point = sct.extract_fname(fname_point) # extract path of schedule file # TODO: include schedule file in sct # TODO: check existence of schedule file file_schedule = path_sct + param.schedule_file # Get input image orientation input_image_orientation = get_orientation_3d(fname_anat, filename=True) # Display arguments print "\nCheck input arguments..." print " Anatomical image: " + fname_anat print " Orientation: " + input_image_orientation print " Point in spinal cord: " + fname_point print " Slice gap: " + str(slice_gap) print " Gaussian kernel: " + str(gaussian_kernel) print " Degree of polynomial: " + str(param.deg_poly) # create temporary folder print ("\nCreate temporary folder...") path_tmp = "tmp." + strftime("%y%m%d%H%M%S") sct.create_folder(path_tmp) print "\nCopy input data..." sct.run("cp " + fname_anat + " " + path_tmp + "/tmp.anat" + ext_anat) sct.run("cp " + fname_point + " " + path_tmp + "/tmp.point" + ext_point) # go to temporary folder os.chdir(path_tmp) # convert to nii im_anat = convert("tmp.anat" + ext_anat, "tmp.anat.nii") im_point = convert("tmp.point" + ext_point, "tmp.point.nii") # Reorient input anatomical volume into RL PA IS orientation print "\nReorient input volume to RL PA IS orientation..." set_orientation(im_anat, "RPI") im_anat.setFileName("tmp.anat_orient.nii") # Reorient binary point into RL PA IS orientation print "\nReorient binary point into RL PA IS orientation..." # sct.run(sct.fsloutput + 'fslswapdim tmp.point RL PA IS tmp.point_orient') set_orientation(im_point, "RPI") im_point.setFileName("tmp.point_orient.nii") # Get image dimensions print "\nGet image dimensions..." nx, ny, nz, nt, px, py, pz, pt = Image("tmp.anat_orient.nii").dim print ".. matrix size: " + str(nx) + " x " + str(ny) + " x " + str(nz) print ".. voxel size: " + str(px) + "mm x " + str(py) + "mm x " + str(pz) + "mm" # Split input volume print "\nSplit input volume..." im_anat_split_list = split_data(im_anat, 2) file_anat_split = [] for im in im_anat_split_list: file_anat_split.append(im.absolutepath) im.save() im_point_split_list = split_data(im_point, 2) file_point_split = [] for im in im_point_split_list: file_point_split.append(im.absolutepath) im.save() # Extract coordinates of input point data_point = Image("tmp.point_orient.nii").data x_init, y_init, z_init = unravel_index(data_point.argmax(), data_point.shape) sct.printv("Coordinates of input point: (" + str(x_init) + ", " + str(y_init) + ", " + str(z_init) + ")", verbose) # Create 2D gaussian mask sct.printv("\nCreate gaussian mask from point...", verbose) xx, yy = mgrid[:nx, :ny] mask2d = zeros((nx, ny)) radius = round(float(gaussian_kernel + 1) / 2) # add 1 because the radius includes the center. sigma = float(radius) mask2d = exp(-(((xx - x_init) ** 2) / (2 * (sigma ** 2)) + ((yy - y_init) ** 2) / (2 * (sigma ** 2)))) # Save mask to 2d file file_mask_split = ["tmp.mask_orient_Z" + str(z).zfill(4) for z in range(0, nz, 1)] nii_mask2d = Image("tmp.anat_orient_Z0000.nii") nii_mask2d.data = mask2d nii_mask2d.setFileName(file_mask_split[z_init] + ".nii") nii_mask2d.save() # initialize variables file_mat = ["tmp.mat_Z" + str(z).zfill(4) for z in range(0, nz, 1)] file_mat_inv = ["tmp.mat_inv_Z" + str(z).zfill(4) for z in range(0, nz, 1)] file_mat_inv_cumul = ["tmp.mat_inv_cumul_Z" + str(z).zfill(4) for z in range(0, nz, 1)] # create identity matrix for initial transformation matrix fid = open(file_mat_inv_cumul[z_init], "w") fid.write("%i %i %i %i\n" % (1, 0, 0, 0)) fid.write("%i %i %i %i\n" % (0, 1, 0, 0)) fid.write("%i %i %i %i\n" % (0, 0, 1, 0)) fid.write("%i %i %i %i\n" % (0, 0, 0, 1)) fid.close() # initialize centerline: give value corresponding to initial point x_centerline = [x_init] y_centerline = [y_init] z_centerline = [z_init] warning_count = 0 # go up (1), then down (2) in reference to the binary point for iUpDown in range(1, 3): if iUpDown == 1: # z increases slice_gap_signed = slice_gap elif iUpDown == 2: # z decreases slice_gap_signed = -slice_gap # reverse centerline (because values will be appended at the end) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # initialization before looping z_dest = z_init # point given by user z_src = z_dest + slice_gap_signed # continue looping if 0 <= z < nz while 0 <= z_src < nz: # print current z: print "z=" + str(z_src) + ":" # estimate transformation sct.run( fsloutput + "flirt -in " + file_anat_split[z_src] + " -ref " + file_anat_split[z_dest] + " -schedule " + file_schedule + " -verbose 0 -omat " + file_mat[z_src] + " -cost normcorr -forcescaling -inweight " + file_mask_split[z_dest] + " -refweight " + file_mask_split[z_dest] ) # display transfo status, output = sct.run("cat " + file_mat[z_src]) print output # check if transformation is bigger than 1.5x slice_gap tx = float(output.split()[3]) ty = float(output.split()[7]) norm_txy = linalg.norm([tx, ty], ord=2) if norm_txy > 1.5 * slice_gap: print "WARNING: Transformation is too large --> using previous one." warning_count = warning_count + 1 # if previous transformation exists, replace current one with previous one if os.path.isfile(file_mat[z_dest]): sct.run("cp " + file_mat[z_dest] + " " + file_mat[z_src]) # estimate inverse transformation matrix sct.run("convert_xfm -omat " + file_mat_inv[z_src] + " -inverse " + file_mat[z_src]) # compute cumulative transformation sct.run( "convert_xfm -omat " + file_mat_inv_cumul[z_src] + " -concat " + file_mat_inv[z_src] + " " + file_mat_inv_cumul[z_dest] ) # apply inverse cumulative transformation to initial gaussian mask (to put it in src space) sct.run( fsloutput + "flirt -in " + file_mask_split[z_init] + " -ref " + file_mask_split[z_init] + " -applyxfm -init " + file_mat_inv_cumul[z_src] + " -out " + file_mask_split[z_src] ) # open inverse cumulative transformation file and generate centerline fid = open(file_mat_inv_cumul[z_src]) mat = fid.read().split() x_centerline.append(x_init + float(mat[3])) y_centerline.append(y_init + float(mat[7])) z_centerline.append(z_src) # z_index = z_index+1 # define new z_dest (target slice) and new z_src (moving slice) z_dest = z_dest + slice_gap_signed z_src = z_src + slice_gap_signed # Reconstruct centerline # ==================================================================================================== # reverse back centerline (because it's been reversed once, so now all values are in the right order) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # fit centerline in the Z-X plane using polynomial function print "\nFit centerline in the Z-X plane using polynomial function..." coeffsx = polyfit(z_centerline, x_centerline, deg=param.deg_poly) polyx = poly1d(coeffsx) x_centerline_fit = polyval(polyx, z_centerline) # calculate RMSE rmse = linalg.norm(x_centerline_fit - x_centerline) / sqrt(len(x_centerline)) # calculate max absolute error max_abs = max(abs(x_centerline_fit - x_centerline)) print ".. RMSE (in mm): " + str(rmse * px) print ".. Maximum absolute error (in mm): " + str(max_abs * px) # fit centerline in the Z-Y plane using polynomial function print "\nFit centerline in the Z-Y plane using polynomial function..." coeffsy = polyfit(z_centerline, y_centerline, deg=param.deg_poly) polyy = poly1d(coeffsy) y_centerline_fit = polyval(polyy, z_centerline) # calculate RMSE rmse = linalg.norm(y_centerline_fit - y_centerline) / sqrt(len(y_centerline)) # calculate max absolute error max_abs = max(abs(y_centerline_fit - y_centerline)) print ".. RMSE (in mm): " + str(rmse * py) print ".. Maximum absolute error (in mm): " + str(max_abs * py) # display if param.debug == 1: import matplotlib.pyplot as plt plt.figure() plt.plot(z_centerline, x_centerline, ".", z_centerline, x_centerline_fit, "r") plt.legend(["Data", "Polynomial Fit"]) plt.title("Z-X plane polynomial interpolation") plt.show() plt.figure() plt.plot(z_centerline, y_centerline, ".", z_centerline, y_centerline_fit, "r") plt.legend(["Data", "Polynomial Fit"]) plt.title("Z-Y plane polynomial interpolation") plt.show() # generate full range z-values for centerline z_centerline_full = [iz for iz in range(0, nz, 1)] # calculate X and Y values for the full centerline x_centerline_fit_full = polyval(polyx, z_centerline_full) y_centerline_fit_full = polyval(polyy, z_centerline_full) # Generate fitted transformation matrices and write centerline coordinates in text file print "\nGenerate fitted transformation matrices and write centerline coordinates in text file..." file_mat_inv_cumul_fit = ["tmp.mat_inv_cumul_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] file_mat_cumul_fit = ["tmp.mat_cumul_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] fid_centerline = open("tmp.centerline_coordinates.txt", "w") for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], "w") fid.write("%i %i %i %f\n" % (1, 0, 0, x_centerline_fit_full[iz] - x_init)) fid.write("%i %i %i %f\n" % (0, 1, 0, y_centerline_fit_full[iz] - y_init)) fid.write("%i %i %i %i\n" % (0, 0, 1, 0)) fid.write("%i %i %i %i\n" % (0, 0, 0, 1)) fid.close() # compute forward cumulative fitted transformation matrix sct.run("convert_xfm -omat " + file_mat_cumul_fit[iz] + " -inverse " + file_mat_inv_cumul_fit[iz]) # write centerline coordinates in x, y, z format fid_centerline.write( "%f %f %f\n" % (x_centerline_fit_full[iz], y_centerline_fit_full[iz], z_centerline_full[iz]) ) fid_centerline.close() # Prepare output data # ==================================================================================================== # write centerline as text file for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], "w") fid.write("%i %i %i %f\n" % (1, 0, 0, x_centerline_fit_full[iz] - x_init)) fid.write("%i %i %i %f\n" % (0, 1, 0, y_centerline_fit_full[iz] - y_init)) fid.write("%i %i %i %i\n" % (0, 0, 1, 0)) fid.write("%i %i %i %i\n" % (0, 0, 0, 1)) fid.close() # write polynomial coefficients savetxt("tmp.centerline_polycoeffs_x.txt", coeffsx) savetxt("tmp.centerline_polycoeffs_y.txt", coeffsy) # apply transformations to data print "\nApply fitted transformation matrices..." file_anat_split_fit = ["tmp.anat_orient_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] file_mask_split_fit = ["tmp.mask_orient_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] file_point_split_fit = ["tmp.point_orient_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] for iz in range(0, nz, 1): # forward cumulative transformation to data sct.run( fsloutput + "flirt -in " + file_anat_split[iz] + " -ref " + file_anat_split[iz] + " -applyxfm -init " + file_mat_cumul_fit[iz] + " -out " + file_anat_split_fit[iz] ) # inverse cumulative transformation to mask sct.run( fsloutput + "flirt -in " + file_mask_split[z_init] + " -ref " + file_mask_split[z_init] + " -applyxfm -init " + file_mat_inv_cumul_fit[iz] + " -out " + file_mask_split_fit[iz] ) # inverse cumulative transformation to point sct.run( fsloutput + "flirt -in " + file_point_split[z_init] + " -ref " + file_point_split[z_init] + " -applyxfm -init " + file_mat_inv_cumul_fit[iz] + " -out " + file_point_split_fit[iz] + " -interp nearestneighbour" ) # Merge into 4D volume print "\nMerge into 4D volume..." # im_anat_list = [Image(fname) for fname in glob.glob('tmp.anat_orient_fit_z*.nii')] fname_anat_list = glob.glob("tmp.anat_orient_fit_z*.nii") im_anat_concat = concat_data(fname_anat_list, 2) im_anat_concat.setFileName("tmp.anat_orient_fit.nii") im_anat_concat.save() # im_mask_list = [Image(fname) for fname in glob.glob('tmp.mask_orient_fit_z*.nii')] fname_mask_list = glob.glob("tmp.mask_orient_fit_z*.nii") im_mask_concat = concat_data(fname_mask_list, 2) im_mask_concat.setFileName("tmp.mask_orient_fit.nii") im_mask_concat.save() # im_point_list = [Image(fname) for fname in glob.glob('tmp.point_orient_fit_z*.nii')] fname_point_list = glob.glob("tmp.point_orient_fit_z*.nii") im_point_concat = concat_data(fname_point_list, 2) im_point_concat.setFileName("tmp.point_orient_fit.nii") im_point_concat.save() # Copy header geometry from input data print "\nCopy header geometry from input data..." im_anat = Image("tmp.anat_orient.nii") im_anat_orient_fit = Image("tmp.anat_orient_fit.nii") im_mask_orient_fit = Image("tmp.mask_orient_fit.nii") im_point_orient_fit = Image("tmp.point_orient_fit.nii") im_anat_orient_fit = copy_header(im_anat, im_anat_orient_fit) im_mask_orient_fit = copy_header(im_anat, im_mask_orient_fit) im_point_orient_fit = copy_header(im_anat, im_point_orient_fit) for im in [im_anat_orient_fit, im_mask_orient_fit, im_point_orient_fit]: im.save() # Reorient outputs into the initial orientation of the input image print "\nReorient the centerline into the initial orientation of the input image..." set_orientation("tmp.point_orient_fit.nii", input_image_orientation, "tmp.point_orient_fit.nii") set_orientation("tmp.mask_orient_fit.nii", input_image_orientation, "tmp.mask_orient_fit.nii") # Generate output file (in current folder) print "\nGenerate output file (in current folder)..." os.chdir("..") # come back to parent folder fname_output_centerline = sct.generate_output_file( path_tmp + "/tmp.point_orient_fit.nii", file_anat + "_centerline" + ext_anat ) # Delete temporary files if remove_tmp_files == 1: print "\nRemove temporary files..." sct.run("rm -rf " + path_tmp, error_exit="warning") # print number of warnings print "\nNumber of warnings: " + str( warning_count ) + " (if >10, you should probably reduce the gap and/or increase the kernel size" # display elapsed time elapsed_time = time() - start_time print "\nFinished! \n\tGenerated file: " + fname_output_centerline + "\n\tElapsed time: " + str( int(round(elapsed_time)) ) + "s\n"