def multicomponent_merge(fname_list): from numpy import zeros, reshape # WARNING: output multicomponent is not optimal yet, some issues may be related to the use of this function im_0 = Image(fname_list[0]) new_shape = list(im_0.data.shape) if len(new_shape) == 3: new_shape.append(1) new_shape.append(len(fname_list)) new_shape = tuple(new_shape) data_out = zeros(new_shape) for i, fname in enumerate(fname_list): im = Image(fname) dat = im.data if len(dat.shape) == 2: data_out[:, :, 0, 0, i] = dat.astype('float32') elif len(dat.shape) == 3: data_out[:, :, :, 0, i] = dat.astype('float32') elif len(dat.shape) == 4: data_out[:, :, :, :, i] = dat.astype('float32') del im del dat im_out = im_0.copy() im_out.data = data_out.astype('float32') im_out.hdr.set_intent('vector', (), '') im_out.setFileName(im_out.file_name+'_multicomponent'+im_out.ext) return im_out
def load_level(list_slices_target, fname_level): verbose = 1 path_level, file_level, ext_level = extract_fname(fname_level) # ####### Check if the level file is an image or a text file # Level file is an image if ext_level in ['.nii', '.nii.gz']: im_level = Image(fname_level) im_level.change_orientation('IRP') list_level = [] list_med_level = [] for slice_level in im_level.data: try: # vertebral level of the slice l = np.mean(slice_level[slice_level > 0]) # median of the vertebral level of the slice: if all voxels are int, med will be an int. med = np.median(slice_level[slice_level > 0]) # change med in int if it is an int med = int(med) if int(med)==med else med except Exception, e: printv('WARNING: ' + str(e) + '\nNo level label found. Level will be set to 0 for this slice', verbose, 'warning') l = 0 med = 0 list_level.append(l) list_med_level.append(med) # if all median of level are int for all slices : consider level as int if all([isinstance(med, int) for med in list_med_level]): # level as int are placed in the middle of each vertebra (that's why there is a "+0.5") list_level = [int(round(l))+0.5 for l in list_level]
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 remove_label(self, symmetry=False): """ Compare two label images and remove any labels in input image that are not in reference image. The symmetry option enables to remove labels from reference image that are not in input image """ # image_output = Image(self.image_input.dim, orientation=self.image_input.orientation, hdr=self.image_input.hdr, verbose=self.verbose) image_output = Image(self.image_input, verbose=self.verbose) image_output.data *= 0 # put all voxels to 0 result_coord_input, result_coord_ref = self.remove_label_coord(self.image_input.getNonZeroCoordinates(coordValue=True), self.image_ref.getNonZeroCoordinates(coordValue=True), symmetry) for coord in result_coord_input: image_output.data[int(coord.x), int(coord.y), int(coord.z)] = int(round(coord.value)) if symmetry: # image_output_ref = Image(self.image_ref.dim, orientation=self.image_ref.orientation, hdr=self.image_ref.hdr, verbose=self.verbose) image_output_ref = Image(self.image_ref, verbose=self.verbose) for coord in result_coord_ref: image_output_ref.data[int(coord.x), int(coord.y), int(coord.z)] = int(round(coord.value)) image_output_ref.setFileName(self.fname_output[1]) image_output_ref.save('minimize_int') self.fname_output = self.fname_output[0] return image_output
def savePredictions(predictions, path_output, list_images, segmentation_image_size): number_of_images = len(list_images) predictions = numpy.reshape(predictions, [number_of_images, segmentation_image_size, segmentation_image_size, NUM_LABELS]) predictions = predictions[:, :, :, 1] for i, pref in enumerate(predictions): im_pred = Image(pref) im_pred.setFileName(path_output+sct.add_suffix(list_images[i], '_pred')) im_pred.save()
def continuous_vertebral_levels(self): """ This function transforms the vertebral levels file from the template into a continuous file. Instead of having integer representing the vertebral level on each slice, a continuous value that represents the position of the slice in the vertebral level coordinate system. The image must be RPI :return: """ im_input = Image(self.image_input, self.verbose) im_output = Image(self.image_input, self.verbose) im_output.data *= 0 # 1. extract vertebral levels from input image # a. extract centerline # b. for each slice, extract corresponding level nx, ny, nz, nt, px, py, pz, pt = im_input.dim from sct_straighten_spinalcord import smooth_centerline x_centerline_fit, y_centerline_fit, z_centerline_fit, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline(self.image_input, algo_fitting='nurbs', verbose=0) value_centerline = np.array([im_input.data[int(x_centerline_fit[it]), int(y_centerline_fit[it]), int(z_centerline_fit[it])] for it in range(len(z_centerline_fit))]) # 2. compute distance for each vertebral level --> Di for i being the vertebral levels vertebral_levels = {} for slice_image, level in enumerate(value_centerline): if level not in vertebral_levels: vertebral_levels[level] = slice_image length_levels = {} for level in vertebral_levels: indexes_slice = np.where(value_centerline == level) length_levels[level] = np.sum([math.sqrt(((x_centerline_fit[indexes_slice[0][index_slice + 1]] - x_centerline_fit[indexes_slice[0][index_slice]])*px)**2 + ((y_centerline_fit[indexes_slice[0][index_slice + 1]] - y_centerline_fit[indexes_slice[0][index_slice]])*py)**2 + ((z_centerline_fit[indexes_slice[0][index_slice + 1]] - z_centerline_fit[indexes_slice[0][index_slice]])*pz)**2) for index_slice in range(len(indexes_slice[0]) - 1)]) # 2. for each slice: # a. identify corresponding vertebral level --> i # b. calculate distance of slice from upper vertebral level --> d # c. compute relative distance in the vertebral level coordinate system --> d/Di continuous_values = {} for it, iz in enumerate(z_centerline_fit): level = value_centerline[it] indexes_slice = np.where(value_centerline == level) indexes_slice = indexes_slice[0][indexes_slice[0] >= it] distance_from_level = np.sum([math.sqrt(((x_centerline_fit[indexes_slice[index_slice + 1]] - x_centerline_fit[indexes_slice[index_slice]]) * px * px) ** 2 + ((y_centerline_fit[indexes_slice[index_slice + 1]] - y_centerline_fit[indexes_slice[index_slice]]) * py * py) ** 2 + ((z_centerline_fit[indexes_slice[index_slice + 1]] - z_centerline_fit[indexes_slice[index_slice]]) * pz * pz) ** 2) for index_slice in range(len(indexes_slice) - 1)]) continuous_values[iz] = level + 2.0 * distance_from_level / float(length_levels[level]) # 3. saving data # for each slice, get all non-zero pixels and replace with continuous values coordinates_input = self.image_input.getNonZeroCoordinates() im_output.changeType('float32') # for all points in input, find the value that has to be set up, depending on the vertebral level for i, coord in enumerate(coordinates_input): im_output.data[int(coord.x), int(coord.y), int(coord.z)] = continuous_values[coord.z] return im_output
def next(self): if self.iteration <= self.num_of_frames: result = Image(self) print "Iteration #" + str(self.iteration) result.data *= float(self.iteration) / float(self.num_of_frames) result.file_name = "tmp."+result.file_name+"_" + str(self.iteration) self.iteration += 1 return result, self.iteration else: raise StopIteration()
def plan_ref(self): """ Generate a plane in the reference space for each label present in the input image """ image_output = Image(self.image_ref, self.verbose) image_output.data *= 0 image_input_neg = Image(self.image_input, self.verbose).copy() image_input_pos = Image(self.image_input, self.verbose).copy() image_input_neg.data *=0 image_input_pos.data *=0 X, Y, Z = (self.image_input.data< 0).nonzero() for i in range(len(X)): image_input_neg.data[X[i], Y[i], Z[i]] = -self.image_input.data[X[i], Y[i], Z[i]] # in order to apply getNonZeroCoordinates X_pos, Y_pos, Z_pos = (self.image_input.data> 0).nonzero() for i in range(len(X_pos)): image_input_pos.data[X_pos[i], Y_pos[i], Z_pos[i]] = self.image_input.data[X_pos[i], Y_pos[i], Z_pos[i]] coordinates_input_neg = image_input_neg.getNonZeroCoordinates() coordinates_input_pos = image_input_pos.getNonZeroCoordinates() image_output.changeType('float32') for coord in coordinates_input_neg: image_output.data[:, :, int(coord.z)] = -coord.value #PB: takes the int value of coord.value for coord in coordinates_input_pos: image_output.data[:, :, int(coord.z)] = coord.value return image_output
def resample_image(fname, suffix='_resampled.nii.gz', binary=False, npx=0.3, npy=0.3, thr=0.0, interpolation='spline'): """ Resampling function: add a padding, resample, crop the padding :param fname: name of the image file to be resampled :param suffix: suffix added to the original fname after resampling :param binary: boolean, image is binary or not :param npx: new pixel size in the x direction :param npy: new pixel size in the y direction :param thr: if the image is binary, it will be thresholded at thr (default=0) after the resampling :param interpolation: type of interpolation used for the resampling :return: file name after resampling (or original fname if it was already in the correct resolution) """ im_in = Image(fname) orientation = get_orientation_3d(im_in) if orientation != 'RPI': im_in = set_orientation(im_in, 'RPI') im_in.save() fname = im_in.absolutepath nx, ny, nz, nt, px, py, pz, pt = im_in.dim if round(px, 2) != round(npx, 2) or round(py, 2) != round(npy, 2): name_resample = sct.extract_fname(fname)[1] + suffix if binary: interpolation = 'nn' sct.run('sct_resample -i ' + fname + ' -mm ' + str(npx) + 'x' + str(npy) + 'x' + str(pz) + ' -o ' + name_resample + ' -x ' + interpolation) if binary: # sct.run('sct_maths -i ' + name_resample + ' -thr ' + str(thr) + ' -o ' + name_resample) sct.run('sct_maths -i ' + name_resample + ' -bin ' + str(thr) + ' -o ' + name_resample) if orientation != 'RPI': im_resample = Image(name_resample) im_resample = set_orientation(im_resample, orientation) im_resample.save() name_resample = im_resample.absolutepath return name_resample else: if orientation != 'RPI': im_in = set_orientation(im_in, orientation) im_in.save() fname = im_in.absolutepath sct.printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz') return fname
def 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 next(self): if self.iteration <= self.num_of_frames: result = Image(self) sct.printv("Iteration #" + str(self.iteration)) result.data *= float(self.iteration) / float(self.num_of_frames) result.file_name = "tmp." + result.file_name + "_" + str( self.iteration) self.iteration += 1 return result, self.iteration else: raise StopIteration()
def extract_slices(self): # open image and re-orient it to RPI if needed im, seg = Image(self.param.fname_im), Image(self.param.fname_seg) if self.orientation_im != self.orientation_extraction: im, seg = set_orientation( im, self.orientation_extraction), set_orientation( seg, self.orientation_extraction) # extract axial slices in self.dct_im_seg self.dct_im_seg['im'], self.dct_im_seg['seg'] = [ im.data[:, :, z] for z in range(im.dim[2]) ], [seg.data[:, :, z] for z in range(im.dim[2])]
def __init__(self, fname_label, fname_output=None, fname_ref=None, cross_radius=5, dilate=False, coordinates=None, verbose='1'): self.image_input = Image(fname_label) if fname_ref is not None: self.image_ref = Image(fname_ref) self.fname_output = fname_output self.cross_radius = cross_radius self.dilate = dilate self.coordinates = coordinates self.verbose = verbose
def label_lesion(self): printv('\nLabel connected regions of the masked image...', self.verbose, 'normal') im = Image(self.fname_mask) im_2save = im.copy() im_2save.data = label(im.data, connectivity=2) im_2save.setFileName(self.fname_label) im_2save.save() self.measure_pd['label'] = [l for l in np.unique(im_2save.data) if l] printv('Lesion count = ' + str(len(self.measure_pd['label'])), self.verbose, 'info')
def compute(self): fname_data = self.fmri # # create temporary folder # sct.printv('\nCreate temporary folder...', self.param.verbose) # path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S/") # status, output = sct.run('mkdir '+path_tmp, self.param.verbose) # # motion correct the fmri data # # sct.printv('\nMotion correct the fMRI data...', self.param.verbose, 'normal') # path_fmri, fname_fmri, ext_fmri = sct.extract_fname(self.fmri) # fname_fmri_moco = fname_fmri # # print sct.slash_at_the_end(path_fmri) + fname_fmri # # sct.run('mcflirt -in ' + sct.slash_at_the_end(path_fmri, 1) + fname_fmri + ' -out ' + fname_fmri_moco) # compute tsnr sct.printv('\nCompute the tSNR...', self.param.verbose, 'normal') fname_data_mean = sct.add_suffix(fname_data, '_mean.nii') sct.run('sct_maths -i ' + fname_data + ' -o ' + fname_data_mean + ' -mean t') # if not average_data_across_dimension(fname_data, fname_data_mean, 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run('fslmaths ' + fname_data + ' -Tmean ' + fname_data_mean) fname_data_std = sct.add_suffix(fname_data, '_std.nii') sct.run('sct_maths -i ' + fname_data + ' -o ' + fname_data_std + ' -mean t') # if not average_data_across_dimension(fname_data, fname_data_std, 3, 1): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run('fslmaths ' + fname_data + ' -Tstd ' + fname_data_std) fname_tsnr = sct.add_suffix(fname_data, '_tsnr') from msct_image import Image nii_mean = Image(fname_data_mean) data_mean = nii_mean.data data_std = Image(fname_data_std).data data_tsnr = data_mean / data_std nii_tsnr = nii_mean nii_tsnr.data = data_tsnr nii_tsnr.setFileName(fname_tsnr) nii_tsnr.save() # sct.run('fslmaths ' + fname_data_mean + ' -div ' + fname_data_std + ' ' + fname_tsnr) # Remove temp files sct.printv('\nRemove temporary files...', self.param.verbose, 'normal') import os os.remove(fname_data_mean) os.remove(fname_data_std) # to view results sct.printv('\nDone! To view results, type:', self.param.verbose, 'normal') sct.printv('fslview ' + fname_tsnr + ' &\n', self.param.verbose, 'info')
def centerline2roi(fname_image, folder_output='./', verbose=0): """ Tis method converts a binary centerline image to a .roi centerline file Args: fname_image: filename of the binary centerline image, in RPI orientation folder_output: path to output folder where to copy .roi centerline verbose: adjusts the verbosity of the logging. Returns: filename of the .roi centerline that has been created """ path_data, file_data, ext_data = sct.extract_fname(fname_image) fname_output = file_data + '.roi' date_now = datetime.now() ROI_TEMPLATE = 'Begin Marker ROI\n' \ ' Build version="7.0_33"\n' \ ' Annotation=""\n' \ ' Colour=0\n' \ ' Image source="{fname_segmentation}"\n' \ ' Created "{creation_date}" by Operator ID="SCT"\n' \ ' Slice={slice_num}\n' \ ' Begin Shape\n' \ ' X={position_x}; Y={position_y}\n' \ ' End Shape\n' \ 'End Marker ROI\n' im = Image(fname_image) nx, ny, nz, nt, px, py, pz, pt = im.dim coordinates_centerline = im.getNonZeroCoordinates(sorting='z') f = open(fname_output, "w") sct.printv('\nWriting ROI file...', verbose) for coord in coordinates_centerline: coord_phys_center = im.transfo_pix2phys([[(nx - 1) / 2.0, (ny - 1) / 2.0, coord.z]])[0] coord_phys = im.transfo_pix2phys([[coord.x, coord.y, coord.z]])[0] f.write( ROI_TEMPLATE.format( fname_segmentation=fname_image, creation_date=date_now.strftime("%d %B %Y %H:%M:%S.%f %Z"), slice_num=coord.z + 1, position_x=coord_phys_center[0] - coord_phys[0], position_y=coord_phys_center[1] - coord_phys[1])) f.close() if os.path.abspath(folder_output) != os.getcwd(): shutil.copy(fname_output, folder_output) return fname_output
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 register_seg(seg_input, seg_dest): """Slice-by-slice registration by translation of two segmentations. For each slice, we estimate the translation vector by calculating the difference of position of the two centers of mass. The segmentations can be of different sizes but the output segmentation must be smaller than the input segmentation. input: seg_input: name of moving segmentation file (type: string) seg_dest: name of fixed segmentation file (type: string) output: x_displacement: list of translation along x axis for each slice (type: list) y_displacement: list of translation along y axis for each slice (type: list) """ seg_input_img = Image(seg_input) seg_dest_img = Image(seg_dest) seg_input_data = seg_input_img.data seg_dest_data = seg_dest_img.data x_center_of_mass_input = [0 for i in range(seg_dest_data.shape[2])] y_center_of_mass_input = [0 for i in range(seg_dest_data.shape[2])] print "\nGet center of mass of the input segmentation for each slice (corresponding to a slice in the output segmentation)..." # different if size of the two seg are different # TO DO: select only the slices corresponding to the output segmentation coord_origin_dest = seg_dest_img.transfo_pix2phys([[0, 0, 0]]) [[x_o, y_o, z_o]] = seg_input_img.transfo_phys2pix(coord_origin_dest) for iz in xrange(seg_dest_data.shape[2]): x_center_of_mass_input[iz], y_center_of_mass_input[iz] = ndimage.measurements.center_of_mass( array(seg_input_data[:, :, z_o + iz]) ) x_center_of_mass_output = [0 for i in range(seg_dest_data.shape[2])] y_center_of_mass_output = [0 for i in range(seg_dest_data.shape[2])] print "\nGet center of mass of the output segmentation for each slice ..." for iz in xrange(seg_dest_data.shape[2]): x_center_of_mass_output[iz], y_center_of_mass_output[iz] = ndimage.measurements.center_of_mass( array(seg_dest_data[:, :, iz]) ) x_displacement = [0 for i in range(seg_input_data.shape[2])] y_displacement = [0 for i in range(seg_input_data.shape[2])] print "\nGet displacement by voxel..." for iz in xrange(seg_dest_data.shape[2]): x_displacement[iz] = -( x_center_of_mass_output[iz] - x_center_of_mass_input[iz] ) # WARNING: in ITK's coordinate system, this is actually Tx and not -Tx y_displacement[iz] = ( y_center_of_mass_output[iz] - y_center_of_mass_input[iz] ) # This is Ty in ITK's and fslview' coordinate systems return x_displacement, y_displacement
def coregister_model_data(self): # get mean image im_mean = Image(param=self.mean_image) # register all slices WM on mean WM for dic_slice in self.slices: # create a directory to get the warping fields warp_dir = 'wf_slice' + str(dic_slice.id) if not os.path.exists(warp_dir): os.mkdir(warp_dir) # get slice mean WM image im_slice = Image(param=dic_slice.im) # register slice image on mean dic image im_slice_reg, fname_src2dest, fname_dest2src = register_data( im_src=im_slice, im_dest=im_mean, param_reg=self.param_data.register_param, path_copy_warp=warp_dir) shape = im_slice_reg.data.shape # use forward warping field to register all slice wm list_wmseg_reg = [] for wm_seg in dic_slice.wm_seg: im_wmseg = Image(param=wm_seg) im_wmseg_reg = apply_transfo(im_src=im_wmseg, im_dest=im_mean, warp=warp_dir + '/' + fname_src2dest, interp='nn') list_wmseg_reg.append(im_wmseg_reg.data.reshape(shape)) # use forward warping field to register gm seg list_gmseg_reg = [] for gm_seg in dic_slice.gm_seg: im_gmseg = Image(param=gm_seg) im_gmseg_reg = apply_transfo(im_src=im_gmseg, im_dest=im_mean, warp=warp_dir + '/' + fname_src2dest, interp='nn') list_gmseg_reg.append(im_gmseg_reg.data.reshape(shape)) # set slice attributes with data registered into the model space dic_slice.set(im_m=im_slice_reg.data) dic_slice.set(wm_seg_m=list_wmseg_reg) dic_slice.set(gm_seg_m=list_gmseg_reg) # remove warping fields directory if self.param.rm_tmp: shutil.rmtree(warp_dir)
def test_integrity(param_test): """ Test integrity of function """ # initializations mse_detection = float('nan') # extract name of output centerline: data_centerline_optic.nii.gz file_ctr = os.path.join( param_test.path_output, sct.add_suffix(param_test.file_input, '_centerline_optic')) # open output segmentation im_ctr = Image(file_ctr) # open ground truth im_seg_manual = Image(param_test.fname_gt) im_ctr_manual = im_seg_manual.copy() # Create Ctr GT from SC seg GT if im_ctr_manual.orientation != 'RPI': im_ctr_manual.change_orientation('RPI') im_ctr_manua_data = im_ctr_manual.data # Compute center of mass of the SC seg on each axial slice. center_of_mass_x_y_z_lst = [[ int(center_of_mass(im_ctr_manua_data[:, :, zz])[0]), int(center_of_mass(im_ctr_manua_data[:, :, zz])[1]), zz ] for zz in range(im_ctr_manual.dim[2])] im_ctr_manual.data *= 0 for x_y_z in center_of_mass_x_y_z_lst: im_ctr_manual.data[x_y_z[0], x_y_z[1], x_y_z[2]] = 1 # compute MSE between generated ctr and ctr from database mse_detection = compute_mse(im_ctr, im_ctr_manual) param_test.output += 'Computed MSE: ' + str(mse_detection) param_test.output += 'MSE threshold (if computed MSE higher: fail): ' + str( param_test.mse_threshold) if mse_detection > param_test.mse_threshold: param_test.status = 99 param_test.output += '--> FAILED' else: param_test.output += '--> PASSED' # update Panda structure param_test.results['mse_detection'] = mse_detection return param_test
def check_if_same_space(fname_1, fname_2): from msct_image import Image from numpy import min, nonzero, all, around from numpy import abs as np_abs from numpy import log10 as np_log10 im_1 = Image(fname_1) im_2 = Image(fname_2) q1 = im_1.hdr.get_qform() q2 = im_2.hdr.get_qform() dec = int(np_abs(round(np_log10(min(np_abs(q1[nonzero(q1)]))))) + 1) dec = 4 if dec > 4 else dec return all(around(q1, dec) == around(q2, dec))
def scad_propseg_validation(folder_input, contrast): from sct_get_centerline import ind2sub import time import math import numpy import sct_convert as cnv t0 = time.time() current_folder = os.getcwd() os.chdir(folder_input) try: patients = next(os.walk('.'))[1] for i in patients: directory = i + "/" + str(contrast) try: os.chdir(directory) except Exception, e: print str(i)+" : "+contrast+" directory not found" try: if os.path.isfile(i+"_"+contrast+".nii.gz"): raw_image = Image(i+"_"+contrast+".nii.gz") elif os.path.isfile(contrast+".nii.gz"): raw_image = Image(contrast+".nii.gz") else: raise Exception("Patient scan not found") if os.path.isfile(i+"_"+contrast+"_manual_segmentation.nii.gz"): manual_segmentation = i+"_"+contrast+"_manual_segmentation.nii.gz" # Using propseg default sct.run("sct_propseg -i "+raw_image.absolutepath+" -t "+contrast) cnv.convert(raw_image.file_name+"_seg.nii.gz", "propseg_default.nii.gz") # Using scad scad = SCAD(raw_image, contrast=contrast, rm_tmp_file=1, verbose=1) scad.execute() # Using propseg with scad sct.run("sct_propseg -i "+raw_image.absolutepath+" -t "+contrast+" -init-centerline "+scad.output_filename) cnv.convert(raw_image.file_name+"_seg.nii.gz", "propseg_scad.nii.gz") # Calculate dice of propseg_default sct.run("sct_dice_coefficient propseg_default.nii.gz "+manual_segmentation+" -o propseg_default_result.txt") # Calculate dice of propseg_scad sct.run("sct_dice_coefficient propseg_scad.nii.gz "+manual_segmentation+" -o propseg_scad_result.txt") else: printv("Cannot find the manual segmentation", type="warning") except Exception, e: print e.message os.chdir(folder_input)
def orient2pir(self): """Orient input data to PIR orientation.""" if self.orientation_im != 'PIR': # open image and re-orient it to PIR if needed im_tmp = Image(self.fname_im) set_orientation(im_tmp, 'PIR', fname_out=''.join( sct.extract_fname(self.fname_im)[1:])) if self.fname_seg is not None: set_orientation(Image(self.fname_seg), 'PIR', fname_out=''.join( sct.extract_fname(self.fname_seg)[1:]))
def concat_data(fname_in, fname_out, dim): """ Concatenate data :param fname_in: list of file names. :param fname_out: :param dim: dimension: 0, 1, 2, 3. :return: none """ # create empty list list_data = [] # loop across files for i in range(len(fname_in)): # append data to list list_data.append(Image(fname_in[i]).data) # expand dimension of all elements in the list if necessary if dim > list_data[0].ndim - 1: list_data = [expand_dims(i, dim) for i in list_data] # concatenate try: data_concat = concatenate(list_data, axis=dim) except Exception as e: sct.printv( '\nERROR: Concatenation on line {}'.format( sys.exc_info()[-1].tb_lineno) + '\n' + str(e) + '\n', 1, 'error') # write file im = Image(fname_in[0]) im.data = data_concat im.setFileName(fname_out) im.save()
def crop_from_mask_with_background(self): from numpy import asarray, einsum image_in = Image(self.input_filename) data_array = asarray(image_in.data) data_mask = asarray(Image(self.mask).data) assert data_array.shape == data_mask.shape # Element-wise matrix multiplication: new_data = None dim = len(data_array.shape) if dim == 3: new_data = einsum('ijk,ijk->ijk', data_mask, data_array) elif dim == 2: new_data = einsum('ij,ij->ij', data_mask, data_array) if self.background != 0: from sct_maths import get_data_or_scalar data_background = get_data_or_scalar(str(self.background), data_array) data_mask_inv = data_mask.max() - data_mask if dim == 3: data_background = einsum('ijk,ijk->ijk', data_mask_inv, data_background) elif dim == 2: data_background = einsum('ij,ij->ij', data_mask_inv, data_background) new_data += data_background # set image out image_in.setFileName(self.output_filename) image_in.data = new_data image_in.save()
def angle_correction(self): # Empty arrays in which angle for each z slice will be stored self.angles = np.zeros(Image(self.fname_mask).dim[2]) if self.fname_sc is not None: im_seg = Image(self.fname_sc) data_seg = im_seg.data X, Y, Z = (data_seg > 0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # fit centerline, smooth it and return the first derivative (in physical space) x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline( self.fname_sc, algo_fitting='hanning', type_window='hanning', window_length=80, nurbs_pts_number=3000, phys_coordinates=True, verbose=self.verbose, all_slices=False) centerline = Centerline(x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv) # average centerline coordinates over slices of the image x_centerline_deriv_rescorr, y_centerline_deriv_rescorr, z_centerline_deriv_rescorr = centerline.average_coordinates_over_slices( im_seg)[3:] # compute Z axis of the image, in physical coordinate axis_Z = im_seg.get_directions()[2] # for iz in xrange(min_z_index, max_z_index + 1): for zz in range(im_seg.dim[2]): if zz >= min_z_index and zz <= max_z_index: # in the case of problematic segmentation (e.g., non continuous segmentation often at the extremities), display a warning but do not crash try: # normalize the tangent vector to the centerline (i.e. its derivative) tangent_vect = self._normalize( np.array([ x_centerline_deriv_rescorr[zz], y_centerline_deriv_rescorr[zz], z_centerline_deriv_rescorr[zz] ])) # compute the angle between the normal vector of the plane and the vector z self.angles[zz] = np.arccos( np.vdot(tangent_vect, axis_Z)) except IndexError: printv( 'WARNING: Your segmentation does not seem continuous, which could cause wrong estimations at the problematic slices. Please check it, especially at the extremities.', type='warning')
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 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 compute(self): fname_data = self.fmri # # create temporary folder # sct.printv('\nCreate temporary folder...', self.param.verbose) # path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S/") # status, output = sct.run('mkdir '+path_tmp, self.param.verbose) # # motion correct the fmri data # # sct.printv('\nMotion correct the fMRI data...', self.param.verbose, 'normal') # path_fmri, fname_fmri, ext_fmri = sct.extract_fname(self.fmri) # fname_fmri_moco = fname_fmri # # print sct.slash_at_the_end(path_fmri) + fname_fmri # # sct.run('mcflirt -in ' + sct.slash_at_the_end(path_fmri, 1) + fname_fmri + ' -out ' + fname_fmri_moco) # compute mean fname_data_mean = sct.add_suffix(fname_data, '_mean') sct_maths.main( args=['-i', fname_data, '-o', fname_data_mean, '-mean', 't']) # compute STD fname_data_std = sct.add_suffix(fname_data, '_std') sct_maths.main( args=['-i', fname_data, '-o', fname_data_std, '-std', 't']) # compute tSNR fname_tsnr = sct.add_suffix(fname_data, '_tsnr') from msct_image import Image nii_mean = Image(fname_data_mean) data_mean = nii_mean.data data_std = Image(fname_data_std).data data_tsnr = data_mean / data_std nii_tsnr = nii_mean nii_tsnr.data = data_tsnr nii_tsnr.setFileName(fname_tsnr) nii_tsnr.save() # Remove temp files sct.printv('\nRemove temporary files...', self.param.verbose, 'normal') import os os.remove(fname_data_mean) os.remove(fname_data_std) # to view results sct.printv('\nDone! To view results, type:', self.param.verbose, 'normal') sct.printv('fslview ' + fname_tsnr + ' &\n', self.param.verbose, 'info')
def generate_qc(fn_in, fn_seg, args, path_qc): """Generate a QC entry allowing to quickly review the segmentation process.""" import spinalcordtoolbox.reports.qc as qc import spinalcordtoolbox.reports.slice as qcslice qc.add_entry( src=fn_in, process="sct_deepseg_sc", args=args, path_qc=path_qc, plane='Axial', qcslice=qcslice.Axial([Image(fn_in), Image(fn_seg)]), qcslice_operations=[qc.QcImage.listed_seg], qcslice_layout=lambda x: x.mosaic(), )
def symmetrizer(self, side='left'): """ This function symmetrize the input image. One side of the image will be copied on the other side. We assume a RPI orientation. :param side: string 'left' or 'right'. Side that will be copied on the other side. :return: """ image_output = Image(self.image_input, self.verbose) image_output[0:] """inspiration: (from atlas creation matlab script) temp_sum = temp_g + temp_d; temp_sum_flip = temp_sum(end:-1:1,:); temp_sym = (temp_sum + temp_sum_flip) / 2; temp_g(1:end / 2,:) = 0; temp_g(1 + end / 2:end,:) = temp_sym(1 + end / 2:end,:); temp_d(1:end / 2,:) = temp_sym(1:end / 2,:); temp_d(1 + end / 2:end,:) = 0; tractsHR {label_l}(:,:, num_slice_ref) = temp_g; tractsHR {label_r}(:,:, num_slice_ref) = temp_d; """ return image_output
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 load_manual_gmseg(list_slices_target, list_fname_manual_gmseg, tmp_dir, im_sc_seg_rpi, new_res, square_size_size_mm, for_model=False, fname_mask=None): if isinstance(list_fname_manual_gmseg, str): # consider fname_manual_gmseg as a list of file names to allow multiple manual GM segmentation list_fname_manual_gmseg = [list_fname_manual_gmseg] for fname_manual_gmseg in list_fname_manual_gmseg: os.chdir('..') shutil.copy(fname_manual_gmseg, tmp_dir) # change fname level to only file name (path = tmp dir now) path_gm, file_gm, ext_gm = extract_fname(fname_manual_gmseg) fname_manual_gmseg = file_gm + ext_gm os.chdir(tmp_dir) im_manual_gmseg = Image(fname_manual_gmseg) # reorient to RPI im_manual_gmseg = set_orientation(im_manual_gmseg, 'RPI') if fname_mask is not None: fname_gmseg_crop = add_suffix(im_manual_gmseg.absolutepath, '_pre_crop') crop_im = ImageCropper(input_file=im_manual_gmseg.absolutepath, output_file=fname_gmseg_crop, mask=fname_mask) im_manual_gmseg_crop = crop_im.crop() im_manual_gmseg = im_manual_gmseg_crop # assert gmseg has the right number of slices assert im_manual_gmseg.data.shape[2] == len( list_slices_target ), 'ERROR: the manual GM segmentation has not the same number of slices than the image.' # interpolate gm to reference image nz_gmseg, nx_gmseg, ny_gmseg, nt_gmseg, pz_gmseg, px_gmseg, py_gmseg, pt_gmseg = im_manual_gmseg.dim list_im_gm = interpolate_im_to_ref(im_manual_gmseg, im_sc_seg_rpi, new_res=new_res, sq_size_size_mm=square_size_size_mm, interpolation_mode=0) # load gm seg in list of slices n_poped = 0 for im_gm, slice_im in zip(list_im_gm, list_slices_target): if im_gm.data.max() == 0 and for_model: list_slices_target.pop(slice_im.id - n_poped) n_poped += 1 else: slice_im.gm_seg.append(im_gm.data) wm_slice = (slice_im.im > 0) - im_gm.data slice_im.wm_seg.append(wm_slice) return list_slices_target
def __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 apply_transfo(im_src, im_dest, warp, interp='spline', rm_tmp=True): # create tmp dir and go in it tmp_dir = sct.tmp_create() # copy warping field to tmp dir sct.copy(warp, tmp_dir) warp = ''.join(extract_fname(warp)[1:]) # go to tmp dir curdir = os.getcwd() os.chdir(tmp_dir) # save image and seg fname_src = 'src.nii.gz' im_src.setFileName(fname_src) im_src.save() fname_dest = 'dest.nii.gz' im_dest.setFileName(fname_dest) im_dest.save() # apply warping field fname_src_reg = add_suffix(fname_src, '_reg') sct_apply_transfo.main( args=['-i', fname_src, '-d', fname_dest, '-w', warp, '-x', interp]) im_src_reg = Image(fname_src_reg) # get out of tmp dir os.chdir(curdir) if rm_tmp: # remove tmp dir sct.rmtree(tmp_dir) # return res image return im_src_reg
def use_viewer_to_define_labels(fname_data, first_label, nb_of_slices_to_mean): from sct_viewer import ClickViewerGroundTruth from msct_image import Image import sct_image image_input = Image(fname_data) image_input_orientation = sct_image.orientation(image_input, get=True, verbose=False) reoriented_image_filename = 'reoriented_image_source.nii.gz' path_tmp_viewer = sct.tmp_create(verbose=False) cmd_image = 'sct_image -i "%s" -o "%s" -setorient SAL -v 0' % ( fname_data, reoriented_image_filename) sct.run(cmd_image, verbose=False) im_input_SAL = prepare_input_image_for_viewer(fname_data) viewer = ClickViewerGroundTruth(im_input_SAL, first_label, orientation_subplot=['sag', 'ax']) set_viewer_parameters(viewer, nb_of_slices_to_mean) mask_points = viewer.start() if not mask_points and viewer.closed: mask_points = viewer.list_points_useful_notation make_labels_image_from_list_points(mask_points, reoriented_image_filename, image_input_orientation)
def test_integrity(param_test): """ Test integrity of function """ path_data = os.path.join(param_test.path_data, param_test.contrast, param_test.file_input) integrity_value = 1.0 # open output segmentation try: im_seg_manual = Image(path_data) except: param_test.output += 'ERROR: Cannot open ground truth segmentation: ' + path_data param_test.status = 99 return param_test # compute dice coefficient between generated image and image from database dice_segmentation = compute_dice(im_seg_manual, im_seg_manual, mode='3d', zboundaries=False) param_test.output += 'Computed dice: ' + str(dice_segmentation) if dice_segmentation != integrity_value: param_test.output += '\nERROR: Dice coefficient should be : ' + str( integrity_value) param_test.status = 99 return param_test
def compute(self): fname_data = self.fmri # open data nii_data = Image(fname_data) data = nii_data.data # compute mean data_mean = np.mean(data, 3) # compute STD data_std = np.std(data, 3, ddof=1) # compute TSNR data_tsnr = data_mean / data_std # save TSNR fname_tsnr = sct.add_suffix(fname_data, '_tsnr') nii_tsnr = nii_data nii_tsnr.data = data_tsnr nii_tsnr.setFileName(fname_tsnr) nii_tsnr.save(type='float32') # to view results sct.printv('\nDone! To view results, type:', self.param.verbose, 'normal') sct.printv('fslview ' + fname_tsnr + ' &\n', self.param.verbose, 'info')
def viewer_centerline(image_fname, interslice_gap, verbose): image_input_reoriented = Image(image_fname) nx, ny, nz, nt, px, py, pz, pt = image_input_reoriented.dim viewer = ClickViewerPropseg(image_input_reoriented) viewer.gap_inter_slice = int( interslice_gap / px) # px because the image is supposed to be SAL orientation viewer.number_of_slices = 0 viewer.calculate_list_slices() # start the viewer that ask the user to enter a few points along the spinal cord mask_points = viewer.start() if not mask_points and viewer.closed: mask_points = viewer.list_points_useful_notation if mask_points: # create the mask containing either the three-points or centerline mask for initialization mask_filename = sct.add_suffix(image_fname, "_mask_viewer") sct.run("sct_label_utils -i " + image_fname + " -create " + mask_points + " -o " + mask_filename, verbose=False) fname_output = mask_filename else: sct.printv( '\nERROR: the viewer has been closed before entering all manual points. Please try again.', 1, type='error') fname_output = None return fname_output
def pad_im(fname_im, nx_full, ny_full, nz_full, xi, xf, yi, yf, zi, zf): fname_im_pad = sct.add_suffix(fname_im, '_pad') pad_xi = str(xi) pad_xf = str(nx_full - (xf + 1)) pad_yi = str(yi) pad_yf = str(ny_full - (yf + 1)) pad_zi = str(zi) pad_zf = str(nz_full - (zf + 1)) pad = ','.join([pad_xi, pad_xf, pad_yi, pad_yf, pad_zi, pad_zf]) if len(Image(fname_im).data.shape) == 5: status, output = sct.run('sct_image -i ' + fname_im + ' -mcs') s = 'Created file(s):\n-->' output_fnames = output[output.find(s) + len(s):].split('\n')[0].split("'") fname_comp_list = [ output_fnames[i] for i in range(1, len(output_fnames), 2) ] fname_comp_pad_list = [] for fname_comp in fname_comp_list: fname_comp_pad = sct.add_suffix(fname_comp, '_pad') sct.run('sct_image -i ' + fname_comp + ' -pad-asym ' + pad + ' -o ' + fname_comp_pad) fname_comp_pad_list.append(fname_comp_pad) components = ','.join(fname_comp_pad_list) sct.run('sct_image -i ' + components + ' -omc -o ' + fname_im_pad) sct.check_file_exist(fname_im_pad, verbose=1) else: sct.run('sct_image -i ' + fname_im + ' -pad-asym ' + pad + ' -o ' + fname_im_pad) return fname_im_pad
def compute_mtsat_from_file(fname_mt, fname_pd, fname_t1, tr_mt, tr_pd, tr_t1, fa_mt, fa_pd, fa_t1, fname_b1map=None, fname_mtsat=None, fname_t1map=None, verbose=1): """ Compute MTsat and T1map. :param fname_mt: :param fname_pd: :param fname_t1: :param tr_mt: :param tr_pd: :param tr_t1: :param fa_mt: :param fa_pd: :param fa_t1: :param fname_b1map: :param fname_mtsat: :param fname_t1map: :param verbose: :return: fname_mtsat: file name for MTsat map :return: fname_t1map: file name for T1 map """ # load data sct.printv('Load data...', verbose) nii_mt = Image(fname_mt) nii_pd = Image(fname_pd) nii_t1 = Image(fname_t1) if fname_b1map is None: nii_b1map = None else: nii_b1map = Image(fname_b1map) # compute MTsat nii_mtsat, nii_t1map = compute_mtsat(nii_mt, nii_pd, nii_t1, tr_mt, tr_pd, tr_t1, fa_mt, fa_pd, fa_t1, nii_b1map=nii_b1map, verbose=verbose) # Output MTsat and T1 maps # by default, output in the same directory as the input images sct.printv('Generate output files...', verbose) if fname_mtsat is None: fname_mtsat = os.path.join(nii_mt.path, "mtsat.nii.gz") nii_mtsat.setFileName(fname_mtsat) nii_mtsat.save() if fname_t1map is None: fname_t1map = os.path.join(nii_mt.path, "t1map.nii.gz") nii_t1map.setFileName(fname_t1map) nii_t1map.save() return fname_mtsat, fname_t1map
def label_segmentation(fname_seg, list_disc_z, list_disc_value, verbose=1): """ Label segmentation image :param fname_seg: fname of the segmentation :param list_disc_z: list of z that correspond to a disc :param list_disc_value: list of associated disc values :param verbose: :return: """ # open segmentation seg = Image(fname_seg) dim = seg.dim ny = dim[1] nz = dim[2] # loop across z for iz in range(nz): # get index of the disc right above iz try: ind_above_iz = max( [i for i in range(len(list_disc_z)) if list_disc_z[i] > iz]) except ValueError: # if ind_above_iz is empty, attribute value 0 vertebral_level = 0 else: # assign vertebral level (add one because iz is BELOW the disk) vertebral_level = list_disc_value[ind_above_iz] + 1 # sct.printv(vertebral_level) # get voxels in mask ind_nonzero = np.nonzero(seg.data[:, :, iz]) seg.data[ind_nonzero[0], ind_nonzero[1], iz] = vertebral_level if verbose == 2: import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt plt.figure(50) plt.scatter(int(round(ny / 2)), iz, c=vertebral_level, vmin=min(list_disc_value), vmax=max(list_disc_value), cmap='prism', marker='_', s=200) # write file seg.file_name += '_labeled' seg.save()
def get_minimum_path_nii(fname): from msct_image import Image data = Image(fname) vesselness_data = data.data raw_orient = data.change_orientation() result, J1, J2 = get_minimum_path(data.data, invert=1) data.data = result data.change_orientation(raw_orient) data.file_name += '_minimalpath' data.save()
def execute(self): """ This method executes the symmetry detection :return: returns the symmetry data """ img = Image(self.input_image) raw_orientation = img.change_orientation() data = np.squeeze(img.data) dim = data.shape section_length = dim[1]/self.nb_sections result = np.zeros(dim) for i in range(0, self.nb_sections): if (i+1)*section_length > dim[1]: y_length = (i+1)*section_length - ((i+1)*section_length - dim[1]) result[:, i*section_length:i*section_length + y_length, :] = symmetry_detector_right_left(data[:, i*section_length:i*section_length + y_length, :], cropped_xy=self.crop_xy) sym = symmetry_detector_right_left(data[:, i*section_length:(i+1)*section_length, :], cropped_xy=self.crop_xy) result[:, i*section_length:(i+1)*section_length, :] = sym result_image = Image(img) if len(result_image.data) == 4: result_image.data = result[:,:,:,np.newaxis] else: result_image.data = result result_image.change_orientation(raw_orientation) return result_image.data
def __init__(self, fname_label, fname_output=None, fname_ref=None, cross_radius=5, dilate=False, coordinates=None, verbose=1): self.image_input = Image(fname_label, verbose=verbose) if fname_ref is not None: self.image_ref = Image(fname_ref, verbose=verbose) if isinstance(fname_output, list): if len(fname_output) == 1: self.fname_output = fname_output[0] else: self.fname_output = fname_output else: self.fname_output = fname_output self.cross_radius = cross_radius self.dilate = dilate self.coordinates = coordinates self.verbose = verbose
def get_minimum_path_nii(fname): from msct_image import Image data=Image(fname) vesselness_data = data.data raw_orient=data.change_orientation() data.data=get_minimum_path(data.data, invert=1) data.change_orientation(raw_orient) data.file_name += '_minimalpath' data.save()
def label_segmentation(fname_seg, list_disc_z, list_disc_value, verbose=1): """ Label segmentation image :param fname_seg: fname of the segmentation :param list_disc_z: list of z that correspond to a disc :param list_disc_value: list of associated disc values :param verbose: :return: """ # open segmentation seg = Image(fname_seg) dim = seg.dim ny = dim[1] nz = dim[2] # open labeled discs im_discs = Image(fname_seg) # loop across z for iz in range(nz): # get index of the disc right above iz try: ind_above_iz = max([i for i in range(len(list_disc_z)) if list_disc_z[i] > iz]) except ValueError: # if ind_above_iz is empty, attribute value 0 vertebral_level = 0 else: # assign vertebral level (add one because iz is BELOW the disk) vertebral_level = list_disc_value[ind_above_iz] + 1 # print vertebral_level # get voxels in mask ind_nonzero = np.nonzero(seg.data[:, :, iz]) seg.data[ind_nonzero[0], ind_nonzero[1], iz] = vertebral_level if verbose == 2: import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt plt.figure(50) plt.scatter(int(round(ny/2)), iz, c=vertebral_level, vmin=min(list_disc_value), vmax=max(list_disc_value), cmap='prism', marker='_', s=200) # write file seg.file_name += '_labeled' seg.save()
def __init__(self, target_fname, sc_seg_fname, t2_data=None, denoising=True): self.t2star = 't2star.nii.gz' self.sc_seg = 't2star_sc_seg.nii.gz' self.t2 = 't2.nii.gz' self.t2_seg = 't2_seg.nii.gz' self.t2_landmarks = 't2_landmarks.nii.gz' self.resample_to = 0.3 sct.run('cp ../' + target_fname + ' ./' + self.t2star) sct.run('cp ../' + sc_seg_fname + ' ./' + self.sc_seg) nx, ny, nz, nt, self.original_px, self.original_py, pz, pt = sct.get_dimension(self.t2star) if round(self.original_px, 2) != self.resample_to or round(self.original_py, 2) != self.resample_to: self.t2star = resample_image(self.t2star, npx=self.resample_to, npy=self.resample_to) self.sc_seg = resample_image(self.sc_seg, binary=True, npx=self.resample_to, npy=self.resample_to) t2star_im = Image(self.t2star) if denoising: t2star_im.denoise_ornlm() t2star_im.save() self.t2star = t2star_im.file_name + t2star_im.ext ''' status, t2_star_orientation = sct.run('sct_orientation -i ' + self.t2star) self.original_orientation = t2_star_orientation[4:7] ''' self.original_orientation = t2star_im.orientation self.square_mask = crop_t2_star(self.t2star, self.sc_seg, box_size=75) self.treated_target = sct.extract_fname(self.t2star)[1] + '_seg_in_croped.nii.gz' self.level_fname = None if t2_data is not None: sct.run('cp ../' + t2_data[0] + ' ./' + self.t2) sct.run('cp ../' + t2_data[1] + ' ./' + self.t2_seg) sct.run('cp ../' + t2_data[2] + ' ./' + self.t2_landmarks) self.level_fname = compute_level_file(self.t2star, self.sc_seg, self.t2, self.t2_seg, self.t2_landmarks)
def 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 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 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 __init__(self, fname): self.image = Image(fname) self.pretreated_im = self.pretreat() self.result_im = self.image.copy() self.result_im.file_name = self.image.file_name + '_vessel_mask' self.result_im.path = './' self.hess = hessian(self.pretreated_im.data) self.vessel_mask = self.compute_vessel_mask() self.result_im.data = self.vessel_mask self.result_im.save()
def __init__( self, fname_gm, fname_wm, path_template, fname_warp_template2target, param=None, fname_warp_target2template=None, apply_warp_template=0, ): 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) if "MNI-Poly-AMU_GM.nii.gz" in os.listdir(self.path_template + "template/"): self.im_template_gm = Image(self.path_template + "template/MNI-Poly-AMU_GM.nii.gz") self.im_template_wm = Image(self.path_template + "template/MNI-Poly-AMU_WM.nii.gz") self.template = "MNI-Poly-AMU" else: self.im_template_gm = Image(self.path_template + "template/PAM50_gm.nii.gz") self.im_template_wm = Image(self.path_template + "template/PAM50_wm.nii.gz") self.template = "PAM50" # 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_wwarp_gm2template = "" # temporary fix - related to issue #871 self.apply_warp_template = apply_warp_template
def segment(self): before = time.time() sct.run('mkdir ' + self.tmp_dir) self.segmentation_pipeline() # Generate output files: for res_fname in self.res_names.values(): sct.generate_output_file(self.tmp_dir+res_fname, self.seg_param.output_path+res_fname) if self.ref_gm_seg_fname is not None: sct.generate_output_file(self.tmp_dir+self.dice_name, self.seg_param.output_path+self.dice_name) sct.generate_output_file(self.tmp_dir+self.hausdorff_name, self.seg_param.output_path+self.hausdorff_name) if compute_ratio: sct.generate_output_file(self.tmp_dir+self.ratio_name, self.seg_param.output_path+self.ratio_name) after = time.time() sct.printv('Done! (in ' + str(after-before) + ' sec) \nTo see the result, type :') if self.seg_param.res_type == 'binary': wm_col = 'Red' gm_col = 'Blue' b = '0,1' else: wm_col = 'Blue-Lightblue' gm_col = 'Red-Yellow' b = '0.3,1' sct.printv('fslview ' + self.target_fname + ' '+self.seg_param.output_path+self.res_names['wm_seg']+' -l '+wm_col+' -t 0.4 -b '+b+' '+self.seg_param.output_path+self.res_names['gm_seg']+' -l '+gm_col+' -t 0.4 -b '+b+' &', self.seg_param.verbose, 'info') if self.seg_param.qc: # output QC image im = Image(self.target_fname) im_gmseg = Image(self.seg_param.output_path+self.res_names['gm_seg']) im.save_quality_control(plane='axial', n_slices=5, seg=im_gmseg, thr=float(b.split(',')[0]), cmap_col='red-yellow', path_output=self.seg_param.output_path) if self.seg_param.remove_tmp: sct.printv('Remove temporary folder ...', self.seg_param.verbose, 'normal') sct.run('rm -rf '+self.tmp_dir)
def register_seg(seg_input, seg_dest): seg_input_img = Image(seg_input) seg_dest_img = Image(seg_dest) seg_input_data = seg_input_img.data seg_dest_data = seg_dest_img.data x_center_of_mass_input = [0 for i in range(seg_dest_data.shape[2])] y_center_of_mass_input = [0 for i in range(seg_dest_data.shape[2])] print "\nGet center of mass of the input segmentation for each slice (corresponding to a slice in the output segmentation)..." # different if size of the two seg are different # TO DO: select only the slices corresponding to the output segmentation coord_origin_dest = seg_dest_img.transfo_pix2phys([[0, 0, 0]]) [[x_o, y_o, z_o]] = seg_input_img.transfo_phys2pix(coord_origin_dest) for iz in xrange(seg_dest_data.shape[2]): print iz x_center_of_mass_input[iz], y_center_of_mass_input[iz] = ndimage.measurements.center_of_mass( array(seg_input_data[:, :, z_o + iz]) ) x_center_of_mass_output = [0 for i in range(seg_dest_data.shape[2])] y_center_of_mass_output = [0 for i in range(seg_dest_data.shape[2])] print "\nGet center of mass of the output segmentation for each slice ..." for iz in xrange(seg_dest_data.shape[2]): x_center_of_mass_output[iz], y_center_of_mass_output[iz] = ndimage.measurements.center_of_mass( array(seg_dest_data[:, :, iz]) ) x_displacement = [0 for i in range(seg_input_data.shape[2])] y_displacement = [0 for i in range(seg_input_data.shape[2])] print "\nGet displacement by voxel..." for iz in xrange(seg_dest_data.shape[2]): x_displacement[iz] = -( x_center_of_mass_output[iz] - x_center_of_mass_input[iz] ) # strangely, this is the inverse of x_displacement when the same equation defines y_displacement y_displacement[iz] = y_center_of_mass_output[iz] - y_center_of_mass_input[iz] return x_displacement, y_displacement
def resample_image(fname, suffix='_resampled.nii.gz', binary=False, npx=0.3, npy=0.3, thr=0.0, interpolation='spline'): """ Resampling function: add a padding, resample, crop the padding :param fname: name of the image file to be resampled :param suffix: suffix added to the original fname after resampling :param binary: boolean, image is binary or not :param npx: new pixel size in the x direction :param npy: new pixel size in the y direction :param thr: if the image is binary, it will be thresholded at thr (default=0) after the resampling :param interpolation: type of interpolation used for the resampling :return: file name after resampling (or original fname if it was already in the correct resolution) """ im_in = Image(fname) orientation = get_orientation_3d(im_in) if orientation != 'RPI': im_in = set_orientation(im_in, 'RPI') im_in.save() fname = im_in.absolutepath nx, ny, nz, nt, px, py, pz, pt = im_in.dim if round(px, 2) != round(npx, 2) or round(py, 2) != round(npy, 2): name_resample = sct.extract_fname(fname)[1] + suffix if binary: interpolation = 'nn' sct.run('sct_resample -i '+fname+' -mm '+str(npx)+'x'+str(npy)+'x'+str(pz)+' -o '+name_resample+' -x '+interpolation) if binary: # sct.run('sct_maths -i ' + name_resample + ' -thr ' + str(thr) + ' -o ' + name_resample) sct.run('sct_maths -i ' + name_resample + ' -bin ' + str(thr) + ' -o ' + name_resample) if orientation != 'RPI': im_resample = Image(name_resample) im_resample = set_orientation(im_resample, orientation) im_resample.save() name_resample = im_resample.absolutepath return name_resample else: if orientation != 'RPI': im_in = set_orientation(im_in, orientation) im_in.save() fname = im_in.absolutepath sct.printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz') return fname
def 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 output_debug_file(self, img, data, file_name): """ This method writes a nifti file that corresponds to a step in the algorithm for easy debug. The new nifti file uses the header from the the image passed as parameter :param data: data to be written to file :param file_name: filename... :return: None """ if self.verbose == 2: current_folder = os.getcwd() # os.chdir(self.path_tmp) try: img = Image(img) img.data = data img.change_orientation(self.raw_orientation) img.file_name = file_name img.save() except Exception, e: print e