def generate_qc(fn_in, fn_seg, args, path_qc): """Generate a QC entry allowing to quickly review the segmentation process.""" import spinalcordtoolbox.reports.qc as qc import spinalcordtoolbox.reports.slice as qcslice from spinalcordtoolbox.resample.nipy_resample import resample_file # Resample to fixed resolution (see #2063) tmp_folder = sct.TempFolder() fn_in_r = os.path.join(tmp_folder.path_tmp, 'img_r.nii.gz') # Orient to RPI and retrieve pixel size in IS direction (z) im_fn = Image(fn_in).change_orientation('RPI').save(fn_in_r) resample_file(fn_in_r, fn_in_r, '0.5x0.5x' + str(im_fn.dim[6]), 'mm', 'nn', 0) fn_seg_r = os.path.join(tmp_folder.path_tmp, 'seg_r.nii.gz') Image(fn_seg).change_orientation('RPI').save(fn_seg_r) resample_file(fn_seg_r, fn_seg_r, '0.5x0.5x' + str(im_fn.dim[6]), 'mm', 'nn', 0) # TODO: investigate the following issue further (Julien 2018-12-01): # fn_in_r and fn_seg_r should be in nii.gz format, otherwise qcslice.Axial outputs an memmap instead of # an array. qc.add_entry( src=fn_in, process="sct_deepseg_sc", args=args, path_qc=path_qc, plane='Axial', qcslice=qcslice.Axial([Image(fn_in_r), Image(fn_seg_r)]), qcslice_operations=[qc.QcImage.listed_seg], qcslice_layout=lambda x: x.mosaic(), )
def generate_qc(fn_in, fn_seg, args, path_qc): """ Generate a QC entry allowing to quickly review the segmentation process. """ import spinalcordtoolbox.reports.qc as qc import spinalcordtoolbox.reports.slice as qcslice from spinalcordtoolbox.resample.nipy_resample import resample_file # Resample to fixed resolution (see #2063) tmp_folder = sct.TempFolder() fn_in_r = os.path.join(tmp_folder.path_tmp, 'img_r.nii.gz') # Orient to RPI and retrieve pixel size in IS direction (z) im_fn = Image(fn_in).change_orientation('RPI').save(fn_in_r) resample_file(fn_in_r, fn_in_r, '0.5x0.5x' + str(im_fn.dim[6]), 'mm', 'nn', 0) fn_seg_r = os.path.join(tmp_folder.path_tmp, 'seg_r.nii.gz') Image(fn_seg).change_orientation('RPI').save(fn_seg_r) resample_file(fn_seg_r, fn_seg_r, '0.5x0.5x' + str(im_fn.dim[6]), 'mm', 'nn', 0) qc.add_entry( src=fn_in, process="sct_propseg", args=args, path_qc=path_qc, plane='Axial', qcslice=qcslice.Axial([Image(fn_in_r), Image(fn_seg_r)]), qcslice_operations=[qc.QcImage.listed_seg], qcslice_layout=lambda x: x.mosaic(), )
def detect_centerline(img, contrast): """Detect spinal cord centerline using OptiC. :param img: input Image() object. :param contrast: str: The type of contrast. Will define the path to Optic model. :returns: Image(): Output centerline """ # Fetch path to Optic model based on contrast optic_models_path = os.path.join(sct.__sct_dir__, 'data', 'optic_models', '{}_model'.format(contrast)) sct.log.debug('Detecting the spinal cord using OptiC') img_orientation = img.orientation temp_folder = sct.TempFolder() temp_folder.chdir() # convert image data type to int16, as required by opencv (backend in OptiC) img_int16 = img.copy() # Replace non-numeric values by zero img_data = img.data img_data[np.where(np.isnan(img_data))] = 0 img_data[np.where(np.isinf(img_data))] = 0 img_int16.data[np.where(np.isnan(img_int16.data))] = 0 img_int16.data[np.where(np.isinf(img_int16.data))] = 0 # rescale intensity min_out = np.iinfo('uint16').min max_out = np.iinfo('uint16').max min_in = np.nanmin(img_data) max_in = np.nanmax(img_data) data_rescaled = img_data.astype('float') * (max_out - min_out) / (max_in - min_in) img_int16.data = data_rescaled - (data_rescaled.min() - min_out) # change data type img_int16.change_type(np.uint16) # reorient the input image to RPI + convert to .nii img_int16.change_orientation('RPI') file_img = 'img_rpi_uint16' img_int16.save(file_img + '.nii') # call the OptiC method to generate the spinal cord centerline optic_input = file_img optic_filename = file_img + '_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) # TODO: output coordinates, for each slice, in continuous (not discrete) values. sct.run(cmd_optic, verbose=0) # convert .img and .hdr files to .nii.gz img_ctl = Image(file_img + '_optic_ctr.hdr') img_ctl.change_orientation(img_orientation) # return to initial folder temp_folder.chdir_undo() return img_ctl
def preprocess_image(image, contrast_type='t1', ctr_algo='svm', ctr_file=None, brain_bool=True, kernel_size='2d', remove_temp_files=1, verbose=1): """ Resamples, reorients to RPI, and applies OptiC cropping to an Image and returns the result as an sct Image. Inputs: image - Image to be cropped Returns: im_nii - resampled Image im_norm_in - resampled, cropped, and normalized Imagect X_CROP_LST, Y_CROP_LST, Z_CROP_LST - coordinates for cropping original image """ im = image.copy() # create temporary folder with intermediate results tmp_folder = sct.TempFolder(verbose=verbose) tmp_folder_path = tmp_folder.get_path() if ctr_algo == 'file': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() # re-orient image to RPI if necessary... original_orientation = im.orientation fname_orient = 'image_in_RPI.nii' im.change_orientation('RPI').save(fname_orient) input_resolution = im.dim[4:7] # resamples image to 0.5x0.5 resolution and finds the spinal cord centerline - execute OptiC binary fname_res, centerline_filename, im_labels = find_centerline( algo=ctr_algo, image_fname=fname_orient, contrast_type=contrast_type, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) # could save the ctr_nii later if desired im_nii, ctr_nii = Image(fname_res), Image(centerline_filename) # crop image around the spinal cord centerline crop_size = 96 if (kernel_size == '3d' and contrast_type == 't2s') else 64 X_CROP_LST, Y_CROP_LST, Z_CROP_LST, im_crop_nii = crop_image_around_centerline( im_in=im_nii, ctr_in=ctr_nii, crop_size=crop_size) # normalize the intensity of the images im_norm_in = apply_intensity_normalization(im_in=im_crop_nii) return im_nii, im_norm_in, X_CROP_LST, Y_CROP_LST, Z_CROP_LST
def _preprocess_segment(fname_t2, fname_t2_seg, contrast_test, dim_3=False): tmp_folder = sct.TempFolder() tmp_folder_path = tmp_folder.get_path() tmp_folder.chdir() img = Image(fname_t2) gt = Image(fname_t2_seg) fname_t2_RPI, fname_t2_seg_RPI = 'img_RPI.nii.gz', 'seg_RPI.nii.gz' img.change_orientation('RPI').save(fname_t2_RPI) gt.change_orientation('RPI').save(fname_t2_seg_RPI) input_resolution = gt.dim[4:7] del img, gt fname_res, fname_ctr, _ = deepseg_sc.find_centerline(algo='svm', image_fname=fname_t2_RPI, contrast_type=contrast_test, brain_bool=False, folder_output=tmp_folder_path, remove_temp_files=1, centerline_fname=None) fname_t2_seg_RPI_res = 'seg_RPI_res.nii.gz' new_resolution = 'x'.join(['0.5', '0.5', str(input_resolution[2])]) resample_file(fname_t2_seg_RPI, fname_t2_seg_RPI_res, new_resolution, 'mm', 'linear', verbose=0) img, ctr, gt = Image(fname_res), Image(fname_ctr), Image(fname_t2_seg_RPI_res) _, _, _, img = deepseg_sc.crop_image_around_centerline(im_in=img, ctr_in=ctr, crop_size=64) _, _, _, gt = deepseg_sc.crop_image_around_centerline(im_in=gt, ctr_in=ctr, crop_size=64) del ctr img = deepseg_sc.apply_intensity_normalization(im_in=img) if dim_3: # If 3D kernels fname_t2_RPI_res_crop, fname_t2_seg_RPI_res_crop = 'img_RPI_res_crop.nii.gz', 'seg_RPI_res_crop.nii.gz' img.save(fname_t2_RPI_res_crop) gt.save(fname_t2_seg_RPI_res_crop) del img, gt fname_t2_RPI_res_crop_res = 'img_RPI_res_crop_res.nii.gz' fname_t2_seg_RPI_res_crop_res = 'seg_RPI_res_crop_res.nii.gz' resample_file(fname_t2_RPI_res_crop, fname_t2_RPI_res_crop_res, new_resolution, 'mm', 'linear', verbose=0) resample_file(fname_t2_seg_RPI_res_crop, fname_t2_seg_RPI_res_crop_res, new_resolution, 'mm', 'linear', verbose=0) img, gt = Image(fname_t2_RPI_res_crop_res), Image(fname_t2_seg_RPI_res_crop_res) tmp_folder.chdir_undo() tmp_folder.cleanup() return img, gt
def deep_segmentation_MSlesion(im_image, contrast_type, ctr_algo='svm', ctr_file=None, brain_bool=True, remove_temp_files=1, verbose=1): """ Segment lesions from MRI data. :param im_image: Image() object containing the lesions to segment :param contrast_type: Constrast of the image. Need to use one supported by the CNN models. :param ctr_algo: Algo to find the centerline. See sct_get_centerline :param ctr_file: Centerline or segmentation (optional) :param brain_bool: If brain if present or not in the image. :param remove_temp_files: :return: """ # create temporary folder with intermediate results tmp_folder = sct.TempFolder(verbose=verbose) tmp_folder_path = tmp_folder.get_path() if ctr_algo == 'file': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() fname_in = im_image.absolutepath # re-orient image to RPI logger.info("Reorient the image to RPI, if necessary...") original_orientation = im_image.orientation # fname_orient = 'image_in_RPI.nii' im_image.change_orientation('RPI') input_resolution = im_image.dim[4:7] # Resample image to 0.5mm in plane im_image_res = \ resampling.resample_nib(im_image, new_size=[0.5, 0.5, im_image.dim[6]], new_size_type='mm', interpolation='linear') fname_orient = 'image_in_RPI_res.nii' im_image_res.save(fname_orient) # find the spinal cord centerline - execute OptiC binary logger.info("\nFinding the spinal cord centerline...") contrast_type_ctr = contrast_type.split('_')[0] _, im_ctl, im_labels_viewer = find_centerline( algo=ctr_algo, image_fname=fname_orient, contrast_type=contrast_type_ctr, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) if ctr_algo == 'file': im_ctl = \ resampling.resample_nib(im_ctl, new_size=[0.5, 0.5, im_image.dim[6]], new_size_type='mm', interpolation='linear') # crop image around the spinal cord centerline logger.info("\nCropping the image around the spinal cord...") crop_size = 48 X_CROP_LST, Y_CROP_LST, Z_CROP_LST, im_crop_nii = crop_image_around_centerline( im_in=im_image_res, ctr_in=im_ctl, crop_size=crop_size) del im_ctl # normalize the intensity of the images logger.info("Normalizing the intensity...") im_norm_in = apply_intensity_normalization(img=im_crop_nii, contrast=contrast_type) del im_crop_nii # resample to 0.5mm isotropic fname_norm = sct.add_suffix(fname_orient, '_norm') im_norm_in.save(fname_norm) fname_res3d = sct.add_suffix(fname_norm, '_resampled3d') resampling.resample_file(fname_norm, fname_res3d, '0.5x0.5x0.5', 'mm', 'linear', verbose=0) # segment data using 3D convolutions logger.info( "\nSegmenting the MS lesions using deep learning on 3D patches...") segmentation_model_fname = sct_dir_local_path( 'data', 'deepseg_lesion_models', '{}_lesion.h5'.format(contrast_type)) fname_seg_crop_res = sct.add_suffix(fname_res3d, '_lesionseg') im_res3d = Image(fname_res3d) seg_im = segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, im=im_res3d.copy()) seg_im.save(fname_seg_crop_res) del im_res3d, seg_im # resample to the initial pz resolution fname_seg_res2d = sct.add_suffix(fname_seg_crop_res, '_resampled2d') initial_2d_resolution = 'x'.join(['0.5', '0.5', str(input_resolution[2])]) resampling.resample_file(fname_seg_crop_res, fname_seg_res2d, initial_2d_resolution, 'mm', 'linear', verbose=0) seg_crop = Image(fname_seg_res2d) # reconstruct the segmentation from the crop data logger.info("\nReassembling the image...") seg_uncrop_nii = uncrop_image(ref_in=im_image_res, data_crop=seg_crop.copy().data, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST, z_crop_lst=Z_CROP_LST) fname_seg_res_RPI = sct.add_suffix(fname_in, '_res_RPI_seg') seg_uncrop_nii.save(fname_seg_res_RPI) del seg_crop # resample to initial resolution logger.info( "Resampling the segmentation to the original image resolution...") initial_resolution = 'x'.join([ str(input_resolution[0]), str(input_resolution[1]), str(input_resolution[2]) ]) fname_seg_RPI = sct.add_suffix(fname_in, '_RPI_seg') resampling.resample_file(fname_seg_res_RPI, fname_seg_RPI, initial_resolution, 'mm', 'linear', verbose=0) seg_initres_nii = Image(fname_seg_RPI) if ctr_algo == 'viewer': # resample and reorient the viewer labels im_labels_viewer_nib = nib.nifti1.Nifti1Image( im_labels_viewer.data, im_labels_viewer.hdr.get_best_affine()) im_viewer_r_nib = resampling.resample_nib(im_labels_viewer_nib, new_size=input_resolution, new_size_type='mm', interpolation='linear') im_viewer = Image( im_viewer_r_nib.get_data(), hdr=im_viewer_r_nib.header, orientation='RPI', dim=im_viewer_r_nib.header.get_data_shape()).change_orientation( original_orientation) else: im_viewer = None if verbose == 2: fname_res_ctr = sct.add_suffix(fname_orient, '_ctr') resampling.resample_file(fname_res_ctr, fname_res_ctr, initial_resolution, 'mm', 'linear', verbose=0) im_image_res_ctr_downsamp = Image(fname_res_ctr).change_orientation( original_orientation) else: im_image_res_ctr_downsamp = None # binarize the resampled image to remove interpolation effects logger.info( "\nBinarizing the segmentation to avoid interpolation effects...") thr = 0.1 seg_initres_nii.data[np.where(seg_initres_nii.data >= thr)] = 1 seg_initres_nii.data[np.where(seg_initres_nii.data < thr)] = 0 # change data type seg_initres_nii.change_type(np.uint8) # reorient to initial orientation logger.info( "\nReorienting the segmentation to the original image orientation...") tmp_folder.chdir_undo() # remove temporary files if remove_temp_files: logger.info("\nRemove temporary files...") tmp_folder.cleanup() # reorient to initial orientation return seg_initres_nii.change_orientation( original_orientation), im_viewer, im_image_res_ctr_downsamp
def run_main(): sct.start_stream_logger() parser = get_parser() args = sys.argv[1:] arguments = parser.parse(args) # Input filename fname_input_data = arguments["-i"] fname_data = os.path.abspath(fname_input_data) # Method used method = 'optic' if "-method" in arguments: method = arguments["-method"] # Contrast type contrast_type = '' if "-c" in arguments: contrast_type = arguments["-c"] if method == 'optic' and not contrast_type: # Contrast must be error = 'ERROR: -c is a mandatory argument when using Optic method.' sct.printv(error, type='error') return # Ga between slices interslice_gap = 10.0 if "-gap" in arguments: interslice_gap = float(arguments["-gap"]) # Output folder if "-ofolder" in arguments: folder_output = sct.slash_at_the_end(arguments["-ofolder"], slash=1) else: folder_output = './' # Remove temporary files remove_temp_files = True if "-r" in arguments: remove_temp_files = bool(int(arguments["-r"])) # Outputs a ROI file output_roi = False if "-roi" in arguments: output_roi = bool(int(arguments["-roi"])) # Verbosity verbose = 0 if "-v" in arguments: verbose = int(arguments["-v"]) if method == 'viewer': path_data, file_data, ext_data = sct.extract_fname(fname_data) # create temporary folder temp_folder = sct.TempFolder() temp_folder.copy_from(fname_data) temp_folder.chdir() # make sure image is in SAL orientation, as it is the orientation used by the viewer image_input = Image(fname_data) image_input_orientation = orientation(image_input, get=True, verbose=False) reoriented_image_filename = sct.add_suffix(file_data + ext_data, "_SAL") cmd_image = 'sct_image -i "%s" -o "%s" -setorient SAL -v 0' % ( fname_data, reoriented_image_filename) sct.run(cmd_image, verbose=False) # extract points manually using the viewer fname_points = viewer_centerline(image_fname=reoriented_image_filename, interslice_gap=interslice_gap, verbose=verbose) if fname_points is not None: image_points_RPI = sct.add_suffix(fname_points, "_RPI") cmd_image = 'sct_image -i "%s" -o "%s" -setorient RPI -v 0' % ( fname_points, image_points_RPI) sct.run(cmd_image, verbose=False) image_input_reoriented = Image(image_points_RPI) # fit centerline, smooth it and return the first derivative (in physical space) x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline( image_points_RPI, algo_fitting='nurbs', nurbs_pts_number=3000, phys_coordinates=True, verbose=verbose, all_slices=False) centerline = Centerline(x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv) # average centerline coordinates over slices of the image x_centerline_fit_rescorr, y_centerline_fit_rescorr, z_centerline_rescorr, x_centerline_deriv_rescorr, y_centerline_deriv_rescorr, z_centerline_deriv_rescorr = centerline.average_coordinates_over_slices( image_input_reoriented) # compute z_centerline in image coordinates for usage in vertebrae mapping voxel_coordinates = image_input_reoriented.transfo_phys2pix([[ x_centerline_fit_rescorr[i], y_centerline_fit_rescorr[i], z_centerline_rescorr[i] ] for i in range(len(z_centerline_rescorr))]) x_centerline_voxel = [coord[0] for coord in voxel_coordinates] y_centerline_voxel = [coord[1] for coord in voxel_coordinates] z_centerline_voxel = [coord[2] for coord in voxel_coordinates] # compute z_centerline in image coordinates with continuous precision voxel_coordinates = image_input_reoriented.transfo_phys2continuouspix( [[ x_centerline_fit_rescorr[i], y_centerline_fit_rescorr[i], z_centerline_rescorr[i] ] for i in range(len(z_centerline_rescorr))]) x_centerline_voxel_cont = [coord[0] for coord in voxel_coordinates] y_centerline_voxel_cont = [coord[1] for coord in voxel_coordinates] z_centerline_voxel_cont = [coord[2] for coord in voxel_coordinates] # Create an image with the centerline image_input_reoriented.data *= 0 min_z_index, max_z_index = int(round( min(z_centerline_voxel))), int(round(max(z_centerline_voxel))) for iz in range(min_z_index, max_z_index + 1): image_input_reoriented.data[ int(round(x_centerline_voxel[iz - min_z_index])), int(round(y_centerline_voxel[iz - min_z_index])), int( iz )] = 1 # if index is out of bounds here for hanning: either the segmentation has holes or labels have been added to the file # Write the centerline image sct.printv('\nWrite NIFTI volumes...', verbose) fname_centerline_oriented = file_data + '_centerline' + ext_data image_input_reoriented.setFileName(fname_centerline_oriented) image_input_reoriented.changeType('uint8') image_input_reoriented.save() sct.printv('\nSet to original orientation...', verbose) sct.run('sct_image -i ' + fname_centerline_oriented + ' -setorient ' + image_input_orientation + ' -o ' + fname_centerline_oriented) # create a txt file with the centerline fname_centerline_oriented_txt = file_data + '_centerline.txt' file_results = open(fname_centerline_oriented_txt, 'w') for i in range(min_z_index, max_z_index + 1): file_results.write( str(int(i)) + ' ' + str(round(x_centerline_voxel_cont[i - min_z_index], 2)) + ' ' + str(round(y_centerline_voxel_cont[i - min_z_index], 2)) + '\n') file_results.close() fname_centerline_oriented_roi = optic.centerline2roi( fname_image=fname_centerline_oriented, folder_output='./', verbose=verbose) # return to initial folder temp_folder.chdir_undo() # copy result to output folder shutil.copy(temp_folder.get_path() + fname_centerline_oriented, folder_output) shutil.copy(temp_folder.get_path() + fname_centerline_oriented_txt, folder_output) if output_roi: shutil.copy( temp_folder.get_path() + fname_centerline_oriented_roi, folder_output) centerline_filename = folder_output + fname_centerline_oriented else: centerline_filename = 'error' # delete temporary folder if remove_temp_files: temp_folder.cleanup() else: # condition on verbose when using OptiC if verbose == 1: verbose = 2 # OptiC models path_script = os.path.dirname(__file__) path_sct = os.path.dirname(path_script) optic_models_path = os.path.join(path_sct, 'data/optic_models', '{}_model'.format(contrast_type)) # Execute OptiC binary _, centerline_filename = optic.detect_centerline( image_fname=fname_data, contrast_type=contrast_type, optic_models_path=optic_models_path, folder_output=folder_output, remove_temp_files=remove_temp_files, output_roi=output_roi, verbose=verbose) sct.printv('\nDone! To view results, type:', verbose) sct.printv( "fslview " + fname_input_data + " " + centerline_filename + " -l Red -b 0,1 -t 0.7 &\n", verbose, 'info')
def detect_c2c3(nii_im, nii_seg, contrast, verbose=1): """ Detect the posterior edge of C2-C3 disc. :param nii_im: :param nii_seg: :param contrast: :param verbose: :return: """ # path to the pmj detector path_sct = os.environ.get("SCT_DIR", os.path.dirname(os.path.dirname(__file__))) path_model = os.path.join(path_sct, 'data', 'c2c3_disc_models', '{}_model'.format(contrast)) orientation_init = nii_im.orientation # Flatten sagittal nii_im = flatten_sagittal(nii_im, nii_seg, centerline_fitting='hanning', verbose=verbose) nii_seg_flat = flatten_sagittal(nii_seg, nii_seg, centerline_fitting='hanning', verbose=verbose) # create temporary folder with intermediate results sct.log.info("Creating temporary folder...") tmp_folder = sct.TempFolder() tmp_folder.chdir() # Extract mid-slice nii_im.change_orientation('PIR') nii_seg_flat.change_orientation('PIR') mid_RL = int(np.rint(nii_im.dim[2] * 1.0 / 2)) midSlice = nii_im.data[:, :, mid_RL] midSlice_seg = nii_seg_flat.data[:, :, mid_RL] nii_midSlice = msct_image.zeros_like(nii_im) nii_midSlice.data = midSlice nii_midSlice.save('data_midSlice.nii') # Run detection sct.printv('Run C2-C3 detector...', verbose) os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR" cmd_detection = 'isct_spine_detect -ctype=dpdt "%s" "%s" "%s"' % \ (path_model, 'data_midSlice', 'data_midSlice_pred') sct.run(cmd_detection, verbose=0, raise_exception=False) # sct.run(cmd_detection, verbose=0) pred = nib.load('data_midSlice_pred_svm.hdr').get_data() # Create mask along centerline midSlice_mask = np.zeros(midSlice_seg.shape) mask_halfSize = 25 for z in range(midSlice_mask.shape[1]): row = midSlice_seg[:, z] if np.any(row): med_y = int(np.rint(np.median(np.where(row)))) midSlice_mask[med_y - mask_halfSize:med_y + mask_halfSize] = 1 # mask prediction pred[midSlice_mask == 0] = 0 # assign label to voxel nii_c2c3 = zeros_like(nii_seg_flat) if np.any(pred > 0): sct.printv('C2-C3 detected...', verbose) coord_max = np.where(pred == np.max(pred)) pa_c2c3, is_c2c3 = coord_max[0][0], coord_max[1][0] nii_seg.change_orientation('PIR') rl_c2c3 = int(np.rint(center_of_mass(nii_seg.data[:, is_c2c3, :])[1])) nii_c2c3.data[pa_c2c3, is_c2c3, rl_c2c3] = 3 else: sct.printv('C2-C3 not detected...', verbose) # remove temporary files tmp_folder.chdir_undo() sct.log.info("Remove temporary files...") tmp_folder.cleanup() nii_c2c3.change_orientation(orientation_init) return nii_c2c3
def deep_segmentation_MSlesion(fname_image, contrast_type, output_folder, ctr_algo='svm', ctr_file=None, brain_bool=True, remove_temp_files=1, verbose=1): """Pipeline.""" path_script = os.path.dirname(__file__) path_sct = os.path.dirname(path_script) # create temporary folder with intermediate results sct.log.info("\nCreating temporary folder...") file_fname = os.path.basename(fname_image) tmp_folder = sct.TempFolder() tmp_folder_path = tmp_folder.get_path() fname_image_tmp = tmp_folder.copy_from(fname_image) if ctr_algo == 'manual': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() # orientation of the image, should be RPI sct.log.info("\nReorient the image to RPI, if necessary...") fname_orient = sct.add_suffix(file_fname, '_RPI') im_2orient = Image(file_fname) original_orientation = im_2orient.orientation if original_orientation != 'RPI': im_orient = msct_image.change_orientation(im_2orient, 'RPI').save(fname_orient) else: im_orient = im_2orient sct.copy(fname_image_tmp, fname_orient) input_resolution = im_orient.dim[4:7] del im_2orient, im_orient # find the spinal cord centerline - execute OptiC binary sct.log.info("\nFinding the spinal cord centerline...") contrast_type_ctr = contrast_type.split('_')[0] fname_res, centerline_filename = find_centerline(algo=ctr_algo, image_fname=fname_orient, path_sct=path_sct, contrast_type=contrast_type_ctr, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) im_nii, ctr_nii = Image(fname_res), Image(centerline_filename) # crop image around the spinal cord centerline sct.log.info("\nCropping the image around the spinal cord...") fname_crop = sct.add_suffix(fname_res, '_crop') crop_size = 48 X_CROP_LST, Y_CROP_LST, im_crop_nii = crop_image_around_centerline(im_in=im_nii, ctr_in=ctr_nii, crop_size=crop_size) del ctr_nii # normalize the intensity of the images sct.log.info("Normalizing the intensity...") im_norm_in = apply_intensity_normalization(img=im_crop_nii, contrast=contrast_type) del im_crop_nii # resample to 0.5mm isotropic fname_norm = sct.add_suffix(fname_orient, '_norm') im_norm_in.save(fname_norm) fname_res3d = sct.add_suffix(fname_norm, '_resampled3d') spinalcordtoolbox.resample.nipy_resample.resample_file(fname_norm, fname_res3d, '0.5x0.5x0.5', 'mm', 'linear', verbose=0) # segment data using 3D convolutions sct.log.info("\nSegmenting the MS lesions using deep learning on 3D patches...") segmentation_model_fname = os.path.join(path_sct, 'data', 'deepseg_lesion_models', '{}_lesion.h5'.format(contrast_type)) fname_seg_crop_res = sct.add_suffix(fname_res3d, '_lesionseg') segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, fname_in=fname_res3d, fname_out=fname_seg_crop_res) # resample to the initial pz resolution fname_seg_res2d = sct.add_suffix(fname_seg_crop_res, '_resampled2d') initial_2d_resolution = 'x'.join(['0.5', '0.5', str(input_resolution[2])]) spinalcordtoolbox.resample.nipy_resample.resample_file(fname_seg_crop_res, fname_seg_res2d, initial_2d_resolution, 'mm', 'linear', verbose=0) seg_crop_data = Image(fname_seg_res2d).data # reconstruct the segmentation from the crop data sct.log.info("\nReassembling the image...") seg_uncrop_nii = uncrop_image(ref_in=im_nii, data_crop=seg_crop_data, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST) fname_seg_res_RPI = sct.add_suffix(file_fname, '_res_RPI_seg') seg_uncrop_nii.save(fname_seg_res_RPI) del seg_uncrop_nii, im_nii, seg_crop_data # resample to initial resolution sct.log.info("Resampling the segmentation to the original image resolution...") initial_resolution = 'x'.join([str(input_resolution[0]), str(input_resolution[1]), str(input_resolution[2])]) fname_seg_RPI = sct.add_suffix(file_fname, '_RPI_seg') spinalcordtoolbox.resample.nipy_resample.resample_file(fname_seg_res_RPI, fname_seg_RPI, initial_resolution, 'mm', 'linear', verbose=0) seg_initres_nii = Image(fname_seg_RPI) # binarize the resampled image to remove interpolation effects sct.log.info("\nBinarizing the segmentation to avoid interpolation effects...") thr = 0.1 seg_initres_nii.data[np.where(seg_initres_nii.data >= thr)] = 1 seg_initres_nii.data[np.where(seg_initres_nii.data < thr)] = 0 # reorient to initial orientation sct.log.info("\nReorienting the segmentation to the original image orientation...") fname_seg = sct.add_suffix(file_fname, '_seg') if original_orientation != 'RPI': out_nii = msct_image.change_orientation(seg_initres_nii, original_orientation) seg_initres_nii.save(fname_seg) del seg_initres_nii tmp_folder.chdir_undo() # copy image from temporary folder into output folder sct.copy(os.path.join(tmp_folder_path, fname_seg), output_folder) # remove temporary files if remove_temp_files: sct.log.info("\nRemove temporary files...") tmp_folder.cleanup() return os.path.join(output_folder, fname_seg)
def detect_c2c3(nii_im, nii_seg, contrast, nb_sag_avg=7.0, verbose=1): """ Detect the posterior edge of C2-C3 disc. :param nii_im: :param nii_seg: :param contrast: :param verbose: :return: """ # path to the pmj detector path_model = os.path.join(sct.__data_dir__, 'c2c3_disc_models', '{}_model'.format(contrast)) # check if model exists if not os.path.isfile(path_model + '.yml'): raise FileNotFoundError( "The model file {} does not exist. Please download it using sct_download_data" .format(path_model + '.yml')) orientation_init = nii_im.orientation z_seg_max = np.max(np.where(nii_seg.change_orientation('PIR').data)[1]) # Flatten sagittal nii_im = flatten_sagittal(nii_im, nii_seg, verbose=verbose) nii_seg_flat = flatten_sagittal(nii_seg, nii_seg, verbose=verbose) # create temporary folder with intermediate results logger.info("Creating temporary folder...") tmp_folder = sct.TempFolder() tmp_folder.chdir() # Extract mid-slice nii_im.change_orientation('PIR') nii_seg_flat.change_orientation('PIR') mid_RL = int(np.rint(nii_im.dim[2] * 1.0 / 2)) nb_sag_avg_half = int(nb_sag_avg / 2 / nii_im.dim[6]) midSlice = np.mean(nii_im.data[:, :, mid_RL - nb_sag_avg_half:mid_RL + nb_sag_avg_half + 1], 2) # average 7 slices midSlice_seg = nii_seg_flat.data[:, :, mid_RL] nii_midSlice = zeros_like(nii_im) nii_midSlice.data = midSlice nii_midSlice.save('data_midSlice.nii') # Run detection logger.info('Run C2-C3 detector...') os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR" cmd_detection = 'isct_spine_detect -ctype=dpdt "%s" "%s" "%s"' % \ (path_model, 'data_midSlice', 'data_midSlice_pred') # The command below will fail, but we don't care because it will output an image (prediction), which we # will use later on. s, o = run_proc(cmd_detection, verbose=0, is_sct_binary=True, raise_exception=False) pred = nib.load('data_midSlice_pred_svm.hdr').get_data() if verbose >= 2: # copy the "prediction data before post-processing" in an Image object nii_pred_before_postPro = nii_midSlice.copy() nii_pred_before_postPro.data = pred # 2D data with orientation, mid sag slice of the original data nii_pred_before_postPro.save( "pred_midSlice_before_postPro.nii.gz") # save it) # DEBUG trick: check if the detection succeed by running: fsleyes data_midSlice data_midSlice_pred_svm -cm red -dr 0 100 # If a "red cluster" is observed in the neighbourhood of C2C3, then the model detected it. # Create mask along centerline midSlice_mask = np.zeros(midSlice_seg.shape) mask_halfSize = int(np.rint(25.0 / nii_midSlice.dim[4])) for z in range(midSlice_mask.shape[1]): row = midSlice_seg[:, z] # 2D data with PI orientation, mid sag slice of the original data if np.any(row > 0): med_y = int(np.rint(np.median(np.where(row > 0)))) midSlice_mask[ med_y - mask_halfSize:med_y + mask_halfSize, z] = 1 # 2D data with PI orientation, mid sag slice of the original data if verbose >= 2: # copy the created mask in an Image object nii_postPro_mask = nii_midSlice.copy() nii_postPro_mask.data = midSlice_mask # 2D data with PI orientation, mid sag slice of the original data nii_postPro_mask.save("mask_midSlice.nii.gz") # save it # mask prediction pred[midSlice_mask == 0] = 0 pred[:, z_seg_max:] = 0 # Mask above SC segmentation if verbose >= 2: # copy the "prediction data after post-processing" in an Image object nii_pred_after_postPro = nii_midSlice.copy() nii_pred_after_postPro.data = pred nii_pred_after_postPro.save( "pred_midSlice_after_postPro.nii.gz") # save it # assign label to voxel nii_c2c3 = zeros_like(nii_seg_flat) # 3D data with PIR orientaion if np.any(pred > 0): logger.info('C2-C3 detected...') pred_bin = (pred > 0).astype(np.int_) coord_max = np.where(pred == np.max(pred)) pa_c2c3, is_c2c3 = coord_max[0][0], coord_max[1][0] nii_seg.change_orientation('PIR') rl_c2c3 = int( np.rint(center_of_mass(np.array(nii_seg.data[:, is_c2c3, :]))[1])) nii_c2c3.data[pa_c2c3, is_c2c3, rl_c2c3] = 3 else: logger.warning('C2-C3 not detected...') # remove temporary files tmp_folder.chdir_undo() if verbose < 2: logger.info("Remove temporary files...") tmp_folder.cleanup() else: logger.info("Temporary files saved to " + tmp_folder.get_path()) nii_c2c3.change_orientation(orientation_init) return nii_c2c3
def deep_segmentation_spinalcord(im_image, contrast_type, ctr_algo='cnn', ctr_file=None, brain_bool=True, kernel_size='2d', threshold_seg=None, remove_temp_files=1, verbose=1): """ Main pipeline for CNN-based segmentation of the spinal cord. :param im_image: :param contrast_type: {'t1', 't2', t2s', 'dwi'} :param ctr_algo: :param ctr_file: :param brain_bool: :param kernel_size: :param threshold_seg: Binarization threshold (between 0 and 1) to apply to the segmentation prediction. Set to -1 for no binarization (i.e. soft segmentation output) :param remove_temp_files: :param verbose: :return: """ if threshold_seg is None: threshold_seg = THR_DEEPSEG[contrast_type] # Display stuff logger.info("Config deepseg_sc:") logger.info(" Centerline algorithm: {}".format(ctr_algo)) logger.info(" Brain in image: {}".format(brain_bool)) logger.info(" Kernel dimension: {}".format(kernel_size)) logger.info(" Contrast: {}".format(contrast_type)) logger.info(" Threshold: {}".format(threshold_seg)) # create temporary folder with intermediate results tmp_folder = sct.TempFolder(verbose=verbose) tmp_folder_path = tmp_folder.get_path() if ctr_algo == 'file': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() # re-orient image to RPI logger.info("Reorient the image to RPI, if necessary...") original_orientation = im_image.orientation # fname_orient = 'image_in_RPI.nii' im_image.change_orientation('RPI') # Resample image to 0.5mm in plane im_image_res = \ resampling.resample_nib(im_image, new_size=[0.5, 0.5, im_image.dim[6]], new_size_type='mm', interpolation='linear') fname_orient = 'image_in_RPI_res.nii' im_image_res.save(fname_orient) # find the spinal cord centerline - execute OptiC binary logger.info("Finding the spinal cord centerline...") _, im_ctl, im_labels_viewer = find_centerline(algo=ctr_algo, image_fname=fname_orient, contrast_type=contrast_type, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) if ctr_algo == 'file': im_ctl = \ resampling.resample_nib(im_ctl, new_size=[0.5, 0.5, im_image.dim[6]], new_size_type='mm', interpolation='linear') # crop image around the spinal cord centerline logger.info("Cropping the image around the spinal cord...") crop_size = 96 if (kernel_size == '3d' and contrast_type == 't2s') else 64 X_CROP_LST, Y_CROP_LST, Z_CROP_LST, im_crop_nii = crop_image_around_centerline(im_in=im_image_res, ctr_in=im_ctl, crop_size=crop_size) # normalize the intensity of the images logger.info("Normalizing the intensity...") im_norm_in = apply_intensity_normalization(im_in=im_crop_nii) del im_crop_nii if kernel_size == '2d': # segment data using 2D convolutions logger.info("Segmenting the spinal cord using deep learning on 2D patches...") segmentation_model_fname = \ os.path.join(sct.__sct_dir__, 'data', 'deepseg_sc_models', '{}_sc.h5'.format(contrast_type)) seg_crop = segment_2d(model_fname=segmentation_model_fname, contrast_type=contrast_type, input_size=(crop_size, crop_size), im_in=im_norm_in) elif kernel_size == '3d': # segment data using 3D convolutions logger.info("Segmenting the spinal cord using deep learning on 3D patches...") segmentation_model_fname = \ os.path.join(sct.__sct_dir__, 'data', 'deepseg_sc_models', '{}_sc_3D.h5'.format(contrast_type)) seg_crop = segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, im_in=im_norm_in) # Postprocessing seg_crop_postproc = np.zeros_like(seg_crop) x_cOm, y_cOm = None, None for zz in range(im_norm_in.dim[2]): # Fill holes (only for binary segmentations) if threshold_seg >= 0: pred_seg_th = fill_holes_2d((seg_crop[:, :, zz] > threshold_seg).astype(int)) pred_seg_pp = keep_largest_object(pred_seg_th, x_cOm, y_cOm) # Update center of mass for slice i+1 if 1 in pred_seg_pp: x_cOm, y_cOm = center_of_mass(pred_seg_pp) x_cOm, y_cOm = np.round(x_cOm), np.round(y_cOm) else: # If soft segmentation, do nothing pred_seg_pp = seg_crop[:, :, zz] seg_crop_postproc[:, :, zz] = pred_seg_pp # dtype is float32 # reconstruct the segmentation from the crop data logger.info("Reassembling the image...") im_seg = uncrop_image(ref_in=im_image_res, data_crop=seg_crop_postproc, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST, z_crop_lst=Z_CROP_LST) # seg_uncrop_nii.save(sct.add_suffix(fname_res, '_seg')) # for debugging del seg_crop, seg_crop_postproc, im_norm_in # resample to initial resolution logger.info("Resampling the segmentation to the native image resolution using linear interpolation...") im_seg_r = resampling.resample_nib(im_seg, image_dest=im_image, interpolation='linear') if ctr_algo == 'viewer': # for debugging im_labels_viewer.save(sct.add_suffix(fname_orient, '_labels-viewer')) # Binarize the resampled image (except for soft segmentation, defined by threshold_seg=-1) if threshold_seg >= 0: logger.info("Binarizing the resampled segmentation...") im_seg_r.data = (im_seg_r.data > 0.5).astype(np.uint8) # post processing step to z_regularized im_seg_r_postproc = post_processing_volume_wise(im_seg_r) # Change data type. By default, dtype is float32 if threshold_seg >= 0: im_seg_r_postproc.change_type(np.uint8) tmp_folder.chdir_undo() # remove temporary files if remove_temp_files: logger.info("Remove temporary files...") tmp_folder.cleanup() # reorient to initial orientation im_seg_r_postproc.change_orientation(original_orientation) # copy q/sform from input image to output segmentation im_seg.copy_qform_from_ref(im_image) return im_seg_r_postproc, im_image_res, im_seg.change_orientation('RPI')
def deep_segmentation_spinalcord(im_image, contrast_type, ctr_algo='cnn', ctr_file=None, brain_bool=True, kernel_size='2d', remove_temp_files=1, verbose=1): """Pipeline.""" path_script = os.path.dirname(__file__) path_sct = os.path.dirname(path_script) # create temporary folder with intermediate results sct.log.info("Creating temporary folder...") # file_fname = os.path.basename(fname_image) tmp_folder = sct.TempFolder() tmp_folder_path = tmp_folder.get_path() # fname_image_tmp = tmp_folder.copy_from(fname_image) if ctr_algo == 'manual': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() # orientation of the image, should be RPI sct.log.info("Reorient the image to RPI, if necessary...") fname_in = im_image.absolutepath original_orientation = im_image.orientation fname_orient = 'image_in_RPI.nii' im_image.change_orientation('RPI').save(fname_orient) input_resolution = im_image.dim[4:7] # find the spinal cord centerline - execute OptiC binary sct.log.info("Finding the spinal cord centerline...") fname_res, centerline_filename = find_centerline( algo=ctr_algo, image_fname=fname_orient, path_sct=path_sct, contrast_type=contrast_type, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) im_nii, ctr_nii = Image(fname_res), Image(centerline_filename) # crop image around the spinal cord centerline sct.log.info("Cropping the image around the spinal cord...") crop_size = 96 if (kernel_size == '3d' and contrast_type == 't2s') else 64 X_CROP_LST, Y_CROP_LST, im_crop_nii = crop_image_around_centerline( im_in=im_nii, ctr_in=ctr_nii, crop_size=crop_size) del ctr_nii # normalize the intensity of the images sct.log.info("Normalizing the intensity...") im_norm_in = apply_intensity_normalization(im_in=im_crop_nii) del im_crop_nii if kernel_size == '2d': # segment data using 2D convolutions sct.log.info( "Segmenting the spinal cord using deep learning on 2D patches...") segmentation_model_fname = os.path.join( path_sct, 'data', 'deepseg_sc_models', '{}_sc.h5'.format(contrast_type)) seg_crop_data = segment_2d(model_fname=segmentation_model_fname, contrast_type=contrast_type, input_size=(crop_size, crop_size), im_in=im_norm_in) del im_norm_in elif kernel_size == '3d': # resample to 0.5mm isotropic fname_norm = sct.add_suffix(fname_orient, '_norm') fname_res3d = sct.add_suffix(fname_norm, '_resampled3d') spinalcordtoolbox.resample.nipy_resample.resample_file(fname_norm, fname_res3d, '0.5x0.5x0.5', 'mm', 'linear', verbose=0) # segment data using 3D convolutions sct.log.info( "Segmenting the spinal cord using deep learning on 3D patches...") fname_seg_crop_res = sct.add_suffix(fname_res3d, '_seg') segmentation_model_fname = os.path.join( path_sct, 'data', 'deepseg_sc_models', '{}_sc_3D.h5'.format(contrast_type)) seg_crop_nii = segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, im_in=Image(fname_res3d)) seg_crop_nii.save(fname_seg_crop_res) del seg_crop_nii # resample to the initial pz resolution # TODO: does this need to be done (if already done below)? fname_seg_res2d = sct.add_suffix(fname_seg_crop_res, '_resampled2d') initial_2d_resolution = 'x'.join( ['0.5', '0.5', str(input_resolution[2])]) spinalcordtoolbox.resample.nipy_resample.resample_image( fname_seg_crop_res, fname_seg_res2d, initial_2d_resolution, 'mm', 'linear', verbose=0) seg_crop_data = Image(fname_seg_res2d).data # reconstruct the segmentation from the crop data sct.log.info("Reassembling the image...") seg_uncrop_nii = uncrop_image(ref_in=im_nii, data_crop=seg_crop_data, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST) fname_res_seg = sct.add_suffix(fname_res, '_seg') seg_uncrop_nii.save(fname_res_seg) del seg_crop_data # resample to initial resolution sct.log.info( "Resampling the segmentation to the original image resolution...") initial_resolution = 'x'.join([ str(input_resolution[0]), str(input_resolution[1]), str(input_resolution[2]) ]) fname_res_seg_downsamp = sct.add_suffix(fname_res_seg, '_downsamp') spinalcordtoolbox.resample.nipy_resample.resample_file( fname_res_seg, fname_res_seg_downsamp, initial_resolution, 'mm', 'linear', verbose=0) im_image_res_seg_downsamp = Image(fname_res_seg_downsamp) # binarize the resampled image to remove interpolation effects sct.log.info( "Binarizing the segmentation to avoid interpolation effects...") thr = 0.0001 if contrast_type in ['t1', 'dwi'] else 0.5 # TODO: optimize speed --> np.where is slow im_image_res_seg_downsamp.data[np.where( im_image_res_seg_downsamp.data >= thr)] = 1 im_image_res_seg_downsamp.data[np.where( im_image_res_seg_downsamp.data < thr)] = 0 # post processing step to z_regularized im_image_res_seg_downsamp_postproc = post_processing_volume_wise( im_in=im_image_res_seg_downsamp) tmp_folder.chdir_undo() # remove temporary files if remove_temp_files: sct.log.info("Remove temporary files...") tmp_folder.cleanup() # reorient to initial orientation return im_image_res_seg_downsamp_postproc.change_orientation( original_orientation), im_nii, seg_uncrop_nii.change_orientation( 'RPI').save('image_in_RPI_resampled_seg.nii.gz')
def deep_segmentation_spinalcord(fname_image, contrast_type, output_folder, ctr_algo='cnn', ctr_file=None, brain_bool=True, kernel_size='2d', remove_temp_files=1, verbose=1): """Pipeline.""" path_script = os.path.dirname(__file__) path_sct = os.path.dirname(path_script) # create temporary folder with intermediate results sct.log.info("Creating temporary folder...") file_fname = os.path.basename(fname_image) tmp_folder = sct.TempFolder() tmp_folder_path = tmp_folder.get_path() fname_image_tmp = tmp_folder.copy_from(fname_image) if ctr_algo == 'manual': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() # orientation of the image, should be RPI sct.log.info("Reorient the image to RPI, if necessary...") fname_orient = sct.add_suffix(file_fname, '_RPI') im_2orient = Image(file_fname) original_orientation = im_2orient.orientation if original_orientation != 'RPI': im_orient = msct_image.change_orientation(im_2orient, 'RPI').save(fname_orient) else: im_orient = im_2orient sct.copy(fname_image_tmp, fname_orient) # resampling RPI image sct.log.info("Resample the image to 0.5 mm isotropic resolution...") fname_res = sct.add_suffix(fname_orient, '_resampled') im_2res = im_orient input_resolution = im_2res.dim[4:7] new_resolution = 'x'.join(['0.5', '0.5', str(input_resolution[2])]) spinalcordtoolbox.resample.nipy_resample.resample_file(fname_orient, fname_res, new_resolution, 'mm', 'linear', verbose=0) # find the spinal cord centerline - execute OptiC binary sct.log.info("Finding the spinal cord centerline...") centerline_filename = find_centerline(algo=ctr_algo, image_fname=fname_res, path_sct=path_sct, contrast_type=contrast_type, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) # crop image around the spinal cord centerline sct.log.info("Cropping the image around the spinal cord...") fname_crop = sct.add_suffix(fname_res, '_crop') crop_size = 96 if (kernel_size == '3d' and contrast_type == 't2s') else 64 X_CROP_LST, Y_CROP_LST = crop_image_around_centerline( filename_in=fname_res, filename_ctr=centerline_filename, filename_out=fname_crop, crop_size=crop_size) # normalize the intensity of the images sct.log.info("Normalizing the intensity...") fname_norm = sct.add_suffix(fname_crop, '_norm') apply_intensity_normalization(img_path=fname_crop, fname_out=fname_norm) if kernel_size == '2d': # segment data using 2D convolutions sct.log.info( "Segmenting the spinal cord using deep learning on 2D patches...") segmentation_model_fname = os.path.join( path_sct, 'data', 'deepseg_sc_models', '{}_sc.h5'.format(contrast_type)) fname_seg_crop = sct.add_suffix(fname_norm, '_seg') seg_crop_data = segment_2d(model_fname=segmentation_model_fname, contrast_type=contrast_type, input_size=(crop_size, crop_size), fname_in=fname_norm, fname_out=fname_seg_crop) elif kernel_size == '3d': # resample to 0.5mm isotropic fname_res3d = sct.add_suffix(fname_norm, '_resampled3d') spinalcordtoolbox.resample.nipy_resample.resample_file(fname_norm, fname_res3d, '0.5x0.5x0.5', 'mm', 'linear', verbose=0) # segment data using 3D convolutions sct.log.info( "Segmenting the spinal cord using deep learning on 3D patches...") segmentation_model_fname = os.path.join( path_sct, 'data', 'deepseg_sc_models', '{}_sc_3D.h5'.format(contrast_type)) fname_seg_crop_res = sct.add_suffix(fname_res3d, '_seg') segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, fname_in=fname_res3d, fname_out=fname_seg_crop_res) # resample to the initial pz resolution fname_seg_res2d = sct.add_suffix(fname_seg_crop_res, '_resampled2d') initial_2d_resolution = 'x'.join( ['0.5', '0.5', str(input_resolution[2])]) spinalcordtoolbox.resample.nipy_resample.resample_file( fname_seg_crop_res, fname_seg_res2d, initial_2d_resolution, 'mm', 'linear', verbose=0) seg_crop_data = Image(fname_seg_res2d).data # reconstruct the segmentation from the crop data sct.log.info("Reassembling the image...") fname_seg_res_RPI = sct.add_suffix(file_fname, '_res_RPI_seg') uncrop_image(fname_ref=fname_res, fname_out=fname_seg_res_RPI, data_crop=seg_crop_data, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST) # resample to initial resolution sct.log.info( "Resampling the segmentation to the original image resolution...") fname_seg_RPI = sct.add_suffix(file_fname, '_RPI_seg') initial_resolution = 'x'.join([ str(input_resolution[0]), str(input_resolution[1]), str(input_resolution[2]) ]) spinalcordtoolbox.resample.nipy_resample.resample_file(fname_seg_res_RPI, fname_seg_RPI, initial_resolution, 'mm', 'linear', verbose=0) # binarize the resampled image to remove interpolation effects sct.log.info( "Binarizing the segmentation to avoid interpolation effects...") thr = '0.0001' if contrast_type in ['t1', 'dwi'] else '0.5' sct.run( ['sct_maths', '-i', fname_seg_RPI, '-bin', thr, '-o', fname_seg_RPI], verbose=0) # post processing step to z_regularized post_processing_volume_wise(fname_in=fname_seg_RPI) # reorient to initial orientation sct.log.info( "Reorienting the segmentation to the original image orientation...") fname_seg = sct.add_suffix(file_fname, '_seg') if original_orientation != 'RPI': im_seg_orient = msct_image.change_orientation( Image(fname_seg_RPI), original_orientation).save(fname_seg) else: sct.copy(fname_seg_RPI, fname_seg) tmp_folder.chdir_undo() # copy image from temporary folder into output folder sct.copy(os.path.join(tmp_folder_path, fname_seg), output_folder) # remove temporary files if remove_temp_files: sct.log.info("Remove temporary files...") tmp_folder.cleanup() return os.path.join(output_folder, fname_seg)
def deep_segmentation_spinalcord(fname_image, contrast_type, output_folder, ctr_algo='cnn', brain_bool=True, kernel_size='2d', remove_temp_files=1, verbose=1): """Pipeline.""" path_script = os.path.dirname(__file__) path_sct = os.path.dirname(path_script) # create temporary folder with intermediate results sct.log.info("Creating temporary folder...") file_fname = os.path.basename(fname_image) tmp_folder = sct.TempFolder() tmp_folder_path = tmp_folder.get_path() fname_image_tmp = tmp_folder.copy_from(fname_image) tmp_folder.chdir() # orientation of the image, should be RPI sct.log.info("Reorient the image to RPI, if necessary...") fname_orient = sct.add_suffix(file_fname, '_RPI') im_2orient = Image(file_fname) original_orientation = im_2orient.orientation if original_orientation != 'RPI': im_orient = set_orientation(im_2orient, 'RPI') im_orient.setFileName(fname_orient) im_orient.save() else: im_orient = im_2orient sct.copy(fname_image_tmp, fname_orient) # resampling RPI image sct.log.info("Resample the image to 0.5 mm isotropic resolution...") fname_res = sct.add_suffix(fname_orient, '_resampled') im_2res = im_orient input_resolution = im_2res.dim[4:7] new_resolution = 'x'.join(['0.5', '0.5', str(input_resolution[2])]) spinalcordtoolbox.resample.nipy_resample.resample_file(fname_orient, fname_res, new_resolution, 'mm', 'linear', verbose=0) # find the spinal cord centerline - execute OptiC binary sct.log.info("Finding the spinal cord centerline...") if ctr_algo == 'svm': # run optic on a heatmap computed by a trained SVM+HoG algorithm optic_models_fname = os.path.join(path_sct, 'data', 'optic_models', '{}_model'.format(contrast_type)) _, centerline_filename = optic.detect_centerline( image_fname=fname_res, contrast_type=contrast_type, optic_models_path=optic_models_fname, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, output_roi=False, verbose=0) elif ctr_algo == 'cnn': # CNN parameters dct_patch_ctr = { 't2': { 'size': (80, 80), 'mean': 51.1417, 'std': 57.4408 }, 't2s': { 'size': (80, 80), 'mean': 68.8591, 'std': 71.4659 }, 't1': { 'size': (80, 80), 'mean': 55.7359, 'std': 64.3149 }, 'dwi': { 'size': (80, 80), 'mean': 55.744, 'std': 45.003 } } dct_params_ctr = { 't2': { 'features': 16, 'dilation_layers': 2 }, 't2s': { 'features': 8, 'dilation_layers': 3 }, 't1': { 'features': 24, 'dilation_layers': 3 }, 'dwi': { 'features': 8, 'dilation_layers': 2 } } # load model ctr_model_fname = os.path.join(path_sct, 'data', 'deepseg_sc_models', '{}_ctr.h5'.format(contrast_type)) ctr_model = nn_architecture_ctr( height=dct_patch_ctr[contrast_type]['size'][0], width=dct_patch_ctr[contrast_type]['size'][1], channels=1, classes=1, features=dct_params_ctr[contrast_type]['features'], depth=2, temperature=1.0, padding='same', batchnorm=True, dropout=0.0, dilation_layers=dct_params_ctr[contrast_type]['dilation_layers']) ctr_model.load_weights(ctr_model_fname) # compute the heatmap fname_heatmap = sct.add_suffix(fname_res, "_heatmap") img_filename = ''.join(sct.extract_fname(fname_heatmap)[:2]) fname_heatmap_nii = img_filename + '.nii' z_max = heatmap(filename_in=fname_res, filename_out=fname_heatmap_nii, model=ctr_model, patch_shape=dct_patch_ctr[contrast_type]['size'], mean_train=dct_patch_ctr[contrast_type]['mean'], std_train=dct_patch_ctr[contrast_type]['std'], brain_bool=brain_bool) # run optic on the heatmap centerline_filename = sct.add_suffix(fname_heatmap, "_ctr") heatmap2optic(fname_heatmap=fname_heatmap_nii, lambda_value=7 if contrast_type == 't2s' else 1, fname_out=centerline_filename, z_max=z_max if brain_bool else None) # crop image around the spinal cord centerline sct.log.info("Cropping the image around the spinal cord...") fname_crop = sct.add_suffix(fname_res, '_crop') crop_size = 64 if kernel_size == '2d' else 96 X_CROP_LST, Y_CROP_LST = crop_image_around_centerline( filename_in=fname_res, filename_ctr=centerline_filename, filename_out=fname_crop, crop_size=crop_size) # normalize the intensity of the images sct.log.info("Normalizing the intensity...") fname_norm = sct.add_suffix(fname_crop, '_norm') apply_intensity_normalization(img_path=fname_crop, fname_out=fname_norm) if kernel_size == '2d': # segment data using 2D convolutions sct.log.info( "Segmenting the spinal cord using deep learning on 2D patches...") segmentation_model_fname = os.path.join( path_sct, 'data', 'deepseg_sc_models', '{}_sc.h5'.format(contrast_type)) fname_seg_crop = sct.add_suffix(fname_norm, '_seg') seg_crop_data = segment_2d(model_fname=segmentation_model_fname, contrast_type=contrast_type, input_size=(crop_size, crop_size), fname_in=fname_norm, fname_out=fname_seg_crop) elif kernel_size == '3d': # resample to 0.5mm isotropic fname_res3d = sct.add_suffix(fname_norm, '_resampled3d') spinalcordtoolbox.resample.nipy_resample.resample_file(fname_norm, fname_res3d, '0.5x0.5x0.5', 'mm', 'linear', verbose=0) # segment data using 3D convolutions sct.log.info( "Segmenting the spinal cord using deep learning on 3D patches...") segmentation_model_fname = os.path.join( path_sct, 'data', 'deepseg_sc_models', '{}_sc_3D.h5'.format(contrast_type)) fname_seg_crop_res = sct.add_suffix(fname_res3d, '_seg') segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, fname_in=fname_res3d, fname_out=fname_seg_crop_res) # resample to the initial pz resolution fname_seg_res2d = sct.add_suffix(fname_seg_crop_res, '_resampled2d') initial_2d_resolution = 'x'.join( ['0.5', '0.5', str(input_resolution[2])]) spinalcordtoolbox.resample.nipy_resample.resample_file( fname_seg_crop_res, fname_seg_res2d, initial_2d_resolution, 'mm', 'linear', verbose=0) seg_crop_data = Image(fname_seg_res2d).data # reconstruct the segmentation from the crop data sct.log.info("Reassembling the image...") fname_seg_res_RPI = sct.add_suffix(file_fname, '_res_RPI_seg') uncrop_image(fname_ref=fname_res, fname_out=fname_seg_res_RPI, data_crop=seg_crop_data, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST) # resample to initial resolution sct.log.info( "Resampling the segmentation to the original image resolution...") fname_seg_RPI = sct.add_suffix(file_fname, '_RPI_seg') initial_resolution = 'x'.join([ str(input_resolution[0]), str(input_resolution[1]), str(input_resolution[2]) ]) spinalcordtoolbox.resample.nipy_resample.resample_file(fname_seg_res_RPI, fname_seg_RPI, initial_resolution, 'mm', 'linear', verbose=0) # binarize the resampled image to remove interpolation effects sct.log.info( "Binarizing the segmentation to avoid interpolation effects...") thr = '0.0001' if contrast_type in ['t1', 'dwi'] else '0.5' sct.run( ['sct_maths', '-i', fname_seg_RPI, '-bin', thr, '-o', fname_seg_RPI], verbose=0) # post processing step to z_regularized post_processing_volume_wise(fname_in=fname_seg_RPI) # reorient to initial orientation sct.log.info( "Reorienting the segmentation to the original image orientation...") fname_seg = sct.add_suffix(file_fname, '_seg') if original_orientation != 'RPI': im_seg_orient = set_orientation(Image(fname_seg_RPI), original_orientation) im_seg_orient.setFileName(fname_seg) im_seg_orient.save() else: sct.copy(fname_seg_RPI, fname_seg) tmp_folder.chdir_undo() # copy image from temporary folder into output folder sct.copy(os.path.join(tmp_folder_path, fname_seg), output_folder) # remove temporary files if remove_temp_files: sct.log.info("Remove temporary files...") tmp_folder.cleanup() return os.path.join(output_folder, fname_seg)
def detect_c2c3(nii_im, nii_seg, contrast, nb_sag_avg=7.0, verbose=1): """ Detect the posterior edge of C2-C3 disc. :param nii_im: :param nii_seg: :param contrast: :param verbose: :return: """ # path to the pmj detector path_sct = os.environ.get("SCT_DIR", os.path.dirname(os.path.dirname(__file__))) path_model = os.path.join(path_sct, 'data', 'c2c3_disc_models', '{}_model'.format(contrast)) orientation_init = nii_im.orientation z_seg_max = np.max(np.where(nii_seg.change_orientation('PIR').data)[1]) # Flatten sagittal nii_im = flatten_sagittal(nii_im, nii_seg, centerline_fitting='hanning', verbose=verbose) nii_seg_flat = flatten_sagittal(nii_seg, nii_seg, centerline_fitting='hanning', verbose=verbose) # create temporary folder with intermediate results sct.log.info("Creating temporary folder...") tmp_folder = sct.TempFolder() # print tmp_folder.path_tmp tmp_folder.chdir() # Extract mid-slice nii_im.change_orientation('PIR') nii_seg_flat.change_orientation('PIR') mid_RL = int(np.rint(nii_im.dim[2] * 1.0 / 2)) nb_sag_avg_half = int(nb_sag_avg / 2 / nii_im.dim[6]) midSlice = np.mean(nii_im.data[:, :, mid_RL - nb_sag_avg_half:mid_RL + nb_sag_avg_half + 1], 2) # average 7 slices midSlice_seg = nii_seg_flat.data[:, :, mid_RL] nii_midSlice = msct_image.zeros_like(nii_im) nii_midSlice.data = midSlice nii_midSlice.save('data_midSlice.nii') # Run detection sct.printv('Run C2-C3 detector...', verbose) os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR" cmd_detection = 'isct_spine_detect -ctype=dpdt "%s" "%s" "%s"' % \ (path_model, 'data_midSlice', 'data_midSlice_pred') sct.run(cmd_detection, verbose=0, raise_exception=False) pred = nib.load('data_midSlice_pred_svm.hdr').get_data() # Create mask along centerline midSlice_mask = np.zeros(midSlice_seg.shape) mask_halfSize = int(np.rint(25.0 / nii_midSlice.dim[4])) for z in range(midSlice_mask.shape[1]): row = midSlice_seg[:, z] if np.any(row): med_y = int(np.rint(np.median(np.where(row)))) midSlice_mask[med_y - mask_halfSize:med_y + mask_halfSize] = 1 # mask prediction pred[midSlice_mask == 0] = 0 # dist_medulla = 30.0 if contrast == 't1' else 40.0 # Observation: segmentation ends higher on t2 images than t1 # z_seg_max -= int(np.rint(dist_medulla / nii_midSlice.dim[5])) # TODO: take into account the curvature pred[:, z_seg_max:] = 0 # Mask above SC segmentation # assign label to voxel nii_c2c3 = zeros_like(nii_seg_flat) if np.any(pred > 0): sct.printv('C2-C3 detected...', verbose) pred_bin = (pred > 0).astype(np.int_) # labeled_pred, nb_regions = label_regions(pred_bin, return_num=True) # if nb_regions > 1: # if there are several clusters of voxels detected # region_idx_top, region_z_top = 0, 0 # for region_idx in range(1, nb_regions+1): # pred_idx = (labeled_pred == region_idx).astype(np.int_) # pa_com, is_com = center_of_mass(pred_idx) # if is_com >= region_z_top: # region_idx_top = region_idx # region_z_top = is_com # pred[labeled_pred != region_idx_top] = 0 # then keep the one located at the top (IS direction) coord_max = np.where(pred == np.max(pred)) pa_c2c3, is_c2c3 = coord_max[0][0], coord_max[1][0] nii_seg.change_orientation('PIR') rl_c2c3 = int(np.rint(center_of_mass(nii_seg.data[:, is_c2c3, :])[1])) nii_c2c3.data[pa_c2c3, is_c2c3, rl_c2c3] = 3 else: sct.printv('C2-C3 not detected...', verbose) # remove temporary files tmp_folder.chdir_undo() sct.log.info("Remove temporary files...") tmp_folder.cleanup() nii_c2c3.change_orientation(orientation_init) return nii_c2c3
def deep_segmentation_spinalcord(im_image, contrast_type, ctr_algo='cnn', ctr_file=None, brain_bool=True, kernel_size='2d', remove_temp_files=1, verbose=1): """Pipeline""" # create temporary folder with intermediate results tmp_folder = sct.TempFolder(verbose=verbose) tmp_folder_path = tmp_folder.get_path() if ctr_algo == 'file': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() # re-orient image to RPI logger.info("Reorient the image to RPI, if necessary...") original_orientation = im_image.orientation fname_orient = 'image_in_RPI.nii' im_image.change_orientation('RPI').save(fname_orient) input_resolution = im_image.dim[4:7] # find the spinal cord centerline - execute OptiC binary logger.info("Finding the spinal cord centerline...") fname_res, centerline_filename = find_centerline( algo=ctr_algo, image_fname=fname_orient, contrast_type=contrast_type, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) im_nii, ctr_nii = Image(fname_res), Image(centerline_filename) # crop image around the spinal cord centerline logger.info("Cropping the image around the spinal cord...") crop_size = 96 if (kernel_size == '3d' and contrast_type == 't2s') else 64 X_CROP_LST, Y_CROP_LST, Z_CROP_LST, im_crop_nii = crop_image_around_centerline( im_in=im_nii, ctr_in=ctr_nii, crop_size=crop_size) del ctr_nii # normalize the intensity of the images logger.info("Normalizing the intensity...") im_norm_in = apply_intensity_normalization(im_in=im_crop_nii) del im_crop_nii if kernel_size == '2d': # segment data using 2D convolutions logger.info( "Segmenting the spinal cord using deep learning on 2D patches...") segmentation_model_fname = \ os.path.join(sct.__sct_dir__, 'data', 'deepseg_sc_models', '{}_sc.h5'.format(contrast_type)) seg_crop_data = segment_2d(model_fname=segmentation_model_fname, contrast_type=contrast_type, input_size=(crop_size, crop_size), im_in=im_norm_in) elif kernel_size == '3d': # segment data using 3D convolutions logger.info( "Segmenting the spinal cord using deep learning on 3D patches...") segmentation_model_fname = \ os.path.join(sct.__sct_dir__, 'data', 'deepseg_sc_models', '{}_sc_3D.h5'.format(contrast_type)) seg_crop_data = segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, im_in=im_norm_in) del im_norm_in # reconstruct the segmentation from the crop data logger.info("Reassembling the image...") seg_uncrop_nii = uncrop_image(ref_in=im_nii, data_crop=seg_crop_data, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST, z_crop_lst=Z_CROP_LST) fname_res_seg = sct.add_suffix(fname_res, '_seg') seg_uncrop_nii.save(fname_res_seg) del seg_crop_data # resample to initial resolution logger.info( "Resampling the segmentation to the original image resolution...") initial_resolution = 'x'.join([ str(input_resolution[0]), str(input_resolution[1]), str(input_resolution[2]) ]) fname_res_seg_downsamp = sct.add_suffix(fname_res_seg, '_downsamp') resampling.resample_file(fname_res_seg, fname_res_seg_downsamp, initial_resolution, 'mm', 'linear', verbose=0) im_image_res_seg_downsamp = Image(fname_res_seg_downsamp) if ctr_algo == 'viewer': # resample and reorient the viewer labels fname_res_labels = sct.add_suffix(fname_orient, '_labels-centerline') resampling.resample_file(fname_res_labels, fname_res_labels, initial_resolution, 'mm', 'linear', verbose=0) im_image_res_labels_downsamp = Image( fname_res_labels).change_orientation(original_orientation) else: im_image_res_labels_downsamp = None if verbose == 2: fname_res_ctr = sct.add_suffix(fname_orient, '_ctr') resampling.resample_file(fname_res_ctr, fname_res_ctr, initial_resolution, 'mm', 'linear', verbose=0) im_image_res_ctr_downsamp = Image(fname_res_ctr).change_orientation( original_orientation) else: im_image_res_ctr_downsamp = None # binarize the resampled image to remove interpolation effects logger.info( "Binarizing the segmentation to avoid interpolation effects...") thr = 0.0001 if contrast_type in ['t1', 'dwi'] else 0.5 # TODO: optimize speed --> np.where is slow im_image_res_seg_downsamp.data[np.where( im_image_res_seg_downsamp.data >= thr)] = 1 im_image_res_seg_downsamp.data[np.where( im_image_res_seg_downsamp.data < thr)] = 0 # post processing step to z_regularized im_image_res_seg_downsamp_postproc = post_processing_volume_wise( im_in=im_image_res_seg_downsamp) tmp_folder.chdir_undo() # remove temporary files if remove_temp_files: logger.info("Remove temporary files...") tmp_folder.cleanup() # reorient to initial orientation return im_image_res_seg_downsamp_postproc.change_orientation(original_orientation), \ im_nii, \ seg_uncrop_nii.change_orientation('RPI'), \ im_image_res_labels_downsamp, \ im_image_res_ctr_downsamp
def deep_segmentation_spinalcord(im_image, contrast_type, ctr_algo='cnn', ctr_file=None, brain_bool=True, kernel_size='2d', remove_temp_files=1, verbose=1): """Pipeline""" # create temporary folder with intermediate results tmp_folder = sct.TempFolder(verbose=verbose) tmp_folder_path = tmp_folder.get_path() if ctr_algo == 'file': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() # re-orient image to RPI logger.info("Reorient the image to RPI, if necessary...") original_orientation = im_image.orientation fname_orient = 'image_in_RPI.nii' im_image.change_orientation('RPI').save(fname_orient) input_resolution = im_image.dim[4:7] # find the spinal cord centerline - execute OptiC binary logger.info("Finding the spinal cord centerline...") fname_res, centerline_filename, im_labels_viewer = find_centerline(algo=ctr_algo, image_fname=fname_orient, contrast_type=contrast_type, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) im_nii, ctr_nii = Image(fname_res), Image(centerline_filename) # crop image around the spinal cord centerline logger.info("Cropping the image around the spinal cord...") crop_size = 96 if (kernel_size == '3d' and contrast_type == 't2s') else 64 X_CROP_LST, Y_CROP_LST, Z_CROP_LST, im_crop_nii = crop_image_around_centerline(im_in=im_nii, ctr_in=ctr_nii, crop_size=crop_size) del ctr_nii # normalize the intensity of the images logger.info("Normalizing the intensity...") im_norm_in = apply_intensity_normalization(im_in=im_crop_nii) del im_crop_nii if kernel_size == '2d': # segment data using 2D convolutions logger.info("Segmenting the spinal cord using deep learning on 2D patches...") segmentation_model_fname = \ os.path.join(sct.__sct_dir__, 'data', 'deepseg_sc_models', '{}_sc.h5'.format(contrast_type)) seg_crop = segment_2d(model_fname=segmentation_model_fname, contrast_type=contrast_type, input_size=(crop_size, crop_size), im_in=im_norm_in) elif kernel_size == '3d': # segment data using 3D convolutions logger.info("Segmenting the spinal cord using deep learning on 3D patches...") segmentation_model_fname = \ os.path.join(sct.__sct_dir__, 'data', 'deepseg_sc_models', '{}_sc_3D.h5'.format(contrast_type)) seg_crop = segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, im_in=im_norm_in) del im_norm_in # reconstruct the segmentation from the crop data logger.info("Reassembling the image...") im_seg = uncrop_image(ref_in=im_nii, data_crop=seg_crop, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST, z_crop_lst=Z_CROP_LST) # fname_res_seg = sct.add_suffix(fname_res, '_seg') # seg_uncrop_nii.save(fname_res_seg) # for debugging del seg_crop # resample to initial resolution logger.info("Resampling the segmentation to the native image resolution using linear interpolation...") # create nibabel object seg_uncrop_nii_nib = nib.nifti1.Nifti1Image(im_seg.data, im_seg.hdr.get_best_affine()) seg_uncrop_nii_nibr = resampling.resample_nib(seg_uncrop_nii_nib, new_size=input_resolution, new_size_type='mm', interpolation='linear') # Convert back to Image type im_seg_r = Image(seg_uncrop_nii_nibr.get_data(), hdr=seg_uncrop_nii_nibr.header, orientation='RPI', dim=seg_uncrop_nii_nibr.header.get_data_shape()) if ctr_algo == 'viewer': # resample and reorient the viewer labels im_labels_viewer_nib = nib.nifti1.Nifti1Image(im_labels_viewer.data, im_labels_viewer.hdr.get_best_affine()) im_viewer_r_nib = resampling.resample_nib(im_labels_viewer_nib, new_size=input_resolution, new_size_type='mm', interpolation='linear') im_viewer = Image(im_viewer_r_nib.get_data(), hdr=im_viewer_r_nib.header, orientation='RPI', dim=im_viewer_r_nib.header.get_data_shape()).change_orientation(original_orientation) else: im_viewer = None # TODO: Deal with that later-- ideally this file should be written when debugging, not with verbose=2 # if verbose == 2: # fname_res_ctr = sct.add_suffix(fname_orient, '_ctr') # resampling.resample_file(fname_res_ctr, fname_res_ctr, initial_resolution, 'mm', 'linear', verbose=0) # im_image_res_ctr_downsamp = Image(fname_res_ctr).change_orientation(original_orientation) # else: im_image_res_ctr_downsamp = None # Binarize the resampled image to remove interpolation effects logger.info("Binarizing the resampled segmentation...") thr = 0.0001 if contrast_type in ['t1', 'dwi'] else 0.5 # TODO: optimize speed --> np.where is slow im_seg_r.data[np.where(im_seg_r.data >= thr)] = 1 im_seg_r.data[np.where(im_seg_r.data < thr)] = 0 # post processing step to z_regularized im_seg_r_postproc = post_processing_volume_wise(im_seg_r) # change data type im_seg_r_postproc.change_type(np.uint8) tmp_folder.chdir_undo() # remove temporary files if remove_temp_files: logger.info("Remove temporary files...") tmp_folder.cleanup() # reorient to initial orientation return im_seg_r_postproc.change_orientation(original_orientation), \ im_nii, \ im_seg.change_orientation('RPI'), \ im_viewer, \ im_image_res_ctr_downsamp
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 = image_input.orientation temp_folder = sct.TempFolder() temp_folder.copy_from(image_fname) curdir = os.getcwd() 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") img = Image(image_fname) img_int16 = img.copy() # rescale intensity min_out = np.iinfo('uint16').min max_out = np.iinfo('uint16').max min_in = np.nanmin(img.data) max_in = np.nanmax(img.data) data_rescaled = img.data.astype('float') * (max_out - min_out) / (max_in - min_in) img_int16.data = data_rescaled - (data_rescaled.min() - min_out) # change data type img_int16.save(image_int_filename, dtype=np.uint16) del img, img_int16 # 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 = os.path.join(curdir, folder_output) sct.printv('Copy output to ' + folder_output, verbose=0) sct.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 sct.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 deep_segmentation_MSlesion(im_image, contrast_type, ctr_algo='svm', ctr_file=None, brain_bool=True, remove_temp_files=1): """ Segment lesions from MRI data. :param im_image: Image() object containing the lesions to segment :param contrast_type: Constrast of the image. Need to use one supported by the CNN models. :param ctr_algo: Algo to find the centerline. See sct_get_centerline :param ctr_file: Centerline or segmentation (optional) :param brain_bool: If brain if present or not in the image. :param remove_temp_files: :return: """ # create temporary folder with intermediate results sct.log.info("\nCreating temporary folder...") tmp_folder = sct.TempFolder() tmp_folder_path = tmp_folder.get_path() if ctr_algo == 'manual': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() # orientation of the image, should be RPI sct.log.info("\nReorient the image to RPI, if necessary...") fname_in = im_image.absolutepath original_orientation = im_image.orientation fname_orient = 'image_in_RPI.nii' im_image.change_orientation('RPI').save(fname_orient) input_resolution = im_image.dim[4:7] # find the spinal cord centerline - execute OptiC binary sct.log.info("\nFinding the spinal cord centerline...") contrast_type_ctr = contrast_type.split('_')[0] fname_res, centerline_filename = find_centerline( algo=ctr_algo, image_fname=fname_orient, contrast_type=contrast_type_ctr, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) im_nii, ctr_nii = Image(fname_res), Image(centerline_filename) # crop image around the spinal cord centerline sct.log.info("\nCropping the image around the spinal cord...") crop_size = 48 X_CROP_LST, Y_CROP_LST, Z_CROP_LST, im_crop_nii = crop_image_around_centerline( im_in=im_nii, ctr_in=ctr_nii, crop_size=crop_size) del ctr_nii # normalize the intensity of the images sct.log.info("Normalizing the intensity...") im_norm_in = apply_intensity_normalization(img=im_crop_nii, contrast=contrast_type) del im_crop_nii # resample to 0.5mm isotropic fname_norm = sct.add_suffix(fname_orient, '_norm') im_norm_in.save(fname_norm) fname_res3d = sct.add_suffix(fname_norm, '_resampled3d') resampling.resample_file(fname_norm, fname_res3d, '0.5x0.5x0.5', 'mm', 'linear', verbose=0) # segment data using 3D convolutions sct.log.info( "\nSegmenting the MS lesions using deep learning on 3D patches...") segmentation_model_fname = os.path.join( sct.__sct_dir__, 'data', 'deepseg_lesion_models', '{}_lesion.h5'.format(contrast_type)) fname_seg_crop_res = sct.add_suffix(fname_res3d, '_lesionseg') im_res3d = Image(fname_res3d) seg_im = segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, im=im_res3d.copy()) seg_im.save(fname_seg_crop_res) del im_res3d, seg_im # resample to the initial pz resolution fname_seg_res2d = sct.add_suffix(fname_seg_crop_res, '_resampled2d') initial_2d_resolution = 'x'.join(['0.5', '0.5', str(input_resolution[2])]) resampling.resample_file(fname_seg_crop_res, fname_seg_res2d, initial_2d_resolution, 'mm', 'linear', verbose=0) seg_crop = Image(fname_seg_res2d) # reconstruct the segmentation from the crop data sct.log.info("\nReassembling the image...") seg_uncrop_nii = uncrop_image(ref_in=im_nii, data_crop=seg_crop.copy().data, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST, z_crop_lst=Z_CROP_LST) fname_seg_res_RPI = sct.add_suffix(fname_in, '_res_RPI_seg') seg_uncrop_nii.save(fname_seg_res_RPI) del seg_crop # resample to initial resolution sct.log.info( "Resampling the segmentation to the original image resolution...") initial_resolution = 'x'.join([ str(input_resolution[0]), str(input_resolution[1]), str(input_resolution[2]) ]) fname_seg_RPI = sct.add_suffix(fname_in, '_RPI_seg') resampling.resample_file(fname_seg_res_RPI, fname_seg_RPI, initial_resolution, 'mm', 'linear', verbose=0) seg_initres_nii = Image(fname_seg_RPI) # binarize the resampled image to remove interpolation effects sct.log.info( "\nBinarizing the segmentation to avoid interpolation effects...") thr = 0.1 seg_initres_nii.data[np.where(seg_initres_nii.data >= thr)] = 1 seg_initres_nii.data[np.where(seg_initres_nii.data < thr)] = 0 # reorient to initial orientation sct.log.info( "\nReorienting the segmentation to the original image orientation...") tmp_folder.chdir_undo() # remove temporary files if remove_temp_files: sct.log.info("\nRemove temporary files...") tmp_folder.cleanup() # reorient to initial orientation return seg_initres_nii.change_orientation(original_orientation)
def detect_c2c3(nii_im, nii_seg, contrast, nb_sag_avg=7.0, verbose=1): """ Detect the posterior edge of C2-C3 disc. :param nii_im: :param nii_seg: :param contrast: :param verbose: :return: """ # path to the pmj detector path_model = os.path.join(sct.__data_dir__, 'c2c3_disc_models', '{}_model'.format(contrast)) orientation_init = nii_im.orientation z_seg_max = np.max(np.where(nii_seg.change_orientation('PIR').data)[1]) # Flatten sagittal nii_im = flatten_sagittal(nii_im, nii_seg, verbose=verbose) nii_seg_flat = flatten_sagittal(nii_seg, nii_seg, verbose=verbose) # create temporary folder with intermediate results sct.log.info("Creating temporary folder...") tmp_folder = sct.TempFolder() tmp_folder.chdir() # Extract mid-slice nii_im.change_orientation('PIR') nii_seg_flat.change_orientation('PIR') mid_RL = int(np.rint(nii_im.dim[2] * 1.0 / 2)) nb_sag_avg_half = int(nb_sag_avg / 2 / nii_im.dim[6]) midSlice = np.mean(nii_im.data[:, :, mid_RL - nb_sag_avg_half:mid_RL + nb_sag_avg_half + 1], 2) # average 7 slices midSlice_seg = nii_seg_flat.data[:, :, mid_RL] nii_midSlice = msct_image.zeros_like(nii_im) nii_midSlice.data = midSlice nii_midSlice.save('data_midSlice.nii') # Run detection sct.printv('Run C2-C3 detector...', verbose) os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR" cmd_detection = 'isct_spine_detect -ctype=dpdt "%s" "%s" "%s"' % \ (path_model, 'data_midSlice', 'data_midSlice_pred') sct.run(cmd_detection, verbose=0, raise_exception=False) pred = nib.load('data_midSlice_pred_svm.hdr').get_data() if verbose >= 2: # copy the "prediction data before post-processing" in an Image object nii_pred_before_postPro = nii_midSlice.copy() nii_pred_before_postPro.data = pred # 2D data with orientation, mid sag slice of the original data nii_pred_before_postPro.save( "pred_midSlice_before_postPro.nii.gz") # save it) # Create mask along centerline midSlice_mask = np.zeros(midSlice_seg.shape) mask_halfSize = int(np.rint(25.0 / nii_midSlice.dim[4])) for z in range(midSlice_mask.shape[1]): row = midSlice_seg[:, z] # 2D data with PI orientation, mid sag slice of the original data if np.any(row > 0): med_y = int(np.rint(np.median(np.where(row > 0)))) midSlice_mask[ med_y - mask_halfSize:med_y + mask_halfSize, z] = 1 # 2D data with PI orientation, mid sag slice of the original data if verbose >= 2: # copy the created mask in an Image object nii_postPro_mask = nii_midSlice.copy() nii_postPro_mask.data = midSlice_mask # 2D data with PI orientation, mid sag slice of the original data nii_postPro_mask.save("mask_midSlice.nii.gz") # save it # mask prediction pred[midSlice_mask == 0] = 0 pred[:, z_seg_max:] = 0 # Mask above SC segmentation if verbose >= 2: # copy the "prediction data after post-processing" in an Image object nii_pred_after_postPro = nii_midSlice.copy() nii_pred_after_postPro.data = pred nii_pred_after_postPro.save( "pred_midSlice_after_postPro.nii.gz") # save it # assign label to voxel nii_c2c3 = zeros_like(nii_seg_flat) # 3D data with PIR orientaion if np.any(pred > 0): sct.printv('C2-C3 detected...', verbose) pred_bin = (pred > 0).astype(np.int_) coord_max = np.where(pred == np.max(pred)) pa_c2c3, is_c2c3 = coord_max[0][0], coord_max[1][0] nii_seg.change_orientation('PIR') rl_c2c3 = int( np.rint(center_of_mass(np.array(nii_seg.data[:, is_c2c3, :]))[1])) nii_c2c3.data[pa_c2c3, is_c2c3, rl_c2c3] = 3 else: sct.printv('C2-C3 not detected...', verbose) # remove temporary files tmp_folder.chdir_undo() if verbose < 2: sct.log.info("Remove temporary files...") tmp_folder.cleanup() nii_c2c3.change_orientation(orientation_init) return nii_c2c3
def deep_segmentation_spinalcord(im_image, contrast_type, ctr_algo='cnn', ctr_file=None, brain_bool=True, kernel_size='2d', remove_temp_files=1, verbose=1): """Pipeline""" # create temporary folder with intermediate results tmp_folder = sct.TempFolder(verbose=verbose) tmp_folder_path = tmp_folder.get_path() if ctr_algo == 'file': # if the ctr_file is provided tmp_folder.copy_from(ctr_file) file_ctr = os.path.basename(ctr_file) else: file_ctr = None tmp_folder.chdir() # re-orient image to RPI logger.info("Reorient the image to RPI, if necessary...") original_orientation = im_image.orientation # fname_orient = 'image_in_RPI.nii' im_image.change_orientation('RPI') # Resample image to 0.5mm in plane im_image_res = \ resampling.resample_nib(im_image, new_size=[0.5, 0.5, im_image.dim[6]], new_size_type='mm', interpolation='linear') fname_orient = 'image_in_RPI_res.nii' im_image_res.save(fname_orient) # find the spinal cord centerline - execute OptiC binary logger.info("Finding the spinal cord centerline...") _, im_ctl, im_labels_viewer = find_centerline( algo=ctr_algo, image_fname=fname_orient, contrast_type=contrast_type, brain_bool=brain_bool, folder_output=tmp_folder_path, remove_temp_files=remove_temp_files, centerline_fname=file_ctr) if ctr_algo == 'file': im_ctl = \ resampling.resample_nib(im_ctl, new_size=[0.5, 0.5, im_image.dim[6]], new_size_type='mm', interpolation='linear') # crop image around the spinal cord centerline logger.info("Cropping the image around the spinal cord...") crop_size = 96 if (kernel_size == '3d' and contrast_type == 't2s') else 64 X_CROP_LST, Y_CROP_LST, Z_CROP_LST, im_crop_nii = crop_image_around_centerline( im_in=im_image_res, ctr_in=im_ctl, crop_size=crop_size) # normalize the intensity of the images logger.info("Normalizing the intensity...") im_norm_in = apply_intensity_normalization(im_in=im_crop_nii) del im_crop_nii if kernel_size == '2d': # segment data using 2D convolutions logger.info( "Segmenting the spinal cord using deep learning on 2D patches...") segmentation_model_fname = \ os.path.join(sct.__sct_dir__, 'data', 'deepseg_sc_models', '{}_sc.h5'.format(contrast_type)) seg_crop = segment_2d(model_fname=segmentation_model_fname, contrast_type=contrast_type, input_size=(crop_size, crop_size), im_in=im_norm_in) elif kernel_size == '3d': # segment data using 3D convolutions logger.info( "Segmenting the spinal cord using deep learning on 3D patches...") segmentation_model_fname = \ os.path.join(sct.__sct_dir__, 'data', 'deepseg_sc_models', '{}_sc_3D.h5'.format(contrast_type)) seg_crop = segment_3d(model_fname=segmentation_model_fname, contrast_type=contrast_type, im_in=im_norm_in) del im_norm_in # reconstruct the segmentation from the crop data logger.info("Reassembling the image...") im_seg = uncrop_image(ref_in=im_image_res, data_crop=seg_crop, x_crop_lst=X_CROP_LST, y_crop_lst=Y_CROP_LST, z_crop_lst=Z_CROP_LST) # seg_uncrop_nii.save(sct.add_suffix(fname_res, '_seg')) # for debugging del seg_crop # Change type uint8 --> float32 otherwise resampling will produce binary output (even with linear interpolation) im_seg.change_type(np.float32) # resample to initial resolution logger.info( "Resampling the segmentation to the native image resolution using linear interpolation..." ) im_seg_r = resampling.resample_nib(im_seg, image_dest=im_image, interpolation='linear') if ctr_algo == 'viewer': # for debugging im_labels_viewer.save(sct.add_suffix(fname_orient, '_labels-viewer')) # Binarize the resampled image to remove interpolation effects logger.info("Binarizing the resampled segmentation...") # thr = 0.0001 if contrast_type in ['t1', 'dwi'] else 0.5 thr = 0.5 # TODO: optimize speed --> np.where is slow im_seg_r.data[np.where(im_seg_r.data > thr)] = 1 im_seg_r.data[np.where(im_seg_r.data <= thr)] = 0 # post processing step to z_regularized im_seg_r_postproc = post_processing_volume_wise(im_seg_r) # change data type im_seg_r_postproc.change_type(np.uint8) tmp_folder.chdir_undo() # remove temporary files if remove_temp_files: logger.info("Remove temporary files...") tmp_folder.cleanup() # reorient to initial orientation return im_seg_r_postproc.change_orientation(original_orientation), \ im_image_res, \ im_seg.change_orientation('RPI')