def set_orientation(im, orientation, data_inversion=False, filename=False, fname_out=''): """ Set orientation on image :param im: either Image object or file name. Carefully set param filename. :param orientation: :param data_inversion: :param filename: :return: """ if fname_out: pass elif filename: path, fname, ext = extract_fname(im) fname_out = fname+'_'+orientation+ext else: fname_out = im.file_name+'_'+orientation+im.ext if not data_inversion: from sct_utils import run if filename: run('isct_orientation3d -i '+im+' -orientation '+orientation+' -o '+fname_out, 0) im_out = fname_out else: run('isct_orientation3d -i '+im.absolutepath+' -orientation '+orientation+' -o '+fname_out, 0) im_out = Image(fname_out) else: im_out = im.copy() im_out.change_orientation(orientation, True) im_out.setFileName(fname_out) return im_out
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 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 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 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 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 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 concat_data(fname_in_list, dim, pixdim=None): """ Concatenate data :param im_in_list: list of images. :param dim: dimension: 0, 1, 2, 3. :param pixdim: pixel resolution to join to image header :return im_out: concatenated image """ # WARNING: calling concat_data in python instead of in command line causes a non understood issue (results are different with both options) from numpy import concatenate, expand_dims, squeeze dat_list = [] data_concat_list = [] # check if shape of first image is smaller than asked dim to concatenate along data0 = Image(fname_in_list[0]).data if len(data0.shape) <= dim: expand_dim = True else: expand_dim = False for i, fname in enumerate(fname_in_list): # if there is more than 100 images to concatenate, then it does it iteratively to avoid memory issue. if i != 0 and i % 100 == 0: data_concat_list.append(concatenate(dat_list, axis=dim)) im = Image(fname) dat = im.data if expand_dim: dat = expand_dims(dat, dim) dat_list = [dat] del im del dat else: im = Image(fname) dat = im.data if expand_dim: dat = expand_dims(dat, dim) dat_list.append(dat) del im del dat if data_concat_list: data_concat_list.append(concatenate(dat_list, axis=dim)) data_concat = concatenate(data_concat_list, axis=dim) else: data_concat = concatenate(dat_list, axis=dim) # write file im_out = Image(fname_in_list[0]).copy() im_out.data = data_concat im_out.setFileName(im_out.file_name+'_concat'+im_out.ext) if pixdim is not None: im_out.hdr['pixdim'] = pixdim return im_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 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 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 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 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 crop_with_gui(self): import matplotlib.pyplot as plt import matplotlib.image as mpimg # Initialization fname_data = self.input_filename suffix_out = '_crop' remove_temp_files = self.rm_tmp_files verbose = self.verbose # 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 # 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 from sct_convert import convert sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) convert(fname_data, path_tmp + 'data.nii') # go to tmp folder os.chdir(path_tmp) # change orientation sct.printv('\nChange orientation to RPI...', verbose) sct.run('sct_image -i data.nii -setorient RPI -o 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 main(args=None): # Initialization # fname_anat = '' # fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) # remove_temp_files = param.remove_temp_files # verbose = param.verbose start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-r' in arguments: remove_temp_files = int(arguments['-r']) if '-v' in arguments: verbose = int(arguments['-v']) # Display arguments print '\nCheck input arguments...' print ' Volume to smooth .................. ' + fname_anat print ' Centerline ........................ ' + fname_centerline print ' Sigma (mm) ........................ '+str(sigma) print ' Verbose ........................... '+str(verbose) # Check that input is 3D: from msct_image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv('WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i '+fname_anat+' -split t -o '+fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname(fname_centerline) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('cp '+fname_anat+' '+path_tmp+'anat'+ext_anat, verbose) sct.run('cp '+fname_centerline+' '+path_tmp+'centerline'+ext_centerline, verbose) # go to tmp folder os.chdir(path_tmp) # convert to nii format convert('anat'+ext_anat, 'anat.nii') convert('centerline'+ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI print '\nOrient input volume to RPI orientation...' fname_anat_rpi = set_orientation('anat.nii', 'RPI', filename=True) move(fname_anat_rpi, 'anat_rpi.nii') # Change orientation of the input image into RPI print '\nOrient centerline to RPI orientation...' fname_centerline_rpi = set_orientation('centerline.nii', 'RPI', filename=True) move(fname_centerline_rpi, 'centerline_rpi.nii') # Straighten the spinal cord # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'): # if they exist, copy them into current folder sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning') shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz') shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz') shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz') # apply straightening sct.run('sct_apply_transfo -i anat_rpi.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o anat_rpi_straight.nii -x spline', verbose) else: sct.run('sct_straighten_spinalcord -i anat_rpi.nii -s centerline_rpi.nii -qc 0 -x spline', verbose) # Smooth the straightened image along z print '\nSmooth the straightened image along z...' sct.run('sct_maths -i anat_rpi_straight.nii -smooth 0,0,'+str(sigma)+' -o anat_rpi_straight_smooth.nii', verbose) # Apply the reversed warping field to get back the curved spinal cord print '\nApply the reversed warping field to get back the curved spinal cord...' sct.run('sct_apply_transfo -i anat_rpi_straight_smooth.nii -o anat_rpi_straight_smooth_curved.nii -d anat.nii -w warp_straight2curve.nii.gz -x spline', verbose) # replace zeroed voxels by original image (issue #937) sct.printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.setFileName('anat_rpi_straight_smooth_curved_nonzero.nii') nii_smooth.save() # come back to parent folder os.chdir('..') # Generate output file print '\nGenerate output file...' sct.generate_output_file(path_tmp+'/anat_rpi_straight_smooth_curved_nonzero.nii', file_anat+'_smooth'+ext_anat) # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp) # Display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s\n' # to view results sct.printv('Done! To view results, type:', verbose) sct.printv('fslview '+file_anat+' '+file_anat+'_smooth &\n', verbose, 'info')
def main(args=None): # Initialization # fname_anat = '' # fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) # remove_temp_files = param.remove_temp_files # verbose = param.verbose start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-r' in arguments: remove_temp_files = int(arguments['-r']) if '-v' in arguments: verbose = int(arguments['-v']) # Display arguments print '\nCheck input arguments...' print ' Volume to smooth .................. ' + fname_anat print ' Centerline ........................ ' + fname_centerline print ' Sigma (mm) ........................ ' + str(sigma) print ' Verbose ........................... ' + str(verbose) # Check that input is 3D: from msct_image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv( 'WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i ' + fname_anat + ' -split t -o ' + fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname( fname_centerline) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir ' + path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('cp ' + fname_anat + ' ' + path_tmp + 'anat' + ext_anat, verbose) sct.run( 'cp ' + fname_centerline + ' ' + path_tmp + 'centerline' + ext_centerline, verbose) # go to tmp folder os.chdir(path_tmp) # convert to nii format convert('anat' + ext_anat, 'anat.nii') convert('centerline' + ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI print '\nOrient input volume to RPI orientation...' fname_anat_rpi = set_orientation('anat.nii', 'RPI', filename=True) move(fname_anat_rpi, 'anat_rpi.nii') # Change orientation of the input image into RPI print '\nOrient centerline to RPI orientation...' fname_centerline_rpi = set_orientation('centerline.nii', 'RPI', filename=True) move(fname_centerline_rpi, 'centerline_rpi.nii') # Straighten the spinal cord # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile( '../warp_straight2curve.nii.gz') and os.path.isfile( '../straight_ref.nii.gz'): # if they exist, copy them into current folder sct.printv( 'WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning') shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz') shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz') shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz') # apply straightening sct.run( 'sct_apply_transfo -i anat_rpi.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o anat_rpi_straight.nii -x spline', verbose) else: sct.run( 'sct_straighten_spinalcord -i anat_rpi.nii -s centerline_rpi.nii -qc 0 -x spline', verbose) # Smooth the straightened image along z print '\nSmooth the straightened image along z...' sct.run( 'sct_maths -i anat_rpi_straight.nii -smooth 0,0,' + str(sigma) + ' -o anat_rpi_straight_smooth.nii', verbose) # Apply the reversed warping field to get back the curved spinal cord print '\nApply the reversed warping field to get back the curved spinal cord...' sct.run( 'sct_apply_transfo -i anat_rpi_straight_smooth.nii -o anat_rpi_straight_smooth_curved.nii -d anat.nii -w warp_straight2curve.nii.gz -x spline', verbose) # replace zeroed voxels by original image (issue #937) sct.printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.setFileName('anat_rpi_straight_smooth_curved_nonzero.nii') nii_smooth.save() # come back to parent folder os.chdir('..') # Generate output file print '\nGenerate output file...' sct.generate_output_file( path_tmp + '/anat_rpi_straight_smooth_curved_nonzero.nii', file_anat + '_smooth' + ext_anat) # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf ' + path_tmp) # Display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's\n' # to view results sct.printv('Done! To view results, type:', verbose) sct.printv('fslview ' + file_anat + ' ' + file_anat + '_smooth &\n', verbose, 'info')
def 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(args) fname_in = arguments["-i"] fname_out = arguments["-o"] verbose = int(arguments['-v']) # Open file(s) im = Image(fname_in) data = im.data # 3d or 4d numpy array dim = im.dim # 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 '-bin' in arguments: bin_thr = arguments['-bin'] data_out = binarise(data, bin_thr=bin_thr) elif '-add' in arguments: from numpy import sum data2 = get_data_or_scalar(arguments["-add"], data) data_concat = concatenate_along_4th_dimension(data, data2) data_out = sum(data_concat, axis=3) elif '-sub' in arguments: data2 = get_data_or_scalar(arguments['-sub'], data) data_out = data - data2 elif "-laplacian" in arguments: sigmas = arguments["-laplacian"] if len(sigmas) == 1: sigmas = [sigmas for i in range(len(data.shape))] elif len(sigmas) != len(data.shape): printv( parser.usage.generate( error= 'ERROR: -laplacian need the same number of inputs as the number of image dimension OR only one input' )) # adjust sigma based on voxel size sigmas = [sigmas[i] / dim[i + 4] for i in range(3)] # smooth data data_out = laplacian(data, sigmas) elif '-mul' in arguments: from numpy import prod data2 = get_data_or_scalar(arguments["-mul"], data) data_concat = concatenate_along_4th_dimension(data, data2) data_out = prod(data_concat, axis=3) elif '-div' in arguments: from numpy import divide data2 = get_data_or_scalar(arguments["-div"], data) data_out = divide(data, data2) elif '-mean' in arguments: from numpy import mean dim = dim_list.index(arguments['-mean']) if dim + 1 > len( np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = mean(data, dim) elif '-rms' in arguments: from numpy import mean, sqrt, square dim = dim_list.index(arguments['-rms']) if dim + 1 > len( np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = sqrt(mean(square(data.astype(float)), dim)) elif '-std' in arguments: from numpy import std dim = dim_list.index(arguments['-std']) if dim + 1 > len( np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = std(data, dim) elif "-smooth" in arguments: sigmas = arguments["-smooth"] if len(sigmas) == 1: sigmas = [sigmas[0] for i in range(len(data.shape))] elif len(sigmas) != len(data.shape): printv( parser.usage.generate( error= 'ERROR: -smooth need the same number of inputs as the number of image dimension OR only one input' )) # adjust sigma based on voxel size sigmas = [sigmas[i] / dim[i + 4] for i in range(3)] # smooth data data_out = smooth(data, sigmas) elif '-dilate' in arguments: data_out = dilate(data, arguments['-dilate']) elif '-erode' in arguments: data_out = erode(data, arguments['-erode']) elif '-denoise' in arguments: # parse denoising arguments p, b = 1, 5 # default arguments list_denoise = arguments['-denoise'] for i in list_denoise: if 'p' in i: p = int(i.split('=')[1]) if 'b' in i: b = int(i.split('=')[1]) data_out = denoise_nlmeans(data, patch_radius=p, block_radius=b) elif '-symmetrize' in arguments: data_out = (data + data[range(data.shape[0] - 1, -1, -1), :, :]) / float(2) elif '-mi' in arguments: # input 1 = from flag -i --> im # input 2 = from flag -mi im_2 = Image(arguments['-mi']) compute_similarity(im.data, im_2.data, fname_out, metric='mi', verbose=verbose) data_out = None elif '-minorm' in arguments: im_2 = Image(arguments['-minorm']) compute_similarity(im.data, im_2.data, fname_out, metric='minorm', verbose=verbose) data_out = None elif '-corr' in arguments: # input 1 = from flag -i --> im # input 2 = from flag -mi im_2 = Image(arguments['-corr']) compute_similarity(im.data, im_2.data, fname_out, metric='corr', verbose=verbose) data_out = None # if no flag is set else: data_out = None printv( parser.usage.generate( error= 'ERROR: you need to specify an operation to do on the input image' )) if data_out is not None: # Write output nii_out = Image(fname_in) # use header of input file nii_out.data = data_out nii_out.setFileName(fname_out) nii_out.save() # TODO: case of multiple outputs # assert len(data_out) == n_out # if n_in == n_out: # for im_in, d_out, fn_out in zip(nii, data_out, fname_out): # im_in.data = d_out # im_in.setFileName(fn_out) # if "-w" in arguments: # im_in.hdr.set_intent('vector', (), '') # im_in.save() # elif n_out == 1: # nii[0].data = data_out[0] # nii[0].setFileName(fname_out[0]) # if "-w" in arguments: # nii[0].hdr.set_intent('vector', (), '') # nii[0].save() # elif n_out > n_in: # for dat_out, name_out in zip(data_out, fname_out): # im_out = nii[0].copy() # im_out.data = dat_out # im_out.setFileName(name_out) # if "-w" in arguments: # im_out.hdr.set_intent('vector', (), '') # im_out.save() # else: # printv(parser.usage.generate(error='ERROR: not the correct numbers of inputs and outputs')) # display message if data_out is not None: printv('\nDone! To view results, type:', verbose) printv('fslview ' + fname_out + ' &\n', verbose, 'info') else: printv('\nDone! File created: ' + fname_out, 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(): # 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 validation(self, fname_manual_gmseg, fname_sc_seg): path_manual_gmseg, file_manual_gmseg, ext_manual_gmseg = sct.extract_fname( fname_manual_gmseg) path_sc_seg, file_sc_seg, ext_sc_seg = sct.extract_fname(fname_sc_seg) # Create tmp folder and copy files in it tmp_dir = sct.tmp_create() sct.run('cp ' + fname_manual_gmseg + ' ' + tmp_dir + file_manual_gmseg + ext_manual_gmseg) sct.run('cp ' + fname_sc_seg + ' ' + tmp_dir + file_sc_seg + ext_sc_seg) sct.run('cp ' + self.param.output_folder + self.fname_warp_template2gm + ' ' + tmp_dir + self.fname_warp_template2gm) os.chdir(tmp_dir) sct.run('sct_warp_template -d ' + fname_manual_gmseg + ' -w ' + self.fname_warp_template2gm + ' -qc 0 -a 0') if 'MNI-Poly-AMU_GM.nii.gz' in os.listdir('label/template/'): im_new_template_gm = Image('label/template/MNI-Poly-AMU_GM.nii.gz') im_new_template_wm = Image('label/template/MNI-Poly-AMU_WM.nii.gz') else: im_new_template_gm = Image('label/template/PAM50_gm.nii.gz') im_new_template_wm = Image('label/template/PAM50_wm.nii.gz') im_new_template_gm = thr_im(im_new_template_gm, self.param.thr, self.param.thr) im_new_template_wm = thr_im(im_new_template_wm, self.param.thr, self.param.thr) self.im_template_gm = thr_im(self.im_template_gm, self.param.thr, self.param.thr) self.im_template_wm = thr_im(self.im_template_wm, self.param.thr, self.param.thr) fname_new_template_gm = 'new_template_gm.nii.gz' im_new_template_gm.setFileName(fname_new_template_gm) im_new_template_gm.save() fname_new_template_wm = 'new_template_wm.nii.gz' im_new_template_wm.setFileName(fname_new_template_wm) im_new_template_wm.save() fname_old_template_wm = 'old_template_wm.nii.gz' self.im_template_wm.setFileName(fname_old_template_wm) self.im_template_wm.save() fname_old_template_gm = 'old_template_gm.nii.gz' self.im_template_gm.setFileName(fname_old_template_gm) self.im_template_gm.save() fname_manual_wmseg = 'target_manual_wmseg.nii.gz' sct.run('sct_maths -i ' + file_sc_seg + ext_sc_seg + ' -sub ' + file_manual_gmseg + ext_manual_gmseg + ' -o ' + fname_manual_wmseg) # Compute Hausdorff distance status, output_old_hd = sct.run('sct_compute_hausdorff_distance -i ' + fname_old_template_gm + ' -r ' + file_manual_gmseg + ext_manual_gmseg + ' -t 1 -v 1') status, output_new_hd = sct.run('sct_compute_hausdorff_distance -i ' + fname_new_template_gm + ' -r ' + file_manual_gmseg + ext_manual_gmseg + ' -t 1 -v 1') hd_name = 'hd_md_multilabel_reg.txt' hd_fic = open(hd_name, 'w') hd_fic.write( 'The "diff" columns are comparisons between regular template registration and corrected template registration according to SC internal structure\n' 'Diff = metric_regular_reg - metric_corrected_reg\n') hd_fic.write('#Slice, HD, HD diff, MD, MD diff\n') no_ref_slices = [] init_hd = "Hausdorff's distance - First relative Hausdorff's distance median - Second relative Hausdorff's distance median(all in mm)\n" old_gm_hd = output_old_hd[output_old_hd.find(init_hd) + len(init_hd):].split('\n') new_gm_hd = output_new_hd[output_new_hd.find(init_hd) + len(init_hd):].split('\n') for i in range(len(old_gm_hd) - 3): # last two lines are informations i_old, val_old = old_gm_hd[i].split(':') i_new, val_new = new_gm_hd[i].split(':') i_old = int(i_old[-2:]) i_new = int(i_new[-2:]) assert i == i_old == i_new, 'ERROR: when comparing Hausdorff distances, slice numbers differs.' hd_old, med1_old, med2_old = val_old.split('-') hd_new, med1_new, med2_new = val_new.split('-') if float(hd_old) == 0.0: no_ref_slices.append(i) hd_fic.write(str(i) + ', NO MANUAL SEGMENTATION\n') else: md_new = max(float(med1_new), float(med2_new)) md_old = max(float(med1_old), float(med2_old)) hd_fic.write( str(i) + ', ' + hd_new + ', ' + str(float(hd_old) - float(hd_new)) + ', ' + str(md_new) + ', ' + str(md_old - md_new) + '\n') hd_fic.close() # Compute Dice coefficient # --- DC old template try: status_old_gm, output_old_gm = sct.run( 'sct_dice_coefficient -i ' + file_manual_gmseg + ext_manual_gmseg + ' -d ' + fname_old_template_gm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: corrected_manual_gmseg = file_manual_gmseg + '_in_old_template_space' + ext_manual_gmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_old_template_gm + ',' + file_manual_gmseg + ext_manual_gmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_gmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_gmseg+' -thr 0.1 -o '+corrected_manual_gmseg) sct.run('sct_maths -i ' + corrected_manual_gmseg + ' -bin 0.1 -o ' + corrected_manual_gmseg) status_old_gm, output_old_gm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_gmseg + ' -d ' + fname_old_template_gm + ' -2d-slices 2', error_exit='warning') try: status_old_wm, output_old_wm = sct.run( 'sct_dice_coefficient -i ' + fname_manual_wmseg + ' -d ' + fname_old_template_wm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: path_manual_wmseg, file_manual_wmseg, ext_manual_wmseg = sct.extract_fname( fname_manual_wmseg) corrected_manual_wmseg = file_manual_wmseg + '_in_old_template_space' + ext_manual_wmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_old_template_wm + ',' + fname_manual_wmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_wmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_wmseg+' -thr 0.1 -o '+corrected_manual_wmseg) sct.run('sct_maths -i ' + corrected_manual_wmseg + ' -bin 0.1 -o ' + corrected_manual_wmseg) status_old_wm, output_old_wm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_wmseg + ' -d ' + fname_old_template_wm + ' -2d-slices 2', error_exit='warning') # --- DC new template try: status_new_gm, output_new_gm = sct.run( 'sct_dice_coefficient -i ' + file_manual_gmseg + ext_manual_gmseg + ' -d ' + fname_new_template_gm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: corrected_manual_gmseg = file_manual_gmseg + '_in_new_template_space' + ext_manual_gmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_new_template_gm + ',' + file_manual_gmseg + ext_manual_gmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_gmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_gmseg+' -thr 0.1 -o '+corrected_manual_gmseg) sct.run('sct_maths -i ' + corrected_manual_gmseg + ' -bin 0.1 -o ' + corrected_manual_gmseg) status_new_gm, output_new_gm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_gmseg + ' -d ' + fname_new_template_gm + ' -2d-slices 2', error_exit='warning') try: status_new_wm, output_new_wm = sct.run( 'sct_dice_coefficient -i ' + fname_manual_wmseg + ' -d ' + fname_new_template_wm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: path_manual_wmseg, file_manual_wmseg, ext_manual_wmseg = sct.extract_fname( fname_manual_wmseg) corrected_manual_wmseg = file_manual_wmseg + '_in_new_template_space' + ext_manual_wmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_new_template_wm + ',' + fname_manual_wmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_wmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_wmseg+' -thr 0.1 -o '+corrected_manual_wmseg) sct.run('sct_maths -i ' + corrected_manual_wmseg + ' -bin 0.1 -o ' + corrected_manual_wmseg) status_new_wm, output_new_wm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_wmseg + ' -d ' + fname_new_template_wm + ' -2d-slices 2', error_exit='warning') dice_name = 'dice_multilabel_reg.txt' dice_fic = open(dice_name, 'w') dice_fic.write( 'The "diff" columns are comparisons between regular template registration and corrected template registration according to SC internal structure\n' 'Diff = metric_corrected_reg - metric_regular_reg\n') dice_fic.write('#Slice, WM DC, WM diff, GM DC, GM diff\n') init_dc = '2D Dice coefficient by slice:\n' old_gm_dc = output_old_gm[output_old_gm.find(init_dc) + len(init_dc):].split('\n') old_wm_dc = output_old_wm[output_old_wm.find(init_dc) + len(init_dc):].split('\n') new_gm_dc = output_new_gm[output_new_gm.find(init_dc) + len(init_dc):].split('\n') new_wm_dc = output_new_wm[output_new_wm.find(init_dc) + len(init_dc):].split('\n') for i in range(len(old_gm_dc)): if i not in no_ref_slices: i_new_gm, val_new_gm = new_gm_dc[i].split(' ') i_new_wm, val_new_wm = new_wm_dc[i].split(' ') i_old_gm, val_old_gm = old_gm_dc[i].split(' ') i_old_wm, val_old_wm = old_wm_dc[i].split(' ') assert i == int(i_new_gm) == int(i_new_wm) == int( i_old_gm ) == int( i_old_wm ), 'ERROR: when comparing Dice coefficients, slice numbers differs.' dice_fic.write( str(i) + ', ' + val_new_wm + ', ' + str(float(val_new_wm) - float(val_old_wm)) + ', ' + val_new_gm + ', ' + str(float(val_new_gm) - float(val_old_gm)) + '\n') else: dice_fic.write(str(i) + ', NO MANUAL SEGMENTATION\n') dice_fic.close() os.chdir('..') sct.generate_output_file(tmp_dir + hd_name, self.param.output_folder + hd_name) sct.generate_output_file(tmp_dir + dice_name, self.param.output_folder + dice_name) if self.param.remove_tmp: sct.run('rm -rf ' + tmp_dir, error_exit='warning')
def register(src, dest, paramreg, param, i_step_str): # initiate default parameters of antsRegistration transformation ants_registration_params = {'rigid': '', 'affine': '', 'compositeaffine': '', 'similarity': '', 'translation': '', 'bspline': ',10', 'gaussiandisplacementfield': ',3,0', 'bsplinedisplacementfield': ',5,10', 'syn': ',3,0', 'bsplinesyn': ',1,3'} output = '' # default output if problem # display arguments sct.printv('Registration parameters:', param.verbose) sct.printv(' type ........... '+paramreg.steps[i_step_str].type, param.verbose) sct.printv(' algo ........... '+paramreg.steps[i_step_str].algo, param.verbose) sct.printv(' slicewise ...... '+paramreg.steps[i_step_str].slicewise, param.verbose) sct.printv(' metric ......... '+paramreg.steps[i_step_str].metric, param.verbose) sct.printv(' iter ........... '+paramreg.steps[i_step_str].iter, param.verbose) sct.printv(' smooth ......... '+paramreg.steps[i_step_str].smooth, param.verbose) sct.printv(' laplacian ...... '+paramreg.steps[i_step_str].laplacian, param.verbose) sct.printv(' shrink ......... '+paramreg.steps[i_step_str].shrink, param.verbose) sct.printv(' gradStep ....... '+paramreg.steps[i_step_str].gradStep, param.verbose) sct.printv(' init ........... '+paramreg.steps[i_step_str].init, param.verbose) sct.printv(' poly ........... '+paramreg.steps[i_step_str].poly, param.verbose) sct.printv(' dof ............ '+paramreg.steps[i_step_str].dof, param.verbose) sct.printv(' smoothWarpXY ... '+paramreg.steps[i_step_str].smoothWarpXY, param.verbose) # set metricSize if paramreg.steps[i_step_str].metric == 'MI': metricSize = '32' # corresponds to number of bins else: metricSize = '4' # corresponds to radius (for CC, MeanSquares...) # set masking if param.fname_mask: fname_mask = 'mask.nii.gz' masking = '-x mask.nii.gz' else: fname_mask = '' masking = '' if paramreg.steps[i_step_str].algo == 'slicereg': # check if user used type=label if paramreg.steps[i_step_str].type == 'label': sct.printv('\nERROR: this algo is not compatible with type=label. Please use type=im or type=seg', 1, 'error') else: from msct_image import find_zmin_zmax # threshold images (otherwise, automatic crop does not work -- see issue #293) src_th = sct.add_suffix(src, '_th') from msct_image import Image nii = Image(src) data = nii.data data[data < 0.1] = 0 nii.data = data nii.setFileName(src_th) nii.save() # sct.run(fsloutput+'fslmaths '+src+' -thr 0.1 '+src_th, param.verbose) dest_th = sct.add_suffix(dest, '_th') nii = Image(dest) data = nii.data data[data < 0.1] = 0 nii.data = data nii.setFileName(dest_th) nii.save() # sct.run(fsloutput+'fslmaths '+dest+' -thr 0.1 '+dest_th, param.verbose) # find zmin and zmax zmin_src, zmax_src = find_zmin_zmax(src_th) zmin_dest, zmax_dest = find_zmin_zmax(dest_th) zmin_total = max([zmin_src, zmin_dest]) zmax_total = min([zmax_src, zmax_dest]) # crop data src_crop = sct.add_suffix(src, '_crop') sct.run('sct_crop_image -i '+src+' -o '+src_crop+' -dim 2 -start '+str(zmin_total)+' -end '+str(zmax_total), param.verbose) dest_crop = sct.add_suffix(dest, '_crop') sct.run('sct_crop_image -i '+dest+' -o '+dest_crop+' -dim 2 -start '+str(zmin_total)+' -end '+str(zmax_total), param.verbose) # update variables src = src_crop dest = dest_crop scr_regStep = sct.add_suffix(src, '_regStep'+i_step_str) # estimate transfo cmd = ('isct_antsSliceRegularizedRegistration ' '-t Translation[0.5] ' '-m '+paramreg.steps[i_step_str].metric+'['+dest+','+src+',1,'+metricSize+',Regular,0.2] ' '-p '+paramreg.steps[i_step_str].poly+' ' '-i '+paramreg.steps[i_step_str].iter+' ' '-f 1 ' '-s '+paramreg.steps[i_step_str].smooth+' ' '-v 1 ' # verbose (verbose=2 does not exist, so we force it to 1) '-o [step'+i_step_str+','+scr_regStep+'] ' # here the warp name is stage10 because antsSliceReg add "Warp" +masking) warp_forward_out = 'step'+i_step_str+'Warp.nii.gz' warp_inverse_out = 'step'+i_step_str+'InverseWarp.nii.gz' # run command status, output = sct.run(cmd, param.verbose) # ANTS 3d elif paramreg.steps[i_step_str].algo.lower() in ants_registration_params and paramreg.steps[i_step_str].slicewise == '0': # make sure type!=label. If type==label, this will be addressed later in the code. if not paramreg.steps[i_step_str].type == 'label': # Pad the destination image (because ants doesn't deform the extremities) # N.B. no need to pad if iter = 0 if not paramreg.steps[i_step_str].iter == '0': dest_pad = sct.add_suffix(dest, '_pad') sct.run('sct_image -i '+dest+' -o '+dest_pad+' -pad 0,0,'+str(param.padding)) dest = dest_pad # apply Laplacian filter if not paramreg.steps[i_step_str].laplacian == '0': sct.printv('\nApply Laplacian filter', param.verbose) sct.run('sct_maths -i '+src+' -laplacian '+paramreg.steps[i_step_str].laplacian+','+paramreg.steps[i_step_str].laplacian+',0 -o '+sct.add_suffix(src, '_laplacian')) sct.run('sct_maths -i '+dest+' -laplacian '+paramreg.steps[i_step_str].laplacian+','+paramreg.steps[i_step_str].laplacian+',0 -o '+sct.add_suffix(dest, '_laplacian')) src = sct.add_suffix(src, '_laplacian') dest = sct.add_suffix(dest, '_laplacian') # Estimate transformation sct.printv('\nEstimate transformation', param.verbose) scr_regStep = sct.add_suffix(src, '_regStep' + i_step_str) cmd = ('isct_antsRegistration ' '--dimensionality 3 ' '--transform '+paramreg.steps[i_step_str].algo+'['+paramreg.steps[i_step_str].gradStep + ants_registration_params[paramreg.steps[i_step_str].algo.lower()]+'] ' '--metric '+paramreg.steps[i_step_str].metric+'['+dest+','+src+',1,'+metricSize+'] ' '--convergence '+paramreg.steps[i_step_str].iter+' ' '--shrink-factors '+paramreg.steps[i_step_str].shrink+' ' '--smoothing-sigmas '+paramreg.steps[i_step_str].smooth+'mm ' '--restrict-deformation 1x1x0 ' '--output [step'+i_step_str+','+scr_regStep+'] ' '--interpolation BSpline[3] ' +masking) # add verbose if param.verbose >= 1: cmd += ' --verbose 1' # add init translation if not paramreg.steps[i_step_str].init == '': init_dict = {'geometric': '0', 'centermass': '1', 'origin': '2'} cmd += ' -r ['+dest+','+src+','+init_dict[paramreg.steps[i_step_str].init]+']' # run command status, output = sct.run(cmd, param.verbose) # get appropriate file name for transformation if paramreg.steps[i_step_str].algo in ['rigid', 'affine', 'translation']: warp_forward_out = 'step'+i_step_str+'0GenericAffine.mat' warp_inverse_out = '-step'+i_step_str+'0GenericAffine.mat' else: warp_forward_out = 'step'+i_step_str+'0Warp.nii.gz' warp_inverse_out = 'step'+i_step_str+'0InverseWarp.nii.gz' # ANTS 2d elif paramreg.steps[i_step_str].algo.lower() in ants_registration_params and paramreg.steps[i_step_str].slicewise == '1': # make sure type!=label. If type==label, this will be addressed later in the code. if not paramreg.steps[i_step_str].type == 'label': from msct_register import register_slicewise # if shrink!=1, force it to be 1 (otherwise, it generates a wrong 3d warping field). TODO: fix that! if not paramreg.steps[i_step_str].shrink == '1': sct.printv('\nWARNING: when using slicewise with SyN or BSplineSyN, shrink factor needs to be one. Forcing shrink=1.', 1, 'warning') paramreg.steps[i_step_str].shrink = '1' warp_forward_out = 'step'+i_step_str + 'Warp.nii.gz' warp_inverse_out = 'step'+i_step_str + 'InverseWarp.nii.gz' register_slicewise(src, dest, paramreg=paramreg.steps[i_step_str], fname_mask=fname_mask, warp_forward_out=warp_forward_out, warp_inverse_out=warp_inverse_out, ants_registration_params=ants_registration_params, path_qc=param.path_qc, verbose=param.verbose) # slice-wise transfo elif paramreg.steps[i_step_str].algo in ['centermass', 'centermassrot', 'columnwise']: # if type=im, sends warning if paramreg.steps[i_step_str].type == 'im': sct.printv('\nWARNING: algo '+paramreg.steps[i_step_str].algo+' should be used with type=seg.\n', 1, 'warning') # if type=label, exit with error elif paramreg.steps[i_step_str].type == 'label': sct.printv('\nERROR: this algo is not compatible with type=label. Please use type=im or type=seg', 1, 'error') # check if user provided a mask-- if so, inform it will be ignored if not fname_mask == '': sct.printv('\nWARNING: algo '+paramreg.steps[i_step_str].algo+' will ignore the provided mask.\n', 1, 'warning') # smooth data if not paramreg.steps[i_step_str].smooth == '0': sct.printv('\nSmooth data', param.verbose) sct.run('sct_maths -i '+src+' -smooth '+paramreg.steps[i_step_str].smooth+','+paramreg.steps[i_step_str].smooth+',0 -o '+sct.add_suffix(src, '_smooth')) sct.run('sct_maths -i '+dest+' -smooth '+paramreg.steps[i_step_str].smooth+','+paramreg.steps[i_step_str].smooth+',0 -o '+sct.add_suffix(dest, '_smooth')) src = sct.add_suffix(src, '_smooth') dest = sct.add_suffix(dest, '_smooth') from msct_register import register_slicewise warp_forward_out = 'step'+i_step_str + 'Warp.nii.gz' warp_inverse_out = 'step'+i_step_str + 'InverseWarp.nii.gz' register_slicewise(src, dest, paramreg=paramreg.steps[i_step_str], fname_mask=fname_mask, warp_forward_out=warp_forward_out, warp_inverse_out=warp_inverse_out, ants_registration_params=ants_registration_params, path_qc=param.path_qc, verbose=param.verbose) else: sct.printv('\nERROR: algo '+paramreg.steps[i_step_str].algo+' does not exist. Exit program\n', 1, 'error') # landmark-based registration if paramreg.steps[i_step_str].type in ['label']: # check if user specified ilabel and dlabel # TODO warp_forward_out = 'step' + i_step_str + '0GenericAffine.txt' warp_inverse_out = '-step' + i_step_str + '0GenericAffine.txt' from msct_register_landmarks import register_landmarks register_landmarks(src, dest, paramreg.steps[i_step_str].dof, fname_affine=warp_forward_out, verbose=param.verbose, path_qc=param.path_qc) if not os.path.isfile(warp_forward_out): # no forward warping field for rigid and affine sct.printv('\nERROR: file '+warp_forward_out+' doesn\'t exist (or is not a file).\n' + output + '\nERROR: ANTs failed. Exit program.\n', 1, 'error') elif not os.path.isfile(warp_inverse_out) and paramreg.steps[i_step_str].algo not in ['rigid', 'affine', 'translation'] and paramreg.steps[i_step_str].type not in ['label']: # no inverse warping field for rigid and affine sct.printv('\nERROR: file '+warp_inverse_out+' doesn\'t exist (or is not a file).\n' + output + '\nERROR: ANTs failed. Exit program.\n', 1, 'error') else: # rename warping fields if (paramreg.steps[i_step_str].algo.lower() in ['rigid', 'affine', 'translation'] and paramreg.steps[i_step_str].slicewise == '0'): # if ANTs is used with affine/rigid --> outputs .mat file warp_forward = 'warp_forward_'+i_step_str+'.mat' os.rename(warp_forward_out, warp_forward) warp_inverse = '-warp_forward_'+i_step_str+'.mat' elif paramreg.steps[i_step_str].type in ['label']: # if label-based registration is used --> outputs .txt file warp_forward = 'warp_forward_'+i_step_str+'.txt' os.rename(warp_forward_out, warp_forward) warp_inverse = '-warp_forward_'+i_step_str+'.txt' else: warp_forward = 'warp_forward_'+i_step_str+'.nii.gz' warp_inverse = 'warp_inverse_'+i_step_str+'.nii.gz' os.rename(warp_forward_out, warp_forward) os.rename(warp_inverse_out, warp_inverse) return warp_forward, warp_inverse
def compute_dti(fname_in, fname_bvals, fname_bvecs, prefix, method, file_mask): """ Compute DTI. :param fname_in: input 4d file. :param bvals: bvals txt file :param bvecs: bvecs txt file :param prefix: output prefix. Example: "dti_" :param method: algo for computing 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. if not file_mask == '': printv('Open mask file...', param.verbose) # open mask file nii_mask = Image(file_mask) mask = nii_mask.data # fit tensor model printv('Computing tensor using "'+method+'" method...', param.verbose) import dipy.reconst.dti as dti if method == 'standard': tenmodel = dti.TensorModel(gtab) if file_mask == '': tenfit = tenmodel.fit(data) else: tenfit = tenmodel.fit(data, mask) elif method == 'restore': import dipy.denoise.noise_estimate as ne sigma = ne.estimate_sigma(data) dti_restore = dti.TensorModel(gtab, fit_method='RESTORE', sigma=sigma) if file_mask == '': tenfit = dti_restore.fit(data) else: tenfit = dti_restore.fit(data, mask) # 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 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(args) fname_in = arguments["-i"] fname_out = arguments["-o"] verbose = int(arguments['-v']) # Open file(s) im = Image(fname_in) data = im.data # 3d or 4d numpy array dim = im.dim # 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 '-bin' in arguments: bin_thr = arguments['-bin'] data_out = binarise(data, bin_thr=bin_thr) elif '-add' in arguments: from numpy import sum data2 = get_data_or_scalar(arguments["-add"], data) data_concat = concatenate_along_4th_dimension(data, data2) data_out = sum(data_concat, axis=3) elif '-sub' in arguments: data2 = get_data_or_scalar(arguments['-sub'], data) data_out = data - data2 elif "-laplacian" in arguments: sigmas = arguments["-laplacian"] if len(sigmas) == 1: sigmas = [sigmas for i in range(len(data.shape))] elif len(sigmas) != len(data.shape): printv(parser.usage.generate(error='ERROR: -laplacian need the same number of inputs as the number of image dimension OR only one input')) # adjust sigma based on voxel size sigmas = [sigmas[i] / dim[i+4] for i in range(3)] # smooth data data_out = laplacian(data, sigmas) elif '-mul' in arguments: from numpy import prod data2 = get_data_or_scalar(arguments["-mul"], data) data_concat = concatenate_along_4th_dimension(data, data2) data_out = prod(data_concat, axis=3) elif '-div' in arguments: from numpy import divide data2 = get_data_or_scalar(arguments["-div"], data) data_out = divide(data, data2) elif '-mean' in arguments: from numpy import mean dim = dim_list.index(arguments['-mean']) if dim+1 > len(np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = mean(data, dim) elif '-std' in arguments: from numpy import std dim = dim_list.index(arguments['-std']) if dim+1 > len(np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = std(data, dim) elif "-smooth" in arguments: sigmas = arguments["-smooth"] if len(sigmas) == 1: sigmas = [sigmas[0] for i in range(len(data.shape))] elif len(sigmas) != len(data.shape): printv(parser.usage.generate(error='ERROR: -smooth need the same number of inputs as the number of image dimension OR only one input')) # adjust sigma based on voxel size sigmas = [sigmas[i] / dim[i+4] for i in range(3)] # smooth data data_out = smooth(data, sigmas) elif '-dilate' in arguments: data_out = dilate(data, arguments['-dilate']) elif '-erode' in arguments: data_out = erode(data, arguments['-erode']) elif '-denoise' in arguments: # parse denoising arguments p, b = 1, 5 # default arguments list_denoise = arguments['-denoise'] for i in list_denoise: if 'p' in i: p = int(i.split('=')[1]) if 'b' in i: b = int(i.split('=')[1]) data_out = denoise_nlmeans(data, patch_radius=p, block_radius=b) elif '-symmetrize' in arguments: data_out = (data + data[range(data.shape[0]-1, -1, -1), :, :]) / float(2) elif '-mi' in arguments: # input 1 = from flag -i --> im # input 2 = from flag -mi im_2 = Image(arguments['-mi']) compute_similarity(im.data, im_2.data, fname_out, metric='mi', verbose=verbose) data_out=None elif '-corr' in arguments: # input 1 = from flag -i --> im # input 2 = from flag -mi im_2 = Image(arguments['-corr']) compute_similarity(im.data, im_2.data, fname_out, metric='corr', verbose=verbose) data_out=None # if no flag is set else: data_out = None printv(parser.usage.generate(error='ERROR: you need to specify an operation to do on the input image')) if data_out is not None: # Write output nii_out = Image(fname_in) # use header of input file nii_out.data = data_out nii_out.setFileName(fname_out) nii_out.save() # TODO: case of multiple outputs # assert len(data_out) == n_out # if n_in == n_out: # for im_in, d_out, fn_out in zip(nii, data_out, fname_out): # im_in.data = d_out # im_in.setFileName(fn_out) # if "-w" in arguments: # im_in.hdr.set_intent('vector', (), '') # im_in.save() # elif n_out == 1: # nii[0].data = data_out[0] # nii[0].setFileName(fname_out[0]) # if "-w" in arguments: # nii[0].hdr.set_intent('vector', (), '') # nii[0].save() # elif n_out > n_in: # for dat_out, name_out in zip(data_out, fname_out): # im_out = nii[0].copy() # im_out.data = dat_out # im_out.setFileName(name_out) # if "-w" in arguments: # im_out.hdr.set_intent('vector', (), '') # im_out.save() # else: # printv(parser.usage.generate(error='ERROR: not the correct numbers of inputs and outputs')) # display message if data_out is not None: printv('\nDone! To view results, type:', verbose) printv('fslview '+fname_out+' &\n', verbose, 'info') else: printv('\nDone! File created: '+fname_out, verbose, 'info')
#!/usr/bin/env python # change type of template data import os import commands import sys from shutil import move # Get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # Append path that contains scripts, to be able to load modules sys.path.append(path_sct + '/scripts') from msct_image import Image import sct_utils as sct path_template = '/Users/julien/data/PAM50/template' folder_PAM50 = 'PAM50/template/' os.chdir(path_template) sct.create_folder(folder_PAM50) for file_template in ['MNI-Poly-AMU_T1.nii.gz', 'MNI-Poly-AMU_T2.nii.gz', 'MNI-Poly-AMU_T2star.nii.gz']: im = Image(file_template) # remove negative values data = im.data data[data<0] = 0 im.data = data im.changeType('uint16') file_new = file_template.replace('MNI-Poly-AMU', 'PAM50') im.setFileName(file_new) im.save() # move to folder move(file_new, folder_PAM50+file_new)
def main(args=None): # initializations output_type = '' param = Param() dim_list = ['x', 'y', 'z', 't'] # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) fname_in = arguments["-i"] n_in = len(fname_in) verbose = int(arguments['-v']) if "-o" in arguments: fname_out = arguments["-o"] else: fname_out = None # Open file(s) # im_in_list = [Image(fn) for fn in fname_in] # run command if "-concat" in arguments: dim = arguments["-concat"] assert dim in dim_list dim = dim_list.index(dim) im_out = [concat_data(fname_in, dim)] # TODO: adapt to fname_in elif "-copy-header" in arguments: im_in = Image(fname_in[0]) im_dest = Image(arguments["-copy-header"]) im_out = [copy_header(im_in, im_dest)] elif '-display-warp' in arguments: im_in = fname_in[0] visualize_warp(im_in, fname_grid=None, step=3, rm_tmp=True) im_out = None elif "-getorient" in arguments: im_in = Image(fname_in[0]) orient = orientation(im_in, get=True, verbose=verbose) im_out = None elif '-keep-vol' in arguments: index_vol = arguments['-keep-vol'] im_in = Image(fname_in[0]) im_out = [remove_vol(im_in, index_vol, todo='keep')] elif '-mcs' in arguments: im_in = Image(fname_in[0]) if n_in != 1: printv( parser.usage.generate(error='ERROR: -mcs need only one input')) if len(im_in.data.shape) != 5: printv( parser.usage.generate( error='ERROR: -mcs input need to be a multi-component image' )) im_out = multicomponent_split(im_in) elif '-omc' in arguments: im_ref = Image(fname_in[0]) for fname in fname_in: im = Image(fname) if im.data.shape != im_ref.data.shape: printv( parser.usage.generate( error= 'ERROR: -omc inputs need to have all the same shapes')) del im im_out = [multicomponent_merge(fname_in)] # TODO: adapt to fname_in elif "-pad" in arguments: im_in = Image(fname_in[0]) ndims = len(im_in.getDataShape()) if ndims != 3: printv('ERROR: you need to specify a 3D input file.', 1, 'error') return pad_arguments = arguments["-pad"].split(',') if len(pad_arguments) != 3: printv('ERROR: you need to specify 3 padding values.', 1, 'error') padx, pady, padz = pad_arguments padx, pady, padz = int(padx), int(pady), int(padz) im_out = [ pad_image(im_in, pad_x_i=padx, pad_x_f=padx, pad_y_i=pady, pad_y_f=pady, pad_z_i=padz, pad_z_f=padz) ] elif "-pad-asym" in arguments: im_in = Image(fname_in[0]) ndims = len(im_in.getDataShape()) if ndims != 3: printv('ERROR: you need to specify a 3D input file.', 1, 'error') return pad_arguments = arguments["-pad-asym"].split(',') if len(pad_arguments) != 6: printv('ERROR: you need to specify 6 padding values.', 1, 'error') padxi, padxf, padyi, padyf, padzi, padzf = pad_arguments padxi, padxf, padyi, padyf, padzi, padzf = int(padxi), int(padxf), int( padyi), int(padyf), int(padzi), int(padzf) im_out = [ pad_image(im_in, pad_x_i=padxi, pad_x_f=padxf, pad_y_i=padyi, pad_y_f=padyf, pad_z_i=padzi, pad_z_f=padzf) ] elif '-remove-vol' in arguments: index_vol = arguments['-remove-vol'] im_in = Image(fname_in[0]) im_out = [remove_vol(im_in, index_vol, todo='remove')] elif "-setorient" in arguments: sct.printv(fname_in[0]) im_in = Image(fname_in[0]) im_out = [ orientation(im_in, ori=arguments["-setorient"], set=True, verbose=verbose, fname_out=fname_out) ] elif "-setorient-data" in arguments: im_in = Image(fname_in[0]) im_out = [ orientation(im_in, ori=arguments["-setorient-data"], set_data=True, verbose=verbose) ] elif "-split" in arguments: dim = arguments["-split"] assert dim in dim_list im_in = Image(fname_in[0]) dim = dim_list.index(dim) im_out = split_data(im_in, dim) elif '-type' in arguments: output_type = arguments['-type'] im_in = Image(fname_in[0]) im_out = [im_in] # TODO: adapt to fname_in else: im_out = None printv( parser.usage.generate( error= 'ERROR: you need to specify an operation to do on the input image' )) # in case fname_out is not defined, use first element of input file name list if fname_out == None: fname_out = fname_in[0] # Write output if im_out is not None: printv('Generate output files...', verbose) # if only one output if len(im_out) == 1 and not '-split' in arguments: im_out[0].setFileName(fname_out) if fname_out is not None else None im_out[0].save(squeeze_data=False, type=output_type, verbose=verbose) sct.display_viewer_syntax([fname_out], verbose=verbose) if '-mcs' in arguments: # use input file name and add _X, _Y _Z. Keep the same extension fname_out = [] for i_dim in range(3): fname_out.append( add_suffix(fname_in[0], '_' + dim_list[i_dim].upper())) im_out[i_dim].setFileName(fname_out[i_dim]) im_out[i_dim].save(verbose=verbose) sct.display_viewer_syntax(fname_out) if '-split' in arguments: # use input file name and add _"DIM+NUMBER". Keep the same extension l_fname_out = [] for i, im in enumerate(im_out): l_fname_out.append( add_suffix(fname_in[0], '_' + dim_list[dim].upper() + str(i).zfill(4))) im.setFileName(l_fname_out[i]) im.save(verbose=verbose) sct.display_viewer_syntax(l_fname_out) elif "-getorient" in arguments: sct.printv(orient) elif '-display-warp' in arguments: printv('Warping grid generated.', verbose, 'info')
def compute_csa(fname_segmentation, verbose, remove_temp_files, step, smoothing_param, figure_fit, file_csa_volume, slices, vert_levels, fname_vertebral_labeling='', algo_fitting = 'hanning', type_window = 'hanning', window_length = 80): # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S") + '_'+str(randint(1, 1000000)), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('sct_convert -i '+fname_segmentation+' -o '+path_tmp+'segmentation.nii.gz', verbose) # go to tmp folder os.chdir(path_tmp) # Change orientation of the input segmentation into RPI sct.printv('\nChange orientation to RPI...', verbose) sct.run('sct_image -i segmentation.nii.gz -setorient RPI -o segmentation_RPI.nii.gz', verbose) # Open segmentation volume sct.printv('\nOpen segmentation volume...', verbose) im_seg = Image('segmentation_RPI.nii.gz') data_seg = im_seg.data # hdr_seg = im_seg.hdr # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_seg.dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # # Extract min and max index in Z direction X, Y, Z = (data_seg > 0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline('segmentation_RPI.nii.gz', algo_fitting=algo_fitting, type_window=type_window, window_length=window_length, verbose=verbose) z_centerline_scaled = [x*pz for x in z_centerline] # Compute CSA sct.printv('\nCompute CSA...', verbose) # Empty arrays in which CSA for each z slice will be stored csa = np.zeros(max_z_index-min_z_index+1) for iz in xrange(min_z_index, max_z_index+1): # compute the vector normal to the plane normal = normalize(np.array([x_centerline_deriv[iz-min_z_index], y_centerline_deriv[iz-min_z_index], z_centerline_deriv[iz-min_z_index]])) # compute the angle between the normal vector of the plane and the vector z angle = np.arccos(np.dot(normal, [0, 0, 1])) # compute the number of voxels, assuming the segmentation is coded for partial volume effect between 0 and 1. number_voxels = np.sum(data_seg[:, :, iz]) # compute CSA, by scaling with voxel size (in mm) and adjusting for oblique plane csa[iz-min_z_index] = number_voxels * px * py * np.cos(angle) sct.printv('\nSmooth CSA across slices...', verbose) if smoothing_param: from msct_smooth import smoothing_window sct.printv('.. Hanning window: '+str(smoothing_param)+' mm', verbose) csa_smooth = smoothing_window(csa, window_len=smoothing_param/pz, window='hanning', verbose=0) # display figure if verbose == 2: import matplotlib.pyplot as plt plt.figure() pltx, = plt.plot(z_centerline_scaled, csa, 'bo') pltx_fit, = plt.plot(z_centerline_scaled, csa_smooth, 'r', linewidth=2) plt.title("Cross-sectional area (CSA)") plt.xlabel('z (mm)') plt.ylabel('CSA (mm^2)') plt.legend([pltx, pltx_fit], ['Raw', 'Smoothed']) plt.show() # update variable csa = csa_smooth else: sct.printv('.. No smoothing!', verbose) # Create output text file sct.printv('\nWrite text file...', verbose) file_results = open('csa.txt', 'w') for i in range(min_z_index, max_z_index+1): file_results.write(str(int(i)) + ',' + str(csa[i-min_z_index])+'\n') # Display results sct.printv('z='+str(i-min_z_index)+': '+str(csa[i-min_z_index])+' mm^2', verbose, 'bold') file_results.close() # output volume of csa values sct.printv('\nCreate volume of CSA values...', verbose) data_csa = data_seg.astype(np.float32, copy=False) # loop across slices for iz in range(min_z_index, max_z_index+1): # retrieve seg pixels x_seg, y_seg = (data_csa[:, :, iz] > 0).nonzero() seg = [[x_seg[i],y_seg[i]] for i in range(0, len(x_seg))] # loop across pixels in segmentation for i in seg: # replace value with csa value data_csa[i[0], i[1], iz] = csa[iz-min_z_index] # replace data im_seg.data = data_csa # set original orientation # TODO: FIND ANOTHER WAY!! # im_seg.change_orientation(orientation) --> DOES NOT WORK! # set file name -- use .gz because faster to write im_seg.setFileName('csa_volume_RPI.nii.gz') im_seg.changeType('float32') # save volume im_seg.save() # get orientation of the input data im_seg_original = Image('segmentation.nii.gz') orientation = im_seg_original.orientation sct.run('sct_image -i csa_volume_RPI.nii.gz -setorient '+orientation+' -o '+file_csa_volume) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) copyfile(path_tmp+'csa.txt', path_data+param.fname_csa) # sct.generate_output_file(path_tmp+'csa.txt', path_data+param.fname_csa) # extension already included in param.fname_csa sct.generate_output_file(path_tmp+file_csa_volume, path_data+file_csa_volume) # extension already included in name_output # average csa across vertebral levels or slices if asked (flag -z or -l) if slices or vert_levels: from sct_extract_metric import save_metrics warning = '' if vert_levels and not fname_vertebral_labeling: sct.printv('\nERROR: Vertebral labeling file is missing. See usage.\n', 1, 'error') elif vert_levels and fname_vertebral_labeling: # from sct_extract_metric import get_slices_matching_with_vertebral_levels sct.printv('\tSelected vertebral levels... '+vert_levels) # convert the vertebral labeling file to RPI orientation im_vertebral_labeling = set_orientation(Image(fname_vertebral_labeling), 'RPI', fname_out=path_tmp+'vertebral_labeling_RPI.nii') # get the slices corresponding to the vertebral levels # slices, vert_levels_list, warning = get_slices_matching_with_vertebral_levels(data_seg, vert_levels, im_vertebral_labeling.data, 1) slices, vert_levels_list, warning = get_slices_matching_with_vertebral_levels_based_centerline(vert_levels, im_vertebral_labeling.data, x_centerline_fit, y_centerline_fit, z_centerline) elif not vert_levels: vert_levels_list = [] sct.printv('Average CSA across slices...', type='info') # parse the selected slices slices_lim = slices.strip().split(':') slices_list = range(int(slices_lim[0]), int(slices_lim[1])+1) CSA_for_selected_slices = [] # Read the file csa.txt and get the CSA for the selected slices with open(path_data+param.fname_csa) as openfile: for line in openfile: line_split = line.strip().split(',') if int(line_split[0]) in slices_list: CSA_for_selected_slices.append(float(line_split[1])) # average the CSA mean_CSA = np.mean(np.asarray(CSA_for_selected_slices)) std_CSA = np.std(np.asarray(CSA_for_selected_slices)) sct.printv('Mean CSA: '+str(mean_CSA)+' +/- '+str(std_CSA)+' mm^2', type='info') # write result into output file save_metrics([0], [file_data], slices, [mean_CSA], [std_CSA], path_data + 'csa_mean.txt', path_data+file_csa_volume, 'nb_voxels x px x py x cos(theta) slice-by-slice (in mm^3)', '', actual_vert=vert_levels_list, warning_vert_levels=warning) # compute volume between the selected slices sct.printv('Compute the volume in between the selected slices...', type='info') nb_vox = np.sum(data_seg[:, :, slices_list]) volume = nb_vox*px*py*pz sct.printv('Volume in between the selected slices: '+str(volume)+' mm^3', type='info') # write result into output file save_metrics([0], [file_data], slices, [volume], [np.nan], path_data + 'volume.txt', path_data+file_data, 'nb_voxels x px x py x pz (in mm^3)', '', actual_vert=vert_levels_list, warning_vert_levels=warning) # Remove temporary files if remove_temp_files: sct.printv('\nRemove temporary files...') sct.run('rm -rf '+path_tmp, error_exit='warning')
def 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(): # 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')
zsum = np.sum(np.sum(data_cord, 0), 0) zmin_cord = np.min(np.nonzero(zsum)) zmax_cord = np.max(np.nonzero(zsum)) # duplicate WM and GM atlas towards the top and bottom slices to match the cord template # bottom slices for iz in range(zmin_cord, zmin_wm): data_wm[:, :, iz] = data_wm[:, :, zmin_wm] data_gm[:, :, iz] = data_gm[:, :, zmin_wm] # top slices for iz in range(zmax_wm, zmax_cord): data_wm[:, :, iz] = data_wm[:, :, zmax_wm] data_gm[:, :, iz] = data_gm[:, :, zmax_wm] # save modified atlases im_wm.setFileName('wm_ext.nii.gz') im_wm.data = data_wm im_wm.save() im_gm.setFileName('gm_ext.nii.gz') im_gm.data = data_gm im_gm.save() # sum modified wm/gm data_wmgm = data_wm + data_gm # save wm/gm im_wm.setFileName('wmgm_ext.nii.gz') im_wm.data = data_wmgm im_wm.save() # register wmgm --> cord
def interpolate_im_to_ref(im_input, im_input_sc, new_res=0.3, sq_size_size_mm=22.5, interpolation_mode=3): nx, ny, nz, nt, px, py, pz, pt = im_input.dim im_input_sc = im_input_sc.copy() im_input= im_input.copy() # keep only spacing and origin in qform to avoid rotation issues input_qform = im_input.hdr.get_qform() for i in range(4): for j in range(4): if i!=j and j!=3: input_qform[i, j] = 0 im_input.hdr.set_qform(input_qform) im_input.hdr.set_sform(input_qform) im_input_sc.hdr = im_input.hdr sq_size = int(sq_size_size_mm/new_res) # create a reference image : square of ones im_ref = Image(np.ones((sq_size, sq_size, 1), dtype=np.int), dim=(sq_size, sq_size, 1, 0, new_res, new_res, pz, 0), orientation='RPI') # copy input qform matrix to reference image im_ref.hdr.set_qform(im_input.hdr.get_qform()) im_ref.hdr.set_sform(im_input.hdr.get_sform()) # set correct header to reference image im_ref.hdr.set_data_shape((sq_size, sq_size, 1)) im_ref.hdr.set_zooms((new_res, new_res, pz)) # save image to set orientation to RPI (not properly done at the creation of the image) fname_ref = 'im_ref.nii.gz' im_ref.setFileName(fname_ref) im_ref.save() im_ref = set_orientation(im_ref, 'RPI', fname_out=fname_ref) # set header origin to zero to get physical coordinates of the center of the square im_ref.hdr.as_analyze_map()['qoffset_x'] = 0 im_ref.hdr.as_analyze_map()['qoffset_y'] = 0 im_ref.hdr.as_analyze_map()['qoffset_z'] = 0 im_ref.hdr.set_sform(im_ref.hdr.get_qform()) im_ref.hdr.set_qform(im_ref.hdr.get_qform()) [[x_square_center_phys, y_square_center_phys, z_square_center_phys]] = im_ref.transfo_pix2phys(coordi=[[int(sq_size / 2), int(sq_size / 2), 0]]) list_interpolate_images = [] # iterate on z dimension of input image for iz in range(nz): # copy reference image: one reference image per slice im_ref_slice_iz = im_ref.copy() # get center of mass of SC for slice iz x_seg, y_seg = (im_input_sc.data[:, :, iz] > 0).nonzero() x_center, y_center = np.mean(x_seg), np.mean(y_seg) [[x_center_phys, y_center_phys, z_center_phys]] = im_input_sc.transfo_pix2phys(coordi=[[x_center, y_center, iz]]) # center reference image on SC for slice iz im_ref_slice_iz.hdr.as_analyze_map()['qoffset_x'] = x_center_phys - x_square_center_phys im_ref_slice_iz.hdr.as_analyze_map()['qoffset_y'] = y_center_phys - y_square_center_phys im_ref_slice_iz.hdr.as_analyze_map()['qoffset_z'] = z_center_phys im_ref_slice_iz.hdr.set_sform(im_ref_slice_iz.hdr.get_qform()) im_ref_slice_iz.hdr.set_qform(im_ref_slice_iz.hdr.get_qform()) # interpolate input image to reference image im_input_interpolate_iz = im_input.interpolate_from_image(im_ref_slice_iz, interpolation_mode=interpolation_mode, border='nearest') # reshape data to 2D if needed if len(im_input_interpolate_iz.data.shape) == 3: im_input_interpolate_iz.data = im_input_interpolate_iz.data.reshape(im_input_interpolate_iz.data.shape[:-1]) # add slice to list list_interpolate_images.append(im_input_interpolate_iz) return list_interpolate_images
def compute_dti(fname_in, fname_bvals, fname_bvecs, prefix, method, file_mask): """ Compute DTI. :param fname_in: input 4d file. :param bvals: bvals txt file :param bvecs: bvecs txt file :param prefix: output prefix. Example: "dti_" :param method: algo for computing 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. if not file_mask == '': printv('Open mask file...', param.verbose) # open mask file nii_mask = Image(file_mask) mask = nii_mask.data # fit tensor model printv('Computing tensor using "' + method + '" method...', param.verbose) import dipy.reconst.dti as dti if method == 'standard': tenmodel = dti.TensorModel(gtab) if file_mask == '': tenfit = tenmodel.fit(data) else: tenfit = tenmodel.fit(data, mask) elif method == 'restore': import dipy.denoise.noise_estimate as ne sigma = ne.estimate_sigma(data) dti_restore = dti.TensorModel(gtab, fit_method='RESTORE', sigma=sigma) if file_mask == '': tenfit = dti_restore.fit(data) else: tenfit = dti_restore.fit(data, mask) # 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 extract_centerline(fname_segmentation, remove_temp_files, verbose = 0, algo_fitting = 'hanning', type_window = 'hanning', window_length = 80): # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S") + '_'+str(randint(1, 1000000)), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying data to tmp folder...', verbose) sct.run('sct_convert -i '+fname_segmentation+' -o '+path_tmp+'segmentation.nii.gz', verbose) # go to tmp folder os.chdir(path_tmp) # Change orientation of the input centerline into RPI sct.printv('\nOrient centerline to RPI orientation...', verbose) # fname_segmentation_orient = 'segmentation_RPI.nii.gz' # BELOW DOES NOT WORK (JULIEN, 2015-10-17) # im_seg = Image(file_data+ext_data) # set_orientation(im_seg, 'RPI') # im_seg.setFileName(fname_segmentation_orient) # im_seg.save() sct.run('sct_image -i segmentation.nii.gz -setorient RPI -o segmentation_RPI.nii.gz', verbose) # Open segmentation volume sct.printv('\nOpen segmentation volume...', verbose) im_seg = Image('segmentation_RPI.nii.gz') data = im_seg.data # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_seg.dim sct.printv('.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz), verbose) sct.printv('.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm', verbose) # # Get dimension # sct.printv('\nGet dimensions...', verbose) # nx, ny, nz, nt, px, py, pz, pt = im_seg.dim # # # Extract orientation of the input segmentation # orientation = get_orientation(im_seg) # sct.printv('\nOrientation of segmentation image: ' + orientation, verbose) # # sct.printv('\nOpen segmentation volume...', verbose) # data = im_seg.data # hdr = im_seg.hdr # Extract min and max index in Z direction X, Y, Z = (data>0).nonzero() min_z_index, max_z_index = min(Z), max(Z) x_centerline = [0 for i in range(0,max_z_index-min_z_index+1)] y_centerline = [0 for i in range(0,max_z_index-min_z_index+1)] z_centerline = [iz for iz in range(min_z_index, max_z_index+1)] # Extract segmentation points and average per slice for iz in range(min_z_index, max_z_index+1): x_seg, y_seg = (data[:,:,iz]>0).nonzero() x_centerline[iz-min_z_index] = np.mean(x_seg) y_centerline[iz-min_z_index] = np.mean(y_seg) for k in range(len(X)): data[X[k], Y[k], Z[k]] = 0 # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline_fit, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline('segmentation_RPI.nii.gz', type_window = type_window, window_length = window_length, algo_fitting = algo_fitting, verbose = verbose) if verbose == 2: import matplotlib.pyplot as plt #Creation of a vector x that takes into account the distance between the labels nz_nonz = len(z_centerline) x_display = [0 for i in range(x_centerline_fit.shape[0])] y_display = [0 for i in range(y_centerline_fit.shape[0])] for i in range(0, nz_nonz, 1): x_display[int(z_centerline[i]-z_centerline[0])] = x_centerline[i] y_display[int(z_centerline[i]-z_centerline[0])] = y_centerline[i] plt.figure(1) plt.subplot(2,1,1) plt.plot(z_centerline_fit, x_display, 'ro') plt.plot(z_centerline_fit, x_centerline_fit) plt.xlabel("Z") plt.ylabel("X") plt.title("x and x_fit coordinates") plt.subplot(2,1,2) plt.plot(z_centerline_fit, y_display, 'ro') plt.plot(z_centerline_fit, y_centerline_fit) plt.xlabel("Z") plt.ylabel("Y") plt.title("y and y_fit coordinates") plt.show() # Create an image with the centerline for iz in range(min_z_index, max_z_index+1): data[round(x_centerline_fit[iz-min_z_index]), round(y_centerline_fit[iz-min_z_index]), iz] = 1 # if index is out of bounds here for hanning: either the segmentation has holes or labels have been added to the file # Write the centerline image in RPI orientation # hdr.set_data_dtype('uint8') # set imagetype to uint8 sct.printv('\nWrite NIFTI volumes...', verbose) im_seg.data = data im_seg.setFileName('centerline_RPI.nii.gz') im_seg.changeType('uint8') im_seg.save() sct.printv('\nSet to original orientation...', verbose) # get orientation of the input data im_seg_original = Image('segmentation.nii.gz') orientation = im_seg_original.orientation sct.run('sct_image -i centerline_RPI.nii.gz -setorient '+orientation+' -o centerline.nii.gz') # create a txt file with the centerline name_output_txt = 'centerline.txt' sct.printv('\nWrite text file...', verbose) file_results = open(name_output_txt, 'w') for i in range(min_z_index, max_z_index+1): file_results.write(str(int(i)) + ' ' + str(x_centerline_fit[i-min_z_index]) + ' ' + str(y_centerline_fit[i-min_z_index]) + '\n') file_results.close() # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp+'centerline.nii.gz', file_data+'_centerline.nii.gz') sct.generate_output_file(path_tmp+'centerline.txt', file_data+'_centerline.txt') # Remove temporary files if remove_temp_files: sct.printv('\nRemove temporary files...', verbose) sct.run('rm -rf '+path_tmp, verbose) return file_data+'_centerline.nii.gz'
class MultiLabelRegistration: def __init__(self, fname_gm, fname_wm, path_template, fname_warp_template2target, param=None, fname_warp_target2template=None, apply_warp_template=0, fname_template_dest=None): if param is None: self.param = Param() else: self.param = param self.im_gm = Image(fname_gm) self.im_wm = Image(fname_wm) self.path_template = sct.slash_at_the_end(path_template, 1) # get GM and WM files from template: fname_template_gm, fname_template_wm = None, None for fname in os.listdir(self.path_template + 'template/'): if 'gm' in fname.lower(): fname_template_gm = self.path_template + 'template/' + fname elif 'wm' in fname.lower(): fname_template_wm = self.path_template + 'template/' + fname if fname_template_gm is not None and fname_template_wm is not None: self.im_template_gm = Image(fname_template_gm) self.im_template_wm = Image(fname_template_wm) if fname_template_gm.split('/')[-1] == 'MNI-Poly-AMU_GM.nii.gz': self.template = 'MNI-Poly-AMU' elif fname_template_gm.split('/')[-1] == 'PAM50_gm.nii.gz': self.template = 'PAM50' else: self.template = 'custom' # template file in its original space: self.fname_template_dest = fname_template_dest # Previous warping fields: self.fname_warp_template2target = fname_warp_template2target self.fname_warp_target2template = fname_warp_target2template # new warping fields: self.fname_warp_template2gm = '' self.fname_warp_gm2template = '' # temporary fix - related to issue #871 self.apply_warp_template = apply_warp_template def register(self): # accentuate separation WM/GM self.im_gm = thr_im(self.im_gm, 0.01, self.param.thr) self.im_wm = thr_im(self.im_wm, 0.01, self.param.thr) self.im_template_gm = thr_im(self.im_template_gm, 0.01, self.param.thr) self.im_template_wm = thr_im(self.im_template_wm, 0.01, self.param.thr) # create multilabel images: # copy GM images to keep header information im_automatic_ml = self.im_gm.copy() im_template_ml = self.im_template_gm.copy() # create multi-label segmentation with GM*200 + WM*100 (100 and 200 encoded in self.param.gap) im_automatic_ml.data = self.param.gap[ 1] * self.im_gm.data + self.param.gap[0] * self.im_wm.data im_template_ml.data = self.param.gap[ 1] * self.im_template_gm.data + self.param.gap[ 0] * self.im_template_wm.data # set new names fname_automatic_ml = 'multilabel_automatic_seg.nii.gz' fname_template_ml = 'multilabel_template_seg.nii.gz' im_automatic_ml.setFileName(fname_automatic_ml) im_template_ml.setFileName(fname_template_ml) # Create temporary folder and put files in it tmp_dir = sct.tmp_create() path_gm, file_gm, ext_gm = sct.extract_fname(fname_gm) path_warp_template2target, file_warp_template2target, ext_warp_template2target = sct.extract_fname( self.fname_warp_template2target) convert(fname_gm, tmp_dir + file_gm + ext_gm) convert(fname_warp_template, tmp_dir + file_warp_template2target + ext_warp_template2target, squeeze_data=0) if self.fname_warp_target2template is not None: path_warp_target2template, file_warp_target2template, ext_warp_target2template = sct.extract_fname( self.fname_warp_target2template) convert(self.fname_warp_target2template, tmp_dir + file_warp_target2template + ext_warp_target2template, squeeze_data=0) os.chdir(tmp_dir) # save images im_automatic_ml.save() im_template_ml.save() # apply template2image warping field if self.apply_warp_template == 1: fname_template_ml_new = sct.add_suffix(fname_template_ml, '_r') sct.run('sct_apply_transfo -i ' + fname_template_ml + ' -d ' + fname_automatic_ml + ' -w ' + file_warp_template2target + ext_warp_template2target + ' -o ' + fname_template_ml_new) fname_template_ml = fname_template_ml_new nx, ny, nz, nt, px, py, pz, pt = im_automatic_ml.dim size_mask = int(22.5 / px) fname_mask = 'square_mask.nii.gz' sct.run('sct_create_mask -i ' + fname_automatic_ml + ' -p centerline,' + fname_automatic_ml + ' -f box -size ' + str(size_mask) + ' -o ' + fname_mask) fname_automatic_ml, xi, xf, yi, yf, zi, zf = crop_im( fname_automatic_ml, fname_mask) fname_template_ml, xi, xf, yi, yf, zi, zf = crop_im( fname_template_ml, fname_mask) # fname_automatic_ml_smooth = sct.add_suffix(fname_automatic_ml, '_smooth') # sct.run('sct_maths -i '+fname_automatic_ml+' -smooth '+str(self.param.smooth)+','+str(self.param.smooth)+',0 -o '+fname_automatic_ml_smooth) # fname_automatic_ml = fname_automatic_ml_smooth path_automatic_ml, file_automatic_ml, ext_automatic_ml = sct.extract_fname( fname_automatic_ml) path_template_ml, file_template_ml, ext_template_ml = sct.extract_fname( fname_template_ml) # Register multilabel images together cmd_reg = 'sct_register_multimodal -i ' + fname_template_ml + ' -d ' + fname_automatic_ml + ' -param ' + self.param.param_reg if 'centermass' in self.param.param_reg: fname_template_ml_seg = sct.add_suffix(fname_template_ml, '_bin') sct.run('sct_maths -i ' + fname_template_ml + ' -bin 0 -o ' + fname_template_ml_seg) fname_automatic_ml_seg = sct.add_suffix(fname_automatic_ml, '_bin') # sct.run('sct_maths -i '+fname_automatic_ml+' -thr 50 -o '+fname_automatic_ml_seg) sct.run('sct_maths -i ' + fname_automatic_ml + ' -bin 50 -o ' + fname_automatic_ml_seg) cmd_reg += ' -iseg ' + fname_template_ml_seg + ' -dseg ' + fname_automatic_ml_seg sct.run(cmd_reg) fname_warp_multilabel_template2auto = 'warp_' + file_template_ml + '2' + file_automatic_ml + '.nii.gz' fname_warp_multilabel_auto2template = 'warp_' + file_automatic_ml + '2' + file_template_ml + '.nii.gz' self.fname_warp_template2gm = sct.extract_fname( self.fname_warp_template2target )[1] + '_reg_gm' + sct.extract_fname( self.fname_warp_template2target)[2] # fname_warp_multilabel_template2auto = pad_im(fname_warp_multilabel_template2auto, nx, ny, nz, xi, xf, yi, yf, zi, zf) # fname_warp_multilabel_auto2template = pad_im(fname_warp_multilabel_auto2template, nx, ny, nz, xi, xf, yi, yf, zi, zf) sct.run('sct_concat_transfo -w ' + file_warp_template2target + ext_warp_template2target + ',' + fname_warp_multilabel_template2auto + ' -d ' + file_gm + ext_gm + ' -o ' + self.fname_warp_template2gm) if self.fname_warp_target2template is not None: if self.fname_template_dest is None: path_script = os.path.dirname(__file__) path_sct = os.path.dirname(path_script) if self.template == 'MNI-Poly-AMU': self.fname_template_dest = path_sct + '/data/MNI-Poly-AMU/template/MNI-Poly-AMU_T2.nii.gz' elif self.template == 'PAM50': self.fname_template_dest = path_sct + '/data/PAM50/template/PAM50_t2.nii.gz' self.fname_warp_gm2template = sct.extract_fname( self.fname_warp_target2template )[1] + '_reg_gm' + sct.extract_fname( self.fname_warp_target2template)[2] sct.run('sct_concat_transfo -w ' + fname_warp_multilabel_auto2template + ',' + file_warp_target2template + ext_warp_target2template + ' -d ' + self.fname_template_dest + ' -o ' + self.fname_warp_gm2template) os.chdir('..') # sct.generate_output_file(tmp_dir+fname_warp_multilabel_template2auto, self.param.output_folder+'warp_template_multilabel2automatic_seg_multilabel.nii.gz') # sct.generate_output_file(tmp_dir+fname_warp_multilabel_auto2template, self.param.output_folder+'warp_automatic_seg_multilabel2template_multilabel.nii.gz') sct.generate_output_file( tmp_dir + self.fname_warp_template2gm, self.param.output_folder + self.fname_warp_template2gm) if self.fname_warp_target2template is not None: sct.generate_output_file( tmp_dir + self.fname_warp_gm2template, self.param.output_folder + self.fname_warp_gm2template) if self.param.qc: fname_grid_warped = visualize_warp( tmp_dir + fname_warp_multilabel_template2auto, rm_tmp=self.param.remove_tmp) path_grid_warped, file_grid_warped, ext_grid_warped = sct.extract_fname( fname_grid_warped) sct.generate_output_file( fname_grid_warped, self.param.output_folder + file_grid_warped + ext_grid_warped) if self.param.remove_tmp: sct.run('rm -rf ' + tmp_dir, error_exit='warning') def validation(self, fname_manual_gmseg, fname_sc_seg): path_manual_gmseg, file_manual_gmseg, ext_manual_gmseg = sct.extract_fname( fname_manual_gmseg) path_sc_seg, file_sc_seg, ext_sc_seg = sct.extract_fname(fname_sc_seg) # Create tmp folder and copy files in it tmp_dir = sct.tmp_create() sct.run('cp ' + fname_manual_gmseg + ' ' + tmp_dir + file_manual_gmseg + ext_manual_gmseg) sct.run('cp ' + fname_sc_seg + ' ' + tmp_dir + file_sc_seg + ext_sc_seg) sct.run('cp ' + self.param.output_folder + self.fname_warp_template2gm + ' ' + tmp_dir + self.fname_warp_template2gm) os.chdir(tmp_dir) sct.run('sct_warp_template -d ' + fname_manual_gmseg + ' -w ' + self.fname_warp_template2gm + ' -qc 0 -a 0') if 'MNI-Poly-AMU_GM.nii.gz' in os.listdir('label/template/'): im_new_template_gm = Image('label/template/MNI-Poly-AMU_GM.nii.gz') im_new_template_wm = Image('label/template/MNI-Poly-AMU_WM.nii.gz') else: im_new_template_gm = Image('label/template/PAM50_gm.nii.gz') im_new_template_wm = Image('label/template/PAM50_wm.nii.gz') im_new_template_gm = thr_im(im_new_template_gm, self.param.thr, self.param.thr) im_new_template_wm = thr_im(im_new_template_wm, self.param.thr, self.param.thr) self.im_template_gm = thr_im(self.im_template_gm, self.param.thr, self.param.thr) self.im_template_wm = thr_im(self.im_template_wm, self.param.thr, self.param.thr) fname_new_template_gm = 'new_template_gm.nii.gz' im_new_template_gm.setFileName(fname_new_template_gm) im_new_template_gm.save() fname_new_template_wm = 'new_template_wm.nii.gz' im_new_template_wm.setFileName(fname_new_template_wm) im_new_template_wm.save() fname_old_template_wm = 'old_template_wm.nii.gz' self.im_template_wm.setFileName(fname_old_template_wm) self.im_template_wm.save() fname_old_template_gm = 'old_template_gm.nii.gz' self.im_template_gm.setFileName(fname_old_template_gm) self.im_template_gm.save() fname_manual_wmseg = 'target_manual_wmseg.nii.gz' sct.run('sct_maths -i ' + file_sc_seg + ext_sc_seg + ' -sub ' + file_manual_gmseg + ext_manual_gmseg + ' -o ' + fname_manual_wmseg) # Compute Hausdorff distance status, output_old_hd = sct.run('sct_compute_hausdorff_distance -i ' + fname_old_template_gm + ' -r ' + file_manual_gmseg + ext_manual_gmseg + ' -t 1 -v 1') status, output_new_hd = sct.run('sct_compute_hausdorff_distance -i ' + fname_new_template_gm + ' -r ' + file_manual_gmseg + ext_manual_gmseg + ' -t 1 -v 1') hd_name = 'hd_md_multilabel_reg.txt' hd_fic = open(hd_name, 'w') hd_fic.write( 'The "diff" columns are comparisons between regular template registration and corrected template registration according to SC internal structure\n' 'Diff = metric_regular_reg - metric_corrected_reg\n') hd_fic.write('#Slice, HD, HD diff, MD, MD diff\n') no_ref_slices = [] init_hd = "Hausdorff's distance - First relative Hausdorff's distance median - Second relative Hausdorff's distance median(all in mm)\n" old_gm_hd = output_old_hd[output_old_hd.find(init_hd) + len(init_hd):].split('\n') new_gm_hd = output_new_hd[output_new_hd.find(init_hd) + len(init_hd):].split('\n') for i in range(len(old_gm_hd) - 3): # last two lines are informations i_old, val_old = old_gm_hd[i].split(':') i_new, val_new = new_gm_hd[i].split(':') i_old = int(i_old[-2:]) i_new = int(i_new[-2:]) assert i == i_old == i_new, 'ERROR: when comparing Hausdorff distances, slice numbers differs.' hd_old, med1_old, med2_old = val_old.split('-') hd_new, med1_new, med2_new = val_new.split('-') if float(hd_old) == 0.0: no_ref_slices.append(i) hd_fic.write(str(i) + ', NO MANUAL SEGMENTATION\n') else: md_new = max(float(med1_new), float(med2_new)) md_old = max(float(med1_old), float(med2_old)) hd_fic.write( str(i) + ', ' + hd_new + ', ' + str(float(hd_old) - float(hd_new)) + ', ' + str(md_new) + ', ' + str(md_old - md_new) + '\n') hd_fic.close() # Compute Dice coefficient # --- DC old template try: status_old_gm, output_old_gm = sct.run( 'sct_dice_coefficient -i ' + file_manual_gmseg + ext_manual_gmseg + ' -d ' + fname_old_template_gm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: corrected_manual_gmseg = file_manual_gmseg + '_in_old_template_space' + ext_manual_gmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_old_template_gm + ',' + file_manual_gmseg + ext_manual_gmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_gmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_gmseg+' -thr 0.1 -o '+corrected_manual_gmseg) sct.run('sct_maths -i ' + corrected_manual_gmseg + ' -bin 0.1 -o ' + corrected_manual_gmseg) status_old_gm, output_old_gm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_gmseg + ' -d ' + fname_old_template_gm + ' -2d-slices 2', error_exit='warning') try: status_old_wm, output_old_wm = sct.run( 'sct_dice_coefficient -i ' + fname_manual_wmseg + ' -d ' + fname_old_template_wm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: path_manual_wmseg, file_manual_wmseg, ext_manual_wmseg = sct.extract_fname( fname_manual_wmseg) corrected_manual_wmseg = file_manual_wmseg + '_in_old_template_space' + ext_manual_wmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_old_template_wm + ',' + fname_manual_wmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_wmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_wmseg+' -thr 0.1 -o '+corrected_manual_wmseg) sct.run('sct_maths -i ' + corrected_manual_wmseg + ' -bin 0.1 -o ' + corrected_manual_wmseg) status_old_wm, output_old_wm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_wmseg + ' -d ' + fname_old_template_wm + ' -2d-slices 2', error_exit='warning') # --- DC new template try: status_new_gm, output_new_gm = sct.run( 'sct_dice_coefficient -i ' + file_manual_gmseg + ext_manual_gmseg + ' -d ' + fname_new_template_gm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: corrected_manual_gmseg = file_manual_gmseg + '_in_new_template_space' + ext_manual_gmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_new_template_gm + ',' + file_manual_gmseg + ext_manual_gmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_gmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_gmseg+' -thr 0.1 -o '+corrected_manual_gmseg) sct.run('sct_maths -i ' + corrected_manual_gmseg + ' -bin 0.1 -o ' + corrected_manual_gmseg) status_new_gm, output_new_gm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_gmseg + ' -d ' + fname_new_template_gm + ' -2d-slices 2', error_exit='warning') try: status_new_wm, output_new_wm = sct.run( 'sct_dice_coefficient -i ' + fname_manual_wmseg + ' -d ' + fname_new_template_wm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: path_manual_wmseg, file_manual_wmseg, ext_manual_wmseg = sct.extract_fname( fname_manual_wmseg) corrected_manual_wmseg = file_manual_wmseg + '_in_new_template_space' + ext_manual_wmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_new_template_wm + ',' + fname_manual_wmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_wmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_wmseg+' -thr 0.1 -o '+corrected_manual_wmseg) sct.run('sct_maths -i ' + corrected_manual_wmseg + ' -bin 0.1 -o ' + corrected_manual_wmseg) status_new_wm, output_new_wm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_wmseg + ' -d ' + fname_new_template_wm + ' -2d-slices 2', error_exit='warning') dice_name = 'dice_multilabel_reg.txt' dice_fic = open(dice_name, 'w') dice_fic.write( 'The "diff" columns are comparisons between regular template registration and corrected template registration according to SC internal structure\n' 'Diff = metric_corrected_reg - metric_regular_reg\n') dice_fic.write('#Slice, WM DC, WM diff, GM DC, GM diff\n') init_dc = '2D Dice coefficient by slice:\n' old_gm_dc = output_old_gm[output_old_gm.find(init_dc) + len(init_dc):].split('\n') old_wm_dc = output_old_wm[output_old_wm.find(init_dc) + len(init_dc):].split('\n') new_gm_dc = output_new_gm[output_new_gm.find(init_dc) + len(init_dc):].split('\n') new_wm_dc = output_new_wm[output_new_wm.find(init_dc) + len(init_dc):].split('\n') for i in range(len(old_gm_dc)): if i not in no_ref_slices: i_new_gm, val_new_gm = new_gm_dc[i].split(' ') i_new_wm, val_new_wm = new_wm_dc[i].split(' ') i_old_gm, val_old_gm = old_gm_dc[i].split(' ') i_old_wm, val_old_wm = old_wm_dc[i].split(' ') assert i == int(i_new_gm) == int(i_new_wm) == int( i_old_gm ) == int( i_old_wm ), 'ERROR: when comparing Dice coefficients, slice numbers differs.' dice_fic.write( str(i) + ', ' + val_new_wm + ', ' + str(float(val_new_wm) - float(val_old_wm)) + ', ' + val_new_gm + ', ' + str(float(val_new_gm) - float(val_old_gm)) + '\n') else: dice_fic.write(str(i) + ', NO MANUAL SEGMENTATION\n') dice_fic.close() os.chdir('..') sct.generate_output_file(tmp_dir + hd_name, self.param.output_folder + hd_name) sct.generate_output_file(tmp_dir + dice_name, self.param.output_folder + dice_name) if self.param.remove_tmp: sct.run('rm -rf ' + tmp_dir, error_exit='warning')
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)