def test_compute_energy_ssd_2d(): sh = (32, 32) #Select arbitrary centers c_f = np.asarray(sh)/2 c_g = c_f + 0.5 #Compute the identity vector field I(x) = x in R^2 x_0 = np.asarray(range(sh[0])) x_1 = np.asarray(range(sh[1])) X = np.ndarray(sh + (2,), dtype = np.float64) O = np.ones(sh) X[...,0]= x_0[:, None] * O X[...,1]= x_1[None, :] * O #Compute the gradient fields of F and G grad_F = X - c_f grad_G = X - c_g #Compute F and G F = 0.5*np.sum(grad_F**2,-1) G = 0.5*np.sum(grad_G**2,-1) # Note: this should include the energy corresponding to the # regularization term, but it is discarded in ANTS (they just # consider the data term, which is not the objective function # being optimized). This test case should be updated after # further investigation expected = ((F - G)**2).sum() actual = ssd.compute_energy_ssd_2d(np.array(F-G, dtype = floating)) assert_almost_equal(expected, actual)
def v_cycle_2d(n, k, delta_field, sigma_sq_field, gradient_field, target, lambda_param, displacement, depth=0): r"""Multi-resolution Gauss-Seidel solver using V-type cycles Multi-resolution Gauss-Seidel solver: solves the Gauss-Newton linear system by first filtering (GS-iterate) the current level, then solves for the residual at a coarser resolution and finally refines the solution at the current resolution. This scheme corresponds to the V-cycle proposed by Bruhn and Weickert[Bruhn05]. Parameters ---------- n : int number of levels of the multi-resolution algorithm (it will be called recursively until level n == 0) k : int the number of iterations at each multi-resolution level delta_field : array, shape (R, C) the difference between the static and moving image (the 'derivative w.r.t. time' in the optical flow model) sigma_sq_field : array, shape (R, C) the variance of the gray level value at each voxel, according to the EM model (for SSD, it is 1 for all voxels). Inf and 0 values are processed specially to support infinite and zero variance. gradient_field : array, shape (R, C, 2) the gradient of the moving image target : array, shape (R, C, 2) right-hand side of the linear system to be solved in the Weickert's multi-resolution algorithm lambda_param : float smoothness parameter, the larger its value the smoother the displacement field displacement : array, shape (R, C, 2) the displacement field to start the optimization from Returns ------- energy : the energy of the EM (or SSD if sigmafield[...]==1) metric at this iteration References ---------- [Bruhn05] Andres Bruhn and Joachim Weickert, "Towards ultimate motion estimation: combining highest accuracy with real-time performance", 10th IEEE International Conference on Computer Vision, 2005. ICCV 2005. """ # pre-smoothing for i in range(k): ssd.iterate_residual_displacement_field_ssd_2d(delta_field, sigma_sq_field, gradient_field, target, lambda_param, displacement) if n == 0: energy = ssd.compute_energy_ssd_2d(delta_field) return energy # solve at coarser grid residual = None residual = ssd.compute_residual_displacement_field_ssd_2d( delta_field, sigma_sq_field, gradient_field, target, lambda_param, displacement, residual) sub_residual = np.array(vfu.downsample_displacement_field_2d(residual)) del residual subsigma_sq_field = None if sigma_sq_field is not None: subsigma_sq_field = vfu.downsample_scalar_field_2d(sigma_sq_field) subdelta_field = vfu.downsample_scalar_field_2d(delta_field) subgradient_field = np.array( vfu.downsample_displacement_field_2d(gradient_field)) shape = np.array(displacement.shape).astype(np.int32) half_shape = ((shape[0] + 1) // 2, (shape[1] + 1) // 2, 2) sub_displacement = np.zeros(shape=half_shape, dtype=floating) sublambda_param = lambda_param * 0.25 v_cycle_2d(n - 1, k, subdelta_field, subsigma_sq_field, subgradient_field, sub_residual, sublambda_param, sub_displacement, depth + 1) # displacement += np.array( # vfu.upsample_displacement_field(sub_displacement, shape)) displacement += vfu.resample_displacement_field_2d(sub_displacement, np.array([0.5, 0.5]), shape) # post-smoothing for i in range(k): ssd.iterate_residual_displacement_field_ssd_2d(delta_field, sigma_sq_field, gradient_field, target, lambda_param, displacement) energy = ssd.compute_energy_ssd_2d(delta_field) return energy
def v_cycle_2d(n, k, delta_field, sigma_sq_field, gradient_field, target, lambda_param, displacement, depth=0): r"""Multi-resolution Gauss-Seidel solver using V-type cycles Multi-resolution Gauss-Seidel solver: solves the Gauss-Newton linear system by first filtering (GS-iterate) the current level, then solves for the residual at a coarser resolution and finally refines the solution at the current resolution. This scheme corresponds to the V-cycle proposed by Bruhn and Weickert[Bruhn05]. Parameters ---------- n : int number of levels of the multi-resolution algorithm (it will be called recursively until level n == 0) k : int the number of iterations at each multi-resolution level delta_field : array, shape (R, C) the difference between the static and moving image (the 'derivative w.r.t. time' in the optical flow model) sigma_sq_field : array, shape (R, C) the variance of the gray level value at each voxel, according to the EM model (for SSD, it is 1 for all voxels). Inf and 0 values are processed specially to support infinite and zero variance. gradient_field : array, shape (R, C, 2) the gradient of the moving image target : array, shape (R, C, 2) right-hand side of the linear system to be solved in the Weickert's multi-resolution algorithm lambda_param : float smoothness parameter, the larger its value the smoother the displacement field displacement : array, shape (R, C, 2) the displacement field to start the optimization from Returns ------- energy : the energy of the EM (or SSD if sigmafield[...]==1) metric at this iteration References ---------- [Bruhn05] Andres Bruhn and Joachim Weickert, "Towards ultimate motion estimation: combining highest accuracy with real-time performance", 10th IEEE International Conference on Computer Vision, 2005. ICCV 2005. """ # pre-smoothing for i in range(k): ssd.iterate_residual_displacement_field_ssd_2d(delta_field, sigma_sq_field, gradient_field, target, lambda_param, displacement) if n == 0: energy = ssd.compute_energy_ssd_2d(delta_field) return energy # solve at coarser grid residual = None residual = ssd.compute_residual_displacement_field_ssd_2d(delta_field, sigma_sq_field, gradient_field, target, lambda_param, displacement, residual) sub_residual = np.array(vfu.downsample_displacement_field_2d(residual)) del residual subsigma_sq_field = None if sigma_sq_field is not None: subsigma_sq_field = vfu.downsample_scalar_field_2d(sigma_sq_field) subdelta_field = vfu.downsample_scalar_field_2d(delta_field) subgradient_field = np.array( vfu.downsample_displacement_field_2d(gradient_field)) shape = np.array(displacement.shape).astype(np.int32) half_shape = ((shape[0] + 1) // 2, (shape[1] + 1) // 2, 2) sub_displacement = np.zeros(shape=half_shape, dtype=floating) sublambda_param = lambda_param*0.25 v_cycle_2d(n-1, k, subdelta_field, subsigma_sq_field, subgradient_field, sub_residual, sublambda_param, sub_displacement, depth+1) # displacement += np.array( # vfu.upsample_displacement_field(sub_displacement, shape)) displacement += vfu.resample_displacement_field_2d(sub_displacement, np.array([0.5, 0.5]), shape) # post-smoothing for i in range(k): ssd.iterate_residual_displacement_field_ssd_2d(delta_field, sigma_sq_field, gradient_field, target, lambda_param, displacement) energy = ssd.compute_energy_ssd_2d(delta_field) return energy