Example #1
0
    def value_and_derivatives(self, transform):
        """Apply the given transform and calculate the dLDP distance between the 
        resulting pair of images.

        Gradient is not implemented.
        
        Args:
            transform: an object implementing the transform API (subclass of BaseTransform)
        
        Returns:
            dLDP measure for given transform, None
        """
        params = transform.get_params()
        c_trans = transforms.make_image_centered_transform(transform, \
                                                     self.ref_image, self.flo_image)

        # Only compare pixels that were part of the floating image, not background.
        # Use a mask of the same shape, transformed in the same way, to determine
        # which pixels are within range
        mask = np.ones(self.flo_image.shape, dtype='bool')

        # Create the output image
        warped_image = np.zeros(self.ref_image.shape)
        warped_mask = np.zeros(self.ref_image.shape)

        # Transform the floating image into the reference image space by applying transformation 'c_trans'
        c_trans.warp(In=self.flo_image,
                     Out=warped_image,
                     mode=self.interpolation,
                     bg_value=0)
        c_trans.warp(In=mask, Out=warped_mask, mode='nearest', bg_value=0)

        # If the overlap is less than 40% then exclude it
        if len(warped_image[np.logical_and(
                warped_mask > 0, self.ref_mask > 0)]) < 0.4 * np.prod(
                    self.flo_image.shape):  #too small an overlap, skip it.
            return np.inf, None

        warped_image_dLDP, warped_mask = self.create_dLDP(
            warped_image, warped_mask)

        value = self.dLDPdist(self.ref_dLDP[np.logical_and(warped_mask > 0, self.ref_mask > 0)], \
                            warped_image_dLDP[np.logical_and(warped_mask > 0, self.ref_mask > 0)])

        paramstr = ' '.join(['%.5f' % p for p in params])
        #        print(f'[{paramstr}] -> %.5f'%value)
        #        if value > self.best_val:
        #            self.best_val = value
        #            self.best_trans = transform.copy()
        #            print('New best value %2.4f at ('%value, ', '.join(['%8.3f']*len(params))%tuple(params), ')')
        grad = None

        return value, grad
    def value_and_derivatives(self, transform):
        """Apply the given transform and calculate the resulting mutual info score. Return the negative of this.

        Gradient is not implemented.
        
        Args:
            transform: an object implementing the transform API (subclass of BaseTransform)
        
        Returns:
            negative of MI score for given transform, None
        """
        params = transform.get_params()
        c_trans = transforms.make_image_centered_transform(transform, \
                                                     self.ref_image, self.flo_image)

        # Only compare pixels that were part of the floating image, not background.
        # Use a mask of the same shape, transformed in the same way, to determine
        # which pixels are within range
        mask = np.ones(self.flo_image.shape)

        # Create the output image
        warped_image = np.zeros(self.ref_image.shape)
        warped_mask = np.zeros(self.ref_image.shape)

        # Transform the floating image into the reference image space by applying transformation 'c_trans'
        c_trans.warp(In=self.flo_image,
                     Out=warped_image,
                     mode='nearest',
                     bg_value=0)
        c_trans.warp(In=mask, Out=warped_mask, mode='nearest', bg_value=0)
        if len(warped_image[np.logical_and(
                warped_mask > 0, self.ref_mask > 0)]) < 0.4 * np.prod(
                    self.flo_image.shape):  #too small an overlap, skip it.
            return 0, None

        # Cast back to integer values for mutual information comparison
        warped_image = np.where(warped_image < 0, 0, warped_image)
        warped_image = np.where(warped_image > self.nLevels, self.nLevels,
                                warped_image)
        warped_image = np.rint(warped_image).astype('uint8')

        value = self.mi_fun(self.ref_image[np.logical_and(warped_mask > 0, self.ref_mask > 0)], \
                            warped_image[np.logical_and(warped_mask>0, self.ref_mask > 0)])
        #        if value > 1:
        #            print("MI function returned a value > 1")
        #        if value > self.best_val:
        #            self.best_val = value
        #            self.best_trans = transform.copy()
        #            print('New best value %2.4f at ('%value, ', '.join(['%8.3f']*len(params))%tuple(params), ')')
        grad = None

        return -value, grad
def main():
    np.random.seed(1000)
    
    if len(sys.argv) < 3:
        print('register_example.py: Too few parameters. Give the path to two gray-scale image files.')
        print('Example: python2 register_example.py reference_image floating_image')
        return False

    ref_im_path = sys.argv[1]
    flo_im_path = sys.argv[2]

    ref_im = scipy.misc.imread(ref_im_path, 'L')
    flo_im = scipy.misc.imread(flo_im_path, 'L')

    # Save copies of original images
    ref_im_orig = ref_im.copy()
    flo_im_orig = flo_im.copy()

    ref_im = filters.normalize(ref_im, 0.0, None)
    flo_im = filters.normalize(flo_im, 0.0, None)
    
    diag = 0.5 * (transforms.image_diagonal(ref_im, spacing) + transforms.image_diagonal(flo_im, spacing))

    weights1 = np.ones(ref_im.shape)
    mask1 = np.ones(ref_im.shape, 'bool')
    weights2 = np.ones(flo_im.shape)
    mask2 = np.ones(flo_im.shape, 'bool')

    # Initialize registration framework for 2d images
    reg = Register(2)

    reg.set_report_freq(param_report_freq)
    reg.set_alpha_levels(alpha_levels)

    reg.set_reference_image(ref_im)
    reg.set_reference_mask(mask1)
    reg.set_reference_weights(weights1)

    reg.set_floating_image(flo_im)
    reg.set_floating_mask(mask2)
    reg.set_floating_weights(weights2)

    # Setup the Gaussian pyramid resolution levels
    
    reg.add_pyramid_level(4, 5.0)
    reg.add_pyramid_level(2, 3.0)
    reg.add_pyramid_level(1, 0.0)

    # Learning-rate / Step lengths [[start1, end1], [start2, end2] ...] (for each pyramid level)
    step_lengths = np.array([[1.0 ,1.0], [1.0, 0.5], [0.5, 0.1]])

    # Create the transform and add it to the registration framework (switch between affine/rigid transforms by commenting/uncommenting)
    # Affine
    reg.add_initial_transform(AffineTransform(2), np.array([1.0/diag, 1.0/diag, 1.0/diag, 1.0/diag, 1.0, 1.0]))
    # Rigid 2D
    #reg.add_initial_transform(Rigid2DTransform(2), np.array([1.0/diag, 1.0, 1.0]))

    # Set the parameters
    reg.set_iterations(param_iterations)
    reg.set_gradient_magnitude_threshold(0.001)
    reg.set_sampling_fraction(param_sampling_fraction)
    reg.set_step_lengths(step_lengths)

    # Create output directory
    directory = os.path.dirname('./test_images/output/')
    if not os.path.exists(directory):
        os.makedirs(directory)

    # Start the pre-processing
    reg.initialize('./test_images/output/')
    
    # Control the formatting of numpy
    np.set_printoptions(suppress=True, linewidth=200)

    # Start the registration
    reg.run()

    (transform, value) = reg.get_output(0)

    ### Warp final image
    c = transforms.make_image_centered_transform(transform, ref_im, flo_im, spacing, spacing)

    # Print out transformation parameters
    print('Transformation parameters: %s.' % str(transform.get_params()))

    # Create the output image
    ref_im_warped = np.zeros(ref_im.shape)

    # Transform the floating image into the reference image space by applying transformation 'c'
    c.warp(In = flo_im_orig, Out = ref_im_warped, in_spacing=spacing, out_spacing=spacing, mode='spline', bg_value = 0.0)

    # Save the registered image
    scipy.misc.imsave('./test_images/output/registered.png', ref_im_warped)

    # Compute the absolute difference image between the reference and registered images
    D1 = np.abs(ref_im_orig-ref_im_warped)
    err = np.sum(D1)
    print("Err: %f" % err)

    scipy.misc.imsave('./test_images/output/diff.png', D1)

    return True
def main():
    np.random.seed(1000)

    if len(sys.argv) < 3:
        print(
            f'{sys.argv[0]}: Too few parameters. Give the path to two gray-scale image files.'
        )
        print(f'Example: python {sys.argv[0]} reference_image floating_image')
        return False

    ref_im_path = sys.argv[1]
    flo_im_path = sys.argv[2]

    ref_im = Image.open(ref_im_path).convert('L')
    flo_im = Image.open(flo_im_path).convert('L')
    ref_im = np.asarray(ref_im) / 255.
    flo_im = np.asarray(flo_im) / 255.

    # Make copies of original images
    ref_im_orig = ref_im.copy()
    flo_im_orig = flo_im.copy()

    # Preprocess images
    ref_im = filters.normalize(ref_im, 0.0, None)
    flo_im = filters.normalize(flo_im, 0.0, None)

    weights1 = np.ones(ref_im.shape)
    mask1 = np.ones(ref_im.shape, 'bool')
    weights2 = np.ones(flo_im.shape)
    mask2 = np.ones(flo_im.shape, 'bool')

    # Initialize registration framework for 2d images
    reg = Register(2)
    reg.set_image_data(ref_im, flo_im, mask1, mask2, weights1, weights2)

    # Choose a registration model
    reg.set_model('alphaAMD', alpha_levels=alpha_levels, \
                  symmetric_measure=symmetric_measure, \
                  squared_measure=squared_measure)

    # Setup the Gaussian pyramid resolution levels
    reg.add_pyramid_level(4, 5.0)
    reg.add_pyramid_level(2, 3.0)
    reg.add_pyramid_level(1, 0.0)

    # Choose an optimizer and set optimizer-specific parameters
    # For GD and adam, learning-rate / Step lengths given by [[start1, end1], [start2, end2] ...] (for each pyramid level)
    reg.set_optimizer('adam', \
                      gradient_magnitude_threshold=0.01, \
                      iterations=param_iterations
                      )
    #    reg.set_optimizer('gd', \
    #                      step_length=np.array([1., 0.5, 0.25]), \
    #                      end_step_length=np.array([0.4, 0.2, 0.01]), \
    #                      gradient_magnitude_threshold=0.01, \
    #                      iterations=param_iterations
    #                      )
    #    reg.set_optimizer('scipy', \
    #                      iterations=param_iterations, \
    #                      epsilon=0.001 \
    #                      )

    # Scale all transform parameters to approximately the same order of magnitude, based on sizes of images
    diag = 0.5 * (transforms.image_diagonal(ref_im, spacing) +
                  transforms.image_diagonal(flo_im, spacing))

    # Create the initial transform and add it to the registration framework
    # (switch between affine/rigid transforms by commenting/uncommenting)
    #    # Affine
    #    initial_transform = transforms.AffineTransform(2)
    #    param_scaling = np.array([1.0/diag, 1.0/diag, 1.0/diag, 1.0/diag, 1.0, 1.0])
    #    reg.add_initial_transform(initial_transform, param_scaling=param_scaling)
    #    # Rigid 2D
    #    initial_transform = transforms.Rigid2DTransform()
    #    param_scaling = np.array([1.0/diag, 1.0, 1.0])
    #    reg.add_initial_transform(initial_transform, param_scaling=param_scaling)
    # Composite scale + rigid
    param_scaling = np.array([1.0 / diag, 1.0 / diag, 1.0, 1.0])
    initial_transform = transforms.CompositeTransform(2, [transforms.ScalingTransform(2, uniform=True), \
                                                transforms.Rigid2DTransform()])
    reg.add_initial_transform(initial_transform, param_scaling=param_scaling)

    # Set up other registration framework parameters
    reg.set_report_freq(param_report_freq)
    reg.set_sampling_fraction(param_sampling_fraction)

    # Create output directory
    directory = os.path.dirname(outdir)
    if not os.path.exists(directory):
        os.makedirs(directory)

    # Start the pre-processing
    reg.initialize(outdir)

    # Control the formatting of numpy
    np.set_printoptions(suppress=True, linewidth=200)

    # Start the registration
    reg.run()

    (transform, value) = reg.get_output(0)

    ### Warp final image
    c = transforms.make_image_centered_transform(transform, ref_im, flo_im,
                                                 spacing, spacing)

    # Print out transformation parameters and status
    print('Starting from %s, optimizer terminated with message: %s'%(str(initial_transform.get_params()), \
                                                                    reg.get_output_messages()[0]))
    print('Final transformation parameters: %s.' % str(transform.get_params()))

    # Create the output image
    ref_im_warped = np.zeros(ref_im.shape)
    mask = np.ones(flo_im_orig.shape, dtype='bool')
    warped_mask = np.zeros(ref_im.shape, dtype='bool')

    # Transform the floating image into the reference image space by applying transformation 'c'
    c.warp(In=flo_im_orig,
           Out=ref_im_warped,
           in_spacing=spacing,
           out_spacing=spacing,
           mode='spline',
           bg_value=0.0)
    c.warp(In=mask,
           Out=warped_mask,
           in_spacing=spacing,
           out_spacing=spacing,
           mode='spline',
           bg_value=0.0)

    # Save the registered image
    Image.fromarray(ref_im_warped).convert('RGB').save(outdir +
                                                       'registered.png')

    # Compute the absolute difference image between the reference and registered images
    D1 = np.abs(ref_im_orig - ref_im_warped)
    err = np.mean(D1[warped_mask])
    print("Err: %f" % err)

    Image.fromarray(D1).convert('RGB').save(outdir + 'diff.png')

    return True
Example #5
0
def main():
    #np.random.seed(1000)

    if len(sys.argv) > 1:
        ref_im_path = sys.argv[1]
    else:
        ref_im_path = example_ref_im
    if len(sys.argv) > 2:
        flo_im_path = sys.argv[2]
    else:
        flo_im_path = example_flo_im

    print('Registering floating image %s with reference image %s' %
          (flo_im_path, ref_im_path))
    print('Similarity measure %s, optimizer %s' %
          (param_method, param_optimizer))

    ref_im = Image.open(ref_im_path).convert('L')
    flo_im = Image.open(flo_im_path).convert('L')
    ref_im = np.asarray(ref_im)
    flo_im = np.asarray(flo_im)

    # Save copies of original images
    ref_im_orig = ref_im.copy()
    flo_im_orig = flo_im.copy()

    # Initialize registration model for 2d images and do specific preprocessing and setup for that model
    if param_method.lower() == 'alphaamd':
        reg = models.RegisterAlphaAMD(2)
        reg.set_alpha_levels(alpha_levels)
        ref_im = filters.normalize(ref_im, 0.0, None)
        flo_im = filters.normalize(flo_im, 0.0, None)
    elif param_method.lower() == 'mi':
        ref_im = filters.normalize(ref_im, 0.0, None)
        flo_im = filters.normalize(flo_im, 0.0, None)
        reg = models.RegisterMI(2)
    else:
        raise NotImplementedError('Method must be one of alphaAMD, MI')
    reg.set_report_freq(param_report_freq)

    # Generic initialization steps required for every registration model
    weights1 = np.ones(ref_im.shape)
    mask1 = np.ones(ref_im.shape, 'bool')
    weights2 = np.ones(flo_im.shape)
    mask2 = np.ones(flo_im.shape, 'bool')

    reg.set_reference_image(ref_im)
    reg.set_reference_mask(mask1)
    reg.set_reference_weights(weights1)

    reg.set_floating_image(flo_im)
    reg.set_floating_mask(mask2)
    reg.set_floating_weights(weights2)

    # Setup the Gaussian pyramid resolution levels
    reg.add_pyramid_level(4, 5.0)
    reg.add_pyramid_level(2, 3.0)
    reg.add_pyramid_level(1, 0.0)

    # Learning-rate / Step lengths [[start1, end1], [start2, end2] ...] (for each pyramid level)
    step_lengths = np.array([[1., 1.], [1., 0.5], [0.5, 0.1]])

    # Estimate an appropriate parameter scaling based on the sizes of the images.
    diag = transforms.image_diagonal(
        ref_im, spacing) + transforms.image_diagonal(flo_im, spacing)
    diag = 2.0 / diag

    # Create the transform and add it to the registration framework (switch between affine/rigid transforms by commenting/uncommenting)
    # Affine
    #    reg.add_initial_transform(transforms.AffineTransform(2), param_scaling=np.array([diag, diag, diag, diag, 1.0, 1.0]))
    # Rigid 2D
    #reg.add_initial_transform(transforms.Rigid2DTransform(), param_scaling=np.array([diag, 1.0, 1.0]))
    # Uniform scale, rotate and translate
    t = transforms.CompositeTransform(2, [transforms.ScalingTransform(2, uniform=True), \
                                          transforms.Rigid2DTransform()])
    reg.add_initial_transform(t,
                              param_scaling=np.array([diag, diag, 1.0, 1.0]))

    # Set the parameters
    reg.set_iterations(param_iterations)
    reg.set_gradient_magnitude_threshold(1e-6)
    reg.set_sampling_fraction(param_sampling_fraction)
    reg.set_step_lengths(step_lengths)
    reg.set_optimizer(param_optimizer)

    # Create output directory
    directory = os.path.dirname(param_outdir)
    if not os.path.exists(directory):
        os.makedirs(directory)

    # Start the pre-processing
    reg.initialize(param_outdir)

    # Control the formatting of numpy
    np.set_printoptions(suppress=True, linewidth=200)

    # Start the registration
    reg.run()

    (transform, value) = reg.get_output(0)

    ### Warp final image
    c = transforms.make_image_centered_transform(transform, ref_im, flo_im,
                                                 spacing, spacing)

    # Print out transformation parameters
    print('Transformation parameters: %s.' % str(transform.get_params()))

    # Create the output image
    ref_im_warped = np.zeros(ref_im.shape)

    # Transform the floating image into the reference image space by applying transformation 'c'
    c.warp(In=flo_im_orig,
           Out=ref_im_warped,
           in_spacing=spacing,
           out_spacing=spacing,
           mode='spline',
           bg_value=0.0)
    # Cast back to integer values for mutual information comparison
    ref_im_warped = np.rint(ref_im_warped).astype('uint8')
    mask = np.ones(flo_im.shape)
    warped_mask = np.zeros(ref_im.shape)
    c.warp(In=mask,
           Out=warped_mask,
           in_spacing=spacing,
           out_spacing=spacing,
           mode='nearest',
           bg_value=0.0)
    value1 = mutual_info_score(ref_im[warped_mask > 0],
                               ref_im_warped[warped_mask > 0])
    print("Mutual info at estimated transform:", value1)

    #    plt.figure()
    #    plt.subplot(121)
    #    plt.imshow(ref_im_warped, vmin=0, vmax=255, cmap='gray')
    #    plt.title("Registered image")
    #    plt.subplot(122)
    #    plt.imshow(warped_mask, vmin=0, vmax=1, cmap='gray')
    #    plt.show()
    # Save the registered image
    Image.fromarray(ref_im_warped).convert('RGB').save(param_outdir +
                                                       'registered.png')

    ### Compare with ground truth
    scaling_trans = transforms.ScalingTransform(2, uniform=True)
    scaling_trans.set_params([
        1,
    ])
    rigid_trans = transforms.Rigid2DTransform()
    rigid_trans.set_params([0.35, 0.5, 0.5])
    gt_transform = transforms.CompositeTransform(2,
                                                 [scaling_trans, rigid_trans])

    c2 = transforms.make_image_centered_transform(gt_transform, ref_im, flo_im,
                                                  spacing, spacing)

    # Print out ground truth transformation parameters
    print('Ground Truth transformation parameters: %s.' %
          str(gt_transform.get_params()))

    # Create the output image
    warped_image = np.zeros(ref_im.shape)
    mask = np.ones(flo_im_orig.shape)
    warped_mask = np.zeros(ref_im.shape)

    # Transform the floating image into the reference image space by applying transformation defined above
    c2.warp(In=flo_im_orig, Out=warped_image, mode='spline', bg_value=0)
    # Apply the same transform to the mask to determine which pixels are within the original image
    c2.warp(In=mask, Out=warped_mask, mode='nearest', bg_value=0)

    # Cast back to integer values for mutual information comparison
    warped_image = np.rint(warped_image).astype('uint8')

    value2 = mutual_info_score(ref_im[warped_mask > 0],
                               warped_image[warped_mask > 0])
    print("Mutual info at ground truth:", value2)

    plt.figure()
    plt.subplot(131)
    plt.imshow(ref_im_warped, vmin=0, vmax=255, cmap='gray')
    plt.title("Registered image")
    plt.subplot(132)
    plt.imshow(warped_image, vmin=0, vmax=255, cmap='gray')
    plt.title("Ground truth")
    plt.subplot(133)
    plt.imshow(abs(ref_im_warped.astype(float) - warped_image.astype(float)),
               vmin=0,
               vmax=255,
               cmap='gray')
    plt.title("Difference")
    plt.show()

    # Compute the absolute difference image between the reference and registered images
    D1 = np.abs(ref_im_orig - ref_im_warped)
    err = np.mean(D1)
    print("Err: %f" % err)

    Image.fromarray(D1).convert('RGB').save(param_outdir + 'diff.png')

    return True
Example #6
0
def register_pairs(server=False):
    #TODO. Docstrings.
    results = []
    id_trans = transforms.CompositeTransform(2, [transforms.ScalingTransform(2, uniform=True), \
                                          transforms.Rigid2DTransform()])

    ##### Running parameters to update each time #####
    modelname = 'dLDP'  #['alphaAMD', 'MI', 'MSE', 'dLDP']
    #    modelparams = {} #mse
    modelparams = {'version': 'dLDP_48'}  #dLDP versions: dLDP_8, dLDP_48, LDP
    #    modelparams = {'alpha_levels':7, 'symmetric_measure':True, 'squared_measure':False}
    #    modelparams = {'mutual_info_fun':'norm'}

    optname = 'bfgs'  #['gd', 'adam', 'gridsearch', 'bfgs']
    optparams = {'gradient_magnitude_threshold': 1e-9, 'epsilon': 0.05}  #bfgs
    #    optparams = {'bounds':gridBounds(id_trans, 0.05, 5, 10), 'steps':11} #gridsearch
    #    optparams = {'gradient_magnitude_threshold':1e-6} #adam, gd

    norm = True
    blur = 3.0
    skip = 0  #5 #manual way to skip pairs that have already been processed
    results_file = 'PartIII_test5.10_48bit.csv'
    limit = 25 - skip
    ##### End running parameters #####

    np.random.seed(
        999)  #For testing, make sure we get the same transforms each time
    rndTransforms = [
        getRandomTransform(maxRot=5, maxScale=1.05, maxTrans=10)
        for _ in range(limit + skip + 1)
    ]
    #Reverse the list as we will pop transforms from the far end. Want these to be the same, even
    #if we change the limit later.
    rndTransforms.reverse()

    folder = local_sr_folder
    if server:
        folder = server_sr_folder
    if server:
        outfile = server_separate_mpm_folder + results_file
    else:
        outfile = local_separate_mpm_folder + results_file

#    id_trans.set_params([1.,0.2,10.,10.]) #Nelder mead doesn't work starting from zeros

#OPTION: Starting from gridmax already found
#    grid_params = get_MI_gridmax(local_separate_mpm_folder+'PartI_test4.csv')

#    for slide, roi_idx, ref_im, flo_im in getNextSRPair(folder, order=True, verbose=True, server=server, norm=norm, blur=blur):
#    for slide, roi_idx, mpm_path, al_path in getNextPair():
    for slide, roi_idx, ref_im, flo_im in getNextMPMPair(verbose=True,
                                                         server=server,
                                                         norm=norm,
                                                         blur=blur):
        # Take the next random transform
        rndTrans = rndTransforms.pop()

        if skip > 0:
            print("Skipping %s_%s" % (slide, roi_idx))
            skip -= 1
            continue
        limit -= 1
        if limit < 0:
            break
        # Open and prepare the images: if using SRs then don't normalize (or do?)
#        ref_im, ref_im_orig = OpenAndPreProcessImage(al_path, copyOrig=True)
#        flo_im, flo_im_orig = OpenAndPreProcessImage(mpm_path, copyOrig=True)
#If aligning SHG + TPEF, keep a copy of the SHG (reference) image
#as it was before random transform is applied
#        flo_im_orig = flo_im.copy()
        ref_im_orig = ref_im.copy()

        print("Random transform applied: %r" % rndTrans.get_params())

        # Apply the transform to the reference image, increasing the canvas size to avoid cutting off parts
        ref_im = centreAndApplyTransform(
            ref_im, rndTrans,
            np.rint(np.array(ref_im.shape) * 1.5).astype('int'))

        # Show the images we are working with
        print("Aligning images for sample %s, region %s"%(slide, roi_idx) \
#              + ". A transform of %r has been applied to the reference image"%str(rndTrans.get_params())
#               + " from folder %s"%folder)


        )
        if False:
            plt.figure(figsize=(12, 6))
            plt.subplot(121)
            plt.imshow(ref_im, cmap='gray', vmin=0, vmax=1)
            plt.title("Reference image")
            plt.subplot(122)
            plt.imshow(flo_im, cmap='gray', vmin=0, vmax=1)
            plt.title("Floating image")
            plt.show()

        # Choose a model, set basic parameters for that model
        reg = Register(2)
        reg.set_model(modelname, **modelparams)

        # Choose an optimzer, set basic parameters for it
        reg.set_optimizer(optname, **optparams)

        # Since we have warped the original reference image, create a mask so that only the relevant
        # pixels are considered. Use the same warping function as above
        ref_mask = np.ones(ref_im_orig.shape, 'bool')
        ref_mask = centreAndApplyTransform(
            ref_mask, rndTrans,
            np.rint(np.array(ref_im_orig.shape) * 1.5).astype('int'))

        reg.set_image_data(ref_im, \
                           flo_im, \
                           ref_mask=ref_mask, \
                           flo_mask=np.ones(flo_im.shape, 'bool'), \
                           ref_weights=None, \
                           flo_weights=None
                           )

        ## Add pyramid levels
        if modelname.lower() == 'alphaamd':
            # Learning-rate / Step lengths [[start1, end1], [start2, end2] ...] (for each pyramid level)
            step_lengths = np.array([[1., 1.], [1., 0.5], [0.5, 0.1]])
            reg.set_step_lengths(step_lengths)
            reg.add_pyramid_levels(factors=[4, 2, 1], sigmas=[5.0, 3.0, 0.0])
            reg.set_sampling_fraction(
                0.5)  #very patchy with 0.1, also tried 0.25
            reg.set_iterations(5000)

        else:
            # I have seen no evidence so far, that pyramid levels lead the search towards the MI maximum.
            reg.add_pyramid_levels(factors=[
                1,
            ], sigmas=[
                0.0,
            ])
            # Try with a blurred full-resolution image first (or only)
#            reg.add_pyramid_levels(factors=[1,1], sigmas=[5.0,0.0])

## Add initial transform(s), with parameter scaling if required
        if optname.lower() == 'gridsearch':
            reg.add_initial_transform(id_trans)
        else:
            #BFGS and AlphaAMD
            # Estimate an appropriate parameter scaling based on the sizes of the images (not used in grid search).
            diag = transforms.image_diagonal(
                ref_im) + transforms.image_diagonal(flo_im)
            diag = 2.0 / diag
            #            p_scaling = np.array([diag*100, diag*100, 5.0, 5.0])
            p_scaling = np.array([diag * 2.0, diag * 2.0, 1.0, 1.0])
            reg.add_initial_transform(id_trans, param_scaling=p_scaling)

            #OPTION: in addition to the ID transform, add a bunch of random starting points
            add_multiple_startpts(reg, count=20, p_scaling=p_scaling)


#            #OPTION: Starting from gridmax already found
#            if not (slide, roi_idx) in grid_params:
#                print(f'Grid results not found for slide {slide}, region {roi_idx}')
#                continue
#            starting_params = grid_params[(slide, roi_idx)]
#            s_trans = transforms.ScalingTransform(2, uniform=True)
#            s_trans.set_params(starting_params[0])
#            r_trans = transforms.Rigid2DTransform()
#            r_trans.set_params(starting_params[1:4])
#            starting_trans = transforms.CompositeTransform(2, [s_trans, r_trans])
#            reg.add_initial_transform(starting_trans, param_scaling=p_scaling)

        reg.set_report_freq(250)

        # Create output directory
        directory = os.path.dirname("./tmp/")
        if not os.path.exists(directory):
            os.makedirs(directory)

        # Start the pre-processing
        reg.initialize("./tmp/", norm=norm)

        # Start the registration
        reg.run(verbose=True)

        # Get the results and find the best one (for the case when there
        # was more than one starting point)
        out_transforms, values = reg.get_outputs()
        transform = out_transforms[np.argmin(values)]
        value = np.min(values)
        successFlag = reg.get_flags()
        if len(successFlag) == 0:
            successFlag = 'N/A'
        else:
            #Use the optimizer flag for the best output transform found. SuccessFlag
            #has one result for each pyramid level, just take the last level.
            successFlag = successFlag[np.argmin(values)][-1]

        ### Warp final image
        c = transforms.make_image_centered_transform(transform, ref_im, flo_im)

        #       # Print out transformation parameters
        #        print('Transformation parameters: %s.' % str(transform.get_params()))

        # Create the output image
        im_warped = np.zeros(ref_im.shape)

        # Transform the floating image into the reference image space by applying transformation 'c'
        c.warp(In=flo_im, Out=im_warped, mode='nearest', bg_value=0.0)

        # Show the images we ended up with
        if False:
            print("Aligned images for sample %s, region %s" % (slide, roi_idx))
            plt.figure(figsize=(12, 6))
            plt.subplot(121)
            plt.imshow(ref_im, cmap='gray', vmin=0, vmax=1)
            plt.title("Reference image")
            plt.subplot(122)
            plt.imshow(im_warped, cmap='gray', vmin=0, vmax=1)
            plt.title("Floating image")
            plt.show()

        centred_gt_trans = transforms.make_image_centered_transform(rndTrans, \
                                                 ref_im, flo_im)

        gtVal = reg.get_value_at(rndTrans)
        err = get_transf_error(c, centred_gt_trans, flo_im.shape)
        print(
            "Estimated transform:\t [",
            ','.join(['%.4f'] * len(c.get_params())) % tuple(c.get_params()) +
            "] with value %.4f" % (value))
        print(
            "True transform:\t\t [",
            ','.join(['%.4f'] * len(rndTrans.get_params())) %
            tuple(rndTrans.get_params()) + "] with value %.4f" % (gtVal))
        print("Average corner error: %5f" % (err / 4))
        print("Value difference: %.5f" % (gtVal - value))
        #        print("Improvement over gridmax: %.5f"%(-value - float(starting_params[-1])))

        resultLine = (slide, roi_idx, *rndTrans.get_params(), \
                      gtVal, \
                      *c.get_params(), \
                      value, \
                      err, successFlag, \
                      time.strftime('%Y-%m-%d %H:%M:%S'))
        results.append(resultLine)
        with open(outfile, 'a') as f:
            writer = csv.writer(f, delimiter=',')
            writer.writerow(resultLine)
Example #7
0
def centreAndApplyTransform(image, transform, outsize, mode='nearest'):
    transformed_im = np.zeros(outsize, image.dtype)
    t = transforms.make_image_centered_transform(transform, \
                                             transformed_im, image)
    t.warp(In=image, Out=transformed_im, mode=mode, bg_value=0)
    return transformed_im
Example #8
0
def register_aamd(ref_im,
                  flo_im,
                  iterations=1.0,
                  param_sampling_fraction=0.01,
                  param_multi_start=True,
                  do_sigmoid=False):
    #    def _no_report_callback(opt):
    #        pass
    np.random.seed(1000)
    # The number of iterations

    param_iterations = [
        int(iterations * 3000),
        int(iterations * 1000),
        int(iterations * 200)
    ]  # 500 -> 200?

    #    do_grayscale = False
    #
    #    if do_grayscale == True:
    #        ref_im = io.imread(ref_im_path, as_gray=True)
    #        flo_im = io.imread(flo_im_path, as_gray=True)
    #        ref_im = np.squeeze(ref_im)
    #        flo_im = np.squeeze(flo_im)
    #        ref_im = ref_im.reshape(ref_im.shape + (1,))
    #        flo_im = flo_im.reshape(flo_im.shape + (1,))
    #    else:
    #        ref_im = io.imread(ref_im_path, as_gray=False)
    #        flo_im = io.imread(flo_im_path, as_gray=False)
    #        ref_im = np.squeeze(ref_im)
    #        flo_im = np.squeeze(flo_im)
    #        if ref_im.ndim == 2:
    #            ref_im = ref_im.reshape(ref_im.shape + (1,))
    #        if flo_im.ndim == 2:
    #            flo_im = flo_im.reshape(flo_im.shape + (1,))
    #
    #    print(ref_im.shape)
    #    print(flo_im.shape)
    if ref_im.ndim == 2:
        ref_im = np.expand_dims(ref_im, axis=-1)
    if flo_im.ndim == 2:
        flo_im = np.expand_dims(flo_im, axis=-1)

    flo_mask = None

    ch = ref_im.shape[-1]

    #    ref_im_orig = ref_im.copy()

    ref_im = filters.channels_to_list(ref_im)
    flo_im = filters.channels_to_list(flo_im)

    #weights1 = generators.make_circular_hann_window_like_image(ref_im[0], rad_factor = 1.0, spacing=None, p=0.25)
    weights1 = np.ones(ref_im[0].shape)
    mask1 = np.ones(ref_im[0].shape, 'bool')
    #weights2 = generators.make_circular_hann_window_like_image(flo_im[0], rad_factor = 1.0, spacing=None, p=0.25)
    weights2 = np.ones(flo_im[0].shape)
    if flo_mask is None:
        mask2 = np.ones(flo_im[0].shape, 'bool')
    else:
        mask2 = (flo_mask >= 0.5)

    # Save copies of original images
#    flo_im_orig = flo_im.copy()

    def inv_sigmoid(x):
        return np.log((x + 1e-7) / (1 - x + 1e-7))

    def sigmoid(x):
        return 1.0 / (1.0 + np.exp(x))

    if do_sigmoid:
        for k in range(ch):
            ref_im[k] = sigmoid(ref_im[k])
            flo_im[k] = sigmoid(flo_im[k])
    else:
        for k in range(ch):
            ref_im[k] = filters.normalize(ref_im[k], 0.0, None)
            flo_im[k] = filters.normalize(flo_im[k], 0.0, mask2)

    diag = 0.5 * (transforms.image_diagonal(ref_im[0], spacing) +
                  transforms.image_diagonal(flo_im[0], spacing))

    # Initialize registration framework for 2d images
    reg = RegisterMultiChannel(2)

    reg.set_report_freq(param_report_freq)
    #    reg.set_report_func(_no_report_callback)
    reg.set_alpha_levels(alpha_levels)
    reg.set_channel_mode(param_channel_mode)

    reg.set_reference_image(ref_im, spacing)
    reg.set_reference_mask(mask1)
    reg.set_reference_weights(weights1)

    reg.set_floating_image(flo_im, spacing)
    reg.set_floating_mask(mask2)
    reg.set_floating_weights(weights2)

    reg.set_squared_measure(squared_measure)

    # Setup the Gaussian pyramid resolution levels
    if iterations < 1:
        reg.add_pyramid_level(4, 7.0)
        reg.add_pyramid_level(2, 3.0)
        reg.add_pyramid_level(1, 1.0)
    else:
        reg.add_pyramid_level(4, 12.0)
        reg.add_pyramid_level(2, 5.0)
        reg.add_pyramid_level(1, 1.0)

    # Learning-rate / Step lengths [[start1, end1], [start2, end2] ...] (for each pyramid level)
    step_lengths = np.array([[1.0, 1.0], [1.0, 1.0], [1.0, 0.1]
                             ]) * 2.0  #* 5e-2

    scale = 0.1 / diag
    tscale = 5.0

    transform_count = 1

    t = Rigid2DTransform()
    reg.add_initial_transform(t, np.array([scale, tscale, tscale]))

    if param_multi_start:
        t = Rigid2DTransform()
        t.set_param(0, -0.4)
        reg.add_initial_transform(t, np.array([scale, tscale, tscale]))
        t = Rigid2DTransform()
        t.set_param(0, 0.4)
        reg.add_initial_transform(t, np.array([scale, tscale, tscale]))
        transform_count += 2

    # Set the parameters
    reg.set_iterations(param_iterations)
    reg.set_gradient_magnitude_threshold(0.0001)
    reg.set_sampling_fraction(param_sampling_fraction)
    reg.set_step_lengths(step_lengths)
    reg.set_optimizer('sgd')

    # Start the pre-processing
    reg.initialize('./test_images/output/')

    # Control the formatting of numpy
    np.set_printoptions(suppress=True, linewidth=200)

    # Start the registration
    reg.run()

    ts = []
    tval = []
    for i in range(transform_count):
        ti, vi = reg.get_output(i)
        ts.append(ti)
        tval.append(vi)
    transform, value = find_best_transform(ts, tval)

    c = transforms.make_image_centered_transform(transform, ref_im[0],
                                                 flo_im[0], spacing, spacing)

    # Create the output image
    ref_im_warped = [np.zeros(ref_im[i].shape) for i in range(ch)]
    ref_im_copied = [np.zeros(ref_im[i].shape) for i in range(ch)]

    # Transform the floating image into the reference image space by applying transformation 'c'
    for k in range(ch):
        ref_im_copied[k] = ref_im[k]
        c.warp(In=flo_im[k],
               Out=ref_im_warped[k],
               in_spacing=spacing,
               out_spacing=spacing,
               mode='linear',
               bg_value=0.0)

    ref_im_copied = np.squeeze(filters.list_to_channels(ref_im_copied))
    ref_im_warped = np.squeeze(filters.list_to_channels(ref_im_warped))

    return ref_im_warped, c