def check_and_correct_segmentation(fname_segmentation, fname_centerline, folder_output='', threshold_distance=5.0, remove_temp_files=1, verbose=0): """ This function takes the outputs of isct_propseg (centerline and segmentation) and check if the centerline of the segmentation is coherent with the centerline provided by the isct_propseg, especially on the edges (related to issue #1074). Args: fname_segmentation: filename of binary segmentation fname_centerline: filename of binary centerline threshold_distance: threshold, in mm, beyond which centerlines are not coherent verbose: Returns: None """ sct.printv('\nCheck consistency of segmentation...', verbose) # creating a temporary folder in which all temporary files will be placed and deleted afterwards path_tmp = sct.tmp_create(basename="propseg", verbose=verbose) from sct_convert import convert convert(fname_segmentation, os.path.join(path_tmp, "tmp.segmentation.nii.gz"), squeeze_data=False, verbose=0) convert(fname_centerline, os.path.join(path_tmp, "tmp.centerline.nii.gz"), squeeze_data=False, verbose=0) fname_seg_absolute = os.path.abspath(fname_segmentation) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # convert segmentation image to RPI im_input = Image('tmp.segmentation.nii.gz') image_input_orientation = orientation(im_input, get=True, verbose=False) sct_image.main( "-i tmp.segmentation.nii.gz -setorient RPI -o tmp.segmentation_RPI.nii.gz -v 0" .split()) sct_image.main( "-i tmp.centerline.nii.gz -setorient RPI -o tmp.centerline_RPI.nii.gz -v 0" .split()) # go through segmentation image, and compare with centerline from propseg im_seg = Image('tmp.segmentation_RPI.nii.gz') im_centerline = Image('tmp.centerline_RPI.nii.gz') # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_seg.dim # extraction of centerline provided by isct_propseg and computation of center of mass for each slice # the centerline is defined as the center of the tubular mesh outputed by propseg. centerline, key_centerline = {}, [] for i in range(nz): slice = im_centerline.data[:, :, i] if np.any(slice): x_centerline, y_centerline = ndi.measurements.center_of_mass(slice) centerline[str(i)] = [x_centerline, y_centerline] key_centerline.append(i) minz_centerline = np.min(key_centerline) maxz_centerline = np.max(key_centerline) mid_slice = int((maxz_centerline - minz_centerline) / 2) # for each slice of the segmentation, check if only one object is present. If not, remove the slice from segmentation. # If only one object (the spinal cord) is present in the slice, check if its center of mass is close to the centerline of isct_propseg. slices_to_remove = [ False ] * nz # flag that decides if the slice must be removed for i in range(minz_centerline, maxz_centerline + 1): # extraction of slice slice = im_seg.data[:, :, i] distance = -1 label_objects, nb_labels = ndi.label( slice) # count binary objects in the slice if nb_labels > 1: # if there is more that one object in the slice, the slice is removed from the segmentation slices_to_remove[i] = True elif nb_labels == 1: # check if the centerline is coherent with the one from isct_propseg x_centerline, y_centerline = ndi.measurements.center_of_mass(slice) slice_nearest_coord = min(key_centerline, key=lambda x: abs(x - i)) coord_nearest_coord = centerline[str(slice_nearest_coord)] distance = np.sqrt(( (x_centerline - coord_nearest_coord[0]) * px)**2 + ( (y_centerline - coord_nearest_coord[1]) * py)**2 + ((i - slice_nearest_coord) * pz)**2) if distance >= threshold_distance: # threshold must be adjusted, default is 5 mm slices_to_remove[i] = True # Check list of removal and keep one continuous centerline (improve this comment) # Method: # starting from mid-centerline (in both directions), the first True encountered is applied to all following slices slice_to_change = False for i in range(mid_slice, nz): if slice_to_change: slices_to_remove[i] = True elif slices_to_remove[i]: slice_to_change = True slice_to_change = False for i in range(mid_slice, 0, -1): if slice_to_change: slices_to_remove[i] = True elif slices_to_remove[i]: slice_to_change = True for i in range(0, nz): # remove the slice if slices_to_remove[i]: im_seg.data[:, :, i] *= 0 # saving the image im_seg.setFileName('tmp.segmentation_RPI_c.nii.gz') im_seg.save() # replacing old segmentation with the corrected one sct_image.main( '-i tmp.segmentation_RPI_c.nii.gz -setorient {} -o {} -v 0'.format( image_input_orientation, fname_seg_absolute).split()) os.chdir(curdir) # display information about how much of the segmentation has been corrected # remove temporary files if remove_temp_files: # sct.printv("\nRemove temporary files...", verbose) sct.rmtree(path_tmp)
def detect_centerline(image_fname, contrast_type, optic_models_path, folder_output, remove_temp_files=False, init_option=None, output_roi=False, verbose=0): """This method will use the OptiC to detect the centerline. :param image_fname: The input image filename. :param init_option: Axial slice where the propagation starts. :param contrast_type: The contrast type. :param optic_models_path: The path with the Optic model files. :param folder_output: The OptiC output folder. :param remove_temp_files: Remove the temporary created files. :param verbose: Adjusts the verbosity of the logging. :returns: The OptiC output filename. """ image_input = Image(image_fname) path_data, file_data, ext_data = sct.extract_fname(image_fname) sct.printv('Detecting the spinal cord using OptiC', verbose=verbose) image_input_orientation = orientation(image_input, get=True, verbose=False) temp_folder = sct.TempFolder() temp_folder.copy_from(image_fname) temp_folder.chdir() # convert image data type to int16, as required by opencv (backend in OptiC) image_int_filename = sct.add_suffix(file_data + ext_data, "_int16") sct_image.main( args=['-i', image_fname, '-type', 'int16', '-o', image_int_filename]) # reorient the input image to RPI + convert to .nii reoriented_image_filename = sct.add_suffix(image_int_filename, "_RPI") img_filename = ''.join(sct.extract_fname(reoriented_image_filename)[:2]) reoriented_image_filename_nii = img_filename + '.nii' cmd_reorient = 'sct_image -i "%s" -o "%s" -setorient RPI -v 0' % \ (image_int_filename, reoriented_image_filename_nii) sct.run(cmd_reorient, verbose=0) image_rpi_init = Image(reoriented_image_filename_nii) nxr, nyr, nzr, ntr, pxr, pyr, pzr, ptr = image_rpi_init.dim if init_option is not None: if init_option > 1: init_option /= (nzr - 1) # call the OptiC method to generate the spinal cord centerline optic_input = img_filename optic_filename = img_filename + '_optic' os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR" cmd_optic = 'isct_spine_detect -ctype=dpdt -lambda=1 "%s" "%s" "%s"' % \ (optic_models_path, optic_input, optic_filename) sct.run(cmd_optic, verbose=0) # convert .img and .hdr files to .nii.gz optic_hdr_filename = img_filename + '_optic_ctr.hdr' centerline_optic_RPI_filename = sct.add_suffix(file_data + ext_data, "_centerline_optic_RPI") img = nib.load(optic_hdr_filename) nib.save(img, centerline_optic_RPI_filename) # reorient the output image to initial orientation centerline_optic_filename = sct.add_suffix(file_data + ext_data, "_centerline_optic") cmd_reorient = 'sct_image -i "%s" -o "%s" -setorient "%s" -v 0' % \ (centerline_optic_RPI_filename, centerline_optic_filename, image_input_orientation) sct.run(cmd_reorient, verbose=0) # copy centerline to parent folder folder_output_from_temp = folder_output if not os.path.isabs(folder_output): folder_output_from_temp = '../' + folder_output sct.printv('Copy output to ' + folder_output, verbose=0) shutil.copy(centerline_optic_filename, folder_output_from_temp) if output_roi: fname_roi_centerline = centerline2roi( fname_image=centerline_optic_RPI_filename, folder_output=folder_output_from_temp, verbose=verbose) # Note: the .roi file is defined in RPI orientation. To be used, it must be applied on the original image with # a RPI orientation. For this reason, this script also outputs the input image in RPI orientation shutil.copy(reoriented_image_filename_nii, folder_output_from_temp) # return to initial folder temp_folder.chdir_undo() # delete temporary folder if remove_temp_files: temp_folder.cleanup() return init_option, os.path.join(folder_output, centerline_optic_filename)
def set_orient(ref, i, i_tmp, o): sct_image.main(args=['-i', ref, '-copy-header', i, '-v', '0']) sct_image.main( args=['-i', i, '-setorient-data', 'IPR', '-o', i_tmp, '-v', '0']) sct_image.main(args=['-i', i_tmp, '-setorient', 'RPI', '-o', o, '-v', '0'])
def check_and_correct_segmentation(fname_segmentation, fname_centerline, folder_output='', threshold_distance=5.0, remove_temp_files=1, verbose=0): """ This function takes the outputs of isct_propseg (centerline and segmentation) and check if the centerline of the segmentation is coherent with the centerline provided by the isct_propseg, especially on the edges (related to issue #1074). Args: fname_segmentation: filename of binary segmentation fname_centerline: filename of binary centerline threshold_distance: threshold, in mm, beyond which centerlines are not coherent verbose: Returns: None """ sct.printv('\nCheck consistency of segmentation...', verbose) # creating a temporary folder in which all temporary files will be placed and deleted afterwards path_tmp = sct.tmp_create(basename="propseg", verbose=verbose) from sct_convert import convert convert(fname_segmentation, os.path.join(path_tmp, "tmp.segmentation.nii.gz"), verbose=0) convert(fname_centerline, os.path.join(path_tmp, "tmp.centerline.nii.gz"), verbose=0) fname_seg_absolute = os.path.abspath(fname_segmentation) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # convert segmentation image to RPI im_input = Image('tmp.segmentation.nii.gz') image_input_orientation = im_input.orientation sct_image.main("-i tmp.segmentation.nii.gz -setorient RPI -o tmp.segmentation_RPI.nii.gz -v 0".split()) sct_image.main("-i tmp.centerline.nii.gz -setorient RPI -o tmp.centerline_RPI.nii.gz -v 0".split()) # go through segmentation image, and compare with centerline from propseg im_seg = Image('tmp.segmentation_RPI.nii.gz') im_centerline = Image('tmp.centerline_RPI.nii.gz') # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_seg.dim # extraction of centerline provided by isct_propseg and computation of center of mass for each slice # the centerline is defined as the center of the tubular mesh outputed by propseg. centerline, key_centerline = {}, [] for i in range(nz): slice = im_centerline.data[:, :, i] if np.any(slice): x_centerline, y_centerline = ndi.measurements.center_of_mass(slice) centerline[str(i)] = [x_centerline, y_centerline] key_centerline.append(i) minz_centerline = np.min(key_centerline) maxz_centerline = np.max(key_centerline) mid_slice = int((maxz_centerline - minz_centerline) / 2) # for each slice of the segmentation, check if only one object is present. If not, remove the slice from segmentation. # If only one object (the spinal cord) is present in the slice, check if its center of mass is close to the centerline of isct_propseg. slices_to_remove = [False] * nz # flag that decides if the slice must be removed for i in range(minz_centerline, maxz_centerline + 1): # extraction of slice slice = im_seg.data[:, :, i] distance = -1 label_objects, nb_labels = ndi.label(slice) # count binary objects in the slice if nb_labels > 1: # if there is more that one object in the slice, the slice is removed from the segmentation slices_to_remove[i] = True elif nb_labels == 1: # check if the centerline is coherent with the one from isct_propseg x_centerline, y_centerline = ndi.measurements.center_of_mass(slice) slice_nearest_coord = min(key_centerline, key=lambda x: abs(x - i)) coord_nearest_coord = centerline[str(slice_nearest_coord)] distance = np.sqrt(((x_centerline - coord_nearest_coord[0]) * px) ** 2 + ((y_centerline - coord_nearest_coord[1]) * py) ** 2 + ((i - slice_nearest_coord) * pz) ** 2) if distance >= threshold_distance: # threshold must be adjusted, default is 5 mm slices_to_remove[i] = True # Check list of removal and keep one continuous centerline (improve this comment) # Method: # starting from mid-centerline (in both directions), the first True encountered is applied to all following slices slice_to_change = False for i in range(mid_slice, nz): if slice_to_change: slices_to_remove[i] = True elif slices_to_remove[i]: slice_to_change = True slice_to_change = False for i in range(mid_slice, 0, -1): if slice_to_change: slices_to_remove[i] = True elif slices_to_remove[i]: slice_to_change = True for i in range(0, nz): # remove the slice if slices_to_remove[i]: im_seg.data[:, :, i] *= 0 # saving the image im_seg.save('tmp.segmentation_RPI_c.nii.gz') # replacing old segmentation with the corrected one sct_image.main('-i tmp.segmentation_RPI_c.nii.gz -setorient {} -o {} -v 0'. format(image_input_orientation, fname_seg_absolute).split()) os.chdir(curdir) # display information about how much of the segmentation has been corrected # remove temporary files if remove_temp_files: # sct.printv("\nRemove temporary files...", verbose) sct.rmtree(path_tmp)