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
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
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)
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
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