Beispiel #1
0
 def register(self, i):
     """
     Aligns a 2D histology image to a 2D blockface image using ANTs 
     registration tools.
     """
     #Create naming convention for aligned files.
     slice_num = ''
     if i < 10:
         slice_num = '000' + str(i)
     elif i < 100:
         slice_num = '00' + str(i)
     elif i < 1000:
         slice_num = '0' + str(i)
     elif slice < 10000:
         slice_num = str(i)
     #Define registration parameters for ANT's Registration command through Nipype.
     reg = Registration()
     #reg.inputs.verbose = True
     reg.inputs.fixed_image = self.BF_vol.slices[i].path
     reg.inputs.moving_image = self.Hist_vol.slices[i].path
     reg.inputs.output_warped_image = 'Hist_to_BF_{}.nii.gz'.format(
         slice_num)
     reg.inputs.output_transform_prefix = "composite_transform_{}.h5".format(
         slice_num)
     if self.reg_method == 'nonlinear':
         reg.inputs.transforms = ['Translation', 'Rigid', 'Affine', 'SyN']
         reg.inputs.transform_parameters = [(0.1, ), (0.1, ), (0.1, ),
                                            (0.1, )]
         reg.inputs.number_of_iterations = ([[1500, 500, 250]] * 4)
         reg.inputs.metric = ['Mattes'] * 4
         reg.inputs.metric_weight = [1] * 4
         reg.inputs.radius_or_number_of_bins = [32] * 4
         reg.inputs.sampling_strategy = ['Regular'] * 4
         reg.inputs.sampling_percentage = [0.3] * 4
         reg.inputs.convergence_threshold = [1.e-6] * 4
         reg.inputs.convergence_window_size = [20] * 4
         reg.inputs.smoothing_sigmas = [[0, 0, 0]] * 4
         reg.inputs.sigma_units = ['vox'] * 4
         reg.inputs.shrink_factors = [[6, 4, 2]] + [[3, 2, 1]] * 3
         reg.inputs.use_estimate_learning_rate_once = [True] * 4
         reg.inputs.use_histogram_matching = [False] * 4
     else:
         if self.reg_method != 'linear':
             warnings.warn(
                 "Can't Interpret registration method, Defaulting to linear alignment."
             )
         reg.inputs.transforms = ['Translation', 'Rigid', 'Affine']
         reg.inputs.transform_parameters = [(0.1, ), (0.1, ), (0.1, )]
         reg.inputs.number_of_iterations = ([[1500, 500, 250]] * 3)
         reg.inputs.metric = ['Mattes'] * 3
         reg.inputs.metric_weight = [1] * 3
         reg.inputs.radius_or_number_of_bins = [32] * 3
         reg.inputs.sampling_strategy = ['Regular'] * 3
         reg.inputs.sampling_percentage = [0.3] * 3
         reg.inputs.convergence_threshold = [1.e-6] * 3
         reg.inputs.convergence_window_size = [20] * 3
         reg.inputs.smoothing_sigmas = [[0, 0, 0]] * 3
         reg.inputs.sigma_units = ['vox'] * 3
         reg.inputs.shrink_factors = [[3, 2, 1]] * 3
         reg.inputs.use_estimate_learning_rate_once = [True] * 3
         reg.inputs.use_histogram_matching = [False] * 3
     reg.inputs.interpolation = 'BSpline'
     reg.inputs.dimension = 2
     reg.inputs.write_composite_transform = True
     reg.inputs.collapse_output_transforms = True
     reg.inputs.initial_moving_transform_com = True
     reg.inputs.float = True
     reg.inputs.ignore_exception = True
     outputs = reg._list_outputs()
     #print(reg.cmdline)
     #Copy output files to output directory.
     print("##################################",
           'Hist_to_BF_{}.nii.gz is RUNNING'.format(slice_num),
           "##################################\n")
     reg.run()
     shutil.move(
         outputs['warped_image'],
         self.out_dir + '/grayscale/Hist_to_BF_{}.nii.gz'.format(slice_num))
     shutil.move(
         outputs['composite_transform'], self.out_dir +
         '/composite_transform/composite_transform_{}.h5'.format(slice_num))
     print("##################################",
           'Hist_to_BF_{}.nii.gz is COMPLETE'.format(slice_num),
           "##################################\n")
def registerImage(itk_moving,
                  itk_fixed,
                  store_to=None,
                  type="affine",
                  metric="MI",
                  speed="fast",
                  itk_InitialMovingAffTrf=None,
                  itk_InitialFixedAffTrf=None,
                  n_cores=8,
                  verbose=False):
    """
    Perform a registration using ANTs
    :param itk_moving: itk volume moving
    :param itk_fixed: itk volume fixed
    :param store_to: path to directory to store output, deletes tmp directory if defined
    :param type: string, "affine", "rigid", "deformable"
    :param metric: string "CC","MI"
    :param speed: string, "accurate","better","normal","fast","debug"
    :param itk_InitialMovingAffTrf: itk Transform, moving
    :param itk_InitialFixedAffTrf: itk Transform, fixed
    """

    # prepare environment / path
    main_dir = os.path.abspath(os.path.dirname(__file__))
    path_dir = os.path.join(main_dir, "bin")
    lib_dir = os.path.join(main_dir, "lib")
    tmp_dir = os.path.join(main_dir, "ants_tmp")
    cwd_old = os.getcwd()

    has_ANTs = bool(find_executable("antsRegistration"))
    has_PATH = "PATH" in os.environ
    if has_PATH:
        PATH_old = os.environ["PATH"]
    if not has_ANTs:
        os.environ["PATH"] = (os.environ["PATH"] +
                              os.pathsep if has_PATH else "") + path_dir
        if not find_executable("antsRegistration"):
            raise Exception("No executable file \"antsRegistration\" in PATH.")

    has_LD_LIBRARY = "LD_LIBRARY_PATH" in os.environ
    if has_LD_LIBRARY:
        LD_LIBRARY_old = os.environ["LD_LIBRARY_PATH"]
    if not has_ANTs:
        os.environ["LD_LIBRARY_PATH"] = (os.environ["LD_LIBRARY_PATH"] +
                                         os.pathsep
                                         if has_LD_LIBRARY else "") + lib_dir

    has_NUMBERCORES = "ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS" in os.environ
    if has_NUMBERCORES:
        NUMBERCORES_old = os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"]
    os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "{}".format(n_cores)

    # clean tmp directory
    shutil.rmtree(tmp_dir, ignore_errors=True)
    os.mkdir(tmp_dir)

    # write volumes
    moving_path = os.path.join(tmp_dir, "moving.nii.gz")
    fixed_path = os.path.join(tmp_dir, "fixed.nii.gz")
    sitk.WriteImage(itk_moving, moving_path)
    sitk.WriteImage(itk_fixed, fixed_path)

    # write initial transforms
    if itk_InitialFixedAffTrf:
        fixedtrf_path = os.path.join(tmp_dir, "initialfixedtrf.mat")
        sitk.WriteTransform(itk_InitialFixedAffTrf, fixedtrf_path)
    if itk_InitialMovingAffTrf:
        movingtrf_path = os.path.join(tmp_dir, "initialmovingtrf.mat")
        sitk.WriteTransform(itk_InitialMovingAffTrf, movingtrf_path)

    # switch to tmp dir
    os.chdir(tmp_dir)

    # setup registration parameters
    reg = Registration()

    reg.inputs.fixed_image = fixed_path
    reg.inputs.moving_image = moving_path
    reg.num_threads = n_cores

    if itk_InitialFixedAffTrf:
        reg.inputs.initial_fixed_transform = fixedtrf_path
    if itk_InitialMovingAffTrf:
        reg.inputs.initial_moving_transform = movingtrf_path

    # shared settings

    # what does this do?
    reg.inputs.args = '--float 0'

    # warped moving image
    reg.inputs.output_warped_image = 'moving_warped.nii.gz'

    # dimensionality
    reg.inputs.dimension = 3
    # output prefix
    reg.inputs.output_transform_prefix = "output_"
    # interpolation
    reg.inputs.interpolation = "Linear"
    # winsorize-image-intensities
    reg.inputs.winsorize_lower_quantile = 0.005
    reg.inputs.winsorize_upper_quantile = 0.995
    # histogram matching
    reg.inputs.use_histogram_matching = False
    # write-composite-transform
    reg.inputs.write_composite_transform = False

    # convergence
    if speed == "accurate":
        reg.inputs.number_of_iterations = ([[1000, 100, 50, 20]])
        reg.inputs.convergence_threshold = [1.e-6]
        reg.inputs.convergence_window_size = [10]

        reg.inputs.shrink_factors = [[8, 4, 3, 1]]
        reg.inputs.sigma_units = ['vox']
        reg.inputs.smoothing_sigmas = [[4, 3, 2, 1]]
    elif speed == "better":
        reg.inputs.number_of_iterations = ([[200, 100, 50, 20]])
        reg.inputs.convergence_threshold = [1.e-6]
        reg.inputs.convergence_window_size = [10]

        reg.inputs.shrink_factors = [[8, 4, 2, 1]]
        reg.inputs.sigma_units = ['vox']
        reg.inputs.smoothing_sigmas = [[4, 3, 2, 1]]
    elif speed == "normal":
        reg.inputs.number_of_iterations = ([[100, 50, 10]])
        reg.inputs.convergence_threshold = [1.e-6]
        reg.inputs.convergence_window_size = [10]

        reg.inputs.shrink_factors = [[8, 4, 2]]
        reg.inputs.sigma_units = ['vox']
        reg.inputs.smoothing_sigmas = [[3, 2, 1]]
    elif speed == "fast":
        reg.inputs.number_of_iterations = ([[100, 50]])
        reg.inputs.convergence_threshold = [1.e-6]
        reg.inputs.convergence_window_size = [10]

        reg.inputs.shrink_factors = [[4, 3]]
        reg.inputs.sigma_units = ['vox']
        reg.inputs.smoothing_sigmas = [[3, 2]]
    elif speed == "debug":
        reg.inputs.number_of_iterations = ([[10]])
        reg.inputs.convergence_threshold = [1.e-6]
        reg.inputs.convergence_window_size = [10]

        reg.inputs.shrink_factors = [[4]]
        reg.inputs.sigma_units = ['vox']
        reg.inputs.smoothing_sigmas = [[3]]
    else:
        raise Exception(
            "Parameter speed must be from the list: accurate, better, normal, fast, debug"
        )

    # metric
    if metric == "MI":
        reg.inputs.metric = ["MI"]
        reg.inputs.metric_weight = [1.0]
        reg.inputs.radius_or_number_of_bins = [32]
        reg.inputs.sampling_strategy = ['Regular']
        reg.inputs.sampling_percentage = [0.25]
    elif metric == "CC":
        reg.inputs.metric = ["MI"]
        reg.inputs.metric_weight = [1.0]
        reg.inputs.radius_or_number_of_bins = [4]
        reg.inputs.sampling_strategy = ['None']
        reg.inputs.sampling_percentage = [0.1]
    else:
        raise Exception("Parameter metric must be from the list: MI,CC")

    # type-specific settings
    if type == "affine":
        reg.inputs.transforms = ['Affine']
        reg.inputs.transform_parameters = [(0.1, )]
    elif type == "rigid":
        reg.inputs.transforms = ['Rigid']
        reg.inputs.transform_parameters = [(0.1, )]
    elif type == "deformable":
        reg.inputs.transforms = ['SyN']
        reg.inputs.transform_parameters = [(0.25, )]
    else:
        raise Exception(
            "Parameter type must be from the list: affine, rigid, deformable")

    if verbose:
        print("Using antsRegistration from: {}".format(
            find_executable("antsRegistration")))
        print("Executing: {}".format(reg.cmdline))
    # perform ants call (retrieve by reg.cmdline)
    reg.run()

    # reset environment
    if not has_ANTs:
        if has_PATH:
            os.environ["PATH"] = PATH_old
        else:
            del os.environ["PATH"]

        if has_LD_LIBRARY:
            os.environ["LD_LIBRARY_PATH"] = LD_LIBRARY_old
        else:
            del os.environ["LD_LIBRARY_PATH"]

    if has_NUMBERCORES:
        os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = NUMBERCORES_old
    else:
        del os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"]

    if type == 'affine':
        #output_trf_path = ["output_0Affine.mat"]
        output_trf_path = reg._list_outputs().get('forward_transforms',
                                                  ["output_0Affine.mat"])
    elif type == 'rigid':
        #output_trf_path = ["output_0Rigid.mat"]
        output_trf_path = reg._list_outputs().get('forward_transforms',
                                                  ["output_0Rigid.mat"])
    elif type == 'deformable':
        #output_trf_path = ["output_0Warp.nii.gz","output_0InverseWarp.nii.gz"]
        output_trf_path = reg._list_outputs().get(
            'forward_transforms',
            ["output_0Warp.nii.gz"]) + reg._list_outputs().get(
                'reverse_transforms', ["output_0InverseWarp.nii.gz"])

    output_paths_trf = [os.path.join(tmp_dir, p) for p in output_trf_path]
    warped_path = os.path.join(tmp_dir, 'moving_warped.nii.gz')

    # switch back to old cwd
    os.chdir(cwd_old)

    if store_to:
        # copy output files
        moved_output_paths_trf = []
        for tf in output_paths_trf:
            moved = os.path.join(store_to, os.path.basename(tf))
            moved_output_paths_trf.append(moved)
            shutil.copy(tf, moved)

        moved_warped_path = os.path.join(store_to,
                                         os.path.basename(warped_path))
        shutil.copy(warped_path, moved_warped_path)

        # clear tmp directory
        shutil.rmtree(tmp_dir)

        if verbose:
            print("Store transform(s) to: {}".format(
                ", ".join(moved_output_paths_trf)))
            print("Store warped volume to: {}".format(
                ", ".join(moved_warped_path)))

        return {
            "transforms_out": moved_output_paths_trf,
            "warpedMovingVolume": moved_warped_path,
        }
    else:
        if verbose:
            print("Store transform(s) to: {}".format(
                ", ".join(output_paths_trf)))
            print("Store warped volume to: {}".format(", ".join(warped_path)))

        return {
            "transforms_out": output_paths_trf,
            "warpedMovingVolume": warped_path,
        }