def compute_demons_step(self, forward_step=True): r"""Demons step for SSD metric Computes the demons step proposed by Vercauteren et al.[Vercauteren09] for the SSD metric. Parameters ---------- forward_step : boolean if True, computes the Demons step in the forward direction (warping the moving towards the static image). If False, computes the backward step (warping the static image to the moving image) Returns ------- displacement : array, shape (R, C, 2) or (S, R, C, 3) the Demons step References ---------- [Vercauteren09] Tom Vercauteren, Xavier Pennec, Aymeric Perchant, Nicholas Ayache, "Diffeomorphic Demons: Efficient Non-parametric Image Registration", Neuroimage 2009 """ sigma_reg_2 = np.sum(self.static_spacing**2)/self.dim if forward_step: gradient = self.gradient_static delta_field = self.static_image - self.moving_image else: gradient = self.gradient_moving delta_field = self.moving_image - self.static_image if self.dim == 2: step, self.energy = ssd.compute_ssd_demons_step_2d(delta_field, gradient, sigma_reg_2, None) else: step, self.energy = ssd.compute_ssd_demons_step_3d(delta_field, gradient, sigma_reg_2, None) for i in range(self.dim): step[..., i] = ndimage.filters.gaussian_filter(step[..., i], self.smooth) return step
def test_compute_ssd_demons_step_2d(): r""" Compares the output of the demons step in 2d against an analytical step. The fixed image is given by $F(x) = \frac{1}{2}||x - c_f||^2$, the moving image is given by $G(x) = \frac{1}{2}||x - c_g||^2$, $x, c_f, c_g \in R^{2}$ References ---------- [Vercauteren09] Vercauteren, T., Pennec, X., Perchant, A., & Ayache, N. (2009). Diffeomorphic demons: efficient non-parametric image registration. NeuroImage, 45(1 Suppl), S61-72. doi:10.1016/j.neuroimage.2008.10.040 """ #Select arbitrary images' shape (same shape for both images) sh = (20, 10) #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 np.random.seed(1137271) grad_F = X - c_f grad_G = X - c_g Fnoise = np.random.ranf(np.size(grad_F)).reshape(grad_F.shape) * grad_F.max() * 0.1 Fnoise = Fnoise.astype(floating) grad_F += Fnoise Gnoise = np.random.ranf(np.size(grad_G)).reshape(grad_G.shape) * grad_G.max() * 0.1 Gnoise = Gnoise.astype(floating) grad_G += Gnoise #The squared norm of grad_G to be used later sq_norm_grad_G = np.sum(grad_G**2,-1) #Compute F and G F = 0.5*np.sum(grad_F**2,-1) G = 0.5*sq_norm_grad_G Fnoise = np.random.ranf(np.size(F)).reshape(F.shape) * F.max() * 0.1 Fnoise = Fnoise.astype(floating) F += Fnoise Gnoise = np.random.ranf(np.size(G)).reshape(G.shape) * G.max() * 0.1 Gnoise = Gnoise.astype(floating) G += Gnoise delta_field = np.array(G - F, dtype = floating) #Select some pixels to force gradient = 0 and F=G random_labels = np.random.randint(0, 2, sh[0]*sh[1]) random_labels = random_labels.reshape(sh) F[random_labels == 0] = G[random_labels == 0] delta_field[random_labels == 0] = 0 grad_G[random_labels == 0, ...] = 0 sq_norm_grad_G[random_labels == 0, ...] = 0 #Set arbitrary values for $\sigma_i$ (eq. 4 in [Vercauteren09]) #The original Demons algorithm used simply |F(x) - G(x)| as an #estimator, so let's use it as well sigma_i_sq = (F - G)**2 #Now select arbitrary parameters for $\sigma_x$ (eq 4 in [Vercauteren09]) for sigma_x_sq in [0.01, 1.5, 4.2]: #Directly compute the demons step according to eq. 4 in [Vercauteren09] num = (sigma_x_sq * (F - G))[random_labels == 1] den = (sigma_x_sq * sq_norm_grad_G + sigma_i_sq)[random_labels == 1] expected = (-1 * np.array(grad_G)) #This is $J^{P}$ in eq. 4 [Vercauteren09] expected[random_labels == 1, 0] *= num / den expected[random_labels == 1, 1] *= num / den expected[random_labels == 0, ...] = 0 #Now compute it using the implementation under test actual = np.empty_like(expected, dtype=floating) ssd.compute_ssd_demons_step_2d(delta_field, np.array(grad_G, dtype=floating), sigma_x_sq, actual) assert_array_almost_equal(actual, expected)