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 create_label_z(fname_seg, z, value): """ Create a label at coordinates x_center, y_center, z :param fname_seg: segmentation :param z: int :return: fname_label """ fname_label = 'labelz.nii.gz' nii = Image(fname_seg) orientation_origin = nii.change_orientation( 'RPI') # change orientation to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions # find x and y coordinates of the centerline at z using center of mass from scipy.ndimage.measurements import center_of_mass x, y = center_of_mass(nii.data[:, :, z]) x, y = int(round(x)), int(round(y)) nii.data[:, :, :] = 0 nii.data[x, y, z] = value # dilate label to prevent it from disappearing due to nearestneighbor interpolation from sct_maths import dilate nii.data = dilate(nii.data, [3]) nii.setFileName(fname_label) nii.change_orientation( orientation_origin) # put back in original orientation nii.save() return fname_label
def 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: fname_in = im.absolutepath if not os.path.exists(fname_in): im.save() 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 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 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 get_minimum_path_nii(fname): from msct_image import Image data=Image(fname) vesselness_data = data.data raw_orient=data.change_orientation() data.data=get_minimum_path(data.data, invert=1) data.change_orientation(raw_orient) data.file_name += '_minimalpath' data.save()
def get_minimum_path_nii(fname): from msct_image import Image data = Image(fname) vesselness_data = data.data raw_orient = data.change_orientation() result, J1, J2 = get_minimum_path(data.data, invert=1) data.data = result data.change_orientation(raw_orient) data.file_name += '_minimalpath' data.save()
def set_orientation(fname_in, orientation, fname_out, inversion=False): if not inversion: sct.run('isct_orientation3d -i '+fname_in+' -orientation '+orientation+' -o '+fname_out, 0) else: from msct_image import Image input_image = Image(fname_in) input_image.change_orientation(orientation, True) input_image.setFileName(fname_out) input_image.save() # return full path return os.path.abspath(fname_out)
def set_orientation(fname_in, orientation, fname_out, inversion=False): if not inversion: sct.run('isct_orientation3d -i '+fname_in+' -orientation '+orientation+' -o '+fname_out, 0) else: from msct_image import Image input_image = Image(fname_in) input_image.change_orientation(orientation, True) input_image.setFileName(fname_out) input_image.save() # return full path return os.path.abspath(fname_out)
def main(fname_anat, fname_centerline, degree_poly, centerline_fitting, interp, remove_temp_files, verbose): # load input image im_anat = Image(fname_anat) nx, ny, nz, nt, px, py, pz, pt = im_anat.dim # re-oriente to RPI orientation_native = im_anat.change_orientation('RPI') # load centerline im_centerline = Image(fname_centerline) im_centerline.change_orientation('RPI') # smooth centerline and return fitted coordinates in voxel space x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline( im_centerline, algo_fitting=centerline_fitting, type_window='hanning', window_length=50, nurbs_pts_number=3000, phys_coordinates=False, verbose=verbose, all_slices=True) # compute translation for each slice, such that the flattened centerline is centered in the medial plane (R-L) and # avoid discontinuity in slices where there is no centerline (in which case, simply copy the translation of the # closest Z). # first, get zmin and zmax spanned by the centerline (i.e. with non-zero values) indz_centerline = np.where([np.sum(im_centerline.data[:, :, iz]) for iz in range(nz)])[0] zmin, zmax = indz_centerline[0], indz_centerline[-1] # then, extend the centerline by padding values below zmin and above zmax x_centerline_extended = np.concatenate([np.ones(zmin) * x_centerline_fit[0], x_centerline_fit, np.ones(nz-zmax-1) * x_centerline_fit[-1]]) # loop across slices and apply translation im_anat_flattened = im_anat.copy() # change type to float32 because of subsequent conversion (img_as_float). See #1790 im_anat_flattened.changeType('float32') for iz in range(nz): # compute translation along x (R-L) translation_x = x_centerline_extended[iz] - round(nx/2.0) # apply transformation to 2D image with linear interpolation # tform = tf.SimilarityTransform(scale=1, rotation=0, translation=(translation_x, 0)) tform = transform.SimilarityTransform(translation=(0, translation_x)) # important to force input in float to skikit image, because it will output float values img = img_as_float(im_anat.data[:, :, iz]) img_reg = transform.warp(img, tform) im_anat_flattened.data[:, :, iz] = img_reg # img_as_uint(img_reg) # change back to native orientation im_anat_flattened.change_orientation(orientation_native) # save output fname_out = sct.add_suffix(fname_anat, '_flatten') im_anat_flattened.setFileName(fname_out) im_anat_flattened.save() sct.display_viewer_syntax([fname_anat, fname_out])
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: if filename: sct.run([ 'isct_orientation3d', '-i', im, '-orientation', orientation, '-o', fname_out ], verbose=0) im_out = fname_out else: fname_in = im.absolutepath if not os.path.exists(fname_in): im.save() sct.run([ 'isct_orientation3d', '-i', im.absolutepath, '-orientation', orientation, '-o', fname_out ], verbose=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 output_debug_file(self, img, data, file_name): """ This method writes a nifti file that corresponds to a step in the algorithm for easy debug. The new nifti file uses the header from the the image passed as parameter :param data: data to be written to file :param file_name: filename... :return: None """ if self.verbose == 2: current_folder = os.getcwd() # os.chdir(self.path_tmp) try: img = Image(img) img.data = data img.change_orientation(self.raw_orientation) img.file_name = file_name img.save() except Exception, e: print e
def output_debug_file(self, img, data, file_name): """ This method writes a nifti file that corresponds to a step in the algorithm for easy debug. The new nifti file uses the header from the the image passed as parameter :param data: data to be written to file :param file_name: filename... :return: None """ if self.produce_output: current_folder = os.getcwd() os.chdir(self.debug_folder) try: img = Image(img) img.data = data img.change_orientation(self.raw_orientation) img.file_name = file_name img.save() except Exception, e: print e os.chdir(current_folder)
def label_discs(fname_seg_labeled, verbose=1): """ Label discs from labaled_segmentation :param fname_seg_labeld: fname of the labeled segmentation :param verbose: :return: """ # open labeled segmentation im_seg_labeled = Image(fname_seg_labeled) orientation_native = im_seg_labeled.change_orientation('RPI') nx, ny, nz = im_seg_labeled.dim[0], im_seg_labeled.dim[ 1], im_seg_labeled.dim[2] data_disc = np.zeros([nx, ny, nz]) vertebral_level_previous = np.max(im_seg_labeled.data) # loop across z for iz in range(nz): # get 2d slice slice = im_seg_labeled.data[:, :, iz] # check if at least one voxel is non-zero if np.any(slice): slice_one = np.copy(slice) # set all non-zero values to 1 slice_one[slice.nonzero()] = 1 # compute center of mass cx, cy = [ int(x) for x in np.round(center_of_mass(slice_one)).tolist() ] # retrieve vertebral level vertebral_level = slice[cx, cy] # if smaller than previous level, then labeled as a disc if vertebral_level < vertebral_level_previous: # label disc # sct.printv('iz='+iz+', disc='+vertebral_level) data_disc[cx, cy, iz] = vertebral_level # update variable vertebral_level_previous = vertebral_level # save disc labeled file im_seg_labeled.file_name += '_disc' im_seg_labeled.data = data_disc im_seg_labeled.change_orientation(orientation_native) im_seg_labeled.save()
def label_discs(fname_seg_labeled, verbose=1): """ Label discs from labaled_segmentation :param fname_seg_labeld: fname of the labeled segmentation :param verbose: :return: """ # open labeled segmentation im_seg_labeled = Image(fname_seg_labeled) orientation_native = im_seg_labeled.change_orientation('RPI') nx, ny, nz = im_seg_labeled.dim[0], im_seg_labeled.dim[1], im_seg_labeled.dim[2] data_disc = np.zeros([nx, ny, nz]) vertebral_level_previous = np.max(im_seg_labeled.data) # loop across z for iz in range(nz): # get 2d slice slice = im_seg_labeled.data[:, :, iz] # check if at least one voxel is non-zero if np.any(slice): slice_one = np.copy(slice) # set all non-zero values to 1 slice_one[slice.nonzero()] = 1 # compute center of mass cx, cy = [int(x) for x in np.round(center_of_mass(slice_one)).tolist()] # retrieve vertebral level vertebral_level = slice[cx, cy] # if smaller than previous level, then labeled as a disc if vertebral_level < vertebral_level_previous: # label disc # print 'iz='+iz+', disc='+vertebral_level data_disc[cx, cy, iz] = vertebral_level # update variable vertebral_level_previous = vertebral_level # save disc labeled file im_seg_labeled.file_name += '_disc' im_seg_labeled.data = data_disc im_seg_labeled.change_orientation(orientation_native) im_seg_labeled.save()
def 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 create_label_z(fname_seg, z, value): """ Create a label at coordinates x_center, y_center, z :param fname_seg: segmentation :param z: int :return: fname_label """ fname_label = 'labelz.nii.gz' nii = Image(fname_seg) orientation_origin = nii.change_orientation('RPI') # change orientation to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions # find x and y coordinates of the centerline at z using center of mass from scipy.ndimage.measurements import center_of_mass x, y = center_of_mass(nii.data[:, :, z]) x, y = int(round(x)), int(round(y)) nii.data[:, :, :] = 0 nii.data[x, y, z] = value # dilate label to prevent it from disappearing due to nearestneighbor interpolation from sct_maths import dilate nii.data = dilate(nii.data, [3]) nii.setFileName(fname_label) nii.change_orientation(orientation_origin) # put back in original orientation nii.save() return fname_label
def test(path_data='', parameters=''): if not parameters: parameters = '-i t2/t2.nii.gz -c t2 -p auto' # parameters folder_data = 't2/' file_data = [ 't2.nii.gz', 't2_centerline_init.nii.gz', 't2_centerline_labels.nii.gz', 't2_seg_manual.nii.gz' ] parser = sct_get_centerline.get_parser() dict_param = parser.parse(parameters.split(), check_file_exist=False) contrast = dict_param['-c'] dict_param_with_path = parser.add_path_to_file(dict_param, path_data, input_file=True) param_with_path = parser.dictionary_to_string(dict_param_with_path) # Check if input files exist if not (os.path.isfile(dict_param_with_path['-i'])): status = 200 output = 'ERROR: the file(s) provided to test function do not exist in folder: ' + path_data return status, output, DataFrame(data={ 'status': status, 'output': output, 'mse': float('nan'), 'dist_max': float('nan') }, index=[path_data]) cmd = 'sct_get_centerline ' + param_with_path status, output = sct.run(cmd, 0) scad_centerline = Image(contrast + "_centerline.nii.gz") manual_seg = Image(path_data + folder_data + contrast + '_seg_manual.nii.gz') max_distance = 0 standard_deviation = 0 average = 0 root_mean_square = 0 overall_distance = 0 max_distance = 0 overall_std = 0 rmse = 0 try: if status == 0: manual_seg.change_orientation() scad_centerline.change_orientation() from scipy.ndimage.measurements import center_of_mass # find COM iterator = range(manual_seg.data.shape[2]) com_x = [0 for ix in iterator] com_y = [0 for iy in iterator] for iz in iterator: com_x[iz], com_y[iz] = center_of_mass(manual_seg.data[:, :, iz]) max_distance = {} distance = {} for iz in range(1, scad_centerline.data.shape[2] - 1): ind1 = np.argmax(scad_centerline.data[:, :, iz]) X, Y = ind2sub(scad_centerline.data[:, :, iz].shape, ind1) com_phys = np.array( manual_seg.transfo_pix2phys([[com_x[iz], com_y[iz], iz]])) scad_phys = np.array( scad_centerline.transfo_pix2phys([[X, Y, iz]])) distance_magnitude = np.linalg.norm([ com_phys[0][0] - scad_phys[0][0], com_phys[0][1] - scad_phys[0][1], 0 ]) if math.isnan(distance_magnitude): print "Value is nan" else: distance[iz] = distance_magnitude max_distance = max(distance.values()) standard_deviation = np.std(np.array(distance.values())) average = sum(distance.values()) / len(distance) root_mean_square = np.sqrt(np.mean(np.square(distance.values()))) overall_distance = average max_distance = max(distance.values()) overall_std = standard_deviation rmse = root_mean_square except Exception, e: sct.printv("Exception found while testing scad integrity") output = e.message
def validate_scad(folder_input, contrast): """ Expecting folder to have the following structure : errsm_01: - t2 -- errsm_01.nii.gz or t2.nii.gz -- :param folder_input: :return: """ from sct_get_centerline import ind2sub import time import math import numpy t0 = time.time() current_folder = os.getcwd() os.chdir(folder_input) try: patients = next(os.walk('.'))[1] overall_distance = {} max_distance = {} standard_deviation = 0 overall_std = {} rmse = {} 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"): raw_orientation = raw_image.change_orientation() scad = SCAD(raw_image, contrast=contrast, rm_tmp_file=1, verbose=1) scad.execute() manual_seg = Image(i+"_"+contrast+"_manual_segmentation.nii.gz") manual_orientation = manual_seg.change_orientation() from scipy.ndimage.measurements import center_of_mass # find COM iterator = range(manual_seg.data.shape[2]) com_x = [0 for ix in iterator] com_y = [0 for iy in iterator] for iz in iterator: com_x[iz], com_y[iz] = center_of_mass(manual_seg.data[:, :, iz]) centerline_scad = Image(i+"_"+contrast+"_centerline.nii.gz") # os.remove(i+"_"+contrast+"_centerline.nii.gz") centerline_scad.change_orientation() distance = {} for iz in range(1, centerline_scad.data.shape[2]-1): ind1 = np.argmax(centerline_scad.data[:, :, iz]) X,Y = ind2sub(centerline_scad.data[:, :, iz].shape,ind1) com_phys = np.array(manual_seg.transfo_pix2phys([[com_x[iz], com_y[iz], iz]])) scad_phys = np.array(centerline_scad.transfo_pix2phys([[X, Y, iz]])) distance_magnitude = np.linalg.norm([com_phys[0][0]-scad_phys[0][0], com_phys[0][1]-scad_phys[0][1], 0]) if math.isnan(distance_magnitude): print "Value is nan" else: distance[iz] = distance_magnitude f = open(i+"_"+contrast+"_results.txt", 'w+') f.write("Patient,Slice,Distance") for key, value in distance.items(): f.write(i+","+str(key)+","+str(value)) standard_deviation = np.std(np.array(distance.values())) average = sum(distance.values())/len(distance) root_mean_square = np.sqrt(np.mean(np.square(distance.values()))) f.write("\nAverage : "+str(average)) f.write("\nStandard Deviation : "+str(standard_deviation)) f.close() overall_distance[i] = average max_distance[i] = max(distance.values()) overall_std[i] = standard_deviation rmse[i] = root_mean_square else: printv("Cannot find the manual segmentation", type="warning") except Exception, e: print e.message os.chdir(folder_input)
class ProcessLabels(object): def __init__(self, fname_label, fname_output=None, fname_ref=None, cross_radius=5, dilate=False, coordinates=None, verbose=1, vertebral_levels=None, value=None): self.image_input = Image(fname_label, verbose=verbose) self.image_ref = None 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.vertebral_levels = vertebral_levels self.dilate = dilate self.coordinates = coordinates self.verbose = verbose self.value = value def process(self, type_process): # for some processes, change orientation of input image to RPI change_orientation = False if type_process in ['vert-body', 'vert-disc', 'vert-continuous']: # get orientation of input image input_orientation = self.image_input.orientation # change orientation self.image_input.change_orientation('RPI') change_orientation = True if type_process == 'add': self.output_image = self.add(self.value) if type_process == 'cross': self.output_image = self.cross() if type_process == 'plan': self.output_image = self.plan(self.cross_radius, 100, 5) if type_process == 'plan_ref': self.output_image = self.plan_ref() if type_process == 'increment': self.output_image = self.increment_z_inverse() if type_process == 'disks': self.output_image = self.labelize_from_disks() if type_process == 'MSE': self.MSE() self.fname_output = None if type_process == 'remove': self.output_image = self.remove_label() if type_process == 'remove-symm': self.output_image = self.remove_label(symmetry=True) if type_process == 'centerline': self.extract_centerline() if type_process == 'create': self.output_image = self.create_label() if type_process == 'create-add': self.output_image = self.create_label(add=True) if type_process == 'display-voxel': self.display_voxel() self.fname_output = None if type_process == 'diff': self.diff() self.fname_output = None if type_process == 'dist-inter': # second argument is in pixel distance self.distance_interlabels(5) self.fname_output = None if type_process == 'cubic-to-point': self.output_image = self.cubic_to_point() if type_process == 'vert-body': self.output_image = self.label_vertebrae(self.vertebral_levels) # if type_process == 'vert-disc': # self.output_image = self.label_disc(self.vertebral_levels) # if type_process == 'label-vertebrae-from-disks': # self.output_image = self.label_vertebrae_from_disks(self.vertebral_levels) if type_process == 'vert-continuous': self.output_image = self.continuous_vertebral_levels() # save the output image as minimized integers if self.fname_output is not None: self.output_image.setFileName(self.fname_output) if change_orientation: self.output_image.change_orientation(input_orientation) if type_process == 'vert-continuous': self.output_image.save('float32') elif type_process != 'plan_ref': self.output_image.save('minimize_int') else: self.output_image.save() def add(self, value): """ This function add a specified value to all non-zero voxels. """ image_output = Image(self.image_input, self.verbose) # image_output.data *= 0 coordinates_input = self.image_input.getNonZeroCoordinates() # for all points with non-zeros neighbors, force the neighbors to 0 for i, coord in enumerate(coordinates_input): image_output.data[int(coord.x), int(coord.y), int(coord.z)] = image_output.data[int(coord.x), int(coord.y), int(coord.z)] + float(value) return image_output def create_label(self, add=False): """ Create an image with labels listed by the user. This method works only if the user inserted correct coordinates. self.coordinates is a list of coordinates (class in msct_types). a Coordinate contains x, y, z and value. If only one label is to be added, coordinates must be completed with '[]' examples: For one label: object_define=ProcessLabels( fname_label, coordinates=[coordi]) where coordi is a 'Coordinate' object from msct_types For two labels: object_define=ProcessLabels( fname_label, coordinates=[coordi1, coordi2]) where coordi1 and coordi2 are 'Coordinate' objects from msct_types """ image_output = self.image_input.copy() if not add: image_output.data *= 0 # loop across labels for i, coord in enumerate(self.coordinates): # display info sct.printv('Label #' + str(i) + ': ' + str(coord.x) + ',' + str(coord.y) + ',' + str(coord.z) + ' --> ' + str(coord.value), 1) image_output.data[int(coord.x), int(coord.y), int(coord.z)] = coord.value return image_output def cross(self): """ create a cross. :return: """ output_image = Image(self.image_input, self.verbose) nx, ny, nz, nt, px, py, pz, pt = Image(self.image_input.absolutepath).dim coordinates_input = self.image_input.getNonZeroCoordinates() d = self.cross_radius # cross radius in pixel dx = d / px # cross radius in mm dy = d / py # clean output_image output_image.data *= 0 cross_coordinates = self.get_crosses_coordinates(coordinates_input, dx, self.image_ref, self.dilate) for coord in cross_coordinates: output_image.data[int(round(coord.x)), int(round(coord.y)), int(round(coord.z))] = coord.value return output_image @staticmethod def get_crosses_coordinates(coordinates_input, gapxy=15, image_ref=None, dilate=False): from msct_types import Coordinate # if reference image is provided (segmentation), we draw the cross perpendicular to the centerline if image_ref is not None: # smooth centerline from sct_straighten_spinalcord import smooth_centerline x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline(self.image_ref, verbose=self.verbose) # compute crosses cross_coordinates = [] for coord in coordinates_input: if image_ref is None: from sct_straighten_spinalcord import compute_cross cross_coordinates_temp = compute_cross(coord, gapxy) else: from sct_straighten_spinalcord import compute_cross_centerline from numpy import where index_z = where(z_centerline == coord.z) deriv = Coordinate([x_centerline_deriv[index_z][0], y_centerline_deriv[index_z][0], z_centerline_deriv[index_z][0], 0.0]) cross_coordinates_temp = compute_cross_centerline(coord, deriv, gapxy) for i, coord_cross in enumerate(cross_coordinates_temp): coord_cross.value = coord.value * 10 + i + 1 # dilate cross to 3x3x3 if dilate: additional_coordinates = [] for coord_temp in cross_coordinates_temp: additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y, coord_temp.z+1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y, coord_temp.z-1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y+1.0, coord_temp.z, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y+1.0, coord_temp.z+1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y+1.0, coord_temp.z-1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y-1.0, coord_temp.z, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y-1.0, coord_temp.z+1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y-1.0, coord_temp.z-1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y, coord_temp.z, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y, coord_temp.z+1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y, coord_temp.z-1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y+1.0, coord_temp.z, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y+1.0, coord_temp.z+1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y+1.0, coord_temp.z-1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y-1.0, coord_temp.z, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y-1.0, coord_temp.z+1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y-1.0, coord_temp.z-1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y, coord_temp.z, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y, coord_temp.z+1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y, coord_temp.z-1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y+1.0, coord_temp.z, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y+1.0, coord_temp.z+1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y+1.0, coord_temp.z-1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y-1.0, coord_temp.z, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y-1.0, coord_temp.z+1.0, coord_temp.value])) additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y-1.0, coord_temp.z-1.0, coord_temp.value])) cross_coordinates_temp.extend(additional_coordinates) cross_coordinates.extend(cross_coordinates_temp) cross_coordinates = sorted(cross_coordinates, key=lambda obj: obj.value) return cross_coordinates def plan(self, width, offset=0, gap=1): """ Create a plane of thickness="width" and changes its value with an offset and a gap between labels. """ image_output = Image(self.image_input, self.verbose) image_output.data *= 0 coordinates_input = self.image_input.getNonZeroCoordinates() # for all points with non-zeros neighbors, force the neighbors to 0 for coord in coordinates_input: image_output.data[:, :, int(coord.z) - width:int(coord.z) + width] = offset + gap * coord.value return image_output 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 cubic_to_point(self): """ Calculate the center of mass of each group of labels and returns a file of same size with only a label by group at the center of mass of this group. It is to be used after applying homothetic warping field to a label file as the labels will be dilated. Be careful: this algorithm computes the center of mass of voxels with same value, if two groups of voxels with the same value are present but separated in space, this algorithm will compute the center of mass of the two groups together. :return: image_output """ # 0. Initialization of output image output_image = self.image_input.copy() output_image.data *= 0 # 1. Extraction of coordinates from all non-null voxels in the image. Coordinates are sorted by value. coordinates = self.image_input.getNonZeroCoordinates(sorting='value') # 2. Separate all coordinates into groups by value groups = dict() for coord in coordinates: if coord.value in groups: groups[coord.value].append(coord) else: groups[coord.value] = [coord] # 3. Compute the center of mass of each group of voxels and write them into the output image for value, list_coord in groups.iteritems(): center_of_mass = sum(list_coord)/float(len(list_coord)) sct.printv("Value = " + str(center_of_mass.value) + " : ("+str(center_of_mass.x) + ", "+str(center_of_mass.y) + ", " + str(center_of_mass.z) + ") --> ( "+ str(round(center_of_mass.x)) + ", " + str(round(center_of_mass.y)) + ", " + str(round(center_of_mass.z)) + ")", verbose=self.verbose) output_image.data[int(round(center_of_mass.x)), int(round(center_of_mass.y)), int(round(center_of_mass.z))] = center_of_mass.value return output_image def increment_z_inverse(self): """ Take all non-zero values, sort them along the inverse z direction, and attributes the values 1, 2, 3, etc. This function assuming RPI orientation. """ image_output = Image(self.image_input, self.verbose) image_output.data *= 0 coordinates_input = self.image_input.getNonZeroCoordinates(sorting='z', reverse_coord=True) # for all points with non-zeros neighbors, force the neighbors to 0 for i, coord in enumerate(coordinates_input): image_output.data[int(coord.x), int(coord.y), int(coord.z)] = i + 1 return image_output def labelize_from_disks(self): """ Create an image with regions labelized depending on values from reference. Typically, user inputs a segmentation image, and labels with disks position, and this function produces a segmentation image with vertebral levels labelized. Labels are assumed to be non-zero and incremented from top to bottom, assuming a RPI orientation """ image_output = Image(self.image_input, self.verbose) image_output.data *= 0 coordinates_input = self.image_input.getNonZeroCoordinates() coordinates_ref = self.image_ref.getNonZeroCoordinates(sorting='value') # 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): for j in range(0, len(coordinates_ref)-1): if coordinates_ref[j+1].z < coord.z <= coordinates_ref[j].z: image_output.data[int(coord.x), int(coord.y), int(coord.z)] = coordinates_ref[j].value return image_output def label_vertebrae(self, levels_user=None): """ Find the center of mass of vertebral levels specified by the user. :return: image_output: Image with labels. """ # get center of mass of each vertebral level image_cubic2point = self.cubic_to_point() # get list of coordinates for each label list_coordinates = image_cubic2point.getNonZeroCoordinates(sorting='value') # if user did not specify levels, include all: if levels_user[0] == 0: levels_user = [int(i.value) for i in list_coordinates] # loop across labels and remove those that are not listed by the user for i_label in range(len(list_coordinates)): # check if this level is NOT in levels_user if not levels_user.count(int(list_coordinates[i_label].value)): # if not, set value to zero image_cubic2point.data[int(list_coordinates[i_label].x), int(list_coordinates[i_label].y), int(list_coordinates[i_label].z)] = 0 # list all labels return image_cubic2point # FUNCTION BELOW REMOVED BY JULIEN ON 2016-07-04 BECAUSE SEEMS NOT TO BE USED (AND DUPLICATION WITH ABOVE) # def label_vertebrae_from_disks(self, levels_user): # """ # Find the center of mass of vertebral levels specified by the user. # :param levels_user: # :return: # """ # image_cubic2point = self.cubic_to_point() # # get list of coordinates for each label # list_coordinates_disks = image_cubic2point.getNonZeroCoordinates(sorting='value') # image_cubic2point.data *= 0 # # compute vertebral labels from disk labels # list_coordinates_vertebrae = [] # for i_label in range(len(list_coordinates_disks)-1): # list_coordinates_vertebrae.append((list_coordinates_disks[i_label] + list_coordinates_disks[i_label+1]) / 2.0) # # loop across labels and remove those that are not listed by the user # for i_label in range(len(list_coordinates_vertebrae)): # # check if this level is NOT in levels_user # if levels_user.count(int(list_coordinates_vertebrae[i_label].value)): # image_cubic2point.data[int(list_coordinates_vertebrae[i_label].x), int(list_coordinates_vertebrae[i_label].y), int(list_coordinates_vertebrae[i_label].z)] = list_coordinates_vertebrae[i_label].value # # return image_cubic2point # BELOW: UNFINISHED BUSINESS (JULIEN) # def label_disc(self, levels_user=None): # """ # Find the edge of vertebral labeling file and assign value corresponding to middle coordinate between two levels. # Assumes RPI orientation. # :return: image_output: Image with labels. # """ # from msct_types import Coordinate # # get dim # nx, ny, nz, nt, px, py, pz, pt = self.image_input.dim # # initialize disc as a coordinate variable # disc = [] # # get center of mass of each vertebral level # image_cubic2point = self.cubic_to_point() # # get list of coordinates for each label # list_centermass = image_cubic2point.getNonZeroCoordinates(sorting='value') # # if user did not specify levels, include all: # if levels_user[0] == 0: # levels_user = [int(i.value) for i in list_centermass] # # get list of all coordinates # list_coordinates = self.display_voxel() # # loop across labels and remove those that are not listed by the user # # for i_label in range(len(list_centermass)): # # # TOP DISC # # get coordinates for value i_level # list_i_level = [list_coordinates[i] for i in xrange(len(list_coordinates)) if int(list_coordinates[i].value) == levels_user[0]] # # get max z-value # zmax = max([list_i_level[i].z for i in xrange(len(list_i_level))]) # # get coordinates corresponding to bottom voxels # list_i_level_top = [list_i_level[i] for i in xrange(len(list_i_level)) if list_i_level[i].z == zmax] # # get center of mass of the top and bottom voxels # arr_voxels_around_disc = np.array([[list_i_level_top[i].x, list_i_level_top[i].y, list_i_level_top[i].z] for i in range(len(list_i_level_top))]) # centermass = list(np.mean(arr_voxels_around_disc, 0)) # centermass.append(levels_user[0]-1) # disc.append(Coordinate(centermass)) # # if minimum level corresponds to z=nz, then remove it (likely corresponds to top edge of the FOV) # if disc[0].z == nz: # sct.printv('WARNING: Maximum level corresponds to z=0. Removing it (likely corresponds to edge of the FOV)', 1, 'warning') # # remove last element of the list # disc.pop() # # # ALL DISCS # # loop across values # for i_level in levels_user: # # get coordinates for value i_level # list_i_level = [list_coordinates[i] for i in xrange(len(list_coordinates)) if int(list_coordinates[i].value) == i_level] # # get min z-value # zmin = min([list_i_level[i].z for i in xrange(len(list_i_level))]) # # get coordinates corresponding to bottom voxels # list_i_level_bottom = [list_i_level[i] for i in xrange(len(list_i_level)) if list_i_level[i].z == zmin] # # get center of mass # # arr_i_level_bottom = np.array([[list_i_level_bottom[i].x, list_i_level_bottom[i].y] for i in range(len(list_i_level_bottom))]) # # centermass_i_level = ndimage.measurements.center_of_mass() # try: # # get coordinates for value i_level+1 # list_i_level_plus_one = [list_coordinates[i] for i in xrange(len(list_coordinates)) if int(list_coordinates[i].value) == i_level+1] # # get max z-value # zmax = max([list_i_level_plus_one[i].z for i in xrange(len(list_i_level_plus_one))]) # # get coordinates corresponding to top voxels # list_i_level_plus_one_top = [list_i_level_plus_one[i] for i in xrange(len(list_i_level_plus_one)) if list_i_level_plus_one[i].z == zmax] # except: # # if maximum level was reached, ignore it and disc will be located at the centermass of the bottom z. # list_i_level_plus_one_top = [] # # stack bottom and top voxels # list_voxels_around_disc = list_i_level_bottom + list_i_level_plus_one_top # # get center of mass of the top and bottom voxels # arr_voxels_around_disc = np.array([[list_voxels_around_disc[i].x, list_voxels_around_disc[i].y, list_voxels_around_disc[i].z] for i in range(len(list_voxels_around_disc))]) # centermass = list(np.mean(arr_voxels_around_disc, 0)) # centermass.append(i_level) # disc.append(Coordinate(centermass)) # # if maximum level corresponds to z=0, then remove it (likely corresponds to edge of the FOV) # if disc[-1].z == 0.0: # sct.printv('WARNING: Maximum level corresponds to z=0. Removing it (likely corresponds to edge of the FOV)', 1, 'warning') # # remove last element of the list # disc.pop() # # # loop across labels and assign voxels in image # image_cubic2point.data[:, :, :] = 0 # for i_label in range(len(disc)): # image_cubic2point.data[int(round(disc[i_label].x)), # int(round(disc[i_label].y)), # int(round(disc[i_label].z))] = disc[i_label].value # # # return image of labels # return image_cubic2point def MSE(self, threshold_mse=0): """ Compute the Mean Square Distance Error between two sets of labels (input and ref). Moreover, a warning is generated for each label mismatch. If the MSE is above the threshold provided (by default = 0mm), a log is reported with the filenames considered here. """ coordinates_input = self.image_input.getNonZeroCoordinates() coordinates_ref = self.image_ref.getNonZeroCoordinates() # check if all the labels in both the images match if len(coordinates_input) != len(coordinates_ref): sct.printv('ERROR: labels mismatch', 1, 'warning') for coord in coordinates_input: if round(coord.value) not in [round(coord_ref.value) for coord_ref in coordinates_ref]: sct.printv('ERROR: labels mismatch', 1, 'warning') for coord_ref in coordinates_ref: if round(coord_ref.value) not in [round(coord.value) for coord in coordinates_input]: sct.printv('ERROR: labels mismatch', 1, 'warning') result = 0.0 for coord in coordinates_input: for coord_ref in coordinates_ref: if round(coord_ref.value) == round(coord.value): result += (coord_ref.z - coord.z) ** 2 break result = math.sqrt(result / len(coordinates_input)) sct.printv('MSE error in Z direction = ' + str(result) + ' mm') if result > threshold_mse: f = open(self.image_input.path + 'error_log_' + self.image_input.file_name + '.txt', 'w') f.write( 'The labels error (MSE) between ' + self.image_input.file_name + ' and ' + self.image_ref.file_name + ' is: ' + str( result)) f.close() return result @staticmethod def remove_label_coord(coord_input, coord_ref, symmetry=False): """ coord_input and coord_ref should be sets of CoordinateValue in order to improve speed of intersection :param coord_input: set of CoordinateValue :param coord_ref: set of CoordinateValue :param symmetry: boolean, :return: intersection of CoordinateValue: list """ from msct_types import CoordinateValue if isinstance(coord_input[0], CoordinateValue) and isinstance(coord_ref[0], CoordinateValue) and symmetry: coord_intersection = list(set(coord_input).intersection(set(coord_ref))) result_coord_input = [coord for coord in coord_input if coord in coord_intersection] result_coord_ref = [coord for coord in coord_ref if coord in coord_intersection] else: result_coord_ref = coord_ref result_coord_input = [coord for coord in coord_input if filter(lambda x: x.value == coord.value, coord_ref)] if symmetry: result_coord_ref = [coord for coord in coord_ref if filter(lambda x: x.value == coord.value, result_coord_input)] return result_coord_input, result_coord_ref 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 extract_centerline(self): """ Write a text file with the coordinates of the centerline. The image is suppose to be RPI """ coordinates_input = self.image_input.getNonZeroCoordinates(sorting='z') fo = open(self.fname_output, "wb") for coord in coordinates_input: line = (coord.x,coord.y, coord.z) fo.write("%i %i %i\n" % line) fo.close() def display_voxel(self): """ Display all the labels that are contained in the input image. The image is suppose to be RPI to display voxels. But works also for other orientations """ coordinates_input = self.image_input.getNonZeroCoordinates(sorting='z') useful_notation = '' for coord in coordinates_input: print 'Position=(' + str(coord.x) + ',' + str(coord.y) + ',' + str(coord.z) + ') -- Value= ' + str(coord.value) if useful_notation: useful_notation = useful_notation + ':' useful_notation = useful_notation + str(coord.x) + ',' + str(coord.y) + ',' + str(coord.z) + ',' + str(coord.value) print 'All labels (useful syntax):' print useful_notation return coordinates_input def diff(self): """ Detect any label mismatch between input image and reference image """ coordinates_input = self.image_input.getNonZeroCoordinates() coordinates_ref = self.image_ref.getNonZeroCoordinates() print "Label in input image that are not in reference image:" for coord in coordinates_input: isIn = False for coord_ref in coordinates_ref: if coord.value == coord_ref.value: isIn = True break if not isIn: print coord.value print "Label in ref image that are not in input image:" for coord_ref in coordinates_ref: isIn = False for coord in coordinates_input: if coord.value == coord_ref.value: isIn = True break if not isIn: print coord_ref.value def distance_interlabels(self, max_dist): """ Calculate the distances between each label in the input image. If a distance is larger than max_dist, a warning message is displayed. """ coordinates_input = self.image_input.getNonZeroCoordinates() # for all points with non-zeros neighbors, force the neighbors to 0 for i in range(0, len(coordinates_input) - 1): dist = math.sqrt((coordinates_input[i].x - coordinates_input[i+1].x)**2 + (coordinates_input[i].y - coordinates_input[i+1].y)**2 + (coordinates_input[i].z - coordinates_input[i+1].z)**2) if dist < max_dist: print 'Warning: the distance between label ' + str(i) + '[' + str(coordinates_input[i].x) + ',' + str(coordinates_input[i].y) + ',' + str( coordinates_input[i].z) + ']=' + str(coordinates_input[i].value) + ' and label ' + str(i+1) + '[' + str( coordinates_input[i+1].x) + ',' + str(coordinates_input[i+1].y) + ',' + str(coordinates_input[i+1].z) + ']=' + str( coordinates_input[i+1].value) + ' is larger than ' + str(max_dist) + '. Distance=' + str(dist) 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 execute(self): print 'Execution of the SCAD algorithm in '+str(os.getcwd()) original_name = self.input_image.file_name vesselness_file_name = "imageVesselNessFilter.nii.gz" raw_file_name = "raw.nii" self.setup_debug_folder() if self.debug: import matplotlib.pyplot as plt # import for debug purposes # create tmp and copy input path_tmp = self.create_temporary_path() conv.convert(self.input_image.absolutepath, path_tmp+raw_file_name) if self.vesselness_provided: sct.run('cp '+vesselness_file_name+' '+path_tmp+vesselness_file_name) os.chdir(path_tmp) # get input image information img = Image(raw_file_name) # save original orientation and change image to RPI self.raw_orientation = img.change_orientation() # get body symmetry if self.enable_symmetry: from msct_image import change_data_orientation sym = SymmetryDetector(raw_file_name, self.contrast, crop_xy=0) self.raw_symmetry = sym.execute() img.change_orientation(self.raw_orientation) self.output_debug_file(img, self.raw_symmetry, "body_symmetry") img.change_orientation() # vesselness filter if not self.vesselness_provided: sct.run('sct_vesselness -i '+raw_file_name+' -t ' + self._contrast+" -radius "+str(self.spinalcord_radius)) # load vesselness filter data and perform minimum path on it img = Image(vesselness_file_name) img.change_orientation() self.minimum_path_data, self.J1_min_path, self.J2_min_path = get_minimum_path(img.data, invert=1, debug=1) self.output_debug_file(img, self.minimum_path_data, "minimal_path") self.output_debug_file(img, self.J1_min_path, "J1_minimal_path") self.output_debug_file(img, self.J2_min_path, "J2_minimal_path") # Apply an exponent to the minimum path self.minimum_path_powered = np.power(self.minimum_path_data, self.minimum_path_exponent) self.output_debug_file(img, self.minimum_path_powered, "minimal_path_power_"+str(self.minimum_path_exponent)) # Saving in Image since smooth_minimal_path needs pixel dimensions img.data = self.minimum_path_powered # smooth resulting minimal path self.smoothed_min_path = smooth_minimal_path(img) self.output_debug_file(img, self.smoothed_min_path.data, "minimal_path_smooth") # normalise symmetry values between 0 and 1 if self.enable_symmetry: normalised_symmetry = normalize_array_histogram(self.raw_symmetry) self.output_debug_file(img, self.smoothed_min_path.data, "minimal_path_smooth") # multiply normalised symmetry data with the minimum path result from msct_image import change_data_orientation self.spine_detect_data = np.multiply(self.smoothed_min_path.data, change_data_orientation(np.power(normalised_symmetry, self.symmetry_exponent), self.raw_orientation, "RPI")) self.output_debug_file(img, self.spine_detect_data, "symmetry_x_min_path") # extract the centerline from the minimal path image self.centerline_with_outliers = get_centerline(self.spine_detect_data, self.spine_detect_data.shape) else: # extract the centerline from the minimal path image self.centerline_with_outliers = get_centerline(self.smoothed_min_path.data, self.smoothed_min_path.data.shape) self.output_debug_file(img, self.centerline_with_outliers, "centerline_with_outliers") # saving centerline with outliers to have img.data = self.centerline_with_outliers img.change_orientation() img.file_name = "centerline_with_outliers" img.save() # use a b-spline to smooth out the centerline x, y, z, dx, dy, dz = smooth_centerline("centerline_with_outliers.nii.gz") # save the centerline nx, ny, nz, nt, px, py, pz, pt = img.dim img.data = np.zeros((nx, ny, nz)) for i in range(0, np.size(x)-1): img.data[int(x[i]), int(y[i]), int(z[i])] = 1 self.output_debug_file(img, img.data, "centerline") img.change_orientation(self.raw_orientation) img.file_name = "centerline" img.save() # copy back centerline os.chdir('../') conv.convert(path_tmp+img.file_name+img.ext, self.output_filename) if self.rm_tmp_file == 1: import shutil shutil.rmtree(path_tmp)
class ProcessLabels(object): def __init__(self, fname_label, fname_output=None, fname_ref=None, cross_radius=5, dilate=False, coordinates=None, verbose=1, vertebral_levels=None, value=None, msg=""): """ Collection of processes that deal with label creation/modification. :param fname_label: :param fname_output: :param fname_ref: :param cross_radius: :param dilate: :param coordinates: :param verbose: :param vertebral_levels: :param value: :param msg: string. message to display to the user. """ self.image_input = Image(fname_label, verbose=verbose) self.image_ref = None 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.vertebral_levels = vertebral_levels self.dilate = dilate self.coordinates = coordinates self.verbose = verbose self.value = value self.msg = msg def process(self, type_process): # for some processes, change orientation of input image to RPI change_orientation = False if type_process in ['vert-body', 'vert-disc', 'vert-continuous']: # get orientation of input image input_orientation = self.image_input.orientation # change orientation self.image_input.change_orientation('RPI') change_orientation = True if type_process == 'add': self.output_image = self.add(self.value) if type_process == 'cross': self.output_image = self.cross() if type_process == 'plan': self.output_image = self.plan(self.cross_radius, 100, 5) if type_process == 'plan_ref': self.output_image = self.plan_ref() if type_process == 'increment': self.output_image = self.increment_z_inverse() if type_process == 'disks': self.output_image = self.labelize_from_disks() if type_process == 'MSE': self.MSE() self.fname_output = None if type_process == 'remove': self.output_image = self.remove_label() if type_process == 'remove-symm': self.output_image = self.remove_label(symmetry=True) if type_process == 'centerline': self.extract_centerline() if type_process == 'create': self.output_image = self.create_label() if type_process == 'create-add': self.output_image = self.create_label(add=True) if type_process == 'create-seg': self.output_image = self.create_label_along_segmentation() if type_process == 'display-voxel': self.display_voxel() self.fname_output = None if type_process == 'diff': self.diff() self.fname_output = None if type_process == 'dist-inter': # second argument is in pixel distance self.distance_interlabels(5) self.fname_output = None if type_process == 'cubic-to-point': self.output_image = self.cubic_to_point() if type_process == 'vert-body': self.output_image = self.label_vertebrae(self.vertebral_levels) if type_process == 'vert-continuous': self.output_image = self.continuous_vertebral_levels() if type_process == 'create-viewer': self.output_image = self.launch_sagittal_viewer(self.value) # save the output image as minimized integers if self.fname_output is not None: self.output_image.setFileName(self.fname_output) if change_orientation: self.output_image.change_orientation(input_orientation) if type_process == 'vert-continuous': self.output_image.save('float32') elif type_process != 'plan_ref': self.output_image.save('minimize_int') else: self.output_image.save() def add(self, value): """ This function add a specified value to all non-zero voxels. """ image_output = Image(self.image_input, self.verbose) # image_output.data *= 0 coordinates_input = self.image_input.getNonZeroCoordinates() # for all points with non-zeros neighbors, force the neighbors to 0 for i, coord in enumerate(coordinates_input): image_output.data[ int(coord.x), int(coord.y), int(coord.z)] = image_output.data[int(coord.x), int(coord.y), int(coord.z)] + float(value) return image_output def create_label(self, add=False): """ Create an image with labels listed by the user. This method works only if the user inserted correct coordinates. self.coordinates is a list of coordinates (class in msct_types). a Coordinate contains x, y, z and value. If only one label is to be added, coordinates must be completed with '[]' examples: For one label: object_define=ProcessLabels( fname_label, coordinates=[coordi]) where coordi is a 'Coordinate' object from msct_types For two labels: object_define=ProcessLabels( fname_label, coordinates=[coordi1, coordi2]) where coordi1 and coordi2 are 'Coordinate' objects from msct_types """ image_output = self.image_input.copy() if not add: image_output.data *= 0 # loop across labels for i, coord in enumerate(self.coordinates): if len(image_output.data.shape) == 3: image_output.data[int(coord.x), int(coord.y), int(coord.z)] = coord.value elif len(image_output.data.shape) == 2: assert str( coord.z ) == '0', "ERROR: 2D coordinates should have a Z value of 0. Z coordinate is :" + str( coord.z) image_output.data[int(coord.x), int(coord.y)] = coord.value else: sct.printv( 'ERROR: Data should be 2D or 3D. Current shape is: ' + str(image_output.data.shape), 1, 'error') # display info sct.printv( 'Label #' + str(i) + ': ' + str(coord.x) + ',' + str(coord.y) + ',' + str(coord.z) + ' --> ' + str(coord.value), 1) return image_output def create_label_along_segmentation(self): """ Create an image with labels defined along the spinal cord segmentation (or centerline) Example: object_define=ProcessLabels(fname_segmentation, coordinates=[coord_1, coord_2, coord_i]), where coord_i='z,value'. If z=-1, then use z=nz/2 (i.e. center of FOV in superior-inferior direction) Returns ------- image_output: Image object with labels. """ # copy input Image object (will use the same header) image_output = self.image_input.copy() # set all voxels to 0 image_output.data *= 0 # loop across labels for i, coord in enumerate(self.coordinates): # split coord string list_coord = coord.split(',') # convert to int() and assign to variable z, value = [int(i) for i in list_coord] # if z=-1, replace with nz/2 if z == -1: z = int(round(image_output.dim[2] / 2.0)) # get center of mass of segmentation at given z x, y = ndimage.measurements.center_of_mass( np.array(self.image_input.data[:, :, z])) # round values to make indices x, y = int(round(x)), int(round(y)) # display info sct.printv( 'Label #' + str(i) + ': ' + str(x) + ',' + str(y) + ',' + str(z) + ' --> ' + str(value), 1) if len(image_output.data.shape) == 3: image_output.data[x, y, z] = value elif len(image_output.data.shape) == 2: assert str( z ) == '0', "ERROR: 2D coordinates should have a Z value of 0. Z coordinate is :" + str( z) image_output.data[x, y] = value return image_output def cross(self): """ create a cross. :return: """ output_image = Image(self.image_input, self.verbose) nx, ny, nz, nt, px, py, pz, pt = Image( self.image_input.absolutepath).dim coordinates_input = self.image_input.getNonZeroCoordinates() d = self.cross_radius # cross radius in pixel dx = d / px # cross radius in mm dy = d / py # clean output_image output_image.data *= 0 cross_coordinates = self.get_crosses_coordinates( coordinates_input, dx, self.image_ref, self.dilate) for coord in cross_coordinates: output_image.data[int(round(coord.x)), int(round(coord.y)), int(round(coord.z))] = coord.value return output_image @staticmethod def get_crosses_coordinates(coordinates_input, gapxy=15, image_ref=None, dilate=False): from msct_types import Coordinate # if reference image is provided (segmentation), we draw the cross perpendicular to the centerline if image_ref is not None: # smooth centerline from sct_straighten_spinalcord import smooth_centerline x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline( self.image_ref, verbose=self.verbose) # compute crosses cross_coordinates = [] for coord in coordinates_input: if image_ref is None: from sct_straighten_spinalcord import compute_cross cross_coordinates_temp = compute_cross(coord, gapxy) else: from sct_straighten_spinalcord import compute_cross_centerline from numpy import where index_z = where(z_centerline == coord.z) deriv = Coordinate([ x_centerline_deriv[index_z][0], y_centerline_deriv[index_z][0], z_centerline_deriv[index_z][0], 0.0 ]) cross_coordinates_temp = compute_cross_centerline( coord, deriv, gapxy) for i, coord_cross in enumerate(cross_coordinates_temp): coord_cross.value = coord.value * 10 + i + 1 # dilate cross to 3x3x3 if dilate: additional_coordinates = [] for coord_temp in cross_coordinates_temp: additional_coordinates.append( Coordinate([ coord_temp.x, coord_temp.y, coord_temp.z + 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x, coord_temp.y, coord_temp.z - 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x, coord_temp.y + 1.0, coord_temp.z, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x, coord_temp.y + 1.0, coord_temp.z + 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x, coord_temp.y + 1.0, coord_temp.z - 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x, coord_temp.y - 1.0, coord_temp.z, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x, coord_temp.y - 1.0, coord_temp.z + 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x, coord_temp.y - 1.0, coord_temp.z - 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x + 1.0, coord_temp.y, coord_temp.z, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x + 1.0, coord_temp.y, coord_temp.z + 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x + 1.0, coord_temp.y, coord_temp.z - 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x + 1.0, coord_temp.y + 1.0, coord_temp.z, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x + 1.0, coord_temp.y + 1.0, coord_temp.z + 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x + 1.0, coord_temp.y + 1.0, coord_temp.z - 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x + 1.0, coord_temp.y - 1.0, coord_temp.z, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x + 1.0, coord_temp.y - 1.0, coord_temp.z + 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x + 1.0, coord_temp.y - 1.0, coord_temp.z - 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x - 1.0, coord_temp.y, coord_temp.z, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x - 1.0, coord_temp.y, coord_temp.z + 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x - 1.0, coord_temp.y, coord_temp.z - 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x - 1.0, coord_temp.y + 1.0, coord_temp.z, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x - 1.0, coord_temp.y + 1.0, coord_temp.z + 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x - 1.0, coord_temp.y + 1.0, coord_temp.z - 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x - 1.0, coord_temp.y - 1.0, coord_temp.z, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x - 1.0, coord_temp.y - 1.0, coord_temp.z + 1.0, coord_temp.value ])) additional_coordinates.append( Coordinate([ coord_temp.x - 1.0, coord_temp.y - 1.0, coord_temp.z - 1.0, coord_temp.value ])) cross_coordinates_temp.extend(additional_coordinates) cross_coordinates.extend(cross_coordinates_temp) cross_coordinates = sorted(cross_coordinates, key=lambda obj: obj.value) return cross_coordinates def plan(self, width, offset=0, gap=1): """ Create a plane of thickness="width" and changes its value with an offset and a gap between labels. """ image_output = Image(self.image_input, self.verbose) image_output.data *= 0 coordinates_input = self.image_input.getNonZeroCoordinates() # for all points with non-zeros neighbors, force the neighbors to 0 for coord in coordinates_input: image_output.data[:, :, int(coord.z) - width:int(coord.z) + width] = offset + gap * coord.value return image_output 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 cubic_to_point(self): """ Calculate the center of mass of each group of labels and returns a file of same size with only a label by group at the center of mass of this group. It is to be used after applying homothetic warping field to a label file as the labels will be dilated. Be careful: this algorithm computes the center of mass of voxels with same value, if two groups of voxels with the same value are present but separated in space, this algorithm will compute the center of mass of the two groups together. :return: image_output """ # 0. Initialization of output image output_image = self.image_input.copy() output_image.data *= 0 # 1. Extraction of coordinates from all non-null voxels in the image. Coordinates are sorted by value. coordinates = self.image_input.getNonZeroCoordinates(sorting='value') # 2. Separate all coordinates into groups by value groups = dict() for coord in coordinates: if coord.value in groups: groups[coord.value].append(coord) else: groups[coord.value] = [coord] # 3. Compute the center of mass of each group of voxels and write them into the output image for value, list_coord in groups.items(): center_of_mass = sum(list_coord) / float(len(list_coord)) sct.printv("Value = " + str(center_of_mass.value) + " : (" + str(center_of_mass.x) + ", " + str(center_of_mass.y) + ", " + str(center_of_mass.z) + ") --> ( " + str(round(center_of_mass.x)) + ", " + str(round(center_of_mass.y)) + ", " + str(round(center_of_mass.z)) + ")", verbose=self.verbose) output_image.data[ int(round(center_of_mass.x)), int(round(center_of_mass.y)), int(round(center_of_mass.z))] = center_of_mass.value return output_image def increment_z_inverse(self): """ Take all non-zero values, sort them along the inverse z direction, and attributes the values 1, 2, 3, etc. This function assuming RPI orientation. """ image_output = Image(self.image_input, self.verbose) image_output.data *= 0 coordinates_input = self.image_input.getNonZeroCoordinates( sorting='z', reverse_coord=True) # for all points with non-zeros neighbors, force the neighbors to 0 for i, coord in enumerate(coordinates_input): image_output.data[int(coord.x), int(coord.y), int(coord.z)] = i + 1 return image_output def labelize_from_disks(self): """ Create an image with regions labelized depending on values from reference. Typically, user inputs a segmentation image, and labels with disks position, and this function produces a segmentation image with vertebral levels labelized. Labels are assumed to be non-zero and incremented from top to bottom, assuming a RPI orientation """ image_output = Image(self.image_input, self.verbose) image_output.data *= 0 coordinates_input = self.image_input.getNonZeroCoordinates() coordinates_ref = self.image_ref.getNonZeroCoordinates(sorting='value') # 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): for j in range(0, len(coordinates_ref) - 1): if coordinates_ref[j + 1].z < coord.z <= coordinates_ref[j].z: image_output.data[int(coord.x), int(coord.y), int(coord.z)] = coordinates_ref[j].value return image_output def label_vertebrae(self, levels_user=None): """ Find the center of mass of vertebral levels specified by the user. :return: image_output: Image with labels. """ # get center of mass of each vertebral level image_cubic2point = self.cubic_to_point() # get list of coordinates for each label list_coordinates = image_cubic2point.getNonZeroCoordinates( sorting='value') # if user did not specify levels, include all: if levels_user[0] == 0: levels_user = [int(i.value) for i in list_coordinates] # loop across labels and remove those that are not listed by the user for i_label in range(len(list_coordinates)): # check if this level is NOT in levels_user if not levels_user.count(int(list_coordinates[i_label].value)): # if not, set value to zero image_cubic2point.data[int(list_coordinates[i_label].x), int(list_coordinates[i_label].y), int(list_coordinates[i_label].z)] = 0 # list all labels return image_cubic2point def MSE(self, threshold_mse=0): """ Compute the Mean Square Distance Error between two sets of labels (input and ref). Moreover, a warning is generated for each label mismatch. If the MSE is above the threshold provided (by default = 0mm), a log is reported with the filenames considered here. """ coordinates_input = self.image_input.getNonZeroCoordinates() coordinates_ref = self.image_ref.getNonZeroCoordinates() # check if all the labels in both the images match if len(coordinates_input) != len(coordinates_ref): sct.printv('ERROR: labels mismatch', 1, 'warning') for coord in coordinates_input: if round(coord.value) not in [ round(coord_ref.value) for coord_ref in coordinates_ref ]: sct.printv('ERROR: labels mismatch', 1, 'warning') for coord_ref in coordinates_ref: if round(coord_ref.value) not in [ round(coord.value) for coord in coordinates_input ]: sct.printv('ERROR: labels mismatch', 1, 'warning') result = 0.0 for coord in coordinates_input: for coord_ref in coordinates_ref: if round(coord_ref.value) == round(coord.value): result += (coord_ref.z - coord.z)**2 break result = math.sqrt(result / len(coordinates_input)) sct.printv('MSE error in Z direction = ' + str(result) + ' mm') if result > threshold_mse: f = open( self.image_input.path + 'error_log_' + self.image_input.file_name + '.txt', 'w') f.write('The labels error (MSE) between ' + self.image_input.file_name + ' and ' + self.image_ref.file_name + ' is: ' + str(result)) f.close() return result @staticmethod def remove_label_coord(coord_input, coord_ref, symmetry=False): """ coord_input and coord_ref should be sets of CoordinateValue in order to improve speed of intersection :param coord_input: set of CoordinateValue :param coord_ref: set of CoordinateValue :param symmetry: boolean, :return: intersection of CoordinateValue: list """ from msct_types import CoordinateValue if isinstance(coord_input[0], CoordinateValue) and isinstance( coord_ref[0], CoordinateValue) and symmetry: coord_intersection = list( set(coord_input).intersection(set(coord_ref))) result_coord_input = [ coord for coord in coord_input if coord in coord_intersection ] result_coord_ref = [ coord for coord in coord_ref if coord in coord_intersection ] else: result_coord_ref = coord_ref result_coord_input = [ coord for coord in coord_input if list(filter(lambda x: x.value == coord.value, coord_ref)) ] if symmetry: result_coord_ref = [ coord for coord in coord_ref if list( filter(lambda x: x.value == coord.value, result_coord_input)) ] return result_coord_input, result_coord_ref 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 extract_centerline(self): """ Write a text file with the coordinates of the centerline. The image is suppose to be RPI """ coordinates_input = self.image_input.getNonZeroCoordinates(sorting='z') fo = open(self.fname_output, "wb") for coord in coordinates_input: line = (coord.x, coord.y, coord.z) fo.write("%i %i %i\n" % line) fo.close() def display_voxel(self): """ Display all the labels that are contained in the input image. The image is suppose to be RPI to display voxels. But works also for other orientations """ coordinates_input = self.image_input.getNonZeroCoordinates( sorting='value') self.useful_notation = '' for coord in coordinates_input: sct.printv('Position=(' + str(coord.x) + ',' + str(coord.y) + ',' + str(coord.z) + ') -- Value= ' + str(coord.value), verbose=self.verbose) if self.useful_notation: self.useful_notation = self.useful_notation + ':' self.useful_notation += str(coord) sct.printv('All labels (useful syntax):', verbose=self.verbose) sct.printv(self.useful_notation, verbose=self.verbose) return coordinates_input def get_physical_coordinates(self): """ This function returns the coordinates of the labels in the physical referential system. :return: a list of CoordinateValue, in the physical (scanner) space """ coord = self.image_input.getNonZeroCoordinates(sorting='value') phys_coord = [] for c in coord: # convert pixelar coordinates to physical coordinates c_p = self.image_input.transfo_pix2phys([[c.x, c.y, c.z]])[0] phys_coord.append( CoordinateValue([c_p[0], c_p[1], c_p[2], c.value])) return phys_coord def get_coordinates_in_destination(self, im_dest, type='discrete'): """ This function calculate the position of labels in the pixelar space of a destination image :param im_dest: Object Image :param type: 'discrete' or 'continuous' :return: a list of CoordinateValue, in the pixelar (image) space of the destination image """ phys_coord = self.get_physical_coordinates() dest_coord = [] for c in phys_coord: if type is 'discrete': c_p = im_dest.transfo_phys2pix([[c.x, c.y, c.y]])[0] elif type is 'continuous': c_p = im_dest.transfo_phys2continuouspix([[c.x, c.y, c.y]])[0] else: raise ValueError( "The value of 'type' should either be 'discrete' or 'continuous'." ) dest_coord.append( CoordinateValue([c_p[0], c_p[1], c_p[2], c.value])) return dest_coord def diff(self): """ Detect any label mismatch between input image and reference image """ coordinates_input = self.image_input.getNonZeroCoordinates() coordinates_ref = self.image_ref.getNonZeroCoordinates() sct.printv("Label in input image that are not in reference image:") for coord in coordinates_input: isIn = False for coord_ref in coordinates_ref: if coord.value == coord_ref.value: isIn = True break if not isIn: sct.printv(coord.value) sct.printv("Label in ref image that are not in input image:") for coord_ref in coordinates_ref: isIn = False for coord in coordinates_input: if coord.value == coord_ref.value: isIn = True break if not isIn: sct.printv(coord_ref.value) def distance_interlabels(self, max_dist): """ Calculate the distances between each label in the input image. If a distance is larger than max_dist, a warning message is displayed. """ coordinates_input = self.image_input.getNonZeroCoordinates() # for all points with non-zeros neighbors, force the neighbors to 0 for i in range(0, len(coordinates_input) - 1): dist = math.sqrt( (coordinates_input[i].x - coordinates_input[i + 1].x)**2 + (coordinates_input[i].y - coordinates_input[i + 1].y)**2 + (coordinates_input[i].z - coordinates_input[i + 1].z)**2) if dist < max_dist: sct.printv('Warning: the distance between label ' + str(i) + '[' + str(coordinates_input[i].x) + ',' + str(coordinates_input[i].y) + ',' + str(coordinates_input[i].z) + ']=' + str(coordinates_input[i].value) + ' and label ' + str(i + 1) + '[' + str(coordinates_input[i + 1].x) + ',' + str(coordinates_input[i + 1].y) + ',' + str(coordinates_input[i + 1].z) + ']=' + str(coordinates_input[i + 1].value) + ' is larger than ' + str(max_dist) + '. Distance=' + str(dist)) 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 launch_sagittal_viewer(self, labels): from spinalcordtoolbox.gui import base from spinalcordtoolbox.gui.sagittal import launch_sagittal_dialog params = base.AnatomicalParams() params.vertebraes = labels params.input_file_name = self.image_input.file_name params.output_file_name = self.fname_output params.subtitle = self.msg output = self.image_input.copy() output.data *= 0 output.setFileName(self.fname_output) launch_sagittal_dialog(self.image_input, output, params) return output
def validate_scad(folder_input): """ Expecting folder to have the following structure : errsm_01: - t2 -- errsm_01.nii.gz or t2.nii.gz :param folder_input: :return: """ current_folder = os.getcwd() os.chdir(folder_input) try: patients = next(os.walk('.'))[1] for i in patients: if i != "errsm_01" and i !="errsm_02": directory = i + "/t2" os.chdir(directory) try: if os.path.isfile(i+"_t2.nii.gz"): raw_image = Image(i+"_t2.nii.gz") elif os.path.isfile("t2.nii.gz"): raw_image = Image("t2.nii.gz") else: raise Exception("t2.nii.gz or "+i+"_t2.nii.gz file is not found") raw_orientation = raw_image.change_orientation() SCAD(raw_image, contrast="t2", rm_tmp_file=1, verbose=1).test_debug() manual_seg = Image(i+"_t2_manual_segmentation.nii.gz") manual_orientation = manual_seg.change_orientation() from scipy.ndimage.measurements import center_of_mass # find COM iterator = range(manual_seg.data.shape[2]) com_x = [0 for ix in iterator] com_y = [0 for iy in iterator] for iz in iterator: com_x[iz], com_y[iz] = center_of_mass(manual_seg.data[:, :, iz]) #raw_image.change_orientation(raw_orientation) #manual_seg.change_orientation(manual_orientation) centerline_scad = Image(i+"_t2_centerline.nii.gz") os.remove(i+"_t2_centerline.nii.gz") centerline_scad.change_orientation() distance = [] for iz in range(centerline_scad.data.shape[2]): ind1 = np.argmax(centerline_scad.data[:, :, iz]) X,Y = scad.ind2sub(centerline_scad.data[:, :, i].shape,ind1) com_phys = centerline_scad.transfo_pix2phys([[com_x[iz], com_y[iz], iz]]) scad_phys = centerline_scad.transfo_pix2phys([[X, Y, iz]]) distance_magnitude = np.linalg.norm(com_phys-scad_phys) distance.append(distance_magnitude) os.chdir(folder_input) except Exception, e: print e.message pass except Exception, e: print e.message
def project_labels_on_spinalcord(fname_label, fname_seg): """ Project labels orthogonally on the spinal cord centerline. The algorithm works by finding the smallest distance between each label and the spinal cord center of mass. :param fname_label: file name of labels :param fname_seg: file name of cord segmentation (could also be of centerline) :return: file name of projected labels """ # build output name fname_label_projected = sct.add_suffix(fname_label, "_projected") # open labels and segmentation im_label = Image(fname_label) im_seg = Image(fname_seg) # orient to RPI native_orient = im_seg.change_orientation('RPI') im_label.change_orientation('RPI') # smooth centerline and return fitted coordinates in voxel space centerline_x, centerline_y, centerline_z, centerline_derivx, centerline_derivy, centerline_derivz = smooth_centerline( im_seg, algo_fitting="hanning", type_window="hanning", window_length=50, nurbs_pts_number=3000, phys_coordinates=False, all_slices=True) # convert pixel into physical coordinates centerline_xyz_transposed = [ im_seg.transfo_pix2phys( [[centerline_x[i], centerline_y[i], centerline_z[i]]])[0] for i in range(len(centerline_x)) ] # transpose list centerline_phys_x, centerline_phys_y, centerline_phys_z = map( list, map(None, *centerline_xyz_transposed)) # get center of mass of label labels = im_label.getCoordinatesAveragedByValue() # initialize image of projected labels. Note that we use the space of the seg (not label). im_label_projected = im_seg.copy() im_label_projected.data = np.zeros(im_label_projected.data.shape, dtype='uint8') # loop across label values for label in labels: # convert pixel into physical coordinates for the label label_phys_x, label_phys_y, label_phys_z = im_label.transfo_pix2phys( [[label.x, label.y, label.z]])[0] # calculate distance between label and each point of the centerline distance_centerline = [ np.linalg.norm([ centerline_phys_x[i] - label_phys_x, centerline_phys_y[i] - label_phys_y, centerline_phys_z[i] - label_phys_z ]) for i in range(len(centerline_x)) ] # get the index corresponding to the min distance ind_min_distance = np.argmin(distance_centerline) # get centerline coordinate (in physical space) [min_phy_x, min_phy_y, min_phy_z] = [ centerline_phys_x[ind_min_distance], centerline_phys_y[ind_min_distance], centerline_phys_z[ind_min_distance] ] # convert coordinate to voxel space minx, miny, minz = im_seg.transfo_phys2pix( [[min_phy_x, min_phy_y, min_phy_z]])[0] # use that index to assign projected label in the centerline im_label_projected.data[minx, miny, minz] = label.value # re-orient projected labels to native orientation and save im_label_projected.change_orientation( native_orient) # note: native_orient refers to im_seg (not im_label) im_label_projected.setFileName(fname_label_projected) im_label_projected.save() return fname_label_projected
def main(args=None): # initializations initz = '' initcenter = '' # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_in = arguments["-i"] fname_seg = arguments['-s'] # contrast = arguments['-t'] if '-o' in arguments: fname_out = arguments["-o"] else: fname_out = '' if '-initz' in arguments: initz = arguments['-initz'] if '-initcenter' in arguments: initcenter = arguments['-initcenter'] verbose = int(arguments['-v']) remove_tmp_files = int(arguments['-r']) denoise = int(arguments['-denoise']) laplacian = int(arguments['-laplacian']) # create temporary folder printv('\nCreate temporary folder...', verbose) path_tmp = slash_at_the_end('tmp.'+strftime("%y%m%d%H%M%S"), 1) run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder printv('\nCopying input data to tmp folder...', verbose) run('sct_convert -i '+fname_in+' -o '+path_tmp+'data.nii') run('sct_convert -i '+fname_seg+' -o '+path_tmp+'segmentation.nii.gz') # Go go temp folder # path_tmp = '/Users/julien/data/biospective/20151013_demo_spinalcordv2.1.b9/200_006_s2_T2/tmp.151013175622/' chdir(path_tmp) # create label to identify disc printv('\nCreate label to identify disc...', verbose) if initz: create_label_z('segmentation.nii.gz', initz[0], initz[1]) # create label located at z_center elif initcenter: # find z centered in FOV nii = Image('segmentation.nii.gz') nii.change_orientation('RPI') # reorient to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions z_center = int(round(nz/2)) # get z_center create_label_z('segmentation.nii.gz', z_center, initcenter) # create label located at z_center else: printv('\nERROR: You need to initialize the disc detection algorithm using one of these two options: -initz, -initcenter\n', 1, 'error') # Straighten spinal cord printv('\nStraighten spinal cord...', verbose) run('sct_straighten_spinalcord -i data.nii -s segmentation.nii.gz -r 0 -param all_labels=0,bspline_meshsize=3x3x5 -qc 0') # here using all_labels=0 because of issue #610 # Apply straightening to segmentation # N.B. Output is RPI printv('\nApply straightening to segmentation...', verbose) run('sct_apply_transfo -i segmentation.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear') # Threshold segmentation to 0.5 run('sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz') # Apply straightening to z-label printv('\nDilate z-label and apply straightening...', verbose) run('sct_apply_transfo -i labelz.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn') # get z value and disk value to initialize labeling printv('\nGet z and disc values from straight label...', verbose) init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz') printv('.. '+str(init_disc), verbose) # denoise data if denoise: printv('\nDenoise data...', verbose) run('sct_maths -i data_straight.nii -denoise h=0.05 -o data_straight.nii') # apply laplacian filtering if laplacian: printv('\nApply Laplacian filter...', verbose) run('sct_maths -i data_straight.nii -laplace 1 -o data_straight.nii') # detect vertebral levels on straight spinal cord vertebral_detection('data_straight.nii', 'segmentation_straight.nii.gz', init_disc, verbose) # un-straighten labelled spinal cord printv('\nUn-straighten labeling...', verbose) run('sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn') # Clean labeled segmentation printv('\nClean labeled segmentation (correct interpolation errors)...', verbose) clean_labeled_segmentation('segmentation_labeled.nii.gz', 'segmentation.nii.gz', 'segmentation_labeled.nii.gz') # Build fname_out if fname_out == '': path_seg, file_seg, ext_seg = extract_fname(fname_seg) fname_out = path_seg+file_seg+'_labeled'+ext_seg # come back to parent folder chdir('..') # Generate output files printv('\nGenerate output files...', verbose) generate_output_file(path_tmp+'segmentation_labeled.nii.gz', fname_out) # Remove temporary files if remove_tmp_files == 1: printv('\nRemove temporary files...', verbose) run('rm -rf '+path_tmp) # to view results printv('\nDone! To view results, type:', verbose) printv('fslview '+fname_in+' '+fname_out+' -l Random-Rainbow -t 0.5 &\n', verbose, 'info')
def main(args=None): # initializations initz = '' initcenter = '' # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_in = arguments["-i"] fname_seg = arguments['-seg'] contrast = arguments['-t'] if '-o' in arguments: fname_out = arguments["-o"] else: fname_out = '' if '-initz' in arguments: initz = arguments['-initz'] if '-initcenter' in arguments: initcenter = arguments['-initcenter'] param.verbose = int(arguments['-v']) param.remove_tmp_files = int(arguments['-r']) # create temporary folder printv('\nCreate temporary folder...', param.verbose) path_tmp = slash_at_the_end('tmp.'+strftime("%y%m%d%H%M%S"), 1) run('mkdir '+path_tmp, param.verbose) # Copying input data to tmp folder printv('\nCopying input data to tmp folder...', param.verbose) run('sct_convert -i '+fname_in+' -o '+path_tmp+'data.nii') run('sct_convert -i '+fname_seg+' -o '+path_tmp+'segmentation.nii.gz') # Go go temp folder path_tmp = '/Users/julien/data/sct_debug/tmp.150826170018/' chdir(path_tmp) # create label to identify disc printv('\nCreate label to identify disc...', param.verbose) if initz: create_label_z('segmentation.nii.gz', initz[0], initz[1]) # create label located at z_center elif initcenter: # find z centered in FOV nii = Image(fname_seg) nii.change_orientation('RPI') # reorient to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions z_center = int(round(nz/2)) # get z_center create_label_z('segmentation.nii.gz', z_center, initcenter) # create label located at z_center # # Straighten spinal cord # printv('\nStraighten spinal cord...', param.verbose) # run('sct_straighten_spinalcord -i data.nii -c segmentation.nii.gz') # Apply straightening to segmentation # N.B. Output is RPI printv('\nApply straightening to segmentation...', param.verbose) run('sct_apply_transfo -i segmentation.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear') # Threshold segmentation to 0.5 run('sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz') # Apply straightening to z-label printv('\nDilate z-label and apply straightening...', param.verbose) run('sct_apply_transfo -i labelz.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn') # get z value and disk value to initialize labeling printv('\nGet z and disc values from straight label...', param.verbose) init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz') printv('.. '+str(init_disc), param.verbose) # detect vertebral levels on straight spinal cord printv('\nDetect inter-vertebral discs and label vertebral levels...', param.verbose) vertebral_detection('data_straight.nii', 'segmentation_straight.nii.gz', contrast, init_disc) # un-straighten spinal cord printv('\nUn-straighten labeling...', param.verbose) run('sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn') # clean labeled segmentation # TODO: (i) find missing voxels wrt. original segmentation, and attribute value closest to neighboring label and (ii) remove additional voxels. # Build fname_out if fname_out == '': path_seg, file_seg, ext_seg = extract_fname(fname_seg) fname_out = path_seg+file_seg+'_labeled'+ext_seg # come back to parent folder chdir('..') # Generate output files printv('\nGenerate output files...', param.verbose) generate_output_file(path_tmp+'segmentation_labeled.nii.gz', fname_out) # Remove temporary files if param.remove_tmp_files == 1: printv('\nRemove temporary files...', param.verbose) run('rm -rf '+path_tmp) # to view results printv('\nDone! To view results, type:', param.verbose) printv('fslview '+fname_in+' '+fname_out+' &\n', param.verbose, 'info')
def validation(self): ext = '.nii.gz' validation_dir = 'validation' sct.run('mkdir ' + validation_dir) # loading the images im_ref_gm_seg = Image('../' + self.ref_gm_seg_fname) im_ref_wm_seg = inverse_gmseg_to_wmseg(im_ref_gm_seg, Image('../' + self.sc_seg_fname), im_ref_gm_seg.path + im_ref_gm_seg.file_name, save=False) res_gm_seg_bin = self.gm_seg.res_gm_seg.copy() res_wm_seg_bin = self.gm_seg.res_wm_seg.copy() if self.param.res_type == 'prob': res_gm_seg_bin.data = np.asarray((res_gm_seg_bin.data >= 0.5).astype(int)) res_wm_seg_bin.data = np.asarray((res_wm_seg_bin.data >= 0.50001).astype(int)) mask = Image(self.preprocessed.square_mask) # doing the validation os.chdir(validation_dir) im_ref_gm_seg.path = './' im_ref_gm_seg.file_name = 'ref_gm_seg' im_ref_gm_seg.ext = ext im_ref_gm_seg.save() ref_gm_seg_new_name = resample_image(im_ref_gm_seg.file_name + ext, npx=self.preprocessed.resample_to, npy=self.preprocessed.resample_to, binary=True) im_ref_gm_seg = Image(ref_gm_seg_new_name) sct.run('rm ' + ref_gm_seg_new_name) im_ref_wm_seg.path = './' im_ref_wm_seg.file_name = 'ref_wm_seg' im_ref_wm_seg.ext = ext im_ref_wm_seg.save() ref_wm_seg_new_name = resample_image(im_ref_wm_seg.file_name + ext, npx=self.preprocessed.resample_to, npy=self.preprocessed.resample_to, binary=True) im_ref_wm_seg = Image(ref_wm_seg_new_name) sct.run('rm ' + ref_wm_seg_new_name) ref_orientation = im_ref_gm_seg.orientation im_ref_gm_seg.change_orientation('IRP') im_ref_wm_seg.change_orientation('IRP') im_ref_gm_seg.crop_and_stack(mask, save=False) im_ref_wm_seg.crop_and_stack(mask, save=False) im_ref_gm_seg.change_orientation('RPI') im_ref_wm_seg.change_orientation('RPI') # saving the images to call the validation functions res_gm_seg_bin.path = './' res_gm_seg_bin.file_name = 'res_gm_seg_bin' res_gm_seg_bin.ext = ext res_gm_seg_bin.save() res_wm_seg_bin.path = './' res_wm_seg_bin.file_name = 'res_wm_seg_bin' res_wm_seg_bin.ext = ext res_wm_seg_bin.save() im_ref_gm_seg.path = './' im_ref_gm_seg.file_name = 'ref_gm_seg' im_ref_gm_seg.ext = ext im_ref_gm_seg.save() im_ref_wm_seg.path = './' im_ref_wm_seg.file_name = 'ref_wm_seg' im_ref_wm_seg.ext = ext im_ref_wm_seg.save() sct.run('sct_orientation -i ' + res_gm_seg_bin.file_name + ext + ' -s RPI') res_gm_seg_bin.file_name += '_RPI' sct.run('sct_orientation -i ' + res_wm_seg_bin.file_name + ext + ' -s RPI') res_wm_seg_bin.file_name += '_RPI' res_gm_seg_bin = Image(res_gm_seg_bin.file_name + ext) im_ref_gm_seg.hdr.set_zooms(res_gm_seg_bin.hdr.get_zooms()) # correcting the pix dimension im_ref_gm_seg.save() res_wm_seg_bin = Image(res_wm_seg_bin.file_name + ext) im_ref_wm_seg.hdr.set_zooms(res_wm_seg_bin.hdr.get_zooms()) # correcting the pix dimension im_ref_wm_seg.save() # Dice try: status_gm, output_gm = sct.run('sct_dice_coefficient ' + im_ref_gm_seg.file_name + ext + ' ' + res_gm_seg_bin.file_name + ext + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: sct.run('c3d ' + res_gm_seg_bin.file_name + ext + ' ' + im_ref_gm_seg.file_name + ext + ' -reslice-identity -o ' + im_ref_gm_seg.file_name + '_in_res_space' + ext) sct.run('fslmaths ' + im_ref_gm_seg.file_name + '_in_res_space' + ext + ' -thr 0.1 ' + im_ref_gm_seg.file_name + '_in_res_space' + ext ) sct.run('fslmaths ' + im_ref_gm_seg.file_name + '_in_res_space' + ext + ' -bin ' + im_ref_gm_seg.file_name + '_in_res_space' + ext ) status_gm, output_gm = sct.run('sct_dice_coefficient ' + im_ref_gm_seg.file_name + '_in_res_space' + ext + ' ' + res_gm_seg_bin.file_name + ext + ' -2d-slices 2', error_exit='warning') try: status_wm, output_wm = sct.run('sct_dice_coefficient ' + im_ref_wm_seg.file_name + ext + ' ' + res_wm_seg_bin.file_name + ext + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: sct.run('c3d ' + res_wm_seg_bin.file_name + ext + ' ' + im_ref_wm_seg.file_name + ext + ' -reslice-identity -o ' + im_ref_wm_seg.file_name + '_in_res_space' + ext) sct.run('fslmaths ' + im_ref_wm_seg.file_name + '_in_res_space' + ext + ' -thr 0.1 ' + im_ref_wm_seg.file_name + '_in_res_space' + ext) sct.run('fslmaths ' + im_ref_wm_seg.file_name + '_in_res_space' + ext + ' -bin ' + im_ref_wm_seg.file_name + '_in_res_space' + ext) status_wm, output_wm = sct.run('sct_dice_coefficient ' + im_ref_wm_seg.file_name + '_in_res_space' + ext + ' ' + res_wm_seg_bin.file_name + ext + ' -2d-slices 2', error_exit='warning') dice_name = 'dice_' + sct.extract_fname(self.target_fname)[1] + '_' + self.param.res_type + '.txt' dice_fic = open('../../' + dice_name, 'w') if self.param.res_type == 'prob': dice_fic.write('WARNING : the probabilistic segmentations were binarized with a threshold at 0.5 to compute the dice coefficient \n') dice_fic.write('\n--------------------------------------------------------------\n' 'Dice coefficient on the Gray Matter segmentation:\n') dice_fic.write(output_gm) dice_fic.write('\n\n--------------------------------------------------------------\n' 'Dice coefficient on the White Matter segmentation:\n') dice_fic.write(output_wm) dice_fic.close() # sct.run(' mv ./' + dice_name + ' ../') hd_name = 'hd_' + sct.extract_fname(self.target_fname)[1] + '_' + self.param.res_type + '.txt' sct.run('sct_compute_hausdorff_distance.py -i ' + res_gm_seg_bin.file_name + ext + ' -r ' + im_ref_gm_seg.file_name + ext + ' -t 1 -o ' + hd_name + ' -v ' + str(self.param.verbose)) sct.run('mv ./' + hd_name + ' ../../') os.chdir('..') return dice_name, hd_name
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 as 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] # Level file is a text file elif ext_level == '.txt': file_level = open(fname_level, 'r') lines_level = file_level.readlines() file_level.close() list_level_by_slice = [] list_type_level = [] # True or int, False for float for line in lines_level[1:]: i_slice, level = line.split(',') # correct level value for c in [' ', '\n', '\r', '\t']: level = level.replace(c, '') try: level = float(level) except Exception as e: # adapt if level value is not unique if len(level) > 2: l1 = l2 = 0 if '-' in level: l1, l2 = level.split('-') elif '/' in level: l1, l2 = level.split('/') # convention = the vertebral disk between two levels belong to the lower level (C2-C3 = C3) level = max(float(l1), float(l2)) else: # level unrecognized level = 0 i_slice = int(i_slice) list_type_level.append(int(level) == level) list_level_by_slice.append((i_slice, level)) # sort list by number of slice list_level_by_slice.sort() if all(list_type_level): # levels are int # add 0.5 to the int level to place in the middle of the vertebra to_add = 0.5 else: # levels are float: keep them untouched to_add = 0 list_level = [l[1] + to_add for l in list_level_by_slice] # Level file is not recognized else: list_level = None printv('ERROR: the level file is nor an image nor a text file ...', verbose, 'error') # ####### Set level number for each slice of list_slices_target: for target_slice, level in zip(list_slices_target, list_level): target_slice.set(level=level) return list_slices_target
def execute(self): print 'Execution of the SCAD algorithm in ' + str(os.getcwd()) original_name = self.input_image.file_name vesselness_file_name = "imageVesselNessFilter.nii.gz" raw_file_name = "raw.nii" self.setup_debug_folder() if self.debug: import matplotlib.pyplot as plt # import for debug purposes # create tmp and copy input path_tmp = self.create_temporary_path() conv.convert(self.input_image.absolutepath, path_tmp + raw_file_name) if self.vesselness_provided: sct.run('cp ' + vesselness_file_name + ' ' + path_tmp + vesselness_file_name) os.chdir(path_tmp) # get input image information img = Image(raw_file_name) # save original orientation and change image to RPI self.raw_orientation = img.change_orientation() # get body symmetry if self.enable_symmetry: from msct_image import change_data_orientation sym = SymmetryDetector(raw_file_name, self.contrast, crop_xy=0) self.raw_symmetry = sym.execute() img.change_orientation(self.raw_orientation) self.output_debug_file(img, self.raw_symmetry, "body_symmetry") img.change_orientation() # vesselness filter if not self.vesselness_provided: sct.run('isct_vesselness -i ' + raw_file_name + ' -t ' + self._contrast + " -radius " + str(self.spinalcord_radius)) # load vesselness filter data and perform minimum path on it img = Image(vesselness_file_name) self.output_debug_file(img, img.data, "Vesselness_Filter") img.change_orientation() self.minimum_path_data, self.J1_min_path, self.J2_min_path = get_minimum_path( img.data, invert=1, debug=1) self.output_debug_file(img, self.minimum_path_data, "minimal_path") self.output_debug_file(img, self.J1_min_path, "J1_minimal_path") self.output_debug_file(img, self.J2_min_path, "J2_minimal_path") # Apply an exponent to the minimum path self.minimum_path_powered = np.power(self.minimum_path_data, self.minimum_path_exponent) self.output_debug_file( img, self.minimum_path_powered, "minimal_path_power_" + str(self.minimum_path_exponent)) # Saving in Image since smooth_minimal_path needs pixel dimensions img.data = self.minimum_path_powered # smooth resulting minimal path self.smoothed_min_path = smooth_minimal_path(img) self.output_debug_file(img, self.smoothed_min_path.data, "minimal_path_smooth") # normalise symmetry values between 0 and 1 if self.enable_symmetry: normalised_symmetry = normalize_array_histogram(self.raw_symmetry) self.output_debug_file(img, self.smoothed_min_path.data, "minimal_path_smooth") # multiply normalised symmetry data with the minimum path result from msct_image import change_data_orientation self.spine_detect_data = np.multiply( self.smoothed_min_path.data, change_data_orientation( np.power(normalised_symmetry, self.symmetry_exponent), self.raw_orientation, "RPI")) self.output_debug_file(img, self.spine_detect_data, "symmetry_x_min_path") # extract the centerline from the minimal path image self.centerline_with_outliers = get_centerline( self.spine_detect_data, self.spine_detect_data.shape) else: # extract the centerline from the minimal path image self.centerline_with_outliers = get_centerline( self.smoothed_min_path.data, self.smoothed_min_path.data.shape) self.output_debug_file(img, self.centerline_with_outliers, "centerline_with_outliers") # saving centerline with outliers to have img.data = self.centerline_with_outliers img.change_orientation() img.file_name = "centerline_with_outliers" img.save() # use a b-spline to smooth out the centerline x, y, z, dx, dy, dz = smooth_centerline( "centerline_with_outliers.nii.gz") # save the centerline nx, ny, nz, nt, px, py, pz, pt = img.dim img.data = np.zeros((nx, ny, nz)) for i in range(0, np.size(x) - 1): img.data[int(x[i]), int(y[i]), int(z[i])] = 1 self.output_debug_file(img, img.data, "centerline") img.change_orientation(self.raw_orientation) img.file_name = "centerline" img.save() # copy back centerline os.chdir('../') conv.convert(path_tmp + img.file_name + img.ext, self.output_filename) if self.rm_tmp_file == 1: import shutil shutil.rmtree(path_tmp) print "To view the output with FSL :" sct.printv( "fslview " + self.input_image.absolutepath + " " + self.output_filename + " -l Red", self.verbose, "info")
def execute(self): print 'Execution of the SCAD algorithm' vesselness_file_name = "imageVesselNessFilter.nii.gz" raw_file_name = "raw.nii" if self.debug: import matplotlib.pyplot as plt # import for debug purposes # create tmp and copy input path_tmp = sct.tmp_create() sct.tmp_copy_nifti(self.input_image.absolutepath, path_tmp, raw_file_name) if self.vesselness_provided: sct.run('cp '+vesselness_file_name+' '+path_tmp+vesselness_file_name) os.chdir(path_tmp) # get input image information img = Image(raw_file_name) # save original orientation and change image to RPI self.raw_orientation = img.change_orientation() # get body symmetry sym = SymmetryDetector(raw_file_name, self.contrast, crop_xy=1) self.raw_symmetry = sym.execute() # vesselness filter if not self.vesselness_provided: sct.run('sct_vesselness -i '+raw_file_name+' -t ' + self._contrast) # load vesselness filter data and perform minimum path on it img = Image(vesselness_file_name) raw_orientation = img.change_orientation() self.minimum_path_data, self.J1_min_path, self.J2_min_path = get_minimum_path(img.data, invert=1, debug=1, smooth_factor=1) # Apply an exponent to the minimum path self.minimum_path_powered = np.power(self.minimum_path_data, self.minimum_path_exponent) # Saving in Image since smooth_minimal_path needs pixel dimensions img.data = self.minimum_path_powered # smooth resulting minimal path self.smoothed_min_path = smooth_minimal_path(img) # normalise symmetry values between 0 and 1 normalised_symmetry = equalize_array_histogram(self.raw_symmetry) # multiply normalised symmetry data with the minimum path result self.spine_detect_data = np.multiply(self.smoothed_min_path.data, normalised_symmetry) # extract the centerline from the minimal path image centerline_with_outliers = get_centerline(self.spine_detect_data, self.spine_detect_data.shape) img.data = centerline_with_outliers img.change_orientation() img.file_name = "centerline_with_outliers" img.save() # use a b-spline to smooth out the centerline x, y, z, dx, dy, dz = smooth_centerline("centerline_with_outliers.nii.gz") # save the centerline centerline_dim = img.dim img.data = np.zeros(centerline_dim) for i in range(0, np.size(x)-1): img.data[int(x[i]), int(y[i]), int(z[i])] = 1 img.change_orientation(raw_orientation) img.file_name = "centerline" img.save() # copy back centerline os.chdir('../') sct.tmp_copy_nifti(path_tmp + 'centerline.nii.gz',self.input_image.path,self.input_image.file_name+'_centerline'+self.input_image.ext) if self.rm_tmp_file == 1: import shutil shutil.rmtree(path_tmp) if self.produce_output: self.produce_output_files()
# path_tmp = tmp_create() # tmp_copy_nifti(input_file, path_tmp, 'raw.nii') # run('cp '+warping_fields_filename[0]+' '+path_tmp) # chdir(path_tmp) run('mkdir images') run('mkdir niftis') while True: try: warping_fields[0].num_of_frames = number_of_frames image_output_iter, iteration = warping_fields[0].next() image_output_iter.save() filename_warp = image_output_iter.path + image_output_iter.file_name + image_output_iter.ext filename_output = "niftis/tmp.warped_image_" + str( iteration - 1) + image_output_iter.ext run("sct_apply_transfo -i " + input_file + " -d " + reference_image + " -w " + filename_warp + " -o " + filename_output) result = Image(filename_output) result.change_orientation() toimage(result.data[int(result.data.shape[0] / 2)].squeeze(), cmin=0.0).save('images/' + extract_fname(filename_output)[1] + '.jpg') filenames_output.append(filename_output) except ValueError: printv('\nError during warping field generation...', 1, 'error') except StopIteration: printv('\nFinished iterations.') break
def validation(self): ext = '.nii.gz' validation_dir = 'validation' sct.run('mkdir ' + validation_dir) # loading the images im_ref_gm_seg = Image('../' + self.ref_gm_seg_fname) im_ref_wm_seg = inverse_gmseg_to_wmseg( im_ref_gm_seg, Image('../' + self.sc_seg_fname), im_ref_gm_seg.path + im_ref_gm_seg.file_name, save=False) res_gm_seg_bin = self.gm_seg.res_gm_seg.copy() res_wm_seg_bin = self.gm_seg.res_wm_seg.copy() if self.param.res_type == 'prob': res_gm_seg_bin.data = np.asarray( (res_gm_seg_bin.data >= 0.5).astype(int)) res_wm_seg_bin.data = np.asarray( (res_wm_seg_bin.data >= 0.50001).astype(int)) mask = Image(self.preprocessed.square_mask) # doing the validation os.chdir(validation_dir) im_ref_gm_seg.path = './' im_ref_gm_seg.file_name = 'ref_gm_seg' im_ref_gm_seg.ext = ext im_ref_gm_seg.save() ref_gm_seg_new_name = resample_image(im_ref_gm_seg.file_name + ext, npx=self.preprocessed.resample_to, npy=self.preprocessed.resample_to, binary=True) im_ref_gm_seg = Image(ref_gm_seg_new_name) sct.run('rm ' + ref_gm_seg_new_name) im_ref_wm_seg.path = './' im_ref_wm_seg.file_name = 'ref_wm_seg' im_ref_wm_seg.ext = ext im_ref_wm_seg.save() ref_wm_seg_new_name = resample_image(im_ref_wm_seg.file_name + ext, npx=self.preprocessed.resample_to, npy=self.preprocessed.resample_to, binary=True) im_ref_wm_seg = Image(ref_wm_seg_new_name) sct.run('rm ' + ref_wm_seg_new_name) ref_orientation = im_ref_gm_seg.orientation im_ref_gm_seg.change_orientation('IRP') im_ref_wm_seg.change_orientation('IRP') im_ref_gm_seg.crop_and_stack(mask, save=False) im_ref_wm_seg.crop_and_stack(mask, save=False) im_ref_gm_seg.change_orientation('RPI') im_ref_wm_seg.change_orientation('RPI') # saving the images to call the validation functions res_gm_seg_bin.path = './' res_gm_seg_bin.file_name = 'res_gm_seg_bin' res_gm_seg_bin.ext = ext res_gm_seg_bin.save() res_wm_seg_bin.path = './' res_wm_seg_bin.file_name = 'res_wm_seg_bin' res_wm_seg_bin.ext = ext res_wm_seg_bin.save() im_ref_gm_seg.path = './' im_ref_gm_seg.file_name = 'ref_gm_seg' im_ref_gm_seg.ext = ext im_ref_gm_seg.save() im_ref_wm_seg.path = './' im_ref_wm_seg.file_name = 'ref_wm_seg' im_ref_wm_seg.ext = ext im_ref_wm_seg.save() sct.run('sct_orientation -i ' + res_gm_seg_bin.file_name + ext + ' -s RPI') res_gm_seg_bin.file_name += '_RPI' sct.run('sct_orientation -i ' + res_wm_seg_bin.file_name + ext + ' -s RPI') res_wm_seg_bin.file_name += '_RPI' res_gm_seg_bin = Image(res_gm_seg_bin.file_name + ext) im_ref_gm_seg.hdr.set_zooms( res_gm_seg_bin.hdr.get_zooms()) # correcting the pix dimension im_ref_gm_seg.save() res_wm_seg_bin = Image(res_wm_seg_bin.file_name + ext) im_ref_wm_seg.hdr.set_zooms( res_wm_seg_bin.hdr.get_zooms()) # correcting the pix dimension im_ref_wm_seg.save() # Dice try: status_gm, output_gm = sct.run( 'sct_dice_coefficient ' + im_ref_gm_seg.file_name + ext + ' ' + res_gm_seg_bin.file_name + ext + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: sct.run('c3d ' + res_gm_seg_bin.file_name + ext + ' ' + im_ref_gm_seg.file_name + ext + ' -reslice-identity -o ' + im_ref_gm_seg.file_name + '_in_res_space' + ext) sct.run('fslmaths ' + im_ref_gm_seg.file_name + '_in_res_space' + ext + ' -thr 0.1 ' + im_ref_gm_seg.file_name + '_in_res_space' + ext) sct.run('fslmaths ' + im_ref_gm_seg.file_name + '_in_res_space' + ext + ' -bin ' + im_ref_gm_seg.file_name + '_in_res_space' + ext) status_gm, output_gm = sct.run( 'sct_dice_coefficient ' + im_ref_gm_seg.file_name + '_in_res_space' + ext + ' ' + res_gm_seg_bin.file_name + ext + ' -2d-slices 2', error_exit='warning') try: status_wm, output_wm = sct.run( 'sct_dice_coefficient ' + im_ref_wm_seg.file_name + ext + ' ' + res_wm_seg_bin.file_name + ext + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: sct.run('c3d ' + res_wm_seg_bin.file_name + ext + ' ' + im_ref_wm_seg.file_name + ext + ' -reslice-identity -o ' + im_ref_wm_seg.file_name + '_in_res_space' + ext) sct.run('fslmaths ' + im_ref_wm_seg.file_name + '_in_res_space' + ext + ' -thr 0.1 ' + im_ref_wm_seg.file_name + '_in_res_space' + ext) sct.run('fslmaths ' + im_ref_wm_seg.file_name + '_in_res_space' + ext + ' -bin ' + im_ref_wm_seg.file_name + '_in_res_space' + ext) status_wm, output_wm = sct.run( 'sct_dice_coefficient ' + im_ref_wm_seg.file_name + '_in_res_space' + ext + ' ' + res_wm_seg_bin.file_name + ext + ' -2d-slices 2', error_exit='warning') dice_name = 'dice_' + sct.extract_fname( self.target_fname)[1] + '_' + self.param.res_type + '.txt' dice_fic = open('../../' + dice_name, 'w') if self.param.res_type == 'prob': dice_fic.write( 'WARNING : the probabilistic segmentations were binarized with a threshold at 0.5 to compute the dice coefficient \n' ) dice_fic.write( '\n--------------------------------------------------------------\n' 'Dice coefficient on the Gray Matter segmentation:\n') dice_fic.write(output_gm) dice_fic.write( '\n\n--------------------------------------------------------------\n' 'Dice coefficient on the White Matter segmentation:\n') dice_fic.write(output_wm) dice_fic.close() # sct.run(' mv ./' + dice_name + ' ../') hd_name = 'hd_' + sct.extract_fname( self.target_fname)[1] + '_' + self.param.res_type + '.txt' sct.run('sct_compute_hausdorff_distance.py -i ' + res_gm_seg_bin.file_name + ext + ' -r ' + im_ref_gm_seg.file_name + ext + ' -t 1 -o ' + hd_name + ' -v ' + str(self.param.verbose)) sct.run('mv ./' + hd_name + ' ../../') os.chdir('..') return dice_name, hd_name
def main(args=None): # initializations initz = '' initcenter = '' initc2 = 'auto' param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_in = arguments["-i"] fname_seg = arguments['-s'] contrast = arguments['-c'] path_template = sct.slash_at_the_end(arguments['-t'], 1) # if '-o' in arguments: # file_out = arguments["-o"] # else: # file_out = '' if '-ofolder' in arguments: path_output = sct.slash_at_the_end(os.path.abspath(arguments['-ofolder']), slash=1) else: path_output = sct.slash_at_the_end(os.path.abspath(os.curdir), slash=1) if '-initz' in arguments: initz = arguments['-initz'] if '-initcenter' in arguments: initcenter = arguments['-initcenter'] # if user provided text file, parse and overwrite arguments if '-initfile' in arguments: # open file file = open(arguments['-initfile'], 'r') initfile = ' '+file.read().replace('\n', '') arg_initfile = initfile.split(' ') for i in xrange(len(arg_initfile)): if arg_initfile[i] == '-initz': initz = [int(x) for x in arg_initfile[i+1].split(',')] if arg_initfile[i] == '-initcenter': initcenter = int(arg_initfile[i+1]) if '-initc2' in arguments: initc2 = 'manual' if '-param' in arguments: param.update(arguments['-param'][0]) verbose = int(arguments['-v']) remove_tmp_files = int(arguments['-r']) denoise = int(arguments['-denoise']) laplacian = int(arguments['-laplacian']) # if verbose, import matplotlib # if verbose == 2: # import matplotlib.pyplot as plt # create temporary folder printv('\nCreate temporary folder...', verbose) path_tmp = tmp_create(verbose=verbose) # path_tmp = '/Users/julien/Dropbox/documents/processing/20160813_wang/t12/tmp.160814213032_725693/' # Copying input data to tmp folder printv('\nCopying input data to tmp folder...', verbose) run('sct_convert -i '+fname_in+' -o '+path_tmp+'data.nii') run('sct_convert -i '+fname_seg+' -o '+path_tmp+'segmentation.nii.gz') # Go go temp folder os.chdir(path_tmp) # create label to identify disc printv('\nCreate label to identify disc...', verbose) initauto = False if initz: create_label_z('segmentation.nii.gz', initz[0], initz[1]) # create label located at z_center elif initcenter: # find z centered in FOV nii = Image('segmentation.nii.gz') nii.change_orientation('RPI') # reorient to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions z_center = int(round(nz/2)) # get z_center create_label_z('segmentation.nii.gz', z_center, initcenter) # create label located at z_center else: initauto = True # printv('\nERROR: You need to initialize the disc detection algorithm using one of these two options: -initz, -initcenter\n', 1, 'error') # Straighten spinal cord printv('\nStraighten spinal cord...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'): # if they exist, copy them into current folder sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning') shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz') shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz') shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz') # apply straightening sct.run('sct_apply_transfo -i data.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o data_straight.nii') else: run('sct_straighten_spinalcord -i data.nii -s segmentation.nii.gz -r 0 -qc 0') # resample to 0.5mm isotropic to match template resolution printv('\nResample to 0.5mm isotropic...', verbose) run('sct_resample -i data_straight.nii -mm 0.5x0.5x0.5 -x linear -o data_straightr.nii', verbose) # run('sct_resample -i segmentation.nii.gz -mm 0.5x0.5x0.5 -x linear -o segmentationr.nii.gz', verbose) # run('sct_resample -i labelz.nii.gz -mm 0.5x0.5x0.5 -x linear -o labelzr.nii', verbose) # Apply straightening to segmentation # N.B. Output is RPI printv('\nApply straightening to segmentation...', verbose) run('sct_apply_transfo -i segmentation.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear', verbose) # Threshold segmentation at 0.5 run('sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz', verbose) if initauto: init_disc = [] else: # Apply straightening to z-label printv('\nDilate z-label and apply straightening...', verbose) run('sct_apply_transfo -i labelz.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn', verbose) # get z value and disk value to initialize labeling printv('\nGet z and disc values from straight label...', verbose) init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz') printv('.. '+str(init_disc), verbose) # denoise data if denoise: printv('\nDenoise data...', verbose) run('sct_maths -i data_straightr.nii -denoise h=0.05 -o data_straightr.nii', verbose) # apply laplacian filtering if laplacian: printv('\nApply Laplacian filter...', verbose) run('sct_maths -i data_straightr.nii -laplacian 1 -o data_straightr.nii', verbose) # detect vertebral levels on straight spinal cord vertebral_detection('data_straightr.nii', 'segmentation_straight.nii.gz', contrast, param, init_disc=init_disc, verbose=verbose, path_template=path_template, initc2=initc2, path_output=path_output) # un-straighten labeled spinal cord printv('\nUn-straighten labeling...', verbose) run('sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn', verbose) # Clean labeled segmentation printv('\nClean labeled segmentation (correct interpolation errors)...', verbose) clean_labeled_segmentation('segmentation_labeled.nii.gz', 'segmentation.nii.gz', 'segmentation_labeled.nii.gz') # label discs printv('\nLabel discs...', verbose) label_discs('segmentation_labeled.nii.gz', verbose=verbose) # come back to parent folder os.chdir('..') # Generate output files path_seg, file_seg, ext_seg = extract_fname(fname_seg) printv('\nGenerate output files...', verbose) generate_output_file(path_tmp+'segmentation_labeled.nii.gz', path_output+file_seg+'_labeled'+ext_seg) generate_output_file(path_tmp+'segmentation_labeled_disc.nii.gz', path_output+file_seg+'_labeled_discs'+ext_seg) # copy straightening files in case subsequent SCT functions need them generate_output_file(path_tmp+'warp_curve2straight.nii.gz', path_output+'warp_curve2straight.nii.gz', verbose) generate_output_file(path_tmp+'warp_straight2curve.nii.gz', path_output+'warp_straight2curve.nii.gz', verbose) generate_output_file(path_tmp+'straight_ref.nii.gz', path_output+'straight_ref.nii.gz', verbose) # Remove temporary files if remove_tmp_files == 1: printv('\nRemove temporary files...', verbose) run('rm -rf '+path_tmp) # to view results printv('\nDone! To view results, type:', verbose) printv('fslview '+fname_in+' '+path_output+file_seg+'_labeled'+' -l Random-Rainbow -t 0.5 &\n', verbose, 'info')
def scadMRValidation(algorithm, isPython=False, verbose=True): if not isinstance(algorithm, str) or not algorithm: print 'ERROR: You must provide the name of your algorithm as a string.' usage() import time import sct_utils as sct # creating a new folder with the experiment path_experiment = 'scad-experiment.'+algorithm+'.'+time.strftime("%y%m%d%H%M%S") #status, output = sct.run('mkdir '+path_experiment, verbose) # copying images from "data" folder into experiment folder sct.copyDirectory('data', path_experiment) # Starting validation os.chdir(path_experiment) # t1 os.chdir('t1/') for subject_dir in os.listdir('./'): if os.path.isdir(subject_dir): os.chdir(subject_dir) # creating list of images and corresponding manual segmentation list_images = dict() for file_name in os.listdir('./'): if not 'manual_segmentation' in file_name: for file_name_corr in os.listdir('./'): if 'manual_segmentation' in file_name_corr and sct.extract_fname(file_name)[1] in file_name_corr: list_images[file_name] = file_name_corr # running the proposed algorithm on images in the folder and analyzing the results for image, image_manual_seg in list_images.items(): print image path_in, file_in, ext_in = sct.extract_fname(image) image_output = file_in+'_centerline'+ext_in if ispython: try: eval(algorithm+'('+image+', t1, verbose='+str(verbose)+')') except Exception as e: print 'Error during spinal cord detection on line {}:'.format(sys.exc_info()[-1].tb_lineno) print 'Subject: t1/'+subject_dir+'/'+image print e sys.exit(2) else: cmd = algorithm+' -i '+image+' -t t1' if verbose: cmd += ' -v' status, output = sct.run(cmd, verbose=verbose) if status != 0: print 'Error during spinal cord detection on Subject: t1/'+subject_dir+'/'+image print output sys.exit(2) # analyzing the resulting centerline from msct_image import Image manual_segmentation_image = Image(image_manual_seg) manual_segmentation_image.change_orientation() centerline_image = Image(image_output) centerline_image.change_orientation() from msct_types import Coordinate # coord_manseg = manual_segmentation_image.getNonZeroCoordinates() coord_centerline = centerline_image.getNonZeroCoordinates() # check if centerline is in manual segmentation result_centerline_in = True for coord in coord_centerline: if manual_segmentation_image.data[coord.x, coord.y, coord.z] == 0: result_centerline_in = False print 'failed on slice #' + str(coord.z) break if result_centerline_in: print 'OK: Centerline is inside manual segmentation.' else: print 'FAIL: Centerline is outside manual segmentation.' # check the length of centerline compared to manual segmentation # import sct_process_segmentation as sct_seg # length_manseg = sct_seg.compute_length(image_manual_seg) # length_centerline = sct_seg.compute_length(image_output) # if length_manseg*0.9 <= length_centerline <= length_manseg*1.1: # print 'OK: Length of centerline correspond to length of manual segmentation.' # else: # print 'FAIL: Length of centerline does not correspond to length of manual segmentation.' os.chdir('..')
warping_fields = [WarpingField(filename) for filename in warping_fields_filename] filenames_output = [] # path_tmp = tmp_create() # tmp_copy_nifti(input_file, path_tmp, 'raw.nii') # run('cp '+warping_fields_filename[0]+' '+path_tmp) # chdir(path_tmp) run('mkdir images') run('mkdir niftis') while True: try: warping_fields[0].num_of_frames = number_of_frames image_output_iter, iteration = warping_fields[0].next() image_output_iter.save() filename_warp = image_output_iter.path + image_output_iter.file_name + image_output_iter.ext filename_output = "niftis/tmp.warped_image_" + str(iteration - 1) + image_output_iter.ext run("sct_apply_transfo -i " + input_file + " -d " + reference_image + " -w " + filename_warp + " -o " + filename_output) result=Image(filename_output) result.change_orientation() toimage(result.data[int(result.data.shape[0]/2)].squeeze(), cmin=0.0).save('images/'+extract_fname(filename_output)[1]+'.jpg') filenames_output.append(filename_output) except ValueError: printv('\nError during warping field generation...', 1, 'error') except StopIteration: printv('\nFinished iterations.') break
def main(args=None): # initializations initz = '' initcenter = '' initc2 = 'auto' param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) fname_in = arguments["-i"] fname_seg = arguments['-s'] contrast = arguments['-c'] path_template = sct.slash_at_the_end(arguments['-t'], 1) # if '-o' in arguments: # file_out = arguments["-o"] # else: # file_out = '' if '-ofolder' in arguments: path_output = sct.slash_at_the_end(os.path.abspath( arguments['-ofolder']), slash=1) else: path_output = sct.slash_at_the_end(os.path.abspath(os.curdir), slash=1) if '-initz' in arguments: initz = arguments['-initz'] if '-initcenter' in arguments: initcenter = arguments['-initcenter'] # if user provided text file, parse and overwrite arguments if '-initfile' in arguments: # open file file = open(arguments['-initfile'], 'r') initfile = ' ' + file.read().replace('\n', '') arg_initfile = initfile.split(' ') for i in xrange(len(arg_initfile)): if arg_initfile[i] == '-initz': initz = [int(x) for x in arg_initfile[i + 1].split(',')] if arg_initfile[i] == '-initcenter': initcenter = int(arg_initfile[i + 1]) if '-initc2' in arguments: initc2 = 'manual' if '-param' in arguments: param.update(arguments['-param'][0]) verbose = int(arguments['-v']) remove_tmp_files = int(arguments['-r']) denoise = int(arguments['-denoise']) laplacian = int(arguments['-laplacian']) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.tmp_create(verbose=verbose) # 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') sct.run('sct_convert -i ' + fname_seg + ' -o ' + path_tmp + 'segmentation.nii.gz') # Go go temp folder os.chdir(path_tmp) # create label to identify disc sct.printv('\nCreate label to identify disc...', verbose) initauto = False if initz: create_label_z('segmentation.nii.gz', initz[0], initz[1]) # create label located at z_center elif initcenter: # find z centered in FOV nii = Image('segmentation.nii.gz') nii.change_orientation('RPI') # reorient to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions z_center = int(round(nz / 2)) # get z_center create_label_z('segmentation.nii.gz', z_center, initcenter) # create label located at z_center else: initauto = True # printv('\nERROR: You need to initialize the disc detection algorithm using one of these two options: -initz, -initcenter\n', 1, 'error') # Straighten spinal cord sct.printv('\nStraighten spinal cord...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile( '../warp_straight2curve.nii.gz') and os.path.isfile( '../straight_ref.nii.gz'): # if they exist, copy them into current folder sct.printv( 'WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning') shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz') shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz') shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz') # apply straightening sct.run( 'sct_apply_transfo -i data.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o data_straight.nii' ) else: sct.run( 'sct_straighten_spinalcord -i data.nii -s segmentation.nii.gz -r 0 -qc 0' ) # resample to 0.5mm isotropic to match template resolution sct.printv('\nResample to 0.5mm isotropic...', verbose) sct.run( 'sct_resample -i data_straight.nii -mm 0.5x0.5x0.5 -x linear -o data_straightr.nii', verbose) # sct.run('sct_resample -i segmentation.nii.gz -mm 0.5x0.5x0.5 -x linear -o segmentationr.nii.gz', verbose) # sct.run('sct_resample -i labelz.nii.gz -mm 0.5x0.5x0.5 -x linear -o labelzr.nii', verbose) # Apply straightening to segmentation # N.B. Output is RPI sct.printv('\nApply straightening to segmentation...', verbose) sct.run( 'sct_apply_transfo -i segmentation.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear', verbose) # Threshold segmentation at 0.5 sct.run( 'sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz', verbose) if initauto: init_disc = [] else: # Apply straightening to z-label sct.printv('\nDilate z-label and apply straightening...', verbose) sct.run( 'sct_apply_transfo -i labelz.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn', verbose) # get z value and disk value to initialize labeling sct.printv('\nGet z and disc values from straight label...', verbose) init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz') sct.printv('.. ' + str(init_disc), verbose) # denoise data if denoise: sct.printv('\nDenoise data...', verbose) sct.run( 'sct_maths -i data_straightr.nii -denoise h=0.05 -o data_straightr.nii', verbose) # apply laplacian filtering if laplacian: sct.printv('\nApply Laplacian filter...', verbose) sct.run( 'sct_maths -i data_straightr.nii -laplacian 1 -o data_straightr.nii', verbose) # detect vertebral levels on straight spinal cord vertebral_detection('data_straightr.nii', 'segmentation_straight.nii.gz', contrast, param, init_disc=init_disc, verbose=verbose, path_template=path_template, initc2=initc2, path_output=path_output) # un-straighten labeled spinal cord sct.printv('\nUn-straighten labeling...', verbose) sct.run( 'sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn', verbose) # Clean labeled segmentation sct.printv( '\nClean labeled segmentation (correct interpolation errors)...', verbose) clean_labeled_segmentation('segmentation_labeled.nii.gz', 'segmentation.nii.gz', 'segmentation_labeled.nii.gz') # label discs sct.printv('\nLabel discs...', verbose) label_discs('segmentation_labeled.nii.gz', verbose=verbose) # come back to parent folder os.chdir('..') # Generate output files path_seg, file_seg, ext_seg = sct.extract_fname(fname_seg) sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp + 'segmentation_labeled.nii.gz', path_output + file_seg + '_labeled' + ext_seg) sct.generate_output_file( path_tmp + 'segmentation_labeled_disc.nii.gz', path_output + file_seg + '_labeled_discs' + ext_seg) # copy straightening files in case subsequent SCT functions need them sct.generate_output_file(path_tmp + 'warp_curve2straight.nii.gz', path_output + 'warp_curve2straight.nii.gz', verbose) sct.generate_output_file(path_tmp + 'warp_straight2curve.nii.gz', path_output + 'warp_straight2curve.nii.gz', verbose) sct.generate_output_file(path_tmp + 'straight_ref.nii.gz', path_output + 'straight_ref.nii.gz', verbose) # Remove temporary files if remove_tmp_files == 1: sct.printv('\nRemove temporary files...', verbose) shutil.rmtree(path_tmp, ignore_errors=True) # Generate QC report try: if '-qc' in arguments and not arguments.get('-noqc', False): qc_path = arguments['-qc'] import spinalcordtoolbox.reports.qc as qc import spinalcordtoolbox.reports.slice as qcslice qc_param = qc.Params(fname_in, 'sct_label_vertebrae', args, 'Sagittal', qc_path) report = qc.QcReport(qc_param, '') @qc.QcImage(report, 'none', [ qc.QcImage.label_vertebrae, ]) def test(qslice): return qslice.single() labeled_seg_file = path_output + file_seg + '_labeled' + ext_seg test(qcslice.Sagittal(Image(fname_in), Image(labeled_seg_file))) sct.printv('Sucessfully generated the QC results in %s' % qc_param.qc_results) sct.printv( 'Use the following command to see the results in a browser:') sct.printv('sct_qc -folder %s' % qc_path, type='info') except Exception as err: sct.printv(err, verbose, 'warning') sct.printv('WARNING: Cannot generate report.', verbose, 'warning') # to view results sct.printv('\nDone! To view results, type:', verbose) sct.printv( 'fslview ' + fname_in + ' ' + path_output + file_seg + '_labeled' + ' -l Random-Rainbow -t 0.5 &\n', verbose, 'info')
def vertebral_detection(fname, fname_seg, contrast): shift_AP = 14 # shift the centerline on the spine in mm default : 17 mm size_AP = 3 # mean around the centerline in the anterior-posterior direction in mm size_RL = 3 # mean around the centerline in the right-left direction in mm verbose = param.verbose if verbose: import matplotlib.pyplot as plt # open anatomical volume img = Image(fname) # orient to RPI img.change_orientation() # get dimension nx, ny, nz, nt, px, py, pz, pt = img.dim #================================================== # Compute intensity profile across vertebrae #================================================== shift_AP = shift_AP * py size_AP = size_AP * py size_RL = size_RL * px # orient segmentation to RPI run('sct_orientation -i ' + fname_seg + ' -s RPI') # smooth segmentation/centerline path_centerline, file_centerline, ext_centerline = extract_fname(fname_seg) x, y, z, Tx, Ty, Tz = smooth_centerline(path_centerline + file_centerline + '_RPI' + ext_centerline) # build intensity profile along the centerline I = np.zeros((len(y), 1)) # mask where intensity profile will be taken if verbose == 2: mat = img.copy() mat.data = np.zeros(mat.dim) for iz in range(len(z)): # define vector orthogonal to the cord in RL direction P1 = np.array([1, 0, -Tx[iz]/Tz[iz]]) P1 = P1/np.linalg.norm(P1) # define vector orthogonal to the cord in AP direction P2 = np.array([0, 1, -Ty[iz]/Tz[iz]]) P2 = P2/np.linalg.norm(P2) # define X and Y coordinates of the voxels to extract intensity profile from indexRL = range(-np.int(round(size_RL)), np.int(round(size_RL))) indexAP = range(0, np.int(round(size_AP)))+np.array(shift_AP) # loop over coordinates of perpendicular plane for i_RL in indexRL: for i_AP in indexAP: i_vect = np.round(np.array([x[iz], y[iz], z[iz]])+P1*i_RL+P2*i_AP) i_vect = np.minimum(np.maximum(i_vect, 0), np.array([nx, ny, nz])-1) # check if index stays in image dimension I[iz] = I[iz] + img.data[i_vect[0], i_vect[1], i_vect[2]] # create a mask with this perpendicular plane if verbose == 2: mat.data[i_vect[0], i_vect[1], i_vect[2]] = 1 if verbose == 2: mat.file_name = 'mask' mat.save() # Detrending Intensity start_centerline_y = y[0] X = np.where(I == 0) mask2 = np.ones((len(y), 1), dtype=bool) mask2[X, 0] = False # low pass filtering import scipy.signal frequency = 2/pz Wn = 0.1/frequency N = 2 #Order of the filter # b, a = scipy.signal.butter(N, Wn, btype='low', analog=False, output='ba') b, a = scipy.signal.iirfilter(N, Wn, rp=None, rs=None, btype='high', analog=False, ftype='bessel', output='ba') I_detrend = scipy.signal.filtfilt(b, a, I[:, 0], axis=-1, padtype='constant', padlen=None) I_detrend = I_detrend/(np.amax(I_detrend)) #================================================== # step 1 : Find the First Peak #================================================== if contrast == 't1': I_detrend2 = np.diff(I_detrend) elif contrast == 't2': space = np.linspace(-10/pz, 10/pz, round(21/pz), endpoint=True) pattern = (np.sinc((space*pz)/20)) ** 20 I_corr = scipy.signal.correlate(-I_detrend.squeeze().squeeze()+1,pattern,'same') b, a = scipy.signal.iirfilter(N, Wn, rp=None, rs=None, btype='high', analog=False, ftype='bessel', output='ba') I_detrend2 = scipy.signal.filtfilt(b, a, I_corr, axis=-1, padtype='constant', padlen=None) I_detrend2[I_detrend2 < 0.2] = 0 ind_locs = np.squeeze(scipy.signal.argrelextrema(I_detrend2, np.greater)) # remove peaks that are too closed locsdiff = np.diff(z[ind_locs]) ind = locsdiff > 10 ind_locs = np.hstack((ind_locs[ind], ind_locs[-1])) locs = z[ind_locs] if verbose == 2: # x=0: most caudal, x=max: most rostral plt.figure() plt.plot(I_detrend2) plt.plot(ind_locs, I_detrend2[ind_locs], '+') plt.show() #===================================================================================== # step 2 : Cross correlation between the adjusted template and the intensity profile. # Local moving of template's peak from the first peak already found #===================================================================================== #For each loop, a peak is located at the most likely position and then local adjustment is done. #The position of the next peak is calculated from previous positions # TODO: use mean distance mean_distance = [12.1600, 20.8300, 18.0000, 16.0000, 15.1667, 15.3333, 15.8333, 18.1667, 18.6667, 18.6667, 19.8333, 20.6667, 21.6667, 22.3333, 23.8333, 24.1667, 26.0000, 28.6667, 30.5000, 33.5000, 33.0000, 31.3330] # # #Creating pattern printv('\nFinding Cross correlation between the adjusted template and the intensity profile...', verbose) space = np.linspace(-10/pz, 10/pz, round(21/pz), endpoint=True) pattern = (np.sinc((space*pz)/20))**20 I_corr = scipy.signal.correlate(I_detrend2.squeeze().squeeze()+1, pattern, 'same') # # level_start=1 # if contrast == 'T1': # mean_distance = mean_distance[level_start-1:len(mean_distance)] # xmax_pattern = np.argmax(pattern) # else: # mean_distance = mean_distance[level_start+1:len(mean_distance)] # xmax_pattern = np.argmin(pattern) # position of the peak in the pattern # pixend = len(pattern) - xmax_pattern #number of pixel after the peaks in the pattern # # # mean_distance_new = mean_distance # mean_ratio = np.zeros(len(mean_distance)) # # L = np.round(1.2*max(mean_distance)) - np.round(0.8*min(mean_distance)) # corr_peak = np.zeros((L,len(mean_distance))) # corr_peak = np.nan #for T2 # # #loop on each peak # for i_peak in range(len(mean_distance)): # scale_min = np.round(0.80*mean_distance_new[i_peak]) - xmax_pattern - pixend # if scale_min<0: # scale_min = 0 # # scale_max = np.round(1.2*mean_distance_new[i_peak]) - xmax_pattern - pixend # scale_peak = np.arange(scale_min,scale_max+1) # # for i_scale in range(len(scale_peak)): # template_resize_peak = np.concatenate([template_truncated,np.zeros(scale_peak[i_scale]),pattern]) # if len(I_detrend[:,0])>len(template_resize_peak): # template_resize_peak1 = np.concatenate((template_resize_peak,np.zeros(len(I_detrend[:,0])-len(template_resize_peak)))) # # #cross correlation # corr_template = scipy.signal.correlate(I_detrend[:,0],template_resize_peak) # # if len(I_detrend[:,0])>len(template_resize_peak): # val = np.dot(I_detrend[:,0],template_resize_peak1.T) # else: # I_detrend_2 = np.concatenate((I_detrend[:,0],np.zeros(len(template_resize_peak)-len(I_detrend[:,0])))) # val = np.dot(I_detrend_2,template_resize_peak.T) # corr_peak[i_scale,i_peak] = val # # if verbose: # plt.xlim(0,len(I_detrend[:,0])) # plt.plot(I_detrend[:,0]) # plt.plot(template_resize_peak) # plt.show(block=False) # # plt.plot(corr_peak[:,i_peak],marker='+',linestyle='None',color='r') # plt.title('correlation value against the displacement of the peak (px)') # plt.show(block=False) # # max_peak = np.amax(corr_peak[:,i_peak]) # index_scale_peak = np.where(corr_peak[:,i_peak]==max_peak) # good_scale_peak = scale_peak[index_scale_peak][0] # Mcorr = Mcorr1 # Mcorr = np.resize(Mcorr,i_peak+2) # Mcorr[i_peak+1] = np.amax(corr_peak[:,0:(i_peak+1)]) # flag = 0 # # #If the correlation coefficient is too low, put the peak at the mean position # if i_peak>0: # if (Mcorr[i_peak+1]-Mcorr[i_peak])<0.4*np.mean(Mcorr[1:i_peak+2]-Mcorr[0:i_peak+1]): # test = i_peak # template_resize_peak = np.concatenate((template_truncated,np.zeros(round(mean_distance[i_peak])-xmax_pattern-pixend),pattern)) # good_scale_peak = np.round(mean_distance[i_peak]) - xmax_pattern - pixend # flag = 1 # if i_peak==0: # if (Mcorr[i_peak+1] - Mcorr[i_peak])<0.4*Mcorr[0]: # template_resize_peak = np.concatenate((template_truncated,np.zeros(round(mean_distance[i_peak])-xmax_pattern-pixend),pattern)) # good_scale_peak = round(mean_distance[i_peak]) - xmax_pattern - pixend # flag = 1 # if flag==0: # template_resize_peak=np.concatenate((template_truncated,np.zeros(good_scale_peak),pattern)) # # #update mean-distance by a adjustement ratio # mean_distance_new[i_peak] = good_scale_peak + xmax_pattern + pixend # mean_ratio[i_peak] = np.mean(mean_distance_new[:,0:i_peak]/mean_distance[:,0:i_peak]) # # template_truncated = template_resize_peak # # if verbose: # plt.plot(I_detrend[:,0]) # plt.plot(template_truncated) # plt.xlim(0,(len(I_detrend[:,0])-1)) # plt.show() # # #finding the maxima of the adjusted template # minpeakvalue = 0.5 # loc_disk = np.arange(len(template_truncated)) # index_disk = [] # for i in range(len(template_truncated)): # if template_truncated[i]>=minpeakvalue: # if i==0: # if template_truncated[i]<template_truncated[i+1]: # index_disk.append(i) # elif i==(len(template_truncated)-1): # if template_truncated[i]<template_truncated[i-1]: # index_disk.append(i) # else: # if template_truncated[i]<template_truncated[i+1]: # index_disk.append(i) # elif template_truncated[i]<template_truncated[i-1]: # index_disk.append(i) # else: # index_disk.append(i) # # mask_disk = np.ones(len(template_truncated), dtype=bool) # mask_disk[index_disk] = False # loc_disk = loc_disk[mask_disk] # X1 = np.where(loc_disk > I_detrend.shape[0]) # mask_disk1 = np.ones(len(loc_disk), dtype=bool) # mask_disk1[X1] = False # loc_disk = loc_disk[mask_disk1] # loc_disk = loc_disk + start_centerline_y - 1 #===================================================================== # Step 3: Label segmentation #===================================================================== # # Project vertebral levels back to the centerline # centerline = Image(fname_seg) # raw_orientation = centerline.change_orientation() # centerline.data[:, :, :] = 0 # for iz in range(locs[0]): # centerline.data[np.round(x[iz]), np.round(y[iz]), iz] = 1 # for i in range(len(locs)-1): # for iz in range(locs[i], min(locs[i+1], len(z))): # centerline.data[np.round(x[iz]), np.round(y[iz]), iz] = i+2 # for iz in range(locs[-1], len(z)): # centerline.data[np.round(x[iz]), np.round(y[iz]), iz] = i+3 # # #centerline.change_orientation(raw_orientation) # centerline.file_name += '_labeled' # centerline.save() # Label segmentation with vertebral number # Method: loop across all voxels of the segmentation, project each voxel to the line passing through the vertebrae # (using minimum distance) and assign vertebral level. printv('\nLabel segmentation...', verbose) seg = Image(fname_seg) seg_raw_orientation = seg.change_orientation() # find all voxels belonging to segmentation x_seg, y_seg, z_seg = np.where(seg.data) # loop across voxels in segmentation for ivox in range(len(x_seg)): # get voxel coordinate vox_coord = np.array([x_seg[ivox], y_seg[ivox], z_seg[ivox]]) # find closest point to the curved line passing through the vertebrae for iplane in range(len(locs)): ind = np.where(z == locs[iplane]) vox_vector = vox_coord - np.hstack((x[ind], y[ind], z[ind])) normal2plane_vector = np.hstack((Tx[ind], Ty[ind], Tz[ind])) # Tx, Ty and Tz are the derivatives of the centerline # if voxel is above the plane --> give the number of the plane if np.dot(vox_vector, normal2plane_vector) > 0: seg.data[vox_coord[0], vox_coord[1], vox_coord[2]] = iplane+2 else: # if the voxel gets below the plane --> next voxel break seg.change_orientation(seg_raw_orientation) seg.file_name += '_labeled' seg.save() # # color the segmentation with vertebral number # printv('\nLabel input segmentation...', verbose) # # if fname_segmentation: # seg = Image(fname_seg) # seg_raw_orientation = seg.change_orientation() # x_seg, y_seg, z_seg = np.where(seg.data) # find all voxels belonging to segmentation # for ivox in range(len(x_seg)): # loop across voxels in segmentation # vox_coord = np.array([x_seg[ivox], y_seg[ivox], z_seg[ivox]]) # get voxel coordinate # for iplane in range(len(locs)): # ind = np.where(z == locs[iplane]) # vox_vector = vox_coord - np.hstack((x[ind], y[ind], z[ind])) # normal2plane_vector = np.hstack((Tx[ind], Ty[ind], Tz[ind])) # Tx, Ty and Tz are the derivatives of the centerline # # # if voxel is above the plane --> give the number of the plane # if np.dot(vox_vector, normal2plane_vector) > 0: # seg.data[vox_coord[0], vox_coord[1], vox_coord[2]] = iplane+2 # else: # if the voxel gets below the plane --> next voxel # break # seg.change_orientation(seg_raw_orientation) # seg.file_name += '_labeled' # seg.save() return locs
def test(path_data='', parameters=''): if not parameters: parameters = '-i t2/t2.nii.gz -c t2 -p auto' # parameters folder_data = 't2/' file_data = ['t2.nii.gz', 't2_centerline_init.nii.gz', 't2_centerline_labels.nii.gz', 't2_seg_manual.nii.gz'] parser = sct_get_centerline.get_parser() dict_param = parser.parse(parameters.split(), check_file_exist=False) contrast = dict_param['-c'] dict_param_with_path = parser.add_path_to_file(dict_param, path_data, input_file=True) param_with_path = parser.dictionary_to_string(dict_param_with_path) # Check if input files exist if not (os.path.isfile(dict_param_with_path['-i'])): status = 200 output = 'ERROR: the file(s) provided to test function do not exist in folder: ' + path_data return status, output, DataFrame(data={'status': status, 'output': output, 'mse': float('nan'), 'dist_max': float('nan')}, index=[path_data]) cmd = 'sct_get_centerline '+param_with_path status, output = sct.run(cmd, 0) scad_centerline = Image(contrast+"_centerline.nii.gz") manual_seg = Image(path_data + folder_data + contrast +'_seg_manual.nii.gz') max_distance = 0 standard_deviation = 0 average = 0 root_mean_square = 0 overall_distance = 0 max_distance = 0 overall_std = 0 rmse = 0 try: if status == 0: manual_seg.change_orientation() scad_centerline.change_orientation() from scipy.ndimage.measurements import center_of_mass # find COM iterator = range(manual_seg.data.shape[2]) com_x = [0 for ix in iterator] com_y = [0 for iy in iterator] for iz in iterator: com_x[iz], com_y[iz] = center_of_mass(manual_seg.data[:, :, iz]) max_distance = {} distance = {} for iz in range(1, scad_centerline.data.shape[2]-1): ind1 = np.argmax(scad_centerline.data[:, :, iz]) X,Y = ind2sub(scad_centerline.data[:, :, iz].shape,ind1) com_phys = np.array(manual_seg.transfo_pix2phys([[com_x[iz], com_y[iz], iz]])) scad_phys = np.array(scad_centerline.transfo_pix2phys([[X, Y, iz]])) distance_magnitude = np.linalg.norm([com_phys[0][0]-scad_phys[0][0], com_phys[0][1]-scad_phys[0][1], 0]) if math.isnan(distance_magnitude): print "Value is nan" else: distance[iz] = distance_magnitude max_distance = max(distance.values()) standard_deviation = np.std(np.array(distance.values())) average = sum(distance.values())/len(distance) root_mean_square = np.sqrt(np.mean(np.square(distance.values()))) overall_distance = average max_distance = max(distance.values()) overall_std = standard_deviation rmse = root_mean_square except Exception, e: sct.printv("Exception found while testing scad integrity") output = e.message