def __init__(self, fname_im, contrast, fname_seg, path_out, verbose): self.fname_im = fname_im self.contrast = contrast self.fname_seg = fname_seg self.path_out = path_out self.verbose = verbose self.tmp_dir = sct.tmp_create( verbose=self.verbose) # path to tmp directory self.orientation_im = get_orientation(Image( self.fname_im)) # to re-orient the data at the end self.slice2D_im = sct.extract_fname( self.fname_im )[1] + '_midSag.nii' # file used to do the detection, with only one slice self.dection_map_pmj = sct.extract_fname( self.fname_im)[1] + '_map_pmj' # file resulting from the detection # path to the pmj detector path_sct = os.environ.get("SCT_DIR", os.path.dirname(os.path.dirname(__file__))) self.pmj_model = os.path.join(path_sct, 'data', 'pmj_models', '{}_model'.format(self.contrast)) self.threshold = -0.75 if self.contrast == 't1' else 0.8 # detection map threshold, depends on the contrast self.fname_out = sct.extract_fname(self.fname_im)[1] + '_pmj.nii.gz' self.fname_qc = 'qc_pmj.png'
def check_if_rpi(fname): from sct_image import get_orientation if not get_orientation(fname, filename=True) == 'RPI': printv( '\nERROR: ' + fname + ' is not in RPI orientation. Use sct_image -setorient to reorient your data. Exit program.\n', 1, 'error')
def __init__(self, param=None, param_glcm=None): self.param = param if param is not None else Param() self.param_glcm = param_glcm if param_glcm is not None else ParamGLCM() # create tmp directory self.tmp_dir = tmp_create(verbose=self.param.verbose) # path to tmp directory if self.param.dim == 'ax': self.orientation_extraction = 'RPI' elif self.param.dim == 'sag': self.orientation_extraction = 'IPR' else: self.orientation_extraction = 'IRP' # metric_lst=['property_distance_angle'] self.metric_lst = [] for m in list(itertools.product(self.param_glcm.feature.split(','), self.param_glcm.angle.split(','))): text_name = m[0] if m[0].upper() != 'asm'.upper() else m[0].upper() self.metric_lst.append(text_name + '_' + str(self.param_glcm.distance) + '_' + str(m[1])) # dct_im_seg{'im': list_of_axial_slice, 'seg': list_of_axial_masked_slice} self.dct_im_seg = {'im': None, 'seg': None} # to re-orient the data at the end if needed self.orientation_im = get_orientation(Image(self.param.fname_im)) self.fname_metric_lst = {}
def resample_image(fname, suffix='_resampled.nii.gz', binary=False, npx=0.3, npy=0.3, thr=0.0, interpolation='spline'): """ Resampling function: add a padding, resample, crop the padding :param fname: name of the image file to be resampled :param suffix: suffix added to the original fname after resampling :param binary: boolean, image is binary or not :param npx: new pixel size in the x direction :param npy: new pixel size in the y direction :param thr: if the image is binary, it will be thresholded at thr (default=0) after the resampling :param interpolation: type of interpolation used for the resampling :return: file name after resampling (or original fname if it was already in the correct resolution) """ im_in = Image(fname) orientation = get_orientation(im_in) if orientation != 'RPI': im_in = set_orientation(im_in, 'RPI') im_in.save() fname = im_in.absolutepath nx, ny, nz, nt, px, py, pz, pt = im_in.dim if round(px, 2) != round(npx, 2) or round(py, 2) != round(npy, 2): name_resample = sct.extract_fname(fname)[1] + suffix if binary: interpolation = 'nn' if nz == 1: # when data is 2d: we convert it to a 3d image in order to avoid nipy problem of conversion nifti-->nipy with 2d data sct.run(['sct_image', '-i', ','.join([fname, fname]), '-concat', 'z', '-o', fname]) sct.run(['sct_resample', '-i', fname, '-mm', str(npx) + 'x' + str(npy) + 'x' + str(pz), '-o', name_resample, '-x', interpolation]) if nz == 1: # when input data was 2d: re-convert data 3d-->2d sct.run(['sct_image', '-i', name_resample, '-split', 'z']) im_split = Image(name_resample.split('.nii.gz')[0] + '_Z0000.nii.gz') im_split.setFileName(name_resample) im_split.save() if binary: sct.run(['sct_maths', '-i', name_resample, '-bin', str(thr), '-o', name_resample]) if orientation != 'RPI': im_resample = Image(name_resample) im_resample = set_orientation(im_resample, orientation) im_resample.save() name_resample = im_resample.absolutepath return name_resample else: if orientation != 'RPI': im_in = set_orientation(im_in, orientation) im_in.save() fname = im_in.absolutepath sct.printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz') return fname
def orient2rpi(self): # save input image orientation self.orientation = get_orientation(Image(self.fname_mask)) if not self.orientation == 'RPI': printv('\nOrient input image(s) to RPI orientation...', self.verbose, 'normal') self._orient(self.fname_mask, 'RPI') if self.fname_sc is not None: self._orient(self.fname_sc, 'RPI') if self.fname_ref is not None: self._orient(self.fname_ref, 'RPI') if self.path_template is not None: self._orient(self.path_levels, 'RPI') for fname_atlas in self.atlas_roi_lst: self._orient(fname_atlas, 'RPI')
def loadFromPath(self, path, verbose): """ This function load an image from an absolute path using nibabel library :param path: path of the file from which the image will be loaded :return: """ from nibabel import load, spatialimages from sct_utils import check_file_exist, printv, extract_fname, run from sct_image import get_orientation # check_file_exist(path, verbose=verbose) im_file = None try: im_file = load(path) except spatialimages.ImageFileError: printv('Error: make sure ' + path + ' is an image.', 1, 'error') self.orientation = get_orientation(path, filename=True) self.data = im_file.get_data() self.hdr = im_file.get_header() self.absolutepath = path self.path, self.file_name, self.ext = extract_fname(path) self.dim = get_dimension(im_file)
def __init__(self, fname_im, contrast, fname_seg, path_out, quality_control, verbose): self.fname_im = fname_im self.contrast = contrast self.fname_seg = fname_seg self.path_out = path_out self.quality_control = quality_control self.verbose = verbose self.tmp_dir = tmp_create( verbose=self.verbose) # path to tmp directory self.orientation_im = get_orientation(Image( self.fname_im)) # to re-orient the data at the end self.slice2D_im = extract_fname( self.fname_im )[1] + '_midSag.nii' # file used to do the detection, with only one slice self.dection_map_pmj = extract_fname( self.fname_im)[1] + '_map_pmj' # file resulting from the detection # path to the pmj detector self.pmj_model = os.path.join( commands.getstatusoutput('echo $SCT_DIR')[1], 'data/pmj_models', '{}_model'.format(self.contrast)) self.threshold = -0.75 if self.contrast == 't1' else 0.8 # detection map threshold, depends on the contrast self.fname_out = extract_fname(self.fname_im)[1] + '_pmj.nii.gz' self.fname_qc = 'qc_pmj.png'
def process(self): # preprocessing os.chdir(self.tmp_dir) im_target = Image(self.original_target) im_sc_seg = Image(self.original_sc_seg) self.original_header = im_target.hdr self.original_orientation = im_target.orientation index_x = self.original_orientation.find('R') if 'R' in self.original_orientation else self.original_orientation.find('L') index_y = self.original_orientation.find('P') if 'P' in self.original_orientation else self.original_orientation.find('A') index_z = self.original_orientation.find('I') if 'I' in self.original_orientation else self.original_orientation.find('S') # resampling of the images nx, ny, nz, nt, px, py, pz, pt = im_target.dim pix_dim = [px, py, pz] self.original_px = pix_dim[index_x] self.original_py = pix_dim[index_y] if round(self.original_px, 2) != self.resample_to or round(self.original_py, 2) != self.resample_to: self.t2star = resample_image(self.original_target, npx=self.resample_to, npy=self.resample_to) self.sc_seg = resample_image(self.original_sc_seg, binary=True, npx=self.resample_to, npy=self.resample_to) # denoising (optional) im_target = Image(self.t2star) if self.denoising: from sct_maths import denoise_ornlm im_target.data = denoise_ornlm(im_target.data) im_target.save() self.t2star = im_target.file_name + im_target.ext box_size = int(22.5/self.resample_to) # Pad in case the spinal cord is too close to the edges pad_size = box_size/2 + 2 self.pad = [str(pad_size)]*3 self.pad[index_z] = str(0) t2star_pad = sct.add_suffix(self.t2star, '_pad') sc_seg_pad = sct.add_suffix(self.sc_seg, '_pad') sct.run('sct_image -i '+self.t2star+' -pad '+self.pad[0]+','+self.pad[1]+','+self.pad[2]+' -o '+t2star_pad) sct.run('sct_image -i '+self.sc_seg+' -pad '+self.pad[0]+','+self.pad[1]+','+self.pad[2]+' -o '+sc_seg_pad) self.t2star = t2star_pad self.sc_seg = sc_seg_pad # put data in RPI t2star_rpi = sct.add_suffix(self.t2star, '_RPI') sc_seg_rpi = sct.add_suffix(self.sc_seg, '_RPI') sct.run('sct_image -i '+self.t2star+' -setorient RPI -o '+t2star_rpi) sct.run('sct_image -i '+self.sc_seg+' -setorient RPI -o '+sc_seg_rpi) self.t2star = t2star_rpi self.sc_seg = sc_seg_rpi self.square_mask, self.processed_target = crop_t2_star(self.t2star, self.sc_seg, box_size=box_size) if self.t2 is not None: self.fname_level = compute_level_file(self.t2star, self.sc_seg, self.t2, self.t2_seg, self.t2_landmarks) elif self.fname_level is not None: level_orientation = get_orientation(self.fname_level, filename=True) if level_orientation != 'IRP': self.fname_level = set_orientation(self.fname_level, 'IRP', filename=True) os.chdir('..')
def main(): # initialization fname_mask = '' # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_data = arguments['-i'] fname_mask = arguments['-m'] vert_label_fname = arguments["-vertfile"] vert_levels = arguments["-vert"] slices_of_interest = arguments["-z"] index_vol = arguments['-vol'] method = arguments["-method"] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) # Check if data are in RPI input_im = Image(fname_data) input_orient = get_orientation(input_im) # If orientation is not RPI, change to RPI if input_orient != 'RPI': sct.printv( '\nCreate temporary folder to change the orientation of the NIFTI files into RPI...', verbose) path_tmp = sct.tmp_create() # change orientation and load data sct.printv('\nChange input image orientation and load it...', verbose) input_im_rpi = orientation(input_im, ori='RPI', set=True, fname_out=os.path.join( path_tmp, "input_RPI.nii")) input_data = input_im_rpi.data # Do the same for the mask sct.printv('\nChange mask orientation and load it...', verbose) mask_im_rpi = orientation(Image(fname_mask), ori='RPI', set=True, fname_out=os.path.join( path_tmp, "mask_RPI.nii")) mask_data = mask_im_rpi.data # Do the same for vertebral labeling if present if vert_levels != 'None': sct.printv( '\nChange vertebral labeling file orientation and load it...', verbose) vert_label_im_rpi = orientation(Image(vert_label_fname), ori='RPI', set=True, fname_out=os.path.join( path_tmp, "vert_labeling_RPI.nii")) vert_labeling_data = vert_label_im_rpi.data # Remove the temporary folder used to change the NIFTI files orientation into RPI if remove_temp_files: sct.printv('\nRemove the temporary folder...', verbose) sct.rmtree(path_tmp, True) else: # Load data sct.printv('\nLoad data...', verbose) input_data = input_im.data mask_data = Image(fname_mask).data if vert_levels != 'None': vert_labeling_data = Image(vert_label_fname).data sct.printv('\tDone.', verbose) # Get slices corresponding to vertebral levels if vert_levels != 'None': from sct_extract_metric import get_slices_matching_with_vertebral_levels slices_of_interest, actual_vert_levels, warning_vert_levels = get_slices_matching_with_vertebral_levels( mask_data, vert_levels, vert_labeling_data, verbose) # Remove slices that were not selected if slices_of_interest == 'None': slices_of_interest = '0:' + str(mask_data.shape[2] - 1) slices_boundary = slices_of_interest.split(':') slices_of_interest_list = range(int(slices_boundary[0]), int(slices_boundary[1]) + 1) # Crop input_data = input_data[:, :, slices_of_interest_list, :] mask_data = mask_data[:, :, slices_of_interest_list] # if user selected all slices (-vol -1), then assign index_vol if index_vol[0] == -1: index_vol = range(0, input_data.shape[3], 1) # Get signal and noise indexes_roi = np.where(mask_data == 1) if method == 'mult': # get voxels in ROI to obtain a (x*y*z)*t 2D matrix input_data_in_roi = input_data[indexes_roi] # compute signal and STD across by averaging across time signal = np.mean(input_data_in_roi[:, index_vol]) std_input_temporal = np.std(input_data_in_roi[:, index_vol], 1) noise = np.mean(std_input_temporal) elif method == 'diff': # if user did not select two volumes, then exit with error if not len(index_vol) == 2: sct.printv( 'ERROR: ' + str(len(index_vol)) + ' volumes were specified. Method "diff" should be used with exactly two volumes.', 1, 'error') data_1 = input_data[:, :, :, index_vol[0]] data_2 = input_data[:, :, :, index_vol[1]] # compute voxel-average of voxelwise sum signal = np.mean(np.add(data_1[indexes_roi], data_2[indexes_roi])) # compute voxel-STD of voxelwise substraction, multiplied by sqrt(2) as described in equation 7 of Dietrich et al. noise = np.std(np.subtract(data_1[indexes_roi], data_2[indexes_roi])) * np.sqrt(2) # compute SNR SNR = signal / noise # Display result sct.printv('\nSNR_' + method + ' = ' + str(SNR) + '\n', type='info')
def main(fname_anat, fname_centerline, degree_poly, centerline_fitting, interp, remove_temp_files, verbose): # extract path of the script path_script = os.path.dirname(__file__)+'/' # Parameters for debug mode if param.debug == 1: print '\n*** WARNING: DEBUG MODE ON ***\n' status, path_sct_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') fname_anat = path_sct_data+'/t2/t2.nii.gz' fname_centerline = path_sct_data+'/t2/t2_seg.nii.gz' # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) # Display arguments print '\nCheck input arguments...' print ' Input volume ...................... '+fname_anat print ' Centerline ........................ '+fname_centerline print '' # Get input image orientation im_anat = Image(fname_anat) input_image_orientation = get_orientation(im_anat) # Reorient input data into RL PA IS orientation im_centerline = Image(fname_centerline) im_anat_orient = set_orientation(im_anat, 'RPI') im_anat_orient.setFileName('tmp.anat_orient.nii') im_centerline_orient = set_orientation(im_centerline, 'RPI') im_centerline_orient.setFileName('tmp.centerline_orient.nii') # Open centerline #========================================================================================== print '\nGet dimensions of input centerline...' nx, ny, nz, nt, px, py, pz, pt = im_centerline_orient.dim print '.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz) print '.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm' print '\nOpen centerline volume...' data = im_centerline_orient.data X, Y, Z = (data>0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # loop across z and associate x,y coordinate with the point having maximum intensity x_centerline = [0 for iz in range(min_z_index, max_z_index+1, 1)] y_centerline = [0 for iz in range(min_z_index, max_z_index+1, 1)] z_centerline = [iz for iz in range(min_z_index, max_z_index+1, 1)] # Two possible scenario: # 1. the centerline is probabilistic: each slices contains voxels with the probability of containing the centerline [0:...:1] # We only take the maximum value of the image to aproximate the centerline. # 2. The centerline/segmentation image contains many pixels per slice with values {0,1}. # We take all the points and approximate the centerline on all these points. X, Y, Z = ((data<1)*(data>0)).nonzero() # X is empty if binary image if (len(X) > 0): # Scenario 1 for iz in range(min_z_index, max_z_index+1, 1): x_centerline[iz-min_z_index], y_centerline[iz-min_z_index] = numpy.unravel_index(data[:,:,iz].argmax(), data[:,:,iz].shape) else: # Scenario 2 for iz in range(min_z_index, max_z_index+1, 1): x_seg, y_seg = (data[:,:,iz]>0).nonzero() if len(x_seg) > 0: x_centerline[iz-min_z_index] = numpy.mean(x_seg) y_centerline[iz-min_z_index] = numpy.mean(y_seg) # TODO: find a way to do the previous loop with this, which is more neat: # [numpy.unravel_index(data[:,:,iz].argmax(), data[:,:,iz].shape) for iz in range(0,nz,1)] # clear variable del data # Fit the centerline points with the kind of curve given as argument of the script and return the new smoothed coordinates if centerline_fitting == 'nurbs': try: x_centerline_fit, y_centerline_fit = b_spline_centerline(x_centerline,y_centerline,z_centerline) except ValueError: print "splines fitting doesn't work, trying with polynomial fitting...\n" x_centerline_fit, y_centerline_fit = polynome_centerline(x_centerline,y_centerline,z_centerline) elif centerline_fitting == 'polynome': x_centerline_fit, y_centerline_fit = polynome_centerline(x_centerline,y_centerline,z_centerline) #========================================================================================== # Split input volume print '\nSplit input volume...' im_anat_orient_split_list = split_data(im_anat_orient, 2) file_anat_split = [] for im in im_anat_orient_split_list: file_anat_split.append(im.absolutepath) im.save() # initialize variables file_mat_inv_cumul = ['tmp.mat_inv_cumul_Z'+str(z).zfill(4) for z in range(0,nz,1)] z_init = min_z_index displacement_max_z_index = x_centerline_fit[z_init-min_z_index]-x_centerline_fit[max_z_index-min_z_index] # write centerline as text file print '\nGenerate fitted transformation matrices...' file_mat_inv_cumul_fit = ['tmp.mat_inv_cumul_fit_Z'+str(z).zfill(4) for z in range(0,nz,1)] for iz in range(min_z_index, max_z_index+1, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') if (x_centerline[iz-min_z_index] == 0 and y_centerline[iz-min_z_index] == 0): displacement = 0 else: displacement = x_centerline_fit[z_init-min_z_index]-x_centerline_fit[iz-min_z_index] fid.write('%i %i %i %f\n' %(1, 0, 0, displacement) ) fid.write('%i %i %i %f\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # we complete the displacement matrix in z direction for iz in range(0, min_z_index, 1): fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, 0) ) fid.write('%i %i %i %f\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() for iz in range(max_z_index+1, nz, 1): fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, displacement_max_z_index) ) fid.write('%i %i %i %f\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # apply transformations to data print '\nApply fitted transformation matrices...' file_anat_split_fit = ['tmp.anat_orient_fit_Z'+str(z).zfill(4) for z in range(0,nz,1)] for iz in range(0, nz, 1): # forward cumulative transformation to data sct.run(fsloutput+'flirt -in '+file_anat_split[iz]+' -ref '+file_anat_split[iz]+' -applyxfm -init '+file_mat_inv_cumul_fit[iz]+' -out '+file_anat_split_fit[iz]+' -interp '+interp) # Merge into 4D volume print '\nMerge into 4D volume...' from glob import glob im_to_concat_list = [Image(fname) for fname in glob('tmp.anat_orient_fit_Z*.nii')] im_concat_out = concat_data(im_to_concat_list, 2) im_concat_out.setFileName('tmp.anat_orient_fit.nii') im_concat_out.save() # sct.run(fsloutput+'fslmerge -z tmp.anat_orient_fit tmp.anat_orient_fit_z*') # Reorient data as it was before print '\nReorient data back into native orientation...' fname_anat_fit_orient = set_orientation(im_concat_out.absolutepath, input_image_orientation, filename=True) move(fname_anat_fit_orient, 'tmp.anat_orient_fit_reorient.nii') # Generate output file (in current folder) print '\nGenerate output file (in current folder)...' sct.generate_output_file('tmp.anat_orient_fit_reorient.nii', file_anat+'_flatten'+ext_anat) # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm -rf tmp.*') # to view results print '\nDone! To view results, type:' print 'fslview '+file_anat+ext_anat+' '+file_anat+'_flatten'+ext_anat+' &\n'
def check_if_rpi(fname): from sct_image import get_orientation if not get_orientation(fname, filename=True) == 'RPI': printv('\nERROR: '+fname+' is not in RPI orientation. Use sct_image -setorient to reorient your data. Exit program.\n', 1, 'error')
def generate_warping_field(fname_dest, x_trans, y_trans, theta_rot=None, center_rotation=None, fname='warping_field.nii.gz', verbose=1): """Generation of a warping field towards an image and given transformation parameters. Given a destination image and transformation parameters this functions creates a NIFTI 3D warping field that can be applied afterwards. The transformation parameters corresponds to a slice-by-slice registration of images, thus the transformation parameters must be precised for each slice of the image. inputs: fname_dest: name of destination image (type: string) x_trans: list of translations along x axis for each slice (type: list, length: height of fname_dest) y_trans: list of translations along y axis for each slice (type: list, length: height of fname_dest) theta_rot[optional]: list of rotation angles in radian (and in ITK's coordinate system) for each slice (type: list) center_rotation[optional]: pixel coordinates in plan xOy of the wanted center of rotation (type: list, length: 2, example: [0,ny/2]) fname[optional]: name of output warp (type: string) verbose: display parameter (type: int) output: creation of a warping field of name 'fname' with an header similar to the destination image. """ from nibabel import load from math import cos, sin from sct_image import get_orientation #Make sure image is in rpi format sct.printv('\nChecking if the image of destination is in RPI orientation for the warping field generation ...', verbose) orientation = get_orientation(fname_dest, filename=True) if orientation != 'RPI': sct.printv('\nWARNING: The image of destination is not in RPI format. Dimensions of the warping field might be inverted.', verbose) else: sct.printv('\tOK', verbose) sct.printv('\n\nCreating warping field ' + fname + ' for transformations along z...', verbose) file_dest = load(fname_dest) hdr_file_dest = file_dest.get_header() hdr_warp = hdr_file_dest.copy() # Get image dimensions sct.printv('\nGet image dimensions of destination image...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_dest).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) #Center of rotation if center_rotation == None: x_a = int(round(nx/2)) y_a = int(round(ny/2)) else: x_a = center_rotation[0] y_a = center_rotation[1] # Calculate displacement for each voxel data_warp = zeros(((((nx, ny, nz, 1, 3))))) # For translations if theta_rot == None: for i in range(nx): for j in range(ny): for k in range(nz): data_warp[i, j, k, 0, 0] = px * x_trans[k] data_warp[i, j, k, 0, 1] = py * y_trans[k] data_warp[i, j, k, 0, 2] = 0 # # For rigid transforms (not optimized) # if theta_rot != None: # for k in range(nz): # for i in range(nx): # for j in range(ny): # # data_warp[i, j, k, 0, 0] = (cos(theta_rot[k])-1) * (i - x_a) - sin(theta_rot[k]) * (j - y_a) - x_trans[k] # # data_warp[i, j, k, 0, 1] = -(sin(theta_rot[k]) * (i - x_a) + (cos(theta_rot[k])-1) * (j - y_a)) + y_trans[k] # # data_warp[i, j, k, 0, 0] = (cos(theta_rot[k]) - 1) * (i - x_a) - sin(theta_rot[k]) * (j - y_a) + x_trans[k] #+ sin(theta_rot[k]) * (int(round(nx/2))-x_a) # data_warp[i, j, k, 0, 1] = - sin(theta_rot[k]) * (i - x_a) - (cos(theta_rot[k]) - 1) * (j - y_a) + y_trans[k] #- sin(theta_rot[k]) * (int(round(nx/2))-x_a) # data_warp[i, j, k, 0, 2] = 0 # For rigid transforms with array (time optimization) if theta_rot != None: vector_i = [[[i-x_a],[j-y_a]] for i in range(nx) for j in range(ny)] for k in range(nz): matrix_rot_a = asarray([[cos(theta_rot[k]), - sin(theta_rot[k])],[-sin(theta_rot[k]), -cos(theta_rot[k])]]) tmp = matrix_rot_a + array(((-1,0),(0,1))) result = dot(tmp, array(vector_i).T[0]) + array([[x_trans[k]], [y_trans[k]]]) for i in range(nx): data_warp[i, :, k, 0, 0] = result[0][i*nx:i*nx+ny] data_warp[i, :, k, 0, 1] = result[1][i*nx:i*nx+ny] # Generate warp file as a warping field hdr_warp.set_intent('vector', (), '') hdr_warp.set_data_dtype('float32') img = nibabel.Nifti1Image(data_warp, None, hdr_warp) nibabel.save(img, fname) sct.printv('\nDONE ! Warping field generated: '+fname, verbose)
def get_centerline_from_point(input_image, point_file, gap=4, gaussian_kernel=4, remove_tmp_files=1): # Initialization fname_anat = input_image fname_point = point_file slice_gap = gap remove_tmp_files = remove_tmp_files gaussian_kernel = gaussian_kernel start_time = time() verbose = 1 # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') path_sct = sct.slash_at_the_end(path_sct, 1) # Parameters for debug mode if param.debug == 1: sct.printv('\n*** WARNING: DEBUG MODE ON ***\n\t\t\tCurrent working directory: '+os.getcwd(), 'warning') status, path_sct_testing_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') fname_anat = path_sct_testing_data+'/t2/t2.nii.gz' fname_point = path_sct_testing_data+'/t2/t2_centerline_init.nii.gz' slice_gap = 5 # check existence of input files sct.check_file_exist(fname_anat) sct.check_file_exist(fname_point) # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_point, file_point, ext_point = sct.extract_fname(fname_point) # extract path of schedule file # TODO: include schedule file in sct # TODO: check existence of schedule file file_schedule = path_sct + param.schedule_file # Get input image orientation input_image_orientation = get_orientation(fname_anat, filename=True) # Display arguments print '\nCheck input arguments...' print ' Anatomical image: '+fname_anat print ' Orientation: '+input_image_orientation print ' Point in spinal cord: '+fname_point print ' Slice gap: '+str(slice_gap) print ' Gaussian kernel: '+str(gaussian_kernel) print ' Degree of polynomial: '+str(param.deg_poly) # create temporary folder print('\nCreate temporary folder...') path_tmp = 'tmp.'+strftime('%y%m%d%H%M%S') sct.create_folder(path_tmp) print '\nCopy input data...' sct.run('cp '+fname_anat+ ' '+path_tmp+'/tmp.anat'+ext_anat) sct.run('cp '+fname_point+ ' '+path_tmp+'/tmp.point'+ext_point) # go to temporary folder os.chdir(path_tmp) # convert to nii im_anat = convert('tmp.anat'+ext_anat, 'tmp.anat.nii') im_point = convert('tmp.point'+ext_point, 'tmp.point.nii') # Reorient input anatomical volume into RL PA IS orientation print '\nReorient input volume to RL PA IS orientation...' set_orientation(im_anat, 'RPI') im_anat.setFileName('tmp.anat_orient.nii') # Reorient binary point into RL PA IS orientation print '\nReorient binary point into RL PA IS orientation...' # sct.run(sct.fsloutput + 'fslswapdim tmp.point RL PA IS tmp.point_orient') set_orientation(im_point, 'RPI') im_point.setFileName('tmp.point_orient.nii') # Get image dimensions print '\nGet image dimensions...' nx, ny, nz, nt, px, py, pz, pt = Image('tmp.anat_orient.nii').dim print '.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz) print '.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm' # Split input volume print '\nSplit input volume...' im_anat_split_list = split_data(im_anat, 2) file_anat_split = [] for im in im_anat_split_list: file_anat_split.append(im.absolutepath) im.save() im_point_split_list = split_data(im_point, 2) file_point_split = [] for im in im_point_split_list: file_point_split.append(im.absolutepath) im.save() # Extract coordinates of input point data_point = Image('tmp.point_orient.nii').data x_init, y_init, z_init = unravel_index(data_point.argmax(), data_point.shape) sct.printv('Coordinates of input point: ('+str(x_init)+', '+str(y_init)+', '+str(z_init)+')', verbose) # Create 2D gaussian mask sct.printv('\nCreate gaussian mask from point...', verbose) xx, yy = mgrid[:nx, :ny] mask2d = zeros((nx, ny)) radius = round(float(gaussian_kernel+1)/2) # add 1 because the radius includes the center. sigma = float(radius) mask2d = exp(-(((xx-x_init)**2)/(2*(sigma**2)) + ((yy-y_init)**2)/(2*(sigma**2)))) # Save mask to 2d file file_mask_split = ['tmp.mask_orient_Z'+str(z).zfill(4) for z in range(0, nz, 1)] nii_mask2d = Image('tmp.anat_orient_Z0000.nii') nii_mask2d.data = mask2d nii_mask2d.setFileName(file_mask_split[z_init]+'.nii') nii_mask2d.save() # initialize variables file_mat = ['tmp.mat_Z'+str(z).zfill(4) for z in range(0, nz, 1)] file_mat_inv = ['tmp.mat_inv_Z'+str(z).zfill(4) for z in range(0, nz, 1)] file_mat_inv_cumul = ['tmp.mat_inv_cumul_Z'+str(z).zfill(4) for z in range(0, nz, 1)] # create identity matrix for initial transformation matrix fid = open(file_mat_inv_cumul[z_init], 'w') fid.write('%i %i %i %i\n' % (1, 0, 0, 0)) fid.write('%i %i %i %i\n' % (0, 1, 0, 0)) fid.write('%i %i %i %i\n' % (0, 0, 1, 0)) fid.write('%i %i %i %i\n' % (0, 0, 0, 1)) fid.close() # initialize centerline: give value corresponding to initial point x_centerline = [x_init] y_centerline = [y_init] z_centerline = [z_init] warning_count = 0 # go up (1), then down (2) in reference to the binary point for iUpDown in range(1, 3): if iUpDown == 1: # z increases slice_gap_signed = slice_gap elif iUpDown == 2: # z decreases slice_gap_signed = -slice_gap # reverse centerline (because values will be appended at the end) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # initialization before looping z_dest = z_init # point given by user z_src = z_dest + slice_gap_signed # continue looping if 0 <= z < nz while 0 <= z_src < nz: # print current z: print 'z='+str(z_src)+':' # estimate transformation sct.run(fsloutput+'flirt -in '+file_anat_split[z_src]+' -ref '+file_anat_split[z_dest]+' -schedule ' + file_schedule + ' -verbose 0 -omat ' + file_mat[z_src] + ' -cost normcorr -forcescaling -inweight ' + file_mask_split[z_dest] + ' -refweight '+file_mask_split[z_dest]) # display transfo status, output = sct.run('cat '+file_mat[z_src]) print output # check if transformation is bigger than 1.5x slice_gap tx = float(output.split()[3]) ty = float(output.split()[7]) norm_txy = linalg.norm([tx, ty], ord=2) if norm_txy > 1.5*slice_gap: print 'WARNING: Transformation is too large --> using previous one.' warning_count = warning_count + 1 # if previous transformation exists, replace current one with previous one if os.path.isfile(file_mat[z_dest]): sct.run('cp '+file_mat[z_dest]+' '+file_mat[z_src]) # estimate inverse transformation matrix sct.run('convert_xfm -omat '+file_mat_inv[z_src]+' -inverse '+file_mat[z_src]) # compute cumulative transformation sct.run('convert_xfm -omat '+file_mat_inv_cumul[z_src]+' -concat '+file_mat_inv[z_src]+' '+file_mat_inv_cumul[z_dest]) # apply inverse cumulative transformation to initial gaussian mask (to put it in src space) sct.run(fsloutput+'flirt -in '+file_mask_split[z_init]+' -ref '+file_mask_split[z_init]+' -applyxfm -init '+file_mat_inv_cumul[z_src]+' -out '+file_mask_split[z_src]) # open inverse cumulative transformation file and generate centerline fid = open(file_mat_inv_cumul[z_src]) mat = fid.read().split() x_centerline.append(x_init + float(mat[3])) y_centerline.append(y_init + float(mat[7])) z_centerline.append(z_src) #z_index = z_index+1 # define new z_dest (target slice) and new z_src (moving slice) z_dest = z_dest + slice_gap_signed z_src = z_src + slice_gap_signed # Reconstruct centerline # ==================================================================================================== # reverse back centerline (because it's been reversed once, so now all values are in the right order) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # fit centerline in the Z-X plane using polynomial function print '\nFit centerline in the Z-X plane using polynomial function...' coeffsx = polyfit(z_centerline, x_centerline, deg=param.deg_poly) polyx = poly1d(coeffsx) x_centerline_fit = polyval(polyx, z_centerline) # calculate RMSE rmse = linalg.norm(x_centerline_fit-x_centerline)/sqrt( len(x_centerline) ) # calculate max absolute error max_abs = max(abs(x_centerline_fit-x_centerline)) print '.. RMSE (in mm): '+str(rmse*px) print '.. Maximum absolute error (in mm): '+str(max_abs*px) # fit centerline in the Z-Y plane using polynomial function print '\nFit centerline in the Z-Y plane using polynomial function...' coeffsy = polyfit(z_centerline, y_centerline, deg=param.deg_poly) polyy = poly1d(coeffsy) y_centerline_fit = polyval(polyy, z_centerline) # calculate RMSE rmse = linalg.norm(y_centerline_fit-y_centerline)/sqrt( len(y_centerline) ) # calculate max absolute error max_abs = max( abs(y_centerline_fit-y_centerline) ) print '.. RMSE (in mm): '+str(rmse*py) print '.. Maximum absolute error (in mm): '+str(max_abs*py) # display if param.debug == 1: import matplotlib.pyplot as plt plt.figure() plt.plot(z_centerline,x_centerline,'.',z_centerline,x_centerline_fit,'r') plt.legend(['Data','Polynomial Fit']) plt.title('Z-X plane polynomial interpolation') plt.show() plt.figure() plt.plot(z_centerline,y_centerline,'.',z_centerline,y_centerline_fit,'r') plt.legend(['Data','Polynomial Fit']) plt.title('Z-Y plane polynomial interpolation') plt.show() # generate full range z-values for centerline z_centerline_full = [iz for iz in range(0, nz, 1)] # calculate X and Y values for the full centerline x_centerline_fit_full = polyval(polyx, z_centerline_full) y_centerline_fit_full = polyval(polyy, z_centerline_full) # Generate fitted transformation matrices and write centerline coordinates in text file print '\nGenerate fitted transformation matrices and write centerline coordinates in text file...' file_mat_inv_cumul_fit = ['tmp.mat_inv_cumul_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] file_mat_cumul_fit = ['tmp.mat_cumul_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] fid_centerline = open('tmp.centerline_coordinates.txt', 'w') for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' % (1, 0, 0, x_centerline_fit_full[iz]-x_init)) fid.write('%i %i %i %f\n' % (0, 1, 0, y_centerline_fit_full[iz]-y_init)) fid.write('%i %i %i %i\n' % (0, 0, 1, 0)) fid.write('%i %i %i %i\n' % (0, 0, 0, 1)) fid.close() # compute forward cumulative fitted transformation matrix sct.run('convert_xfm -omat '+file_mat_cumul_fit[iz]+' -inverse '+file_mat_inv_cumul_fit[iz]) # write centerline coordinates in x, y, z format fid_centerline.write('%f %f %f\n' %(x_centerline_fit_full[iz], y_centerline_fit_full[iz], z_centerline_full[iz]) ) fid_centerline.close() # Prepare output data # ==================================================================================================== # write centerline as text file for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' % (1, 0, 0, x_centerline_fit_full[iz]-x_init)) fid.write('%i %i %i %f\n' % (0, 1, 0, y_centerline_fit_full[iz]-y_init)) fid.write('%i %i %i %i\n' % (0, 0, 1, 0)) fid.write('%i %i %i %i\n' % (0, 0, 0, 1)) fid.close() # write polynomial coefficients savetxt('tmp.centerline_polycoeffs_x.txt',coeffsx) savetxt('tmp.centerline_polycoeffs_y.txt',coeffsy) # apply transformations to data print '\nApply fitted transformation matrices...' file_anat_split_fit = ['tmp.anat_orient_fit_z'+str(z).zfill(4) for z in range(0, nz, 1)] file_mask_split_fit = ['tmp.mask_orient_fit_z'+str(z).zfill(4) for z in range(0, nz, 1)] file_point_split_fit = ['tmp.point_orient_fit_z'+str(z).zfill(4) for z in range(0, nz, 1)] for iz in range(0, nz, 1): # forward cumulative transformation to data sct.run(fsloutput+'flirt -in '+file_anat_split[iz]+' -ref '+file_anat_split[iz]+' -applyxfm -init '+file_mat_cumul_fit[iz]+' -out '+file_anat_split_fit[iz]) # inverse cumulative transformation to mask sct.run(fsloutput+'flirt -in '+file_mask_split[z_init]+' -ref '+file_mask_split[z_init]+' -applyxfm -init '+file_mat_inv_cumul_fit[iz]+' -out '+file_mask_split_fit[iz]) # inverse cumulative transformation to point sct.run(fsloutput+'flirt -in '+file_point_split[z_init]+' -ref '+file_point_split[z_init]+' -applyxfm -init '+file_mat_inv_cumul_fit[iz]+' -out '+file_point_split_fit[iz]+' -interp nearestneighbour') # Merge into 4D volume print '\nMerge into 4D volume...' im_anat_list = [Image(fname) for fname in glob.glob('tmp.anat_orient_fit_z*.nii')] im_anat_concat = concat_data(im_anat_list, 2) im_anat_concat.setFileName('tmp.anat_orient_fit.nii') im_anat_concat.save() im_mask_list = [Image(fname) for fname in glob.glob('tmp.mask_orient_fit_z*.nii')] im_mask_concat = concat_data(im_mask_list, 2) im_mask_concat.setFileName('tmp.mask_orient_fit.nii') im_mask_concat.save() im_point_list = [Image(fname) for fname in glob.glob('tmp.point_orient_fit_z*.nii')] im_point_concat = concat_data(im_point_list, 2) im_point_concat.setFileName('tmp.point_orient_fit.nii') im_point_concat.save() # Copy header geometry from input data print '\nCopy header geometry from input data...' im_anat = Image('tmp.anat_orient.nii') im_anat_orient_fit = Image('tmp.anat_orient_fit.nii') im_mask_orient_fit = Image('tmp.mask_orient_fit.nii') im_point_orient_fit = Image('tmp.point_orient_fit.nii') im_anat_orient_fit = copy_header(im_anat, im_anat_orient_fit) im_mask_orient_fit = copy_header(im_anat, im_mask_orient_fit) im_point_orient_fit = copy_header(im_anat, im_point_orient_fit) for im in [im_anat_orient_fit, im_mask_orient_fit, im_point_orient_fit]: im.save() # Reorient outputs into the initial orientation of the input image print '\nReorient the centerline into the initial orientation of the input image...' set_orientation('tmp.point_orient_fit.nii', input_image_orientation, 'tmp.point_orient_fit.nii') set_orientation('tmp.mask_orient_fit.nii', input_image_orientation, 'tmp.mask_orient_fit.nii') # Generate output file (in current folder) print '\nGenerate output file (in current folder)...' os.chdir('..') # come back to parent folder fname_output_centerline = sct.generate_output_file(path_tmp+'/tmp.point_orient_fit.nii', file_anat+'_centerline'+ext_anat) # Delete temporary files if remove_tmp_files == 1: print '\nRemove temporary files...' sct.run('rm -rf '+path_tmp, error_exit='warning') # print number of warnings print '\nNumber of warnings: '+str(warning_count)+' (if >10, you should probably reduce the gap and/or increase the kernel size' # display elapsed time elapsed_time = time() - start_time print '\nFinished! \n\tGenerated file: '+fname_output_centerline+'\n\tElapsed time: '+str(int(round(elapsed_time)))+'s\n'
def get_centerline_from_labels(fname_in, list_fname_labels, param, output_file_name=None, remove_temp_files=1, verbose=0): path, file, ext = sct.extract_fname(fname_in) # create temporary folder path_tmp = sct.slash_at_the_end('tmp.'+strftime('%y%m%d%H%M%S'), 1) sct.run('mkdir '+path_tmp) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder...', verbose) sct.run('sct_convert -i '+fname_in+' -o '+path_tmp+'data.nii') file_labels = [] for i in range(len(list_fname_labels)): file_labels.append('labels_'+str(i)+'.nii.gz') sct.run('sct_convert -i '+list_fname_labels[i]+' -o '+path_tmp+file_labels[i]) # go to tmp folder os.chdir(path_tmp) ## Concatenation of the files # Concatenation : sum of matrices file_0 = Image('data.nii') data_concatenation = file_0.data hdr_0 = file_0.hdr orientation_file_0 = get_orientation(file_0) if len(list_fname_labels) > 0: for i in range(0, len(list_fname_labels)): orientation_file_temp = get_orientation(file_labels[i], filename=True) if orientation_file_0 != orientation_file_temp : print 'ERROR: The files ', fname_in, ' and ', file_labels[i], ' are not in the same orientation. Use sct_image -setorient to change the orientation of a file.' sys.exit(2) file_temp = load(file_labels[i]) data_temp = file_temp.get_data() data_concatenation = data_concatenation + data_temp # Save concatenation as a file print '\nWrite NIFTI volumes...' img = Nifti1Image(data_concatenation, None, hdr_0) save(img, 'concatenation_file.nii.gz') # Applying nurbs to the concatenation and save file as binary file fname_output = extract_centerline('concatenation_file.nii.gz', remove_temp_files = remove_temp_files, verbose = verbose, algo_fitting=param.algo_fitting, type_window=param.type_window, window_length=param.window_length) # Rename files after processing if output_file_name != None: output_file_name = output_file_name else : output_file_name = 'generated_centerline.nii.gz' os.rename(fname_output, output_file_name) path_binary, file_binary, ext_binary = sct.extract_fname(output_file_name) os.rename('concatenation_file_centerline.txt', file_binary+'.txt') # Process for a binary file as output: sct.run('cp '+output_file_name+' ../') # Process for a text file as output: sct.run('cp '+file_binary+ '.txt'+ ' ../') os.chdir('../') # Remove temporary files if remove_temp_files: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp)
def create_mask(): fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI # parse argument for method method_type = param.process[0] # check method val if not method_type == 'center': method_val = param.process[1] # check existence of input files if method_type == 'centerline': sct.check_file_exist(method_val, param.verbose) # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) # Get output folder and file name if param.fname_out == '': param.fname_out = param.file_prefix+file_data+ext_data # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.tmp_create(param.verbose) # )sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) # sct.run('mkdir '+path_tmp, param.verbose) sct.printv('\nCheck orientation...', param.verbose) orientation_input = get_orientation(Image(param.fname_data)) sct.printv('.. '+orientation_input, param.verbose) reorient_coordinates = False # copy input data to tmp folder convert(param.fname_data, path_tmp+'data.nii') if method_type == 'centerline': convert(method_val, path_tmp+'centerline.nii.gz') if method_type == 'point': convert(method_val, path_tmp+'point.nii.gz') # go to tmp folder os.chdir(path_tmp) # reorient to RPI sct.printv('\nReorient to RPI...', param.verbose) # if not orientation_input == 'RPI': sct.run('sct_image -i data.nii -o data_RPI.nii -setorient RPI -v 0', verbose=False) if method_type == 'centerline': sct.run('sct_image -i centerline.nii.gz -o centerline_RPI.nii.gz -setorient RPI -v 0', verbose=False) if method_type == 'point': sct.run('sct_image -i point.nii.gz -o point_RPI.nii.gz -setorient RPI -v 0', verbose=False) # # if method_type == 'centerline': # orientation_centerline = get_orientation_3d(method_val, filename=True) # if not orientation_centerline == 'RPI': # sct.run('sct_image -i ' + method_val + ' -o ' + path_tmp + 'centerline.nii.gz' + ' -setorient RPI -v 0', verbose=False) # else: # convert(method_val, path_tmp+'centerline.nii.gz') # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image('data_RPI.nii').dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz)+ ' x ' + str(nt), param.verbose) # in case user input 4d data if nt != 1: sct.printv('WARNING in '+os.path.basename(__file__)+': Input image is 4d but output mask will 3D.', param.verbose, 'warning') # extract first volume to have 3d reference nii = Image('data_RPI.nii') data3d = nii.data[:,:,:,0] nii.data = data3d nii.save() if method_type == 'coord': # parse to get coordinate coord = map(int, method_val.split('x')) if method_type == 'point': # get file name fname_point = method_val # extract coordinate of point sct.printv('\nExtract coordinate of point...', param.verbose) # TODO: change this way to remove dependence to sct.run. ProcessLabels.display_voxel returns list of coordinates status, output = sct.run('sct_label_utils -i point_RPI.nii.gz -display', param.verbose) # parse to get coordinate coord = output[output.find('Position=')+10:-17].split(',') if method_type == 'center': # set coordinate at center of FOV coord = round(float(nx)/2), round(float(ny)/2) if method_type == 'centerline': # get name of centerline from user argument fname_centerline = 'centerline_RPI.nii.gz' else: # generate volume with line along Z at coordinates 'coord' sct.printv('\nCreate line...', param.verbose) fname_centerline = create_line('data_RPI.nii', coord, nz) # create mask sct.printv('\nCreate mask...', param.verbose) centerline = nibabel.load(fname_centerline) # open centerline hdr = centerline.get_header() # get header hdr.set_data_dtype('uint8') # set imagetype to uint8 spacing = hdr.structarr['pixdim'] data_centerline = centerline.get_data() # get centerline z_centerline_not_null = [iz for iz in range(0, nz, 1) if data_centerline[:, :, iz].any()] # get center of mass of the centerline cx = [0] * nz cy = [0] * nz for iz in range(0, nz, 1): if iz in z_centerline_not_null: cx[iz], cy[iz] = ndimage.measurements.center_of_mass(numpy.array(data_centerline[:, :, iz])) # create 2d masks file_mask = 'data_mask' for iz in range(nz): if iz not in z_centerline_not_null: # write an empty nifty volume img = nibabel.Nifti1Image(data_centerline[:, :, iz], None, hdr) nibabel.save(img, (file_mask + str(iz) + '.nii')) else: center = numpy.array([cx[iz], cy[iz]]) mask2d = create_mask2d(center, param.shape, param.size, nx, ny, even=param.even, spacing=spacing) # Write NIFTI volumes img = nibabel.Nifti1Image(mask2d, None, hdr) nibabel.save(img, (file_mask+str(iz)+'.nii')) # merge along Z # cmd = 'fslmerge -z mask ' # CHANGE THAT CAN IMPACT SPEED: # related to issue #755, we cannot open more than 256 files at one time. # to solve this issue, we do not open more than 100 files ''' im_list = [] im_temp = [] for iz in range(nz_not_null): if iz != 0 and iz % 100 == 0: im_temp.append(concat_data(im_list, 2)) im_list = [Image(file_mask + str(iz) + '.nii')] else: im_list.append(Image(file_mask+str(iz)+'.nii')) if im_temp: im_temp.append(concat_data(im_list, 2)) im_out = concat_data(im_temp, 2, no_expand=True) else: im_out = concat_data(im_list, 2) ''' fname_list = [file_mask + str(iz) + '.nii' for iz in range(nz)] im_out = concat_data(fname_list, dim=2) im_out.setFileName('mask_RPI.nii.gz') im_out.save() # reorient if necessary # if not orientation_input == 'RPI': sct.run('sct_image -i mask_RPI.nii.gz -o mask.nii.gz -setorient ' + orientation_input, param.verbose) # copy header input --> mask im_dat = Image('data.nii') im_mask = Image('mask.nii.gz') im_mask = copy_header(im_dat, im_mask) im_mask.save() # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp+'mask.nii.gz', param.fname_out) # Remove temporary files if param.remove_tmp_files == 1: sct.printv('\nRemove temporary files...', param.verbose) sct.run('rm -rf '+path_tmp, param.verbose, error_exit='warning') # to view results sct.printv('\nDone! To view results, type:', param.verbose) sct.printv('fslview '+param.fname_data+' '+param.fname_out+' -l Red -t 0.5 &', param.verbose, 'info') print
def create_mask(): fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI # parse argument for method method_type = param.process[0] # check method val if not method_type == 'center': method_val = param.process[1] # check existence of input files if method_type == 'centerline': sct.check_file_exist(method_val, param.verbose) # check if orientation is RPI sct.printv('\nCheck if orientation is RPI...', param.verbose) ori = get_orientation(param.fname_data, filename=True) if not ori == 'RPI': sct.printv('\nERROR in '+os.path.basename(__file__)+': Orientation of input image should be RPI. Use sct_image -setorient to put your image in RPI.\n', 1, 'error') # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) # Get output folder and file name if param.fname_out == '': param.fname_out = param.file_prefix+file_data+ext_data #fname_out = os.path.abspath(path_out+file_out+ext_out) # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, param.verbose) # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, path_tmp+'data.nii') # sct.run('cp '+param.fname_data+' '+path_tmp+'data'+ext_data, param.verbose) if method_type == 'centerline': convert(method_val, path_tmp+'centerline.nii.gz') # go to tmp folder os.chdir(path_tmp) # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image('data.nii').dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz)+ ' x ' + str(nt), param.verbose) # in case user input 4d data if nt != 1: sct.printv('WARNING in '+os.path.basename(__file__)+': Input image is 4d but output mask will 3D.', param.verbose, 'warning') # extract first volume to have 3d reference nii = Image('data.nii') data3d = nii.data[:,:,:,0] nii.data = data3d nii.save() if method_type == 'coord': # parse to get coordinate coord = map(int, method_val.split('x')) if method_type == 'point': # get file name fname_point = method_val # extract coordinate of point sct.printv('\nExtract coordinate of point...', param.verbose) status, output = sct.run('sct_label_utils -i '+fname_point+' -p display-voxel', param.verbose) # parse to get coordinate coord = output[output.find('Position=')+10:-17].split(',') if method_type == 'center': # set coordinate at center of FOV coord = round(float(nx)/2), round(float(ny)/2) if method_type == 'centerline': # get name of centerline from user argument fname_centerline = 'centerline.nii.gz' else: # generate volume with line along Z at coordinates 'coord' sct.printv('\nCreate line...', param.verbose) fname_centerline = create_line('data.nii', coord, nz) # create mask sct.printv('\nCreate mask...', param.verbose) centerline = nibabel.load(fname_centerline) # open centerline hdr = centerline.get_header() # get header hdr.set_data_dtype('uint8') # set imagetype to uint8 data_centerline = centerline.get_data() # get centerline z_centerline = [iz for iz in range(0, nz, 1) if data_centerline[:, :, iz].any()] nz = len(z_centerline) # get center of mass of the centerline cx = [0] * nz cy = [0] * nz for iz in range(0, nz, 1): cx[iz], cy[iz] = ndimage.measurements.center_of_mass(numpy.array(data_centerline[:, :, z_centerline[iz]])) # create 2d masks file_mask = 'data_mask' for iz in range(nz): center = numpy.array([cx[iz], cy[iz]]) mask2d = create_mask2d(center, param.shape, param.size, nx, ny, param.even) # Write NIFTI volumes img = nibabel.Nifti1Image(mask2d, None, hdr) nibabel.save(img, (file_mask+str(iz)+'.nii')) # merge along Z # cmd = 'fslmerge -z mask ' im_list = [] for iz in range(nz): im_list.append(Image(file_mask+str(iz)+'.nii')) im_out = concat_data(im_list, 2) im_out.setFileName('mask.nii.gz') im_out.save() # copy geometry im_dat = Image('data.nii') im_mask = Image('mask.nii.gz') im_mask = copy_header(im_dat, im_mask) im_mask.save() # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp+'mask.nii.gz', param.fname_out) # Remove temporary files if param.remove_tmp_files == 1: sct.printv('\nRemove temporary files...', param.verbose) sct.run('rm -rf '+path_tmp, param.verbose, error_exit='warning') # to view results sct.printv('\nDone! To view results, type:', param.verbose) sct.printv('fslview '+param.fname_data+' '+param.fname_out+' -l Red -t 0.5 &', param.verbose, 'info') print
def create_mask(): fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI # parse argument for method method_type = param.process[0] # check method val if not method_type == 'center': method_val = param.process[1] # check existence of input files if method_type == 'centerline': sct.check_file_exist(method_val, param.verbose) # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) # Get output folder and file name if param.fname_out == '': param.fname_out = param.file_prefix + file_data + ext_data # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.tmp_create(param.verbose) # )sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) # sct.run('mkdir '+path_tmp, param.verbose) sct.printv('\nCheck orientation...', param.verbose) orientation_input = get_orientation(Image(param.fname_data)) sct.printv('.. ' + orientation_input, param.verbose) reorient_coordinates = False # copy input data to tmp folder convert(param.fname_data, path_tmp + 'data.nii') if method_type == 'centerline': convert(method_val, path_tmp + 'centerline.nii.gz') if method_type == 'point': convert(method_val, path_tmp + 'point.nii.gz') # go to tmp folder os.chdir(path_tmp) # reorient to RPI sct.printv('\nReorient to RPI...', param.verbose) # if not orientation_input == 'RPI': sct.run('sct_image -i data.nii -o data_RPI.nii -setorient RPI -v 0', verbose=False) if method_type == 'centerline': sct.run( 'sct_image -i centerline.nii.gz -o centerline_RPI.nii.gz -setorient RPI -v 0', verbose=False) if method_type == 'point': sct.run( 'sct_image -i point.nii.gz -o point_RPI.nii.gz -setorient RPI -v 0', verbose=False) # # if method_type == 'centerline': # orientation_centerline = get_orientation_3d(method_val, filename=True) # if not orientation_centerline == 'RPI': # sct.run('sct_image -i ' + method_val + ' -o ' + path_tmp + 'centerline.nii.gz' + ' -setorient RPI -v 0', verbose=False) # else: # convert(method_val, path_tmp+'centerline.nii.gz') # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image('data_RPI.nii').dim sct.printv( ' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), param.verbose) # in case user input 4d data if nt != 1: sct.printv( 'WARNING in ' + os.path.basename(__file__) + ': Input image is 4d but output mask will 3D.', param.verbose, 'warning') # extract first volume to have 3d reference nii = Image('data_RPI.nii') data3d = nii.data[:, :, :, 0] nii.data = data3d nii.save() if method_type == 'coord': # parse to get coordinate coord = map(int, method_val.split('x')) if method_type == 'point': # get file name fname_point = method_val # extract coordinate of point sct.printv('\nExtract coordinate of point...', param.verbose) # TODO: change this way to remove dependence to sct.run. ProcessLabels.display_voxel returns list of coordinates status, output = sct.run( 'sct_label_utils -i point_RPI.nii.gz -display', param.verbose) # parse to get coordinate coord = output[output.find('Position=') + 10:-17].split(',') if method_type == 'center': # set coordinate at center of FOV coord = round(float(nx) / 2), round(float(ny) / 2) if method_type == 'centerline': # get name of centerline from user argument fname_centerline = 'centerline_RPI.nii.gz' else: # generate volume with line along Z at coordinates 'coord' sct.printv('\nCreate line...', param.verbose) fname_centerline = create_line('data_RPI.nii', coord, nz) # create mask sct.printv('\nCreate mask...', param.verbose) centerline = nibabel.load(fname_centerline) # open centerline hdr = centerline.get_header() # get header hdr.set_data_dtype('uint8') # set imagetype to uint8 spacing = hdr.structarr['pixdim'] data_centerline = centerline.get_data() # get centerline # if data is 2D, reshape with empty third dimension if len(data_centerline.shape) == 2: data_centerline_shape = list(data_centerline.shape) data_centerline_shape.append(1) data_centerline = data_centerline.reshape(data_centerline_shape) z_centerline_not_null = [ iz for iz in range(0, nz, 1) if data_centerline[:, :, iz].any() ] # get center of mass of the centerline cx = [0] * nz cy = [0] * nz for iz in range(0, nz, 1): if iz in z_centerline_not_null: cx[iz], cy[iz] = ndimage.measurements.center_of_mass( numpy.array(data_centerline[:, :, iz])) # create 2d masks file_mask = 'data_mask' for iz in range(nz): if iz not in z_centerline_not_null: # write an empty nifty volume img = nibabel.Nifti1Image(data_centerline[:, :, iz], None, hdr) nibabel.save(img, (file_mask + str(iz) + '.nii')) else: center = numpy.array([cx[iz], cy[iz]]) mask2d = create_mask2d(center, param.shape, param.size, nx, ny, even=param.even, spacing=spacing) # Write NIFTI volumes img = nibabel.Nifti1Image(mask2d, None, hdr) nibabel.save(img, (file_mask + str(iz) + '.nii')) # merge along Z # cmd = 'fslmerge -z mask ' # CHANGE THAT CAN IMPACT SPEED: # related to issue #755, we cannot open more than 256 files at one time. # to solve this issue, we do not open more than 100 files ''' im_list = [] im_temp = [] for iz in range(nz_not_null): if iz != 0 and iz % 100 == 0: im_temp.append(concat_data(im_list, 2)) im_list = [Image(file_mask + str(iz) + '.nii')] else: im_list.append(Image(file_mask+str(iz)+'.nii')) if im_temp: im_temp.append(concat_data(im_list, 2)) im_out = concat_data(im_temp, 2, no_expand=True) else: im_out = concat_data(im_list, 2) ''' fname_list = [file_mask + str(iz) + '.nii' for iz in range(nz)] im_out = concat_data(fname_list, dim=2) im_out.setFileName('mask_RPI.nii.gz') im_out.save() # reorient if necessary # if not orientation_input == 'RPI': sct.run( 'sct_image -i mask_RPI.nii.gz -o mask.nii.gz -setorient ' + orientation_input, param.verbose) # copy header input --> mask im_dat = Image('data.nii') im_mask = Image('mask.nii.gz') im_mask = copy_header(im_dat, im_mask) im_mask.save() # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp + 'mask.nii.gz', param.fname_out) # Remove temporary files if param.remove_tmp_files == 1: sct.printv('\nRemove temporary files...', param.verbose) sct.run('rm -rf ' + path_tmp, param.verbose, error_exit='warning') # to view results sct.printv('\nDone! To view results, type:', param.verbose) sct.printv( 'fslview ' + param.fname_data + ' ' + param.fname_out + ' -l Red -t 0.5 &', param.verbose, 'info') print
def change_orientation(self, orientation='RPI', inversion_orient=False): """ This function changes the orientation of the data by swapping the image axis. Warning: the nifti image header is not changed!!! :param orientation: string of three character representing the new orientation (ex: AIL, default: RPI) inversion_orient: boolean. If True, the data change to match the orientation in the header, based on the orientation provided as the argument orientation. :return: """ opposite_character = {'L': 'R', 'R': 'L', 'A': 'P', 'P': 'A', 'I': 'S', 'S': 'I'} if self.orientation is None: from sct_image import get_orientation self.orientation = get_orientation(self) # get orientation to return at the end of function raw_orientation = self.orientation if inversion_orient: temp_orientation = self.orientation self.orientation = orientation orientation = temp_orientation # change the orientation of the image perm = [0, 1, 2] inversion = [1, 1, 1] for i, character in enumerate(self.orientation): try: perm[i] = orientation.index(character) except ValueError: perm[i] = orientation.index(opposite_character[character]) inversion[i] = -1 # axes inversion self.data = self.data[::inversion[0], ::inversion[1], ::inversion[2]] # axes manipulations from numpy import swapaxes if perm == [1, 0, 2]: self.data = swapaxes(self.data, 0, 1) elif perm == [2, 1, 0]: self.data = swapaxes(self.data, 0, 2) elif perm == [0, 2, 1]: self.data = swapaxes(self.data, 1, 2) elif perm == [2, 0, 1]: self.data = swapaxes(self.data, 0, 2) # transform [2, 0, 1] to [1, 0, 2] self.data = swapaxes(self.data, 0, 1) # transform [1, 0, 2] to [0, 1, 2] elif perm == [1, 2, 0]: self.data = swapaxes(self.data, 0, 2) # transform [1, 2, 0] to [0, 2, 1] self.data = swapaxes(self.data, 1, 2) # transform [0, 2, 1] to [0, 1, 2] elif perm == [0, 1, 2]: # do nothing pass else: print 'Error: wrong orientation' # update dim # http://math.stackexchange.com/questions/122916/what-is-the-inverse-cycle-of-permutation dim_temp = list(self.dim) dim_temp[0] = self.dim[[i for i, x in enumerate(perm) if x == 0][0]] # nx dim_temp[1] = self.dim[[i for i, x in enumerate(perm) if x == 1][0]] # ny dim_temp[2] = self.dim[[i for i, x in enumerate(perm) if x == 2][0]] # nz dim_temp[4] = self.dim[[i for i, x in enumerate(perm) if x == 0][0]+4] # px dim_temp[5] = self.dim[[i for i, x in enumerate(perm) if x == 1][0]+4] # py dim_temp[6] = self.dim[[i for i, x in enumerate(perm) if x == 2][0]+4] # pz self.dim = tuple(dim_temp) # update orientation self.orientation = orientation return raw_orientation