def crop_from_mask_with_background(self): from numpy import asarray, einsum image_in = Image(self.input_filename) data_array = asarray(image_in.data) data_mask = asarray(Image(self.mask).data) assert data_array.shape == data_mask.shape # Element-wise matrix multiplication: new_data = None dim = len(data_array.shape) if dim == 3: new_data = einsum('ijk,ijk->ijk', data_mask, data_array) elif dim == 2: new_data = einsum('ij,ij->ij', data_mask, data_array) if self.background != 0: from sct_maths import get_data_or_scalar data_background = get_data_or_scalar(str(self.background), data_array) data_mask_inv = data_mask.max() - data_mask if dim == 3: data_background = einsum('ijk,ijk->ijk', data_mask_inv, data_background) elif dim == 2: data_background = einsum('ij,ij->ij', data_mask_inv, data_background) new_data += data_background # set image out image_in.setFileName(self.output_filename) image_in.data = new_data image_in.save()
def concat_data(fname_in, fname_out, dim): """ Concatenate data :param fname_in: list of file names. :param fname_out: :param dim: dimension: 0, 1, 2, 3. :return: none """ # create empty list list_data = [] # loop across files for i in range(len(fname_in)): # append data to list list_data.append(Image(fname_in[i]).data) # expand dimension of all elements in the list if necessary if dim > list_data[0].ndim-1: list_data = [expand_dims(i, dim) for i in list_data] # concatenate try: data_concat = concatenate(list_data, axis=dim) except Exception as e: sct.printv('\nERROR: Concatenation on line {}'.format(sys.exc_info()[-1].tb_lineno)+'\n'+str(e)+'\n', 1, 'error') # write file im = Image(fname_in[0]) im.data = data_concat im.setFileName(fname_out) im.save()
def clean_labeled_segmentation(fname_labeled_seg, fname_seg, fname_labeled_seg_new): """ Clean labeled segmentation by: (i) removing voxels in segmentation_labeled that are not in segmentation and (ii) adding voxels in segmentation that are not in segmentation_labeled :param fname_labeled_seg: :param fname_seg: :param fname_labeled_seg_new: output :return: none """ # remove voxels in segmentation_labeled that are not in segmentation sct.run('sct_maths -i ' + fname_labeled_seg + ' -mul ' + fname_seg + ' -o segmentation_labeled_mul.nii.gz') # add voxels in segmentation that are not in segmentation_labeled sct.run('sct_maths -i ' + fname_labeled_seg + ' -dilate 2 -o segmentation_labeled_dilate.nii.gz' ) # dilate labeled segmentation data_label_dilate = Image('segmentation_labeled_dilate.nii.gz').data sct.run( 'sct_maths -i segmentation_labeled_mul.nii.gz -bin 0 -o segmentation_labeled_mul_bin.nii.gz' ) data_label_bin = Image('segmentation_labeled_mul_bin.nii.gz').data data_seg = Image(fname_seg).data data_diff = data_seg - data_label_bin ind_nonzero = np.where(data_diff) im_label = Image('segmentation_labeled_mul.nii.gz') for i_vox in range(len(ind_nonzero[0])): # assign closest label value for this voxel ix, iy, iz = ind_nonzero[0][i_vox], ind_nonzero[1][i_vox], ind_nonzero[ 2][i_vox] im_label.data[ix, iy, iz] = data_label_dilate[ix, iy, iz] # save new label file (overwrite) im_label.setFileName(fname_labeled_seg_new) im_label.save()
def remove_label(self, symmetry=False): """ Compare two label images and remove any labels in input image that are not in reference image. The symmetry option enables to remove labels from reference image that are not in input image """ # image_output = Image(self.image_input.dim, orientation=self.image_input.orientation, hdr=self.image_input.hdr, verbose=self.verbose) image_output = Image(self.image_input, verbose=self.verbose) image_output.data *= 0 # put all voxels to 0 result_coord_input, result_coord_ref = self.remove_label_coord(self.image_input.getNonZeroCoordinates(coordValue=True), self.image_ref.getNonZeroCoordinates(coordValue=True), symmetry) for coord in result_coord_input: image_output.data[int(coord.x), int(coord.y), int(coord.z)] = int(round(coord.value)) if symmetry: # image_output_ref = Image(self.image_ref.dim, orientation=self.image_ref.orientation, hdr=self.image_ref.hdr, verbose=self.verbose) image_output_ref = Image(self.image_ref, verbose=self.verbose) for coord in result_coord_ref: image_output_ref.data[int(coord.x), int(coord.y), int(coord.z)] = int(round(coord.value)) image_output_ref.setFileName(self.fname_output[1]) image_output_ref.save('minimize_int') self.fname_output = self.fname_output[0] return image_output
def clean_labeled_segmentation(fname_labeled_seg, fname_seg, fname_labeled_seg_new): """ Clean labeled segmentation by: (i) removing voxels in segmentation_labeled that are not in segmentation and (ii) adding voxels in segmentation that are not in segmentation_labeled :param fname_labeled_seg: :param fname_seg: :param fname_labeled_seg_new: output :return: none """ # remove voxels in segmentation_labeled that are not in segmentation run('sct_maths -i '+fname_labeled_seg+' -mul '+fname_seg+' -o segmentation_labeled_mul.nii.gz') # add voxels in segmentation that are not in segmentation_labeled run('sct_maths -i '+fname_labeled_seg+' -dilate 2 -o segmentation_labeled_dilate.nii.gz') # dilate labeled segmentation data_label_dilate = Image('segmentation_labeled_dilate.nii.gz').data run('sct_maths -i segmentation_labeled_mul.nii.gz -bin 0 -o segmentation_labeled_mul_bin.nii.gz') data_label_bin = Image('segmentation_labeled_mul_bin.nii.gz').data data_seg = Image(fname_seg).data data_diff = data_seg - data_label_bin ind_nonzero = np.where(data_diff) im_label = Image('segmentation_labeled_mul.nii.gz') for i_vox in range(len(ind_nonzero[0])): # assign closest label value for this voxel ix, iy, iz = ind_nonzero[0][i_vox], ind_nonzero[1][i_vox], ind_nonzero[2][i_vox] im_label.data[ix, iy, iz] = data_label_dilate[ix, iy, iz] # save new label file (overwrite) im_label.setFileName(fname_labeled_seg_new) im_label.save()
def concat_data(fname_in, fname_out, dim): """ Concatenate data :param fname_in: list of file names. :param fname_out: :param dim: dimension: 0, 1, 2, 3. :return: none """ # create empty list list_data = [] # loop across files for i in range(len(fname_in)): # append data to list list_data.append(Image(fname_in[i]).data) # expand dimension of all elements in the list if necessary if dim > list_data[0].ndim - 1: list_data = [expand_dims(i, dim) for i in list_data] # concatenate try: data_concat = concatenate(list_data, axis=dim) except Exception as e: sct.printv( '\nERROR: Concatenation on line {}'.format( sys.exc_info()[-1].tb_lineno) + '\n' + str(e) + '\n', 1, 'error') # write file im = Image(fname_in[0]) im.data = data_concat im.setFileName(fname_out) im.save()
def crop_from_mask_with_background(self): from numpy import asarray, einsum image_in = Image(self.input_filename) data_array = asarray(image_in.data) data_mask = asarray(Image(self.mask).data) assert data_array.shape == data_mask.shape # Element-wise matrix multiplication: new_data = None dim = len(data_array.shape) if dim == 3: new_data = einsum('ijk,ijk->ijk', data_mask, data_array) elif dim == 2: new_data = einsum('ij,ij->ij', data_mask, data_array) if self.background != 0: from sct_maths import get_data_or_scalar data_background = get_data_or_scalar(str(self.background), data_array) data_mask_inv = data_mask.max() - data_mask if dim == 3: data_background = einsum('ijk,ijk->ijk', data_mask_inv, data_background) elif dim == 2: data_background = einsum('ij,ij->ij', data_mask_inv, data_background) new_data += data_background # set image out image_in.setFileName(self.output_filename) image_in.data = new_data image_in.save()
def create_label_z(fname_seg, z, value): """ Create a label at coordinates x_center, y_center, z :param fname_seg: segmentation :param z: int :return: fname_label """ fname_label = 'labelz.nii.gz' nii = Image(fname_seg) orientation_origin = nii.change_orientation( 'RPI') # change orientation to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions # find x and y coordinates of the centerline at z using center of mass from scipy.ndimage.measurements import center_of_mass x, y = center_of_mass(nii.data[:, :, z]) x, y = int(round(x)), int(round(y)) nii.data[:, :, :] = 0 nii.data[x, y, z] = value # dilate label to prevent it from disappearing due to nearestneighbor interpolation from sct_maths import dilate nii.data = dilate(nii.data, [3]) nii.setFileName(fname_label) nii.change_orientation( orientation_origin) # put back in original orientation nii.save() return fname_label
def main(args = None): dim_list = ['x', 'y', 'z', 't'] if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_in = arguments["-i"] fname_out = arguments["-o"] verbose = int(arguments['-v']) # Build fname_out if fname_out == '': path_in, file_in, ext_in = extract_fname(fname_in) fname_out = path_in+file_in+'_mean'+ext_in # Open file. nii = Image(fname_in) data = nii.data # run command if '-otsu' in arguments: param = arguments['-otsu'] data_out = otsu(data, param) elif '-otsu_adap' in arguments: param = arguments['-otsu_adap'] data_out = otsu_adap(data, param[0], param[1]) elif '-otsu_median' in arguments: param = arguments['-otsu_median'] data_out = otsu_median(data, param[0], param[1]) elif '-thr' in arguments: param = arguments['-thr'] data_out = threshold(data, param) elif '-percent' in arguments: param = arguments['-percent'] data_out = perc(data, param) elif '-mean' in arguments: dim = dim_list.index(arguments['-mean']) data_out = compute_mean(data, dim) elif '-std' in arguments: dim = dim_list.index(arguments['-std']) data_out = compute_std(data, dim) elif '-dilate' in arguments: data_out = dilate(data, arguments['-dilate']) elif '-erode' in arguments: data_out = erode(data, arguments['-dilate']) else: printv('No process applied.', 1, 'warning') return # Write output nii.data = data_out nii.setFileName(fname_out) nii.save() # display message printv('Created file:\n--> '+fname_out+'\n', verbose, 'info')
def main(args=None): dim_list = ['x', 'y', 'z', 't'] if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_in = arguments["-i"] fname_out = arguments["-o"] verbose = int(arguments['-v']) # Build fname_out if fname_out == '': path_in, file_in, ext_in = extract_fname(fname_in) fname_out = path_in + file_in + '_mean' + ext_in # Open file. nii = Image(fname_in) data = nii.data # run command if '-otsu' in arguments: param = arguments['-otsu'] data_out = otsu(data, param) elif '-otsu_adap' in arguments: param = arguments['-otsu_adap'] data_out = otsu_adap(data, param[0], param[1]) elif '-otsu_median' in arguments: param = arguments['-otsu_median'] data_out = otsu_median(data, param[0], param[1]) elif '-thr' in arguments: param = arguments['-thr'] data_out = threshold(data, param) elif '-percent' in arguments: param = arguments['-percent'] data_out = perc(data, param) elif '-mean' in arguments: dim = dim_list.index(arguments['-mean']) data_out = compute_mean(data, dim) elif '-std' in arguments: dim = dim_list.index(arguments['-std']) data_out = compute_std(data, dim) elif '-dilate' in arguments: data_out = dilate(data, arguments['-dilate']) elif '-erode' in arguments: data_out = erode(data, arguments['-dilate']) else: printv('No process applied.', 1, 'warning') return # Write output nii.data = data_out nii.setFileName(fname_out) nii.save() # display message printv('Created file:\n--> ' + fname_out + '\n', verbose, 'info')
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 remove_label(self, symmetry=False): """ Compare two label images and remove any labels in input image that are not in reference image. The symmetry option enables to remove labels from reference image that are not in input image """ # image_output = Image(self.image_input.dim, orientation=self.image_input.orientation, hdr=self.image_input.hdr, verbose=self.verbose) image_output = Image(self.image_input, verbose=self.verbose) image_output.data *= 0 # put all voxels to 0 result_coord_input, result_coord_ref = self.remove_label_coord( self.image_input.getNonZeroCoordinates(coordValue=True), self.image_ref.getNonZeroCoordinates(coordValue=True), symmetry) for coord in result_coord_input: image_output.data[int(coord.x), int(coord.y), int(coord.z)] = int(round(coord.value)) if symmetry: # image_output_ref = Image(self.image_ref.dim, orientation=self.image_ref.orientation, hdr=self.image_ref.hdr, verbose=self.verbose) image_output_ref = Image(self.image_ref, verbose=self.verbose) for coord in result_coord_ref: image_output_ref.data[int(coord.x), int(coord.y), int(coord.z)] = int(round(coord.value)) image_output_ref.setFileName(self.fname_output[1]) image_output_ref.save('minimize_int') self.fname_output = self.fname_output[0] return image_output
def savePredictions(predictions, path_output, list_images, segmentation_image_size): number_of_images = len(list_images) predictions = numpy.reshape(predictions, [number_of_images, segmentation_image_size, segmentation_image_size, NUM_LABELS]) predictions = predictions[:, :, :, 1] for i, pref in enumerate(predictions): im_pred = Image(pref) im_pred.setFileName(path_output+sct.add_suffix(list_images[i], '_pred')) im_pred.save()
def get_minimum_path_nii(fname): from msct_image import Image data=Image(fname) vesselness_data = data.data raw_orient=data.change_orientation() data.data=get_minimum_path(data.data, invert=1) data.change_orientation(raw_orient) data.file_name += '_minimalpath' data.save()
def get_minimum_path_nii(fname): from msct_image import Image data = Image(fname) vesselness_data = data.data raw_orient = data.change_orientation() result, J1, J2 = get_minimum_path(data.data, invert=1) data.data = result data.change_orientation(raw_orient) data.file_name += '_minimalpath' data.save()
def __init__(self, target_fname, sc_seg_fname, t2_data=None, denoising=True): self.t2star = 't2star.nii.gz' self.sc_seg = 't2star_sc_seg.nii.gz' self.t2 = 't2.nii.gz' self.t2_seg = 't2_seg.nii.gz' self.t2_landmarks = 't2_landmarks.nii.gz' self.resample_to = 0.3 sct.run('cp ../' + target_fname + ' ./' + self.t2star) sct.run('cp ../' + sc_seg_fname + ' ./' + self.sc_seg) nx, ny, nz, nt, self.original_px, self.original_py, pz, pt = sct.get_dimension( self.t2star) if round(self.original_px, 2) != self.resample_to or round( self.original_py, 2) != self.resample_to: self.t2star = resample_image(self.t2star, npx=self.resample_to, npy=self.resample_to) self.sc_seg = resample_image(self.sc_seg, binary=True, npx=self.resample_to, npy=self.resample_to) t2star_im = Image(self.t2star) if denoising: t2star_im.denoise_ornlm() t2star_im.save() self.t2star = t2star_im.file_name + t2star_im.ext ''' status, t2_star_orientation = sct.run('sct_orientation -i ' + self.t2star) self.original_orientation = t2_star_orientation[4:7] ''' self.original_orientation = t2star_im.orientation self.square_mask = crop_t2_star(self.t2star, self.sc_seg, box_size=75) self.treated_target = sct.extract_fname( self.t2star)[1] + '_seg_in_croped.nii.gz' self.level_fname = None if t2_data is not None: sct.run('cp ../' + t2_data[0] + ' ./' + self.t2) sct.run('cp ../' + t2_data[1] + ' ./' + self.t2_seg) sct.run('cp ../' + t2_data[2] + ' ./' + self.t2_landmarks) self.level_fname = compute_level_file(self.t2star, self.sc_seg, self.t2, self.t2_seg, self.t2_landmarks)
def set_orientation(fname_in, orientation, fname_out, inversion=False): if not inversion: sct.run('isct_orientation3d -i '+fname_in+' -orientation '+orientation+' -o '+fname_out, 0) else: from msct_image import Image input_image = Image(fname_in) input_image.change_orientation(orientation, True) input_image.setFileName(fname_out) input_image.save() # return full path return os.path.abspath(fname_out)
def set_orientation(fname_in, orientation, fname_out, inversion=False): if not inversion: sct.run('isct_orientation3d -i '+fname_in+' -orientation '+orientation+' -o '+fname_out, 0) else: from msct_image import Image input_image = Image(fname_in) input_image.change_orientation(orientation, True) input_image.setFileName(fname_out) input_image.save() # return full path return os.path.abspath(fname_out)
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 copy_header(fname_src, fname_dest): """ Copy header :param fname_src: source file name :param fname_dest: destination file name :return: """ nii_src = Image(fname_src) data_dest = Image(fname_dest).data nii_src.setFileName(fname_dest) nii_src.data = data_dest nii_src.save()
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 copy_header(fname_src, fname_dest): """ Copy header :param fname_src: source file name :param fname_dest: destination file name :return: """ nii_src = Image(fname_src) data_dest = Image(fname_dest).data nii_src.setFileName(fname_dest) nii_src.data = data_dest nii_src.save()
def convert(fname_in, fname_out): """ Convert data :return True/False """ from msct_image import Image # Open file im = Image(fname_in) # Save file im.setFileName(fname_out) im.save() return True
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True): if fname_grid is None: from numpy import zeros tmp_dir = tmp_create() im_warp = Image(fname_warp) status, out = sct.run(['fslhd', fname_warp]) curdir = os.getcwd() os.chdir(tmp_dir) dim1 = 'dim1 ' dim2 = 'dim2 ' dim3 = 'dim3 ' nx = int( out[out.find(dim1):][len(dim1):out[out.find(dim1):].find('\n')]) ny = int( out[out.find(dim2):][len(dim2):out[out.find(dim2):].find('\n')]) nz = int( out[out.find(dim3):][len(dim3):out[out.find(dim3):].find('\n')]) sq = zeros((step, step)) sq[step - 1] = 1 sq[:, step - 1] = 1 dat = zeros((nx, ny, nz)) for i in range(0, dat.shape[0], step): for j in range(0, dat.shape[1], step): for k in range(dat.shape[2]): if dat[i:i + step, j:j + step, k].shape == (step, step): dat[i:i + step, j:j + step, k] = sq fname_grid = 'grid_' + str(step) + '.nii.gz' im_grid = Image(param=dat) grid_hdr = im_warp.hdr im_grid.hdr = grid_hdr im_grid.setFileName(fname_grid) im_grid.save() fname_grid_resample = add_suffix(fname_grid, '_resample') sct.run([ 'sct_resample', '-i', fname_grid, '-f', '3x3x1', '-x', 'nn', '-o', fname_grid_resample ]) fname_grid = tmp_dir + fname_grid_resample os.chdir(curdir) path_warp, file_warp, ext_warp = extract_fname(fname_warp) grid_warped = path_warp + extract_fname( fname_grid)[1] + '_' + file_warp + ext_warp sct.run([ 'sct_apply_transfo', '-i', fname_grid, '-d', fname_grid, '-w', fname_warp, '-o', grid_warped ]) if rm_tmp: sct.rmtree(tmp_dir)
def remove_overlap(file_centerline_generated_by_labels, file_with_seg_or_centerline, output_file_name, parameter=0): # Image file process if parameter == 0: image_with_seg_or_centerline = Image( file_with_seg_or_centerline).copy() z_test = ComputeZMinMax(image_with_seg_or_centerline) zmax = z_test.Zmax zmin = z_test.Zmin tab1 = Image(file_centerline_generated_by_labels).copy() size_x = tab1.data.shape[0] size_y = tab1.data.shape[1] #X_coor, Y_coor, Z_coor = (tab1.data).nonzero() #nb_one = X_coor.shape[0] #for i in range(0, nb_one): # tab1.data[X_coor[i], Y_coor[i], X_coor[i]] = 0 #each slice under zmax is filled with zeros print zmax for i in range(zmin, zmax): tab1.data[:, :, i] = np.zeros((size_x, size_y)) #Save file tab1.setFileName(output_file_name) tab1.save('minimize') #size of image should be minimized # Text file process if parameter == 1: z_test = ComputeZMinMax(file_with_seg_or_centerline) zmax = z_test.Zmax zmin = z_test.Zmin print zmax #create output txt file tab2 = open(output_file_name, "w") tab2.close() #Delete lines under zmax with open(file_centerline_generated_by_labels) as f: data_line = f.readlines() with open(output_file_name, "w") as f1: for line in data_line: words = line.split() if int(words[0]) < zmin or int(words[0]) > zmax: f1.write(line)
def convert(fname_in, fname_out, squeeze_data=True, type=None, verbose=1): """ Convert data :return True/False """ from msct_image import Image from sct_utils import printv printv('sct_convert -i ' + fname_in + ' -o ' + fname_out, verbose, 'code') # Open file im = Image(fname_in) # Save file im.setFileName(fname_out) if type is not None: im.changeType(type=type) im.save(squeeze_data=squeeze_data) return im
def convert(fname_in, fname_out, squeeze_data=True, type=None, verbose=1): """ Convert data :return True/False """ from msct_image import Image from sct_utils import printv printv('sct_convert -i '+fname_in+' -o '+fname_out, verbose, 'code') # Open file im = Image(fname_in) # Save file im.setFileName(fname_out) if type is not None: im.changeType(type=type) im.save(squeeze_data=squeeze_data) return im
def create_mask_template(dataset_info, contrast='t1'): path_template = dataset_info['path_template'] subject_name = dataset_info['subjects'][0] template_mask = Image(path_template + subject_name + '_' + contrast + '.nii.gz') template_mask.data *= 0.0 template_mask.data += 1.0 template_mask.setFileName(path_template + 'template_mask.nii.gz') template_mask.save() # if mask already present, deleting it if os.path.isfile(path_template + 'template_mask.mnc'): os.remove(path_template + 'template_mask.mnc') sct.run('nii2mnc ' + path_template + 'template_mask.nii.gz ' + ' ' + path_template + 'template_mask.mnc') return path_template + 'template_mask.mnc'
def validation(self): name_ref_gm_seg = sct.extract_fname(self.ref_gm_seg) im_ref_gm_seg = Image('../' + self.ref_gm_seg) res_gm_seg_bin = Image('../' + self.res_names['gm_seg']) res_wm_seg_bin = Image('../' + self.res_names['wm_seg']) sct.run('cp ../' + self.ref_gm_seg + ' ./ref_gm_seg.nii.gz') im_ref_wm_seg = inverse_gmseg_to_wmseg(im_ref_gm_seg, Image('../' + self.sc_seg_fname), 'ref_gm_seg') im_ref_wm_seg.file_name = 'ref_wm_seg' im_ref_wm_seg.ext = '.nii.gz' im_ref_wm_seg.save() if self.param.res_type == 'prob': res_gm_seg_bin.data = np.asarray((res_gm_seg_bin.data >= 0.5).astype(int)) res_wm_seg_bin.data = np.asarray((res_wm_seg_bin.data >= 0.50001).astype(int)) res_gm_seg_bin.path = './' res_gm_seg_bin.file_name = 'res_gm_seg_bin' res_gm_seg_bin.ext = '.nii.gz' res_gm_seg_bin.save() res_wm_seg_bin.path = './' res_wm_seg_bin.file_name = 'res_wm_seg_bin' res_wm_seg_bin.ext = '.nii.gz' res_wm_seg_bin.save() try: status_gm, output_gm = sct.run('sct_dice_coefficient ref_gm_seg.nii.gz res_gm_seg_bin.nii.gz -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: sct.run('c3d res_gm_seg_bin.nii.gz ref_gm_seg.nii.gz -reslice-identity -o ref_in_res_space_gm.nii.gz ') status_gm, output_gm = sct.run('sct_dice_coefficient ref_in_res_space_gm.nii.gz res_gm_seg_bin.nii.gz -2d-slices 2', error_exit='warning') try: status_wm, output_wm = sct.run('sct_dice_coefficient ref_wm_seg.nii.gz res_wm_seg_bin.nii.gz -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: sct.run('c3d res_wm_seg_bin.nii.gz ref_wm_seg.nii.gz -reslice-identity -o ref_in_res_space_wm.nii.gz ') status_wm, output_wm = sct.run('sct_dice_coefficient ref_in_res_space_wm.nii.gz res_wm_seg_bin.nii.gz -2d-slices 2', error_exit='warning') dice_name = 'dice_' + self.param.res_type + '.txt' dice_fic = open('../' + dice_name, 'w') if self.param.res_type == 'prob': dice_fic.write('WARNING : the probabilistic segmentations were binarized with a threshold at 0.5 to compute the dice coefficient \n') dice_fic.write('\n--------------------------------------------------------------\nDice coefficient on the Gray Matter segmentation:\n') dice_fic.write(output_gm) dice_fic.write('\n\n--------------------------------------------------------------\nDice coefficient on the White Matter segmentation:\n') dice_fic.write(output_wm) dice_fic.close() # sct.run(' mv ./' + dice_name + ' ../') return dice_name
def label_segmentation(fname_seg, list_disc_z, list_disc_value, verbose=1): """ Label segmentation image :param fname_seg: fname of the segmentation :param list_disc_z: list of z that correspond to a disc :param list_disc_value: list of associated disc values :param verbose: :return: """ # open segmentation seg = Image(fname_seg) dim = seg.dim ny = dim[1] nz = dim[2] # loop across z for iz in range(nz): # get index of the disc right above iz try: ind_above_iz = max( [i for i in range(len(list_disc_z)) if list_disc_z[i] > iz]) except ValueError: # if ind_above_iz is empty, attribute value 0 vertebral_level = 0 else: # assign vertebral level (add one because iz is BELOW the disk) vertebral_level = list_disc_value[ind_above_iz] + 1 # sct.printv(vertebral_level) # get voxels in mask ind_nonzero = np.nonzero(seg.data[:, :, iz]) seg.data[ind_nonzero[0], ind_nonzero[1], iz] = vertebral_level if verbose == 2: import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt plt.figure(50) plt.scatter(int(round(ny / 2)), iz, c=vertebral_level, vmin=min(list_disc_value), vmax=max(list_disc_value), cmap='prism', marker='_', s=200) # write file seg.file_name += '_labeled' seg.save()
def compute_dti(fname_in, fname_bvals, fname_bvecs, prefix): """ Compute DTI. :param fname_in: input 4d file. :param bvals: bvals txt file :param bvecs: bvecs txt file :param prefix: output prefix. Example: "dti_" :return: True/False """ # Open file. from msct_image import Image nii = Image(fname_in) data = nii.data print('data.shape (%d, %d, %d, %d)' % data.shape) # open bvecs/bvals from dipy.io import read_bvals_bvecs bvals, bvecs = read_bvals_bvecs(fname_bvals, fname_bvecs) from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs) # # mask and crop the data. This is a quick way to avoid calculating Tensors on the background of the image. # from dipy.segment.mask import median_otsu # maskdata, mask = median_otsu(data, 3, 1, True, vol_idx=range(10, 50), dilate=2) # print('maskdata.shape (%d, %d, %d, %d)' % maskdata.shape) # fit tensor model import dipy.reconst.dti as dti tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(data) # Compute metrics printv('Computing metrics...', param.verbose) # FA from dipy.reconst.dti import fractional_anisotropy nii.data = fractional_anisotropy(tenfit.evals) nii.setFileName(prefix+'FA.nii.gz') nii.save('float32') # MD from dipy.reconst.dti import mean_diffusivity nii.data = mean_diffusivity(tenfit.evals) nii.setFileName(prefix+'MD.nii.gz') nii.save('float32') # RD from dipy.reconst.dti import radial_diffusivity nii.data = radial_diffusivity(tenfit.evals) nii.setFileName(prefix+'RD.nii.gz') nii.save('float32') # AD from dipy.reconst.dti import axial_diffusivity nii.data = axial_diffusivity(tenfit.evals) nii.setFileName(prefix+'AD.nii.gz') nii.save('float32') return True
def remove_overlap(file_centerline_generated_by_labels, file_with_seg_or_centerline, output_file_name, parameter=0): # Image file process if parameter == 0 : image_with_seg_or_centerline = Image(file_with_seg_or_centerline).copy() z_test = ComputeZMinMax(image_with_seg_or_centerline) zmax = z_test.Zmax zmin = z_test.Zmin tab1 = Image(file_centerline_generated_by_labels).copy() size_x=tab1.data.shape[0] size_y=tab1.data.shape[1] #X_coor, Y_coor, Z_coor = (tab1.data).nonzero() #nb_one = X_coor.shape[0] #for i in range(0, nb_one): # tab1.data[X_coor[i], Y_coor[i], X_coor[i]] = 0 #each slice under zmax is filled with zeros print zmax for i in range(zmin, zmax): tab1.data[:,:,i] = np.zeros((size_x,size_y)) #Save file tab1.setFileName(output_file_name) tab1.save('minimize') #size of image should be minimized # Text file process if parameter == 1 : z_test = ComputeZMinMax(file_with_seg_or_centerline) zmax = z_test.Zmax zmin = z_test.Zmin print zmax #create output txt file tab2 = open(output_file_name , "w") tab2.close() #Delete lines under zmax with open(file_centerline_generated_by_labels) as f: data_line = f.readlines() with open(output_file_name, "w") as f1: for line in data_line: words = line.split() if int(words [0]) < zmin or int(words [0]) > zmax : f1.write(line)
def compute_dti(fname_in, fname_bvals, fname_bvecs, prefix): """ Compute DTI. :param fname_in: input 4d file. :param bvals: bvals txt file :param bvecs: bvecs txt file :param prefix: output prefix. Example: "dti_" :return: True/False """ # Open file. from msct_image import Image nii = Image(fname_in) data = nii.data print('data.shape (%d, %d, %d, %d)' % data.shape) # open bvecs/bvals from dipy.io import read_bvals_bvecs bvals, bvecs = read_bvals_bvecs(fname_bvals, fname_bvecs) from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs) # # mask and crop the data. This is a quick way to avoid calculating Tensors on the background of the image. # from dipy.segment.mask import median_otsu # maskdata, mask = median_otsu(data, 3, 1, True, vol_idx=range(10, 50), dilate=2) # print('maskdata.shape (%d, %d, %d, %d)' % maskdata.shape) # fit tensor model import dipy.reconst.dti as dti tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(data) # Compute metrics printv('Computing metrics...', param.verbose) # FA from dipy.reconst.dti import fractional_anisotropy nii.data = fractional_anisotropy(tenfit.evals) nii.setFileName(prefix + 'FA.nii.gz') nii.save('float32') # MD from dipy.reconst.dti import mean_diffusivity nii.data = mean_diffusivity(tenfit.evals) nii.setFileName(prefix + 'MD.nii.gz') nii.save('float32') # RD from dipy.reconst.dti import radial_diffusivity nii.data = radial_diffusivity(tenfit.evals) nii.setFileName(prefix + 'RD.nii.gz') nii.save('float32') # AD from dipy.reconst.dti import axial_diffusivity nii.data = axial_diffusivity(tenfit.evals) nii.setFileName(prefix + 'AD.nii.gz') nii.save('float32') return True
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True): if fname_grid is None: from numpy import zeros tmp_dir = tmp_create() im_warp = Image(fname_warp) status, out = run('fslhd ' + fname_warp) from os import chdir chdir(tmp_dir) dim1 = 'dim1 ' dim2 = 'dim2 ' dim3 = 'dim3 ' nx = int( out[out.find(dim1):][len(dim1):out[out.find(dim1):].find('\n')]) ny = int( out[out.find(dim2):][len(dim2):out[out.find(dim2):].find('\n')]) nz = int( out[out.find(dim3):][len(dim3):out[out.find(dim3):].find('\n')]) sq = zeros((step, step)) sq[step - 1] = 1 sq[:, step - 1] = 1 dat = zeros((nx, ny, nz)) for i in range(0, dat.shape[0], step): for j in range(0, dat.shape[1], step): for k in range(dat.shape[2]): if dat[i:i + step, j:j + step, k].shape == (step, step): dat[i:i + step, j:j + step, k] = sq fname_grid = 'grid_' + str(step) + '.nii.gz' im_grid = Image(param=dat) grid_hdr = im_warp.hdr im_grid.hdr = grid_hdr im_grid.setFileName(fname_grid) im_grid.save() fname_grid_resample = add_suffix(fname_grid, '_resample') run('sct_resample -i ' + fname_grid + ' -f 3x3x1 -x nn -o ' + fname_grid_resample) fname_grid = tmp_dir + fname_grid_resample chdir('..') path_warp, file_warp, ext_warp = extract_fname(fname_warp) grid_warped = path_warp + extract_fname( fname_grid)[1] + '_' + file_warp + ext_warp run('sct_apply_transfo -i ' + fname_grid + ' -d ' + fname_grid + ' -w ' + fname_warp + ' -o ' + grid_warped) if rm_tmp: run('rm -rf ' + tmp_dir, error_exit='warning')
def output_debug_file(self, img, data, file_name): """ This method writes a nifti file that corresponds to a step in the algorithm for easy debug. The new nifti file uses the header from the the image passed as parameter :param data: data to be written to file :param file_name: filename... :return: None """ if self.verbose == 2: current_folder = os.getcwd() # os.chdir(self.path_tmp) try: img = Image(img) img.data = data img.change_orientation(self.raw_orientation) img.file_name = file_name img.save() except Exception, e: print e
def post_processing_volume_wise(fname_in): """Post processing function.""" im_in = Image(fname_in) data_in = im_in.data.astype(np.int) data_in = _remove_blobs(data_in) zz_zeros = [ zz for zz in range(im_in.dim[2]) if 1 not in list(np.unique(data_in[:, :, zz])) ] zz_holes = _remove_extrem_holes(zz_zeros, im_in.dim[2] - 1, 0) # filling z_holes, i.e. interpolate for z_slice not segmented im_in.data = _fill_z_holes(zz_holes, data_in, im_in.dim[6]) if len(zz_holes) else data_in im_in.setFileName(fname_in) im_in.save() del im_in
def straighten_all_subjects(dataset_info, normalized=False, contrast='t1'): """ This function straighten all images based on template centerline :param dataset_info: dictionary containing dataset information :param normalized: True if images were normalized before straightening :param contrast: {'t1', 't2'} """ path_data = dataset_info['path_data'] path_template = dataset_info['path_template'] list_subjects = dataset_info['subjects'] if normalized: fname_in = contrast + '_norm.nii.gz' fname_out = contrast + '_straight_norm.nii.gz' else: fname_in = contrast + '.nii.gz' fname_out = contrast + '_straight.nii.gz' # straightening of each subject on the new template timer_straightening = sct.Timer(len(list_subjects)) timer_straightening.start() for subject_name in list_subjects: path_data_subject = path_data + subject_name + '/' + contrast + '/' # go to output folder sct.printv('\nStraightening ' + path_data_subject) os.chdir(path_data_subject) sct.run('sct_straighten_spinalcord' ' -i ' + fname_in + ' -s ' + contrast + dataset_info['suffix_centerline'] + '.nii.gz' ' -disks-input ' + contrast + dataset_info['suffix_disks'] + '.nii.gz' ' -ref ' + path_template + 'template_centerline.nii.gz' ' -disks-ref ' + path_template + 'template_disks.nii.gz' ' -disable-straight2curved' ' -param threshold_distance=1', verbose=1) image_straight = Image(sct.add_suffix(fname_in, '_straight')) image_straight.setFileName(fname_out) image_straight.save(type='float32') timer_straightening.add_iteration() timer_straightening.stop()
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 output_debug_file(self, img, data, file_name): """ This method writes a nifti file that corresponds to a step in the algorithm for easy debug. The new nifti file uses the header from the the image passed as parameter :param data: data to be written to file :param file_name: filename... :return: None """ if self.produce_output: current_folder = os.getcwd() os.chdir(self.debug_folder) try: img = Image(img) img.data = data img.change_orientation(self.raw_orientation) img.file_name = file_name img.save() except Exception, e: print e os.chdir(current_folder)
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True): if fname_grid is None: from numpy import zeros tmp_dir = sct.tmp_create() im_warp = Image(fname_warp) os.chdir(tmp_dir) assert len(im_warp.data.shape ) == 5, 'ERROR: Warping field does bot have 5 dimensions...' nx, ny, nz, nt, ndimwarp = im_warp.data.shape # nx, ny, nz, nt, px, py, pz, pt = im_warp.dim # This does not work because dimensions of a warping field are not correctly read : it would be 1,1,1,1,1,1,1,1 sq = zeros((step, step)) sq[step - 1] = 1 sq[:, step - 1] = 1 dat = zeros((nx, ny, nz)) for i in range(0, dat.shape[0], step): for j in range(0, dat.shape[1], step): for k in range(dat.shape[2]): if dat[i:i + step, j:j + step, k].shape == (step, step): dat[i:i + step, j:j + step, k] = sq fname_grid = 'grid_' + str(step) + '.nii.gz' im_grid = Image(param=dat) grid_hdr = im_warp.hdr im_grid.hdr = grid_hdr im_grid.setFileName(fname_grid) im_grid.save() fname_grid_resample = sct.add_suffix(fname_grid, '_resample') sct.run('sct_resample -i ' + fname_grid + ' -f 3x3x1 -x nn -o ' + fname_grid_resample) fname_grid = tmp_dir + fname_grid_resample os.chdir('..') path_warp, file_warp, ext_warp = sct.extract_fname(fname_warp) grid_warped = path_warp + 'grid_warped_gm' + ext_warp sct.run('sct_apply_transfo -i ' + fname_grid + ' -d ' + fname_grid + ' -w ' + fname_warp + ' -o ' + grid_warped) if rm_tmp: sct.run('rm -rf ' + tmp_dir, error_exit='warning') return grid_warped
def label_discs(fname_seg_labeled, verbose=1): """ Label discs from labaled_segmentation :param fname_seg_labeld: fname of the labeled segmentation :param verbose: :return: """ # open labeled segmentation im_seg_labeled = Image(fname_seg_labeled) orientation_native = im_seg_labeled.change_orientation('RPI') nx, ny, nz = im_seg_labeled.dim[0], im_seg_labeled.dim[ 1], im_seg_labeled.dim[2] data_disc = np.zeros([nx, ny, nz]) vertebral_level_previous = np.max(im_seg_labeled.data) # loop across z for iz in range(nz): # get 2d slice slice = im_seg_labeled.data[:, :, iz] # check if at least one voxel is non-zero if np.any(slice): slice_one = np.copy(slice) # set all non-zero values to 1 slice_one[slice.nonzero()] = 1 # compute center of mass cx, cy = [ int(x) for x in np.round(center_of_mass(slice_one)).tolist() ] # retrieve vertebral level vertebral_level = slice[cx, cy] # if smaller than previous level, then labeled as a disc if vertebral_level < vertebral_level_previous: # label disc # sct.printv('iz='+iz+', disc='+vertebral_level) data_disc[cx, cy, iz] = vertebral_level # update variable vertebral_level_previous = vertebral_level # save disc labeled file im_seg_labeled.file_name += '_disc' im_seg_labeled.data = data_disc im_seg_labeled.change_orientation(orientation_native) im_seg_labeled.save()
def heatmap2optic(fname_heatmap, lambda_value, fname_out, z_max, algo='dpdt'): """Run OptiC on the heatmap computed by CNN_1.""" import nibabel as nib os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR" optic_input = fname_heatmap.split('.nii')[0] cmd_optic = 'isct_spine_detect -ctype="%s" -lambda="%s" "%s" "%s" "%s"' % \ (algo, str(lambda_value), "NONE", optic_input, optic_input) sct.run(cmd_optic, verbose=1) optic_hdr_filename = optic_input + '_ctr.hdr' img = nib.load(optic_hdr_filename) nib.save(img, fname_out) # crop the centerline if z_max < data.shape[2] and -brain == 1 if z_max is not None: sct.printv('Cropping brain section.') ctr_nii = Image(fname_out) ctr_nii.data[:, :, z_max:] = 0 ctr_nii.save()
def __init__(self, target_fname, sc_seg_fname, t2_data=None, denoising=True): self.t2star = 't2star.nii.gz' self.sc_seg = 't2star_sc_seg.nii.gz' self.t2 = 't2.nii.gz' self.t2_seg = 't2_seg.nii.gz' self.t2_landmarks = 't2_landmarks.nii.gz' self.resample_to = 0.3 sct.run('cp ../' + target_fname + ' ./' + self.t2star) sct.run('cp ../' + sc_seg_fname + ' ./' + self.sc_seg) nx, ny, nz, nt, self.original_px, self.original_py, pz, pt = sct.get_dimension(self.t2star) if round(self.original_px, 2) != self.resample_to or round(self.original_py, 2) != self.resample_to: self.t2star = resample_image(self.t2star, npx=self.resample_to, npy=self.resample_to) self.sc_seg = resample_image(self.sc_seg, binary=True, npx=self.resample_to, npy=self.resample_to) t2star_im = Image(self.t2star) if denoising: t2star_im.denoise_ornlm() t2star_im.save() self.t2star = t2star_im.file_name + t2star_im.ext ''' status, t2_star_orientation = sct.run('sct_orientation -i ' + self.t2star) self.original_orientation = t2_star_orientation[4:7] ''' self.original_orientation = t2star_im.orientation self.square_mask = crop_t2_star(self.t2star, self.sc_seg, box_size=75) self.treated_target = sct.extract_fname(self.t2star)[1] + '_seg_in_croped.nii.gz' self.level_fname = None if t2_data is not None: sct.run('cp ../' + t2_data[0] + ' ./' + self.t2) sct.run('cp ../' + t2_data[1] + ' ./' + self.t2_seg) sct.run('cp ../' + t2_data[2] + ' ./' + self.t2_landmarks) self.level_fname = compute_level_file(self.t2star, self.sc_seg, self.t2, self.t2_seg, self.t2_landmarks)
def label_segmentation(fname_seg, list_disc_z, list_disc_value, verbose=1): """ Label segmentation image :param fname_seg: fname of the segmentation :param list_disc_z: list of z that correspond to a disc :param list_disc_value: list of associated disc values :param verbose: :return: """ # open segmentation seg = Image(fname_seg) dim = seg.dim ny = dim[1] nz = dim[2] # open labeled discs im_discs = Image(fname_seg) # loop across z for iz in range(nz): # get index of the disc right above iz try: ind_above_iz = max([i for i in range(len(list_disc_z)) if list_disc_z[i] > iz]) except ValueError: # if ind_above_iz is empty, attribute value 0 vertebral_level = 0 else: # assign vertebral level (add one because iz is BELOW the disk) vertebral_level = list_disc_value[ind_above_iz] + 1 # print vertebral_level # get voxels in mask ind_nonzero = np.nonzero(seg.data[:, :, iz]) seg.data[ind_nonzero[0], ind_nonzero[1], iz] = vertebral_level if verbose == 2: import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt plt.figure(50) plt.scatter(int(round(ny/2)), iz, c=vertebral_level, vmin=min(list_disc_value), vmax=max(list_disc_value), cmap='prism', marker='_', s=200) # write file seg.file_name += '_labeled' seg.save()
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True): if fname_grid is None: from numpy import zeros tmp_dir = sct.tmp_create() im_warp = Image(fname_warp) curdir = os.getcwd() os.chdir(tmp_dir) assert len(im_warp.data.shape) == 5, 'ERROR: Warping field does bot have 5 dimensions...' nx, ny, nz, nt, ndimwarp = im_warp.data.shape # nx, ny, nz, nt, px, py, pz, pt = im_warp.dim # This does not work because dimensions of a warping field are not correctly read : it would be 1,1,1,1,1,1,1,1 sq = zeros((step, step)) sq[step - 1] = 1 sq[:, step - 1] = 1 dat = zeros((nx, ny, nz)) for i in range(0, dat.shape[0], step): for j in range(0, dat.shape[1], step): for k in range(dat.shape[2]): if dat[i:i + step, j:j + step, k].shape == (step, step): dat[i:i + step, j:j + step, k] = sq fname_grid = 'grid_' + str(step) + '.nii.gz' im_grid = Image(param=dat) grid_hdr = im_warp.hdr im_grid.hdr = grid_hdr im_grid.setFileName(fname_grid) im_grid.save() fname_grid_resample = sct.add_suffix(fname_grid, '_resample') sct.run(['sct_resample', '-i', fname_grid, '-f', '3x3x1', '-x', 'nn', '-o', fname_grid_resample]) fname_grid = os.path.join(tmp_dir, fname_grid_resample) os.chdir(curdir) path_warp, file_warp, ext_warp = sct.extract_fname(fname_warp) grid_warped = os.path.join(path_warp, 'grid_warped_gm' + ext_warp) sct.run(['sct_apply_transfo', '-i', fname_grid, '-d', fname_grid, '-w', fname_warp, '-o', grid_warped]) if rm_tmp: sct.rmtree(tmp_dir) return grid_warped
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True): if fname_grid is None: from numpy import zeros tmp_dir = sct.tmp_create() im_warp = Image(fname_warp) os.chdir(tmp_dir) assert len(im_warp.data.shape) == 5, "ERROR: Warping field does bot have 5 dimensions..." nx, ny, nz, nt, ndimwarp = im_warp.data.shape # nx, ny, nz, nt, px, py, pz, pt = im_warp.dim # This does not work because dimensions of a warping field are not correctly read : it would be 1,1,1,1,1,1,1,1 sq = zeros((step, step)) sq[step - 1] = 1 sq[:, step - 1] = 1 dat = zeros((nx, ny, nz)) for i in range(0, dat.shape[0], step): for j in range(0, dat.shape[1], step): for k in range(dat.shape[2]): if dat[i : i + step, j : j + step, k].shape == (step, step): dat[i : i + step, j : j + step, k] = sq fname_grid = "grid_" + str(step) + ".nii.gz" im_grid = Image(param=dat) grid_hdr = im_warp.hdr im_grid.hdr = grid_hdr im_grid.setFileName(fname_grid) im_grid.save() fname_grid_resample = sct.add_suffix(fname_grid, "_resample") sct.run("sct_resample -i " + fname_grid + " -f 3x3x1 -x nn -o " + fname_grid_resample) fname_grid = tmp_dir + fname_grid_resample os.chdir("..") path_warp, file_warp, ext_warp = sct.extract_fname(fname_warp) grid_warped = path_warp + "grid_warped_gm" + ext_warp sct.run("sct_apply_transfo -i " + fname_grid + " -d " + fname_grid + " -w " + fname_warp + " -o " + grid_warped) if rm_tmp: sct.run("rm -rf " + tmp_dir, error_exit="warning") return grid_warped
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True): if fname_grid is None: from numpy import zeros tmp_dir = tmp_create() im_warp = Image(fname_warp) status, out = run('fslhd '+fname_warp) from os import chdir chdir(tmp_dir) dim1 = 'dim1 ' dim2 = 'dim2 ' dim3 = 'dim3 ' nx = int(out[out.find(dim1):][len(dim1):out[out.find(dim1):].find('\n')]) ny = int(out[out.find(dim2):][len(dim2):out[out.find(dim2):].find('\n')]) nz = int(out[out.find(dim3):][len(dim3):out[out.find(dim3):].find('\n')]) sq = zeros((step, step)) sq[step-1] = 1 sq[:, step-1] = 1 dat = zeros((nx, ny, nz)) for i in range(0, dat.shape[0], step): for j in range(0, dat.shape[1], step): for k in range(dat.shape[2]): if dat[i:i+step, j:j+step, k].shape == (step, step): dat[i:i+step, j:j+step, k] = sq fname_grid = 'grid_'+str(step)+'.nii.gz' im_grid = Image(param=dat) grid_hdr = im_warp.hdr im_grid.hdr = grid_hdr im_grid.setFileName(fname_grid) im_grid.save() fname_grid_resample = add_suffix(fname_grid, '_resample') run('sct_resample -i '+fname_grid+' -f 3x3x1 -x nn -o '+fname_grid_resample) fname_grid = tmp_dir+fname_grid_resample chdir('..') path_warp, file_warp, ext_warp = extract_fname(fname_warp) grid_warped = path_warp+extract_fname(fname_grid)[1]+'_'+file_warp+ext_warp run('sct_apply_transfo -i '+fname_grid+' -d '+fname_grid+' -w '+fname_warp+' -o '+grid_warped) if rm_tmp: run('rm -rf '+tmp_dir, error_exit='warning')
def label_discs(fname_seg_labeled, verbose=1): """ Label discs from labaled_segmentation :param fname_seg_labeld: fname of the labeled segmentation :param verbose: :return: """ # open labeled segmentation im_seg_labeled = Image(fname_seg_labeled) orientation_native = im_seg_labeled.change_orientation('RPI') nx, ny, nz = im_seg_labeled.dim[0], im_seg_labeled.dim[1], im_seg_labeled.dim[2] data_disc = np.zeros([nx, ny, nz]) vertebral_level_previous = np.max(im_seg_labeled.data) # loop across z for iz in range(nz): # get 2d slice slice = im_seg_labeled.data[:, :, iz] # check if at least one voxel is non-zero if np.any(slice): slice_one = np.copy(slice) # set all non-zero values to 1 slice_one[slice.nonzero()] = 1 # compute center of mass cx, cy = [int(x) for x in np.round(center_of_mass(slice_one)).tolist()] # retrieve vertebral level vertebral_level = slice[cx, cy] # if smaller than previous level, then labeled as a disc if vertebral_level < vertebral_level_previous: # label disc # print 'iz='+iz+', disc='+vertebral_level data_disc[cx, cy, iz] = vertebral_level # update variable vertebral_level_previous = vertebral_level # save disc labeled file im_seg_labeled.file_name += '_disc' im_seg_labeled.data = data_disc im_seg_labeled.change_orientation(orientation_native) im_seg_labeled.save()
def create_label_z(fname_seg, z, value): """ Create a label at coordinates x_center, y_center, z :param fname_seg: segmentation :param z: int :return: fname_label """ fname_label = 'labelz.nii.gz' nii = Image(fname_seg) orientation_origin = nii.change_orientation('RPI') # change orientation to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions # find x and y coordinates of the centerline at z using center of mass from scipy.ndimage.measurements import center_of_mass x, y = center_of_mass(nii.data[:, :, z]) x, y = int(round(x)), int(round(y)) nii.data[:, :, :] = 0 nii.data[x, y, z] = value # dilate label to prevent it from disappearing due to nearestneighbor interpolation from sct_maths import dilate nii.data = dilate(nii.data, [3]) nii.setFileName(fname_label) nii.change_orientation(orientation_origin) # put back in original orientation nii.save() return fname_label
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 dmri_moco(param): file_data = 'dmri' ext_data = '.nii' file_b0 = 'b0' file_dwi = 'dwi' mat_final = 'mat_final/' file_dwi_group = 'dwi_averaged_groups' # no extension fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image(file_data+'.nii').dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), param.verbose) # Identify b=0 and DWI images sct.printv('\nIdentify b=0 and DWI images...', param.verbose) index_b0, index_dwi, nb_b0, nb_dwi = identify_b0('bvecs.txt', param.fname_bvals, param.bval_min, param.verbose) # check if dmri and bvecs are the same size if not nb_b0 + nb_dwi == nt: sct.printv('\nERROR in '+os.path.basename(__file__)+': Size of data ('+str(nt)+') and size of bvecs ('+str(nb_b0+nb_dwi)+') are not the same. Check your bvecs file.\n', 1, 'error') sys.exit(2) # Prepare NIFTI (mean/groups...) #=================================================================================================================== # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) status, output = sct.run('sct_split_data -i ' + file_data + ext_data + ' -dim t -suffix _T', param.verbose) # Merge b=0 images sct.printv('\nMerge b=0...', param.verbose) # cmd = fsloutput + 'fslmerge -t ' + file_b0 # for it in range(nb_b0): # cmd = cmd + ' ' + file_data + '_T' + str(index_b0[it]).zfill(4) cmd = 'sct_concat_data -dim t -o ' + file_b0 + ext_data + ' -i ' for it in range(nb_b0): cmd = cmd + file_data + '_T' + str(index_b0[it]).zfill(4) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string status, output = sct.run(cmd, param.verbose) sct.printv((' File created: ' + file_b0), param.verbose) # Average b=0 images sct.printv('\nAverage b=0...', param.verbose) file_b0_mean = file_b0+'_mean' sct.run('sct_maths -i '+file_b0+'.nii'+' -o '+file_b0_mean+'.nii'+' -mean t', param.verbose) # if not average_data_across_dimension(file_b0+'.nii', file_b0_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_b0 + ' -Tmean ' + file_b0_mean # status, output = sct.run(cmd, param.verbose) # Number of DWI groups nb_groups = int(math.floor(nb_dwi/param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_dwi[(iGroup*param.group_size):((iGroup+1)*param.group_size)]) # add the remaining images to the last DWI group nb_remaining = nb_dwi%param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_dwi[len(index_dwi)-nb_remaining:len(index_dwi)]) # DWI groups for iGroup in range(nb_groups): sct.printv('\nDWI group: ' +str((iGroup+1))+'/'+str(nb_groups), param.verbose) # get index index_dwi_i = group_indexes[iGroup] nb_dwi_i = len(index_dwi_i) # Merge DW Images sct.printv('Merge DW images...', param.verbose) file_dwi_merge_i = file_dwi + '_' + str(iGroup) cmd = 'sct_concat_data -dim t -o ' + file_dwi_merge_i + ext_data + ' -i ' for it in range(nb_dwi_i): cmd = cmd + file_data + '_T' + str(index_dwi_i[it]).zfill(4) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string sct.run(cmd, param.verbose) # cmd = fsloutput + 'fslmerge -t ' + file_dwi_merge_i # for it in range(nb_dwi_i): # cmd = cmd +' ' + file_data + '_T' + str(index_dwi_i[it]).zfill(4) # Average DW Images sct.printv('Average DW images...', param.verbose) file_dwi_mean = file_dwi + '_mean_' + str(iGroup) sct.run('sct_maths -i '+file_dwi_merge_i+'.nii'+' -o '+file_dwi_mean+'.nii'+' -mean t', param.verbose) # if not average_data_across_dimension(file_dwi_merge_i+'.nii', file_dwi_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_dwi_merge_i + ' -Tmean ' + file_dwi_mean # sct.run(cmd, param.verbose) # Merge DWI groups means sct.printv('\nMerging DW files...', param.verbose) # file_dwi_groups_means_merge = 'dwi_averaged_groups' cmd = 'sct_concat_data -dim t -o ' + file_dwi_group + ext_data + ' -i ' for iGroup in range(nb_groups): cmd = cmd + file_dwi + '_mean_' + str(iGroup) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string sct.run(cmd, param.verbose) # cmd = fsloutput + 'fslmerge -t ' + file_dwi_group # for iGroup in range(nb_groups): # cmd = cmd + ' ' + file_dwi + '_mean_' + str(iGroup) # Average DW Images # TODO: USEFULL ??? sct.printv('\nAveraging all DW images...', param.verbose) fname_dwi_mean = 'dwi_mean' sct.run('sct_maths -i '+file_dwi_group+'.nii'+' -o '+file_dwi_group+'_mean.nii'+' -mean t', param.verbose) # if not average_data_across_dimension(file_dwi_group+'.nii', file_dwi_group+'_mean.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run(fsloutput + 'fslmaths ' + file_dwi_group + ' -Tmean ' + file_dwi_group+'_mean', param.verbose) # segment dwi images using otsu algorithm if param.otsu: sct.printv('\nSegment group DWI using OTSU algorithm...', param.verbose) # import module otsu = importlib.import_module('sct_otsu') # get class from module param_otsu = otsu.param() #getattr(otsu, param) param_otsu.fname_data = file_dwi_group+'.nii' param_otsu.threshold = param.otsu param_otsu.file_suffix = '_seg' # run otsu otsu.otsu(param_otsu) file_dwi_group = file_dwi_group+'_seg' # extract first DWI volume as target for registration nii = Image(file_dwi_group+'.nii') data_crop = nii.data[:, :, :, index_dwi[0]:index_dwi[0]+1] nii.data = data_crop nii.setFileName('target_dwi.nii') nii.save() # START MOCO #=================================================================================================================== # Estimate moco on b0 groups sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on b=0 images...', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'b0' if index_dwi[0] != 0: # If first DWI is not the first volume (most common), then there is a least one b=0 image before. In that case # select it as the target image for registration of all b=0 param_moco.file_target = file_data + '_T' + str(index_b0[index_dwi[0]-1]).zfill(4) else: # If first DWI is the first volume, then the target b=0 is the first b=0 from the index_b0. param_moco.file_target = file_data + '_T' + str(index_b0[0]).zfill(4) param_moco.path_out = '' param_moco.todo = 'estimate' param_moco.mat_moco = 'mat_b0groups' moco.moco(param_moco) # Estimate moco on dwi groups sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on DW images...', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = file_dwi_group param_moco.file_target = 'target_dwi' # target is the first DW image (closest to the first b=0) param_moco.path_out = '' #param_moco.todo = 'estimate' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_dwigroups' moco.moco(param_moco) # create final mat folder sct.create_folder(mat_final) # Copy b=0 registration matrices sct.printv('\nCopy b=0 registration matrices...', param.verbose) for it in range(nb_b0): sct.run('cp '+'mat_b0groups/'+'mat.T'+str(it)+ext_mat+' '+mat_final+'mat.T'+str(index_b0[it])+ext_mat, param.verbose) # Copy DWI registration matrices sct.printv('\nCopy DWI registration matrices...', param.verbose) for iGroup in range(nb_groups): for dwi in range(len(group_indexes[iGroup])): sct.run('cp '+'mat_dwigroups/'+'mat.T'+str(iGroup)+ext_mat+' '+mat_final+'mat.T'+str(group_indexes[iGroup][dwi])+ext_mat, param.verbose) # Spline Regularization along T if param.spline_fitting: moco.spline(mat_final, nt, nz, param.verbose, np.array(index_b0), param.plot_graph) # combine Eddy Matrices if param.run_eddy: param.mat_2_combine = 'mat_eddy' param.mat_final = mat_final moco.combine_matrix(param) # Apply moco on all dmri data sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = 'dmri' param_moco.file_target = file_dwi+'_mean_'+str(0) # reference for reslicing into proper coordinate system param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". copy_header('dmri.nii', 'dmri_moco.nii') # generate b0_moco_mean and dwi_moco_mean cmd = 'sct_dmri_separate_b0_and_dwi -i dmri'+param.suffix+'.nii -b bvecs.txt -a 1' if not param.fname_bvals == '': cmd = cmd+' -m '+param.fname_bvals sct.run(cmd, param.verbose)
def vertebral_detection(fname, fname_seg, contrast): shift_AP = 14 # shift the centerline on the spine in mm default : 17 mm size_AP = 3 # mean around the centerline in the anterior-posterior direction in mm size_RL = 3 # mean around the centerline in the right-left direction in mm verbose = param.verbose if verbose: import matplotlib.pyplot as plt # open anatomical volume img = Image(fname) # orient to RPI img.change_orientation() # get dimension nx, ny, nz, nt, px, py, pz, pt = img.dim #================================================== # Compute intensity profile across vertebrae #================================================== shift_AP = shift_AP * py size_AP = size_AP * py size_RL = size_RL * px # orient segmentation to RPI run('sct_orientation -i ' + fname_seg + ' -s RPI') # smooth segmentation/centerline path_centerline, file_centerline, ext_centerline = extract_fname(fname_seg) x, y, z, Tx, Ty, Tz = smooth_centerline(path_centerline + file_centerline + '_RPI' + ext_centerline) # build intensity profile along the centerline I = np.zeros((len(y), 1)) # mask where intensity profile will be taken if verbose == 2: mat = img.copy() mat.data = np.zeros(mat.dim) for iz in range(len(z)): # define vector orthogonal to the cord in RL direction P1 = np.array([1, 0, -Tx[iz]/Tz[iz]]) P1 = P1/np.linalg.norm(P1) # define vector orthogonal to the cord in AP direction P2 = np.array([0, 1, -Ty[iz]/Tz[iz]]) P2 = P2/np.linalg.norm(P2) # define X and Y coordinates of the voxels to extract intensity profile from indexRL = range(-np.int(round(size_RL)), np.int(round(size_RL))) indexAP = range(0, np.int(round(size_AP)))+np.array(shift_AP) # loop over coordinates of perpendicular plane for i_RL in indexRL: for i_AP in indexAP: i_vect = np.round(np.array([x[iz], y[iz], z[iz]])+P1*i_RL+P2*i_AP) i_vect = np.minimum(np.maximum(i_vect, 0), np.array([nx, ny, nz])-1) # check if index stays in image dimension I[iz] = I[iz] + img.data[i_vect[0], i_vect[1], i_vect[2]] # create a mask with this perpendicular plane if verbose == 2: mat.data[i_vect[0], i_vect[1], i_vect[2]] = 1 if verbose == 2: mat.file_name = 'mask' mat.save() # Detrending Intensity start_centerline_y = y[0] X = np.where(I == 0) mask2 = np.ones((len(y), 1), dtype=bool) mask2[X, 0] = False # low pass filtering import scipy.signal frequency = 2/pz Wn = 0.1/frequency N = 2 #Order of the filter # b, a = scipy.signal.butter(N, Wn, btype='low', analog=False, output='ba') b, a = scipy.signal.iirfilter(N, Wn, rp=None, rs=None, btype='high', analog=False, ftype='bessel', output='ba') I_detrend = scipy.signal.filtfilt(b, a, I[:, 0], axis=-1, padtype='constant', padlen=None) I_detrend = I_detrend/(np.amax(I_detrend)) #================================================== # step 1 : Find the First Peak #================================================== if contrast == 't1': I_detrend2 = np.diff(I_detrend) elif contrast == 't2': space = np.linspace(-10/pz, 10/pz, round(21/pz), endpoint=True) pattern = (np.sinc((space*pz)/20)) ** 20 I_corr = scipy.signal.correlate(-I_detrend.squeeze().squeeze()+1,pattern,'same') b, a = scipy.signal.iirfilter(N, Wn, rp=None, rs=None, btype='high', analog=False, ftype='bessel', output='ba') I_detrend2 = scipy.signal.filtfilt(b, a, I_corr, axis=-1, padtype='constant', padlen=None) I_detrend2[I_detrend2 < 0.2] = 0 ind_locs = np.squeeze(scipy.signal.argrelextrema(I_detrend2, np.greater)) # remove peaks that are too closed locsdiff = np.diff(z[ind_locs]) ind = locsdiff > 10 ind_locs = np.hstack((ind_locs[ind], ind_locs[-1])) locs = z[ind_locs] if verbose == 2: # x=0: most caudal, x=max: most rostral plt.figure() plt.plot(I_detrend2) plt.plot(ind_locs, I_detrend2[ind_locs], '+') plt.show() #===================================================================================== # step 2 : Cross correlation between the adjusted template and the intensity profile. # Local moving of template's peak from the first peak already found #===================================================================================== #For each loop, a peak is located at the most likely position and then local adjustment is done. #The position of the next peak is calculated from previous positions # TODO: use mean distance mean_distance = [12.1600, 20.8300, 18.0000, 16.0000, 15.1667, 15.3333, 15.8333, 18.1667, 18.6667, 18.6667, 19.8333, 20.6667, 21.6667, 22.3333, 23.8333, 24.1667, 26.0000, 28.6667, 30.5000, 33.5000, 33.0000, 31.3330] # # #Creating pattern printv('\nFinding Cross correlation between the adjusted template and the intensity profile...', verbose) space = np.linspace(-10/pz, 10/pz, round(21/pz), endpoint=True) pattern = (np.sinc((space*pz)/20))**20 I_corr = scipy.signal.correlate(I_detrend2.squeeze().squeeze()+1, pattern, 'same') # # level_start=1 # if contrast == 'T1': # mean_distance = mean_distance[level_start-1:len(mean_distance)] # xmax_pattern = np.argmax(pattern) # else: # mean_distance = mean_distance[level_start+1:len(mean_distance)] # xmax_pattern = np.argmin(pattern) # position of the peak in the pattern # pixend = len(pattern) - xmax_pattern #number of pixel after the peaks in the pattern # # # mean_distance_new = mean_distance # mean_ratio = np.zeros(len(mean_distance)) # # L = np.round(1.2*max(mean_distance)) - np.round(0.8*min(mean_distance)) # corr_peak = np.zeros((L,len(mean_distance))) # corr_peak = np.nan #for T2 # # #loop on each peak # for i_peak in range(len(mean_distance)): # scale_min = np.round(0.80*mean_distance_new[i_peak]) - xmax_pattern - pixend # if scale_min<0: # scale_min = 0 # # scale_max = np.round(1.2*mean_distance_new[i_peak]) - xmax_pattern - pixend # scale_peak = np.arange(scale_min,scale_max+1) # # for i_scale in range(len(scale_peak)): # template_resize_peak = np.concatenate([template_truncated,np.zeros(scale_peak[i_scale]),pattern]) # if len(I_detrend[:,0])>len(template_resize_peak): # template_resize_peak1 = np.concatenate((template_resize_peak,np.zeros(len(I_detrend[:,0])-len(template_resize_peak)))) # # #cross correlation # corr_template = scipy.signal.correlate(I_detrend[:,0],template_resize_peak) # # if len(I_detrend[:,0])>len(template_resize_peak): # val = np.dot(I_detrend[:,0],template_resize_peak1.T) # else: # I_detrend_2 = np.concatenate((I_detrend[:,0],np.zeros(len(template_resize_peak)-len(I_detrend[:,0])))) # val = np.dot(I_detrend_2,template_resize_peak.T) # corr_peak[i_scale,i_peak] = val # # if verbose: # plt.xlim(0,len(I_detrend[:,0])) # plt.plot(I_detrend[:,0]) # plt.plot(template_resize_peak) # plt.show(block=False) # # plt.plot(corr_peak[:,i_peak],marker='+',linestyle='None',color='r') # plt.title('correlation value against the displacement of the peak (px)') # plt.show(block=False) # # max_peak = np.amax(corr_peak[:,i_peak]) # index_scale_peak = np.where(corr_peak[:,i_peak]==max_peak) # good_scale_peak = scale_peak[index_scale_peak][0] # Mcorr = Mcorr1 # Mcorr = np.resize(Mcorr,i_peak+2) # Mcorr[i_peak+1] = np.amax(corr_peak[:,0:(i_peak+1)]) # flag = 0 # # #If the correlation coefficient is too low, put the peak at the mean position # if i_peak>0: # if (Mcorr[i_peak+1]-Mcorr[i_peak])<0.4*np.mean(Mcorr[1:i_peak+2]-Mcorr[0:i_peak+1]): # test = i_peak # template_resize_peak = np.concatenate((template_truncated,np.zeros(round(mean_distance[i_peak])-xmax_pattern-pixend),pattern)) # good_scale_peak = np.round(mean_distance[i_peak]) - xmax_pattern - pixend # flag = 1 # if i_peak==0: # if (Mcorr[i_peak+1] - Mcorr[i_peak])<0.4*Mcorr[0]: # template_resize_peak = np.concatenate((template_truncated,np.zeros(round(mean_distance[i_peak])-xmax_pattern-pixend),pattern)) # good_scale_peak = round(mean_distance[i_peak]) - xmax_pattern - pixend # flag = 1 # if flag==0: # template_resize_peak=np.concatenate((template_truncated,np.zeros(good_scale_peak),pattern)) # # #update mean-distance by a adjustement ratio # mean_distance_new[i_peak] = good_scale_peak + xmax_pattern + pixend # mean_ratio[i_peak] = np.mean(mean_distance_new[:,0:i_peak]/mean_distance[:,0:i_peak]) # # template_truncated = template_resize_peak # # if verbose: # plt.plot(I_detrend[:,0]) # plt.plot(template_truncated) # plt.xlim(0,(len(I_detrend[:,0])-1)) # plt.show() # # #finding the maxima of the adjusted template # minpeakvalue = 0.5 # loc_disk = np.arange(len(template_truncated)) # index_disk = [] # for i in range(len(template_truncated)): # if template_truncated[i]>=minpeakvalue: # if i==0: # if template_truncated[i]<template_truncated[i+1]: # index_disk.append(i) # elif i==(len(template_truncated)-1): # if template_truncated[i]<template_truncated[i-1]: # index_disk.append(i) # else: # if template_truncated[i]<template_truncated[i+1]: # index_disk.append(i) # elif template_truncated[i]<template_truncated[i-1]: # index_disk.append(i) # else: # index_disk.append(i) # # mask_disk = np.ones(len(template_truncated), dtype=bool) # mask_disk[index_disk] = False # loc_disk = loc_disk[mask_disk] # X1 = np.where(loc_disk > I_detrend.shape[0]) # mask_disk1 = np.ones(len(loc_disk), dtype=bool) # mask_disk1[X1] = False # loc_disk = loc_disk[mask_disk1] # loc_disk = loc_disk + start_centerline_y - 1 #===================================================================== # Step 3: Label segmentation #===================================================================== # # Project vertebral levels back to the centerline # centerline = Image(fname_seg) # raw_orientation = centerline.change_orientation() # centerline.data[:, :, :] = 0 # for iz in range(locs[0]): # centerline.data[np.round(x[iz]), np.round(y[iz]), iz] = 1 # for i in range(len(locs)-1): # for iz in range(locs[i], min(locs[i+1], len(z))): # centerline.data[np.round(x[iz]), np.round(y[iz]), iz] = i+2 # for iz in range(locs[-1], len(z)): # centerline.data[np.round(x[iz]), np.round(y[iz]), iz] = i+3 # # #centerline.change_orientation(raw_orientation) # centerline.file_name += '_labeled' # centerline.save() # Label segmentation with vertebral number # Method: loop across all voxels of the segmentation, project each voxel to the line passing through the vertebrae # (using minimum distance) and assign vertebral level. printv('\nLabel segmentation...', verbose) seg = Image(fname_seg) seg_raw_orientation = seg.change_orientation() # find all voxels belonging to segmentation x_seg, y_seg, z_seg = np.where(seg.data) # loop across voxels in segmentation for ivox in range(len(x_seg)): # get voxel coordinate vox_coord = np.array([x_seg[ivox], y_seg[ivox], z_seg[ivox]]) # find closest point to the curved line passing through the vertebrae for iplane in range(len(locs)): ind = np.where(z == locs[iplane]) vox_vector = vox_coord - np.hstack((x[ind], y[ind], z[ind])) normal2plane_vector = np.hstack((Tx[ind], Ty[ind], Tz[ind])) # Tx, Ty and Tz are the derivatives of the centerline # if voxel is above the plane --> give the number of the plane if np.dot(vox_vector, normal2plane_vector) > 0: seg.data[vox_coord[0], vox_coord[1], vox_coord[2]] = iplane+2 else: # if the voxel gets below the plane --> next voxel break seg.change_orientation(seg_raw_orientation) seg.file_name += '_labeled' seg.save() # # color the segmentation with vertebral number # printv('\nLabel input segmentation...', verbose) # # if fname_segmentation: # seg = Image(fname_seg) # seg_raw_orientation = seg.change_orientation() # x_seg, y_seg, z_seg = np.where(seg.data) # find all voxels belonging to segmentation # for ivox in range(len(x_seg)): # loop across voxels in segmentation # vox_coord = np.array([x_seg[ivox], y_seg[ivox], z_seg[ivox]]) # get voxel coordinate # for iplane in range(len(locs)): # ind = np.where(z == locs[iplane]) # vox_vector = vox_coord - np.hstack((x[ind], y[ind], z[ind])) # normal2plane_vector = np.hstack((Tx[ind], Ty[ind], Tz[ind])) # Tx, Ty and Tz are the derivatives of the centerline # # # if voxel is above the plane --> give the number of the plane # if np.dot(vox_vector, normal2plane_vector) > 0: # seg.data[vox_coord[0], vox_coord[1], vox_coord[2]] = iplane+2 # else: # if the voxel gets below the plane --> next voxel # break # seg.change_orientation(seg_raw_orientation) # seg.file_name += '_labeled' # seg.save() return locs
def create_mask(): fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI # parse argument for method method_type = param.process[0] # check method val if not method_type == 'center': method_val = param.process[1] # check existence of input files if method_type == 'centerline': sct.check_file_exist(method_val, param.verbose) # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) # Get output folder and file name if param.fname_out == '': param.fname_out = param.file_prefix+file_data+ext_data # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.tmp_create(param.verbose) # )sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) # sct.run('mkdir '+path_tmp, param.verbose) sct.printv('\nCheck orientation...', param.verbose) orientation_input = get_orientation(Image(param.fname_data)) sct.printv('.. '+orientation_input, param.verbose) reorient_coordinates = False # copy input data to tmp folder convert(param.fname_data, path_tmp+'data.nii') if method_type == 'centerline': convert(method_val, path_tmp+'centerline.nii.gz') if method_type == 'point': convert(method_val, path_tmp+'point.nii.gz') # go to tmp folder os.chdir(path_tmp) # reorient to RPI sct.printv('\nReorient to RPI...', param.verbose) # if not orientation_input == 'RPI': sct.run('sct_image -i data.nii -o data_RPI.nii -setorient RPI -v 0', verbose=False) if method_type == 'centerline': sct.run('sct_image -i centerline.nii.gz -o centerline_RPI.nii.gz -setorient RPI -v 0', verbose=False) if method_type == 'point': sct.run('sct_image -i point.nii.gz -o point_RPI.nii.gz -setorient RPI -v 0', verbose=False) # # if method_type == 'centerline': # orientation_centerline = get_orientation_3d(method_val, filename=True) # if not orientation_centerline == 'RPI': # sct.run('sct_image -i ' + method_val + ' -o ' + path_tmp + 'centerline.nii.gz' + ' -setorient RPI -v 0', verbose=False) # else: # convert(method_val, path_tmp+'centerline.nii.gz') # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image('data_RPI.nii').dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz)+ ' x ' + str(nt), param.verbose) # in case user input 4d data if nt != 1: sct.printv('WARNING in '+os.path.basename(__file__)+': Input image is 4d but output mask will 3D.', param.verbose, 'warning') # extract first volume to have 3d reference nii = Image('data_RPI.nii') data3d = nii.data[:,:,:,0] nii.data = data3d nii.save() if method_type == 'coord': # parse to get coordinate coord = map(int, method_val.split('x')) if method_type == 'point': # get file name fname_point = method_val # extract coordinate of point sct.printv('\nExtract coordinate of point...', param.verbose) # TODO: change this way to remove dependence to sct.run. ProcessLabels.display_voxel returns list of coordinates status, output = sct.run('sct_label_utils -i point_RPI.nii.gz -display', param.verbose) # parse to get coordinate coord = output[output.find('Position=')+10:-17].split(',') if method_type == 'center': # set coordinate at center of FOV coord = round(float(nx)/2), round(float(ny)/2) if method_type == 'centerline': # get name of centerline from user argument fname_centerline = 'centerline_RPI.nii.gz' else: # generate volume with line along Z at coordinates 'coord' sct.printv('\nCreate line...', param.verbose) fname_centerline = create_line('data_RPI.nii', coord, nz) # create mask sct.printv('\nCreate mask...', param.verbose) centerline = nibabel.load(fname_centerline) # open centerline hdr = centerline.get_header() # get header hdr.set_data_dtype('uint8') # set imagetype to uint8 spacing = hdr.structarr['pixdim'] data_centerline = centerline.get_data() # get centerline z_centerline_not_null = [iz for iz in range(0, nz, 1) if data_centerline[:, :, iz].any()] # get center of mass of the centerline cx = [0] * nz cy = [0] * nz for iz in range(0, nz, 1): if iz in z_centerline_not_null: cx[iz], cy[iz] = ndimage.measurements.center_of_mass(numpy.array(data_centerline[:, :, iz])) # create 2d masks file_mask = 'data_mask' for iz in range(nz): if iz not in z_centerline_not_null: # write an empty nifty volume img = nibabel.Nifti1Image(data_centerline[:, :, iz], None, hdr) nibabel.save(img, (file_mask + str(iz) + '.nii')) else: center = numpy.array([cx[iz], cy[iz]]) mask2d = create_mask2d(center, param.shape, param.size, nx, ny, even=param.even, spacing=spacing) # Write NIFTI volumes img = nibabel.Nifti1Image(mask2d, None, hdr) nibabel.save(img, (file_mask+str(iz)+'.nii')) # merge along Z # cmd = 'fslmerge -z mask ' # CHANGE THAT CAN IMPACT SPEED: # related to issue #755, we cannot open more than 256 files at one time. # to solve this issue, we do not open more than 100 files ''' im_list = [] im_temp = [] for iz in range(nz_not_null): if iz != 0 and iz % 100 == 0: im_temp.append(concat_data(im_list, 2)) im_list = [Image(file_mask + str(iz) + '.nii')] else: im_list.append(Image(file_mask+str(iz)+'.nii')) if im_temp: im_temp.append(concat_data(im_list, 2)) im_out = concat_data(im_temp, 2, no_expand=True) else: im_out = concat_data(im_list, 2) ''' fname_list = [file_mask + str(iz) + '.nii' for iz in range(nz)] im_out = concat_data(fname_list, dim=2) im_out.setFileName('mask_RPI.nii.gz') im_out.save() # reorient if necessary # if not orientation_input == 'RPI': sct.run('sct_image -i mask_RPI.nii.gz -o mask.nii.gz -setorient ' + orientation_input, param.verbose) # copy header input --> mask im_dat = Image('data.nii') im_mask = Image('mask.nii.gz') im_mask = copy_header(im_dat, im_mask) im_mask.save() # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp+'mask.nii.gz', param.fname_out) # Remove temporary files if param.remove_tmp_files == 1: sct.printv('\nRemove temporary files...', param.verbose) sct.run('rm -rf '+path_tmp, param.verbose, error_exit='warning') # to view results sct.printv('\nDone! To view results, type:', param.verbose) sct.printv('fslview '+param.fname_data+' '+param.fname_out+' -l Red -t 0.5 &', param.verbose, 'info') print
def main(): # Initialization fname_data = '' suffix_out = '_crop' remove_temp_files = param.remove_temp_files verbose = param.verbose fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI remove_temp_files = param.remove_temp_files # Parameters for debug mode if param.debug: print '\n*** WARNING: DEBUG MODE ON ***\n' fname_data = path_sct+'/testing/data/errsm_23/t2/t2.nii.gz' remove_temp_files = 0 else: # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:],'hi:r:v:') except getopt.GetoptError: usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_data = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_data == '': usage() # Check file existence sct.printv('\nCheck file existence...', verbose) sct.check_file_exist(fname_data, verbose) # Get dimensions of data sct.printv('\nGet dimensions of data...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_data).dim sct.printv('.. '+str(nx)+' x '+str(ny)+' x '+str(nz), verbose) # check if 4D data if not nt == 1: sct.printv('\nERROR in '+os.path.basename(__file__)+': Data should be 3D.\n', 1, 'error') sys.exit(2) # print arguments print '\nCheck parameters:' print ' data ................... '+fname_data print # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(fname_data) path_out, file_out, ext_out = '', file_data+suffix_out, ext_data # create temporary folder path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S")+'/' sct.run('mkdir '+path_tmp) # copy files into tmp folder sct.run('isct_c3d '+fname_data+' -o '+path_tmp+'data.nii') # go to tmp folder os.chdir(path_tmp) # change orientation sct.printv('\nChange orientation to RPI...', verbose) set_orientation('data.nii', 'RPI', 'data_rpi.nii') # get image of medial slab sct.printv('\nGet image of medial slab...', verbose) image_array = nibabel.load('data_rpi.nii').get_data() nx, ny, nz = image_array.shape scipy.misc.imsave('image.jpg', image_array[math.floor(nx/2), :, :]) # Display the image sct.printv('\nDisplay image and get cropping region...', verbose) fig = plt.figure() # fig = plt.gcf() # ax = plt.gca() ax = fig.add_subplot(111) img = mpimg.imread("image.jpg") implot = ax.imshow(img.T) implot.set_cmap('gray') plt.gca().invert_yaxis() # mouse callback ax.set_title('Left click on the top and bottom of your cropping field.\n Right click to remove last point.\n Close window when your done.') line, = ax.plot([], [], 'ro') # empty line cropping_coordinates = LineBuilder(line) plt.show() # disconnect callback # fig.canvas.mpl_disconnect(line) # check if user clicked two times if len(cropping_coordinates.xs) != 2: sct.printv('\nERROR: You have to select two points. Exit program.\n', 1, 'error') sys.exit(2) # convert coordinates to integer zcrop = [int(i) for i in cropping_coordinates.ys] # sort coordinates zcrop.sort() # crop image sct.printv('\nCrop image...', verbose) nii = Image('data_rpi.nii') data_crop = nii.data[:, :, zcrop[0]:zcrop[1]] nii.data = data_crop nii.setFileName('data_rpi_crop.nii') nii.save() # come back to parent folder os.chdir('..') sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp+'data_rpi_crop.nii', path_out+file_out+ext_out) # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp) # to view results print '\nDone! To view results, type:' print 'fslview '+path_out+file_out+ext_out+' &' print
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 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 main(): # get default parameters step1 = Paramreg(step='1', type='seg', algo='slicereg', metric='MeanSquares', iter='10') step2 = Paramreg(step='2', type='im', algo='syn', metric='MI', iter='3') # step1 = Paramreg() paramreg = ParamregMultiStep([step1, step2]) # step1 = Paramreg_step(step='1', type='seg', algo='bsplinesyn', metric='MeanSquares', iter='10', shrink='1', smooth='0', gradStep='0.5') # step2 = Paramreg_step(step='2', type='im', algo='syn', metric='MI', iter='10', shrink='1', smooth='0', gradStep='0.5') # paramreg = ParamregMultiStep([step1, step2]) # Initialize the parser parser = Parser(__file__) parser.usage.set_description('Register anatomical image to the template.') parser.add_option(name="-i", type_value="file", description="Anatomical image.", mandatory=True, example="anat.nii.gz") parser.add_option(name="-s", type_value="file", description="Spinal cord segmentation.", mandatory=True, example="anat_seg.nii.gz") parser.add_option(name="-l", type_value="file", description="Labels. See: http://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/", mandatory=True, default_value='', example="anat_labels.nii.gz") parser.add_option(name="-t", type_value="folder", description="Path to MNI-Poly-AMU template.", mandatory=False, default_value=param.path_template) parser.add_option(name="-p", type_value=[[':'], 'str'], description="""Parameters for registration (see sct_register_multimodal). Default:\n--\nstep=1\ntype="""+paramreg.steps['1'].type+"""\nalgo="""+paramreg.steps['1'].algo+"""\nmetric="""+paramreg.steps['1'].metric+"""\npoly="""+paramreg.steps['1'].poly+"""\n--\nstep=2\ntype="""+paramreg.steps['2'].type+"""\nalgo="""+paramreg.steps['2'].algo+"""\nmetric="""+paramreg.steps['2'].metric+"""\niter="""+paramreg.steps['2'].iter+"""\nshrink="""+paramreg.steps['2'].shrink+"""\nsmooth="""+paramreg.steps['2'].smooth+"""\ngradStep="""+paramreg.steps['2'].gradStep+"""\n--""", mandatory=False, example="step=2,type=seg,algo=bsplinesyn,metric=MeanSquares,iter=5,shrink=2:step=3,type=im,algo=syn,metric=MI,iter=5,shrink=1,gradStep=0.3") parser.add_option(name="-r", type_value="multiple_choice", description="""Remove temporary files.""", mandatory=False, default_value='1', example=['0', '1']) parser.add_option(name="-v", type_value="multiple_choice", description="""Verbose. 0: nothing. 1: basic. 2: extended.""", mandatory=False, default_value=param.verbose, example=['0', '1', '2']) if param.debug: print '\n*** WARNING: DEBUG MODE ON ***\n' fname_data = '/Users/julien/data/temp/sct_example_data/t2/t2.nii.gz' fname_landmarks = '/Users/julien/data/temp/sct_example_data/t2/labels.nii.gz' fname_seg = '/Users/julien/data/temp/sct_example_data/t2/t2_seg.nii.gz' path_template = param.path_template remove_temp_files = 0 verbose = 2 # speed = 'superfast' #param_reg = '2,BSplineSyN,0.6,MeanSquares' else: arguments = parser.parse(sys.argv[1:]) # get arguments fname_data = arguments['-i'] fname_seg = arguments['-s'] fname_landmarks = arguments['-l'] path_template = arguments['-t'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) if '-p' in arguments: paramreg_user = arguments['-p'] # update registration parameters for paramStep in paramreg_user: paramreg.addStep(paramStep) # initialize other parameters file_template = param.file_template file_template_label = param.file_template_label file_template_seg = param.file_template_seg output_type = param.output_type zsubsample = param.zsubsample # smoothing_sigma = param.smoothing_sigma # start timer start_time = time.time() # get absolute path - TO DO: remove! NEVER USE ABSOLUTE PATH... path_template = os.path.abspath(path_template) # get fname of the template + template objects fname_template = sct.slash_at_the_end(path_template, 1)+file_template fname_template_label = sct.slash_at_the_end(path_template, 1)+file_template_label fname_template_seg = sct.slash_at_the_end(path_template, 1)+file_template_seg # check file existence sct.printv('\nCheck template files...') sct.check_file_exist(fname_template, verbose) sct.check_file_exist(fname_template_label, verbose) sct.check_file_exist(fname_template_seg, verbose) # print arguments sct.printv('\nCheck parameters:', verbose) sct.printv('.. Data: '+fname_data, verbose) sct.printv('.. Landmarks: '+fname_landmarks, verbose) sct.printv('.. Segmentation: '+fname_seg, verbose) sct.printv('.. Path template: '+path_template, verbose) sct.printv('.. Output type: '+str(output_type), verbose) sct.printv('.. Remove temp files: '+str(remove_temp_files), verbose) sct.printv('\nParameters for registration:') for pStep in range(1, len(paramreg.steps)+1): sct.printv('Step #'+paramreg.steps[str(pStep)].step, verbose) sct.printv('.. Type #'+paramreg.steps[str(pStep)].type, verbose) sct.printv('.. Algorithm................ '+paramreg.steps[str(pStep)].algo, verbose) sct.printv('.. Metric................... '+paramreg.steps[str(pStep)].metric, verbose) sct.printv('.. Number of iterations..... '+paramreg.steps[str(pStep)].iter, verbose) sct.printv('.. Shrink factor............ '+paramreg.steps[str(pStep)].shrink, verbose) sct.printv('.. Smoothing factor......... '+paramreg.steps[str(pStep)].smooth, verbose) sct.printv('.. Gradient step............ '+paramreg.steps[str(pStep)].gradStep, verbose) sct.printv('.. Degree of polynomial..... '+paramreg.steps[str(pStep)].poly, verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) sct.printv('\nCheck input labels...') # check if label image contains coherent labels image_label = Image(fname_landmarks) # -> all labels must be different labels = image_label.getNonZeroCoordinates(sorting='value') hasDifferentLabels = True for lab in labels: for otherlabel in labels: if lab != otherlabel and lab.hasEqualValue(otherlabel): hasDifferentLabels = False break if not hasDifferentLabels: sct.printv('ERROR: Wrong landmarks input. All labels must be different.', verbose, 'error') # all labels must be available in tempalte image_label_template = Image(fname_template_label) labels_template = image_label_template.getNonZeroCoordinates(sorting='value') if labels[-1].value > labels_template[-1].value: sct.printv('ERROR: Wrong landmarks input. Labels must have correspondance in tempalte space. \nLabel max ' 'provided: ' + str(labels[-1].value) + '\nLabel max from template: ' + str(labels_template[-1].value), verbose, 'error') # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") status, output = sct.run('mkdir '+path_tmp) # copy files to temporary folder sct.printv('\nCopy files...', verbose) sct.run('isct_c3d '+fname_data+' -o '+path_tmp+'/data.nii') sct.run('isct_c3d '+fname_landmarks+' -o '+path_tmp+'/landmarks.nii.gz') sct.run('isct_c3d '+fname_seg+' -o '+path_tmp+'/segmentation.nii.gz') sct.run('isct_c3d '+fname_template+' -o '+path_tmp+'/template.nii') sct.run('isct_c3d '+fname_template_label+' -o '+path_tmp+'/template_labels.nii.gz') sct.run('isct_c3d '+fname_template_seg+' -o '+path_tmp+'/template_seg.nii.gz') # go to tmp folder os.chdir(path_tmp) # resample data to 1mm isotropic sct.printv('\nResample data to 1mm isotropic...', verbose) sct.run('isct_c3d data.nii -resample-mm 1.0x1.0x1.0mm -interpolation Linear -o datar.nii') sct.run('isct_c3d segmentation.nii.gz -resample-mm 1.0x1.0x1.0mm -interpolation NearestNeighbor -o segmentationr.nii.gz') # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling with neighrest neighbour can make them disappear. Therefore a more clever approach is required. resample_labels('landmarks.nii.gz', 'datar.nii', 'landmarksr.nii.gz') # # TODO # sct.run('sct_label_utils -i datar.nii -t create -x 124,186,19,2:129,98,23,8 -o landmarksr.nii.gz') # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) set_orientation('datar.nii', 'RPI', 'data_rpi.nii') set_orientation('landmarksr.nii.gz', 'RPI', 'landmarks_rpi.nii.gz') set_orientation('segmentationr.nii.gz', 'RPI', 'segmentation_rpi.nii.gz') # # Change orientation of input images to RPI # sct.printv('\nChange orientation of input images to RPI...', verbose) # set_orientation('data.nii', 'RPI', 'data_rpi.nii') # set_orientation('landmarks.nii.gz', 'RPI', 'landmarks_rpi.nii.gz') # set_orientation('segmentation.nii.gz', 'RPI', 'segmentation_rpi.nii.gz') # get landmarks in native space # crop segmentation # output: segmentation_rpi_crop.nii.gz sct.run('sct_crop_image -i segmentation_rpi.nii.gz -o segmentation_rpi_crop.nii.gz -dim 2 -bzmax') # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) sct.run('sct_straighten_spinalcord -i segmentation_rpi_crop.nii.gz -c segmentation_rpi_crop.nii.gz -r 0 -v '+str(verbose), verbose) # re-define warping field using non-cropped space (to avoid issue #367) sct.run('sct_concat_transfo -w warp_straight2curve.nii.gz -d data_rpi.nii -o warp_straight2curve.nii.gz') # Label preparation: # -------------------------------------------------------------------------------- # Remove unused label on template. Keep only label present in the input label image sct.printv('\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run('sct_label_utils -t remove -i template_labels.nii.gz -o template_label.nii.gz -r landmarks_rpi.nii.gz') # Make sure landmarks are INT sct.printv('\nConvert landmarks to INT...', verbose) sct.run('isct_c3d template_label.nii.gz -type int -o template_label.nii.gz', verbose) # Create a cross for the template labels - 5 mm sct.printv('\nCreate a 5 mm cross for the template labels...', verbose) sct.run('sct_label_utils -t cross -i template_label.nii.gz -o template_label_cross.nii.gz -c 5') # Create a cross for the input labels and dilate for straightening preparation - 5 mm sct.printv('\nCreate a 5mm cross for the input labels and dilate for straightening preparation...', verbose) sct.run('sct_label_utils -t cross -i landmarks_rpi.nii.gz -o landmarks_rpi_cross3x3.nii.gz -c 5 -d') # Apply straightening to labels sct.printv('\nApply straightening to labels...', verbose) sct.run('sct_apply_transfo -i landmarks_rpi_cross3x3.nii.gz -o landmarks_rpi_cross3x3_straight.nii.gz -d segmentation_rpi_crop_straight.nii.gz -w warp_curve2straight.nii.gz -x nn') # Convert landmarks from FLOAT32 to INT sct.printv('\nConvert landmarks from FLOAT32 to INT...', verbose) sct.run('isct_c3d landmarks_rpi_cross3x3_straight.nii.gz -type int -o landmarks_rpi_cross3x3_straight.nii.gz') # Remove labels that do not correspond with each others. sct.printv('\nRemove labels that do not correspond with each others.', verbose) sct.run('sct_label_utils -t remove-symm -i landmarks_rpi_cross3x3_straight.nii.gz -o landmarks_rpi_cross3x3_straight.nii.gz,template_label_cross.nii.gz -r template_label_cross.nii.gz') # Estimate affine transfo: straight --> template (landmark-based)' sct.printv('\nEstimate affine transfo: straight anat --> template (landmark-based)...', verbose) # converting landmarks straight and curved to physical coordinates image_straight = Image('landmarks_rpi_cross3x3_straight.nii.gz') landmark_straight = image_straight.getNonZeroCoordinates(sorting='value') image_template = Image('template_label_cross.nii.gz') landmark_template = image_template.getNonZeroCoordinates(sorting='value') # Reorganize landmarks points_fixed, points_moving = [], [] landmark_straight_mean = [] for coord in landmark_straight: if coord.value not in [c.value for c in landmark_straight_mean]: temp_landmark = coord temp_number = 1 for other_coord in landmark_straight: if coord.hasEqualValue(other_coord) and coord != other_coord: temp_landmark += other_coord temp_number += 1 landmark_straight_mean.append(temp_landmark / temp_number) for coord in landmark_straight_mean: point_straight = image_straight.transfo_pix2phys([[coord.x, coord.y, coord.z]]) points_moving.append([point_straight[0][0], point_straight[0][1], point_straight[0][2]]) for coord in landmark_template: point_template = image_template.transfo_pix2phys([[coord.x, coord.y, coord.z]]) points_fixed.append([point_template[0][0], point_template[0][1], point_template[0][2]]) # Register curved landmarks on straight landmarks based on python implementation sct.printv('\nComputing rigid transformation (algo=translation-scaling-z) ...', verbose) import msct_register_landmarks (rotation_matrix, translation_array, points_moving_reg, points_moving_barycenter) = \ msct_register_landmarks.getRigidTransformFromLandmarks( points_fixed, points_moving, constraints='translation-scaling-z', show=False) # writing rigid transformation file text_file = open("straight2templateAffine.txt", "w") text_file.write("#Insight Transform File V1.0\n") text_file.write("#Transform 0\n") text_file.write("Transform: FixedCenterOfRotationAffineTransform_double_3_3\n") text_file.write("Parameters: %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f\n" % ( 1.0/rotation_matrix[0, 0], rotation_matrix[0, 1], rotation_matrix[0, 2], rotation_matrix[1, 0], 1.0/rotation_matrix[1, 1], rotation_matrix[1, 2], rotation_matrix[2, 0], rotation_matrix[2, 1], 1.0/rotation_matrix[2, 2], translation_array[0, 0], translation_array[0, 1], -translation_array[0, 2])) text_file.write("FixedParameters: %.9f %.9f %.9f\n" % (points_moving_barycenter[0], points_moving_barycenter[1], points_moving_barycenter[2])) text_file.close() # Apply affine transformation: straight --> template sct.printv('\nApply affine transformation: straight --> template...', verbose) sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt -d template.nii -o warp_curve2straightAffine.nii.gz') sct.run('sct_apply_transfo -i data_rpi.nii -o data_rpi_straight2templateAffine.nii -d template.nii -w warp_curve2straightAffine.nii.gz') sct.run('sct_apply_transfo -i segmentation_rpi.nii.gz -o segmentation_rpi_straight2templateAffine.nii.gz -d template.nii -w warp_curve2straightAffine.nii.gz -x linear') # threshold to 0.5 nii = Image('segmentation_rpi_straight2templateAffine.nii.gz') data = nii.data data[data < 0.5] = 0 nii.data = data nii.setFileName('segmentation_rpi_straight2templateAffine_th.nii.gz') nii.save() # find min-max of anat2template (for subsequent cropping) zmin_template, zmax_template = find_zmin_zmax('segmentation_rpi_straight2templateAffine_th.nii.gz') # crop template in z-direction (for faster processing) sct.printv('\nCrop data in template space (for faster processing)...', verbose) sct.run('sct_crop_image -i template.nii -o template_crop.nii -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) sct.run('sct_crop_image -i template_seg.nii.gz -o template_seg_crop.nii.gz -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) sct.run('sct_crop_image -i data_rpi_straight2templateAffine.nii -o data_rpi_straight2templateAffine_crop.nii -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) sct.run('sct_crop_image -i segmentation_rpi_straight2templateAffine.nii.gz -o segmentation_rpi_straight2templateAffine_crop.nii.gz -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) # sub-sample in z-direction sct.printv('\nSub-sample in z-direction (for faster processing)...', verbose) sct.run('sct_resample -i template_crop.nii -o template_crop_r.nii -f 1x1x'+zsubsample, verbose) sct.run('sct_resample -i template_seg_crop.nii.gz -o template_seg_crop_r.nii.gz -f 1x1x'+zsubsample, verbose) sct.run('sct_resample -i data_rpi_straight2templateAffine_crop.nii -o data_rpi_straight2templateAffine_crop_r.nii -f 1x1x'+zsubsample, verbose) sct.run('sct_resample -i segmentation_rpi_straight2templateAffine_crop.nii.gz -o segmentation_rpi_straight2templateAffine_crop_r.nii.gz -f 1x1x'+zsubsample, verbose) # Registration straight spinal cord to template sct.printv('\nRegister straight spinal cord to template...', verbose) # loop across registration steps warp_forward = [] warp_inverse = [] for i_step in range(1, len(paramreg.steps)+1): sct.printv('\nEstimate transformation for step #'+str(i_step)+'...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = 'data_rpi_straight2templateAffine_crop_r.nii' dest = 'template_crop_r.nii' interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = 'segmentation_rpi_straight2templateAffine_crop_r.nii.gz' dest = 'template_seg_crop_r.nii.gz' interp_step = 'nn' else: sct.printv('ERROR: Wrong image type.', 1, 'error') # if step>1, apply warp_forward_concat to the src image to be used if i_step > 1: # sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose) sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose) src = sct.add_suffix(src, '_reg') # register src --> dest warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.append(warp_inverse_out) # Concatenate transformations: sct.printv('\nConcatenate transformations: anat --> template...', verbose) sct.run('sct_concat_transfo -w warp_curve2straightAffine.nii.gz,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose) # sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose) warp_inverse.reverse() sct.run('sct_concat_transfo -w '+','.join(warp_inverse)+',-straight2templateAffine.txt,warp_straight2curve.nii.gz -d data.nii -o warp_template2anat.nii.gz', verbose) # Apply warping fields to anat and template if output_type == 1: sct.run('sct_apply_transfo -i template.nii -o template2anat.nii.gz -d data.nii -w warp_template2anat.nii.gz -c 1', verbose) sct.run('sct_apply_transfo -i data.nii -o anat2template.nii.gz -d template.nii -w warp_anat2template.nii.gz -c 1', verbose) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp+'/warp_template2anat.nii.gz', 'warp_template2anat.nii.gz', verbose) sct.generate_output_file(path_tmp+'/warp_anat2template.nii.gz', 'warp_anat2template.nii.gz', verbose) if output_type == 1: sct.generate_output_file(path_tmp+'/template2anat.nii.gz', 'template2anat'+ext_data, verbose) sct.generate_output_file(path_tmp+'/anat2template.nii.gz', 'anat2template'+ext_data, verbose) # Delete temporary files if remove_temp_files: sct.printv('\nDelete temporary files...', verbose) 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', verbose) # to view results sct.printv('\nTo view results, type:', verbose) sct.printv('fslview '+fname_data+' template2anat -b 0,4000 &', verbose, 'info') sct.printv('fslview '+fname_template+' -b 0,5000 anat2template &\n', verbose, 'info')
def main(): parser = get_parser() param = Param() arguments = parser.parse(sys.argv[1:]) # get arguments fname_data = arguments['-i'] fname_seg = arguments['-s'] fname_landmarks = arguments['-l'] if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = '' path_template = sct.slash_at_the_end(arguments['-t'], 1) contrast_template = arguments['-c'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) if '-param-straighten' in arguments: param.param_straighten = arguments['-param-straighten'] if 'cpu-nb' in arguments: arg_cpu = ' -cpu-nb '+arguments['-cpu-nb'] else: arg_cpu = '' if '-param' in arguments: paramreg_user = arguments['-param'] # update registration parameters for paramStep in paramreg_user: paramreg.addStep(paramStep) # initialize other parameters file_template_label = param.file_template_label output_type = param.output_type zsubsample = param.zsubsample # smoothing_sigma = param.smoothing_sigma # capitalize letters for contrast if contrast_template == 't1': contrast_template = 'T1' elif contrast_template == 't2': contrast_template = 'T2' # retrieve file_template based on contrast fname_template_list = glob(path_template+param.folder_template+'*'+contrast_template+'.nii.gz') # TODO: make sure there is only one file -- check if file is there otherwise it crashes fname_template = fname_template_list[0] # retrieve file_template_seg fname_template_seg_list = glob(path_template+param.folder_template+'*cord.nii.gz') # TODO: make sure there is only one file fname_template_seg = fname_template_seg_list[0] # start timer start_time = time.time() # get absolute path - TO DO: remove! NEVER USE ABSOLUTE PATH... path_template = os.path.abspath(path_template+param.folder_template) # get fname of the template + template objects # fname_template = sct.slash_at_the_end(path_template, 1)+file_template fname_template_label = sct.slash_at_the_end(path_template, 1)+file_template_label # fname_template_seg = sct.slash_at_the_end(path_template, 1)+file_template_seg # check file existence sct.printv('\nCheck template files...') sct.check_file_exist(fname_template, verbose) sct.check_file_exist(fname_template_label, verbose) sct.check_file_exist(fname_template_seg, verbose) # print arguments sct.printv('\nCheck parameters:', verbose) sct.printv('.. Data: '+fname_data, verbose) sct.printv('.. Landmarks: '+fname_landmarks, verbose) sct.printv('.. Segmentation: '+fname_seg, verbose) sct.printv('.. Path template: '+path_template, verbose) sct.printv('.. Path output: '+path_output, verbose) sct.printv('.. Output type: '+str(output_type), verbose) sct.printv('.. Remove temp files: '+str(remove_temp_files), verbose) sct.printv('\nParameters for registration:') for pStep in range(1, len(paramreg.steps)+1): sct.printv('Step #'+paramreg.steps[str(pStep)].step, verbose) sct.printv('.. Type #'+paramreg.steps[str(pStep)].type, verbose) sct.printv('.. Algorithm................ '+paramreg.steps[str(pStep)].algo, verbose) sct.printv('.. Metric................... '+paramreg.steps[str(pStep)].metric, verbose) sct.printv('.. Number of iterations..... '+paramreg.steps[str(pStep)].iter, verbose) sct.printv('.. Shrink factor............ '+paramreg.steps[str(pStep)].shrink, verbose) sct.printv('.. Smoothing factor......... '+paramreg.steps[str(pStep)].smooth, verbose) sct.printv('.. Gradient step............ '+paramreg.steps[str(pStep)].gradStep, verbose) sct.printv('.. Degree of polynomial..... '+paramreg.steps[str(pStep)].poly, verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) sct.printv('\nCheck input labels...') # check if label image contains coherent labels image_label = Image(fname_landmarks) # -> all labels must be different labels = image_label.getNonZeroCoordinates(sorting='value') hasDifferentLabels = True for lab in labels: for otherlabel in labels: if lab != otherlabel and lab.hasEqualValue(otherlabel): hasDifferentLabels = False break if not hasDifferentLabels: sct.printv('ERROR: Wrong landmarks input. All labels must be different.', verbose, 'error') # all labels must be available in tempalte image_label_template = Image(fname_template_label) labels_template = image_label_template.getNonZeroCoordinates(sorting='value') if labels[-1].value > labels_template[-1].value: sct.printv('ERROR: Wrong landmarks input. Labels must have correspondence in template space. \nLabel max ' 'provided: ' + str(labels[-1].value) + '\nLabel max from template: ' + str(labels_template[-1].value), verbose, 'error') # create temporary folder path_tmp = sct.tmp_create(verbose=verbose) # set temporary file names ftmp_data = 'data.nii' ftmp_seg = 'seg.nii.gz' ftmp_label = 'label.nii.gz' ftmp_template = 'template.nii' ftmp_template_seg = 'template_seg.nii.gz' ftmp_template_label = 'template_label.nii.gz' # copy files to temporary folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('sct_convert -i '+fname_data+' -o '+path_tmp+ftmp_data) sct.run('sct_convert -i '+fname_seg+' -o '+path_tmp+ftmp_seg) sct.run('sct_convert -i '+fname_landmarks+' -o '+path_tmp+ftmp_label) sct.run('sct_convert -i '+fname_template+' -o '+path_tmp+ftmp_template) sct.run('sct_convert -i '+fname_template_seg+' -o '+path_tmp+ftmp_template_seg) sct.run('sct_convert -i '+fname_template_label+' -o '+path_tmp+ftmp_template_label) # go to tmp folder os.chdir(path_tmp) # smooth segmentation (jcohenadad, issue #613) sct.printv('\nSmooth segmentation...', verbose) sct.run('sct_maths -i '+ftmp_seg+' -smooth 1.5 -o '+add_suffix(ftmp_seg, '_smooth')) ftmp_seg = add_suffix(ftmp_seg, '_smooth') # resample data to 1mm isotropic sct.printv('\nResample data to 1mm isotropic...', verbose) sct.run('sct_resample -i '+ftmp_data+' -mm 1.0x1.0x1.0 -x linear -o '+add_suffix(ftmp_data, '_1mm')) ftmp_data = add_suffix(ftmp_data, '_1mm') sct.run('sct_resample -i '+ftmp_seg+' -mm 1.0x1.0x1.0 -x linear -o '+add_suffix(ftmp_seg, '_1mm')) ftmp_seg = add_suffix(ftmp_seg, '_1mm') # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling with neighrest neighbour can make them disappear. Therefore a more clever approach is required. resample_labels(ftmp_label, ftmp_data, add_suffix(ftmp_label, '_1mm')) ftmp_label = add_suffix(ftmp_label, '_1mm') # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) sct.run('sct_image -i '+ftmp_data+' -setorient RPI -o '+add_suffix(ftmp_data, '_rpi')) ftmp_data = add_suffix(ftmp_data, '_rpi') sct.run('sct_image -i '+ftmp_seg+' -setorient RPI -o '+add_suffix(ftmp_seg, '_rpi')) ftmp_seg = add_suffix(ftmp_seg, '_rpi') sct.run('sct_image -i '+ftmp_label+' -setorient RPI -o '+add_suffix(ftmp_label, '_rpi')) ftmp_label = add_suffix(ftmp_label, '_rpi') # get landmarks in native space # crop segmentation # output: segmentation_rpi_crop.nii.gz status_crop, output_crop = sct.run('sct_crop_image -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_crop')+' -dim 2 -bzmax', verbose) ftmp_seg = add_suffix(ftmp_seg, '_crop') cropping_slices = output_crop.split('Dimension 2: ')[1].split('\n')[0].split(' ') # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) sct.run('sct_straighten_spinalcord -i '+ftmp_seg+' -s '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_straight')+' -qc 0 -r 0 -v '+str(verbose)+' '+param.param_straighten+arg_cpu, verbose) # N.B. DO NOT UPDATE VARIABLE ftmp_seg BECAUSE TEMPORARY USED LATER # re-define warping field using non-cropped space (to avoid issue #367) sct.run('sct_concat_transfo -w warp_straight2curve.nii.gz -d '+ftmp_data+' -o warp_straight2curve.nii.gz') # Label preparation: # -------------------------------------------------------------------------------- # Remove unused label on template. Keep only label present in the input label image sct.printv('\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run('sct_label_utils -p remove -i '+ftmp_template_label+' -o '+ftmp_template_label+' -r '+ftmp_label) # Dilating the input label so they can be straighten without losing them sct.printv('\nDilating input labels using 3vox ball radius') sct.run('sct_maths -i '+ftmp_label+' -o '+add_suffix(ftmp_label, '_dilate')+' -dilate 3') ftmp_label = add_suffix(ftmp_label, '_dilate') # Apply straightening to labels sct.printv('\nApply straightening to labels...', verbose) sct.run('sct_apply_transfo -i '+ftmp_label+' -o '+add_suffix(ftmp_label, '_straight')+' -d '+add_suffix(ftmp_seg, '_straight')+' -w warp_curve2straight.nii.gz -x nn') ftmp_label = add_suffix(ftmp_label, '_straight') # Create crosses for the template labels and get coordinates sct.printv('\nCreate a 15 mm cross for the template labels...', verbose) template_image = Image(ftmp_template_label) coordinates_input = template_image.getNonZeroCoordinates(sorting='value') # jcohenadad, issue #628 <<<<< # landmark_template = ProcessLabels.get_crosses_coordinates(coordinates_input, gapxy=15) landmark_template = coordinates_input # >>>>> if verbose == 2: # TODO: assign cross to image before saving template_image.setFileName(add_suffix(ftmp_template_label, '_cross')) template_image.save(type='minimize_int') # Create crosses for the input labels into straight space and get coordinates sct.printv('\nCreate a 15 mm cross for the input labels...', verbose) label_straight_image = Image(ftmp_label) coordinates_input = label_straight_image.getCoordinatesAveragedByValue() # landmarks are sorted by value # jcohenadad, issue #628 <<<<< # landmark_straight = ProcessLabels.get_crosses_coordinates(coordinates_input, gapxy=15) landmark_straight = coordinates_input # >>>>> if verbose == 2: # TODO: assign cross to image before saving label_straight_image.setFileName(add_suffix(ftmp_label, '_cross')) label_straight_image.save(type='minimize_int') # Reorganize landmarks points_fixed, points_moving = [], [] for coord in landmark_straight: point_straight = label_straight_image.transfo_pix2phys([[coord.x, coord.y, coord.z]]) points_moving.append([point_straight[0][0], point_straight[0][1], point_straight[0][2]]) for coord in landmark_template: point_template = template_image.transfo_pix2phys([[coord.x, coord.y, coord.z]]) points_fixed.append([point_template[0][0], point_template[0][1], point_template[0][2]]) # Register curved landmarks on straight landmarks based on python implementation sct.printv('\nComputing rigid transformation (algo=translation-scaling-z) ...', verbose) import msct_register_landmarks # for some reason, the moving and fixed points are inverted between ITK transform and our python-based transform. # and for another unknown reason, x and y dimensions have a negative sign (at least for translation and center of rotation). if verbose == 2: show_transfo = True else: show_transfo = False (rotation_matrix, translation_array, points_moving_reg, points_moving_barycenter) = msct_register_landmarks.getRigidTransformFromLandmarks(points_moving, points_fixed, constraints='translation-scaling-z', show=show_transfo) # writing rigid transformation file text_file = open("straight2templateAffine.txt", "w") text_file.write("#Insight Transform File V1.0\n") text_file.write("#Transform 0\n") text_file.write("Transform: AffineTransform_double_3_3\n") text_file.write("Parameters: %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f\n" % ( rotation_matrix[0, 0], rotation_matrix[0, 1], rotation_matrix[0, 2], rotation_matrix[1, 0], rotation_matrix[1, 1], rotation_matrix[1, 2], rotation_matrix[2, 0], rotation_matrix[2, 1], rotation_matrix[2, 2], -translation_array[0, 0], -translation_array[0, 1], translation_array[0, 2])) text_file.write("FixedParameters: %.9f %.9f %.9f\n" % (-points_moving_barycenter[0], -points_moving_barycenter[1], points_moving_barycenter[2])) text_file.close() # Concatenate transformations: curve --> straight --> affine sct.printv('\nConcatenate transformations: curve --> straight --> affine...', verbose) sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt -d template.nii -o warp_curve2straightAffine.nii.gz') # Apply transformation sct.printv('\nApply transformation...', verbose) sct.run('sct_apply_transfo -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_straightAffine')+' -d '+ftmp_template+' -w warp_curve2straightAffine.nii.gz') ftmp_data = add_suffix(ftmp_data, '_straightAffine') sct.run('sct_apply_transfo -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_straightAffine')+' -d '+ftmp_template+' -w warp_curve2straightAffine.nii.gz -x linear') ftmp_seg = add_suffix(ftmp_seg, '_straightAffine') # threshold and binarize sct.printv('\nBinarize segmentation...', verbose) sct.run('sct_maths -i '+ftmp_seg+' -thr 0.4 -o '+add_suffix(ftmp_seg, '_thr')) sct.run('sct_maths -i '+add_suffix(ftmp_seg, '_thr')+' -bin -o '+add_suffix(ftmp_seg, '_thr_bin')) ftmp_seg = add_suffix(ftmp_seg, '_thr_bin') # find min-max of anat2template (for subsequent cropping) zmin_template, zmax_template = find_zmin_zmax(ftmp_seg) # crop template in z-direction (for faster processing) sct.printv('\nCrop data in template space (for faster processing)...', verbose) sct.run('sct_crop_image -i '+ftmp_template+' -o '+add_suffix(ftmp_template, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) ftmp_template = add_suffix(ftmp_template, '_crop') sct.run('sct_crop_image -i '+ftmp_template_seg+' -o '+add_suffix(ftmp_template_seg, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) ftmp_template_seg = add_suffix(ftmp_template_seg, '_crop') sct.run('sct_crop_image -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) ftmp_data = add_suffix(ftmp_data, '_crop') sct.run('sct_crop_image -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) ftmp_seg = add_suffix(ftmp_seg, '_crop') # sub-sample in z-direction sct.printv('\nSub-sample in z-direction (for faster processing)...', verbose) sct.run('sct_resample -i '+ftmp_template+' -o '+add_suffix(ftmp_template, '_sub')+' -f 1x1x'+zsubsample, verbose) ftmp_template = add_suffix(ftmp_template, '_sub') sct.run('sct_resample -i '+ftmp_template_seg+' -o '+add_suffix(ftmp_template_seg, '_sub')+' -f 1x1x'+zsubsample, verbose) ftmp_template_seg = add_suffix(ftmp_template_seg, '_sub') sct.run('sct_resample -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_sub')+' -f 1x1x'+zsubsample, verbose) ftmp_data = add_suffix(ftmp_data, '_sub') sct.run('sct_resample -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_sub')+' -f 1x1x'+zsubsample, verbose) ftmp_seg = add_suffix(ftmp_seg, '_sub') # Registration straight spinal cord to template sct.printv('\nRegister straight spinal cord to template...', verbose) # loop across registration steps warp_forward = [] warp_inverse = [] for i_step in range(1, len(paramreg.steps)+1): sct.printv('\nEstimate transformation for step #'+str(i_step)+'...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = ftmp_data dest = ftmp_template interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = ftmp_seg dest = ftmp_template_seg interp_step = 'nn' else: sct.printv('ERROR: Wrong image type.', 1, 'error') # if step>1, apply warp_forward_concat to the src image to be used if i_step > 1: # sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose) sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+add_suffix(src, '_reg')+' -x '+interp_step, verbose) src = add_suffix(src, '_reg') # register src --> dest warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.append(warp_inverse_out) # Concatenate transformations: sct.printv('\nConcatenate transformations: anat --> template...', verbose) sct.run('sct_concat_transfo -w warp_curve2straightAffine.nii.gz,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose) # sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose) sct.printv('\nConcatenate transformations: template --> anat...', verbose) warp_inverse.reverse() sct.run('sct_concat_transfo -w '+','.join(warp_inverse)+',-straight2templateAffine.txt,warp_straight2curve.nii.gz -d data.nii -o warp_template2anat.nii.gz', verbose) # Apply warping fields to anat and template if output_type == 1: sct.run('sct_apply_transfo -i template.nii -o template2anat.nii.gz -d data.nii -w warp_template2anat.nii.gz -crop 1', verbose) sct.run('sct_apply_transfo -i data.nii -o anat2template.nii.gz -d template.nii -w warp_anat2template.nii.gz -crop 1', verbose) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp+'warp_template2anat.nii.gz', path_output+'warp_template2anat.nii.gz', verbose) sct.generate_output_file(path_tmp+'warp_anat2template.nii.gz', path_output+'warp_anat2template.nii.gz', verbose) if output_type == 1: sct.generate_output_file(path_tmp+'template2anat.nii.gz', path_output+'template2anat'+ext_data, verbose) sct.generate_output_file(path_tmp+'anat2template.nii.gz', path_output+'anat2template'+ext_data, verbose) # Delete temporary files if remove_temp_files: sct.printv('\nDelete temporary files...', verbose) 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', verbose) # to view results sct.printv('\nTo view results, type:', verbose) sct.printv('fslview '+fname_data+' '+path_output+'template2anat -b 0,4000 &', verbose, 'info') sct.printv('fslview '+fname_template+' -b 0,5000 '+path_output+'anat2template &\n', verbose, 'info')
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')