def derivative(self, displacement): """Derivative of the operator at ``displacement``. Parameters ---------- displacement : `domain` `element-like` Point at which the derivative is computed. Returns ------- derivative : `PointwiseInner` The derivative evaluated at ``displacement``. """ # To implement the complex case we need to be able to embed the real # vector field space into the range of the gradient. Issue #59. if not self.range.is_rn: raise NotImplementedError('derivative not implemented for complex ' 'spaces.') displacement = self.domain.element(displacement) # TODO: allow users to select what method to use here. grad = Gradient(domain=self.range, method='central', pad_mode='symmetric') grad_templ = grad(self.template) def_grad = self.domain.element( [_linear_deform(gf, displacement) for gf in grad_templ]) return PointwiseInner(self.domain, def_grad)
def __init__(self, DomainField, Ntrans, kernel, partialderdim1kernel, partialder2kernel): """Initialize a new instance. DomainField : space on wich vector fields will be defined Ntrans : number of translations Kernel : kernel partialderdim1kernel : partial derivative with respect to one componenet of the 1st component, to be used as partialder2kernel(x, y, d) where d is in [|0, dim-1|] partialder2kernel : partial derivative with respect to 2nd component, to be used as partialder2kernel(x, y, u) with x and y points, and u vectors for differentiation (same number of vectors as the number of points for y) """ self.Ntrans = Ntrans self.kernel = kernel self.partialderdim1kernel = partialderdim1kernel self.partialder2kernel = partialder2kernel self.dim = DomainField.ndim self.get_unstructured_op = usefun.get_from_structured_to_unstructured( DomainField, kernel) self.gradient_op = Gradient(DomainField) GDspace = odl.ProductSpace(odl.space.rn(self.dim), self.Ntrans) Contspace = odl.ProductSpace(odl.space.rn(self.dim), self.Ntrans) self.dimCont = self.Ntrans * self.dim super().__init__(GDspace, Contspace, DomainField)
def _call(self, X): """ Shooting equations and Integration Args: X = [I0, p] with I0 template image and p scalarvalued momentum Returns: I: deformed Image over time (N+1 timesteps) M: vectorvalued momentum in eulerian coordinates over time V: vectorfields over time """ template = X[0] P0 = X[1] series_image_space_integration = ProductSpace(template.space, self.N + 1) series_vector_space_integration = ProductSpace( template.space.tangent_bundle, self.N + 1) inv_N = 1 / self.N I = series_image_space_integration.element() I[0] = template.copy() P = series_image_space_integration.element() P[0] = P0.copy() U = series_vector_space_integration.element() # Create the gradient op grad_op = Gradient(domain=self.space, method='forward', pad_mode='symmetric') # Create the divergence op div_op = -grad_op.adjoint for i in range(self.N): # compute vectorvalued momentum from scalarvalued momentum gradI = grad_op(I[i]) m = self.space.tangent_bundle.element(-P[i] * gradI) # Kernel convolution to obtain velocity fields from momentum # u = K*m U[i] = (2 * np.pi)**( self.dim / 2.0) * self.vectorial_ft_fit_op.inverse( self.vectorial_ft_fit_op(m) * self.ft_kernel_fitting) # Integration step dtI = -sum(gradI * U[i]) dtP = -div_op(P[i] * U[i]) I[i + 1] = I[i] + inv_N * dtI P[i + 1] = P[i] + inv_N * dtP U[self.N] = (2 * np.pi)**( self.dim / 2.0) * self.vectorial_ft_fit_op.inverse( self.vectorial_ft_fit_op( self.space.tangent_bundle.element(-P[self.N] * gradI)) * self.ft_kernel_fitting) return I, P, U
def ConvolveIntegrate(self,grad_S_init,H,j0,vector_field_list,zeta_list): dim = self.image_domain.ndim k_j=self.k_j_list[j0] h=odl.ProductSpace(self.image_domain.tangent_bundle,k_j+1).zero() eta=odl.ProductSpace(self.image_domain,k_j+1).zero() grad_op = Gradient(domain=self.image_domain, method='forward', pad_mode='symmetric') # Create the divergence op div_op = -grad_op.adjoint delta0=self.data_time_points[j0] -(k_j/self.N) detDphi=self.image_domain.element( 1+delta0 * div_op(vector_field_list[k_j])).copy() grad_S=self.image_domain.element( linear_deform(grad_S_init, delta0 * vector_field_list[k_j])).copy() if (not delta0==0): tmp=H[k_j].copy() tmp1=(grad_S * detDphi).copy() for d in range(dim): tmp[d] *= tmp1 tmp3=(2 * np.pi) ** (dim / 2.0) * self.vectorial_ft_fit_op.inverse(self.vectorial_ft_fit_op(tmp) * self.ft_kernel_fitting) h[k_j]+=tmp3.copy() eta[k_j]+=detDphi*grad_S delta_t= self.inv_N for u in range(k_j): k=k_j -u-1 detDphi=self.image_domain.element( linear_deform(detDphi, delta_t*vector_field_list[k])).copy() detDphi=self.image_domain.element(detDphi* self.image_domain.element(1+delta_t * div_op(vector_field_list[k]))).copy() grad_S=self.image_domain.element( linear_deform(grad_S, delta_t * vector_field_list[k])).copy() tmp=H[k].copy() tmp1=(grad_S * detDphi).copy() for d in range(dim): tmp[d] *= tmp1.copy() tmp3=(2 * np.pi) ** (dim / 2.0) * self.vectorial_ft_fit_op.inverse(self.vectorial_ft_fit_op(tmp) * self.ft_kernel_fitting) h[k]+=tmp3.copy() eta[k]+=detDphi*grad_S return [h,eta]
def __init__(self, DomainField, GDspace, NbGD, kernel, partialderdim1kernel, partialder2kernel, pointfunctionlist, pointfunctiondifflist, vectorfunctionlist, vectorfunctiondifflist): """Initialize a new instance. DomainField : space on wich vector fields will be defined dimGD = dimension of GDspace Kernel : kernel partialderdim1kernel : partial derivative with respect to one componenet of the 1st component, to be used as partialder2kernel(x, y, d) where d is in [|0, dim-1|] partialder2kernel : partial derivative with respect to 2nd component, to be used as partialder2kernel(x, y, u) with x and y points, and u vectors for differentiation (same number of vectors as the number of points for y) get_columnvector_gd : function that takes a GD and return a vector so that when we multiply it by a point/vectorfunctiondiff we obtain its differential get_spaceelement_gd : function, inverse of get_columnvector_gd (takes a vector and return the corresponding GD) pointfunctionlist : list of functions, each one associating a list of points to a geometrical descriptor pointfunctiondifflist : list of functions, each one associating a list of matrix to a geometrical descriptor, the k-th matrix is the differential of the k-th point with respect to the geometrical descriptor vectorfunctionlist : list of functions, each one associating a list of vectors to a geometrical descriptor vectorfunctiondifflist : list of functions, each one associating a list of matrix to a geometrical descriptor, the k-th matrix is the differential of the k-th vector with respect to the geometrical descriptor """ self.kernel = kernel self.partialderdim1kernel = partialderdim1kernel self.partialder2kernel = partialder2kernel self.dim = DomainField.ndim self.NbGD = NbGD self.dimGD = NbGD * self.dim self.get_unstructured_op = usefun.get_from_structured_to_unstructured( DomainField, kernel) self.gradient_op = Gradient(DomainField) self.ptfun = pointfunctionlist self.vecfun = vectorfunctionlist self.ptfundiff = pointfunctiondifflist self.vecfundiff = vectorfunctiondifflist self.dimCont = len(pointfunctionlist) Contspace = odl.space.rn(self.dimCont) super().__init__(GDspace, Contspace, DomainField)
def __init__(self, DomainField, inv_N): """Initialize a new instance. DomainField : space to which GD belongs invN : used for small differences for applying vector field (supposed to be stepsize for integration) """ self.dim = DomainField.ndim self.inv_N = inv_N self.grad_op = Gradient(DomainField) GDspace = DomainField Contspace = odl.space.rn(0) self.dimCont = 0 super().__init__(GDspace, Contspace, DomainField)
def _call(self, X): """ Compute Gradient of the Energy functional Gradient of regularization term norm(v): K*m Gradient of data term: mhat """ dim=2 Imv = self.operator.Shooting(X) I = Imv[0] m = Imv[1] v = Imv[2] N = I.shape[0] - 1 mhat = m[0].space.zero() Ihat = self.operator.reg_param * self.operator.attach.gradient(I[-1]) # Create the gradient op grad_op = Gradient(domain=self.operator.space, method='forward',pad_mode='constant', pad_const = 0) # Create the divergence op div_op = -grad_op.adjoint for i in range(N): gradmhat0 = grad_op(mhat[0]) gradmhat1 = grad_op(mhat[1]) gradmhat = [gradmhat0, gradmhat1] gradI = grad_op(I[N-i]) coad0 = sum(grad_op(m[N-i][0])*mhat) + sum([gradmhat[j][0] * m[N-i][j] for j in range(dim)]) + div_op(mhat)*m[N-i][0] coad1 = sum(grad_op(m[N-i][1])*mhat) + sum([gradmhat[j][1] * m[N-i][j] for j in range(dim)]) + div_op(mhat)*m[N-i][1] coad_mhat_m = mhat.space.element([coad0, coad1]) vhat = (2 * np.pi) ** (dim / 2.0) * self.vectorial_ft_fit_op.inverse(self.vectorial_ft_fit_op(-coad_mhat_m + Ihat * gradI) * self.ft_kernel_fitting) Ihat = Ihat + 1/N * div_op(Ihat * v[N-i]) advm0 = sum(grad_op(v[N-i][0])*mhat) - sum(grad_op(mhat[0])*v[N-i]) advm1 = sum(grad_op(v[N-i][1])*mhat) - sum(grad_op(mhat[1])*v[N-i]) advm = mhat.space.element([advm0, advm1]) mhat = mhat - 1/N*(advm + vhat) Km = (2 * np.pi) ** (dim / 2.0) * self.vectorial_ft_fit_op.inverse(self.vectorial_ft_fit_op(m[0]) * self.ft_kernel_fitting) return Km + mhat
def _call(self, X): vector_field_list=X[0] zeta_list=X[1] h=vector_field_list.space.zero() eta=zeta_list.space.zero() # Compute the metamorphosis at the data time points # zeta_transp [k] = zeta_list [k] \circ \varphi_{\tau_k} zeta_transp=ShootSourceTermBackwardlist(vector_field_list, zeta_list).copy() image_list=functional.ComputeMetamorphosisFromZetaTransp(vector_field_list,zeta_transp).copy() template_deformation=ShootTemplateFromVectorFields(vector_field_list,functional.template).copy() # Computation of G_k=\nabla (template \circ \phi_{\tau_k}^-1) for each k G=odl.ProductSpace(functional.image_domain.tangent_bundle, functional.N +1).element() grad_op = Gradient(domain=functional.image_domain, method='forward', pad_mode='symmetric') for k in range(functional.N +1): G[k]=grad_op(template_deformation[k]).copy() # Addition of \int_0^\tau_k \nabla (\zeta(\tau) \circ \phi_{\tau_k , \tau}) d\tau for k in range(functional.N +1): #G[k]+= (functional.inv_N * grad_op(zeta_list[k])).copy() for q in range(k): p=k-1-q temp=ShootTemplateFromVectorFieldsFinal(vector_field_list,zeta_transp[p],0,k).copy() G[k]+= (functional.inv_N * grad_op(temp)).copy() for j in range(functional.nb_data): conv=functional.ConvolveIntegrate((functional.Norm_list[j]*(functional.forward_op[j] - functional.data[j])).gradient(image_list[j]),G,j,vector_field_list,zeta_list).copy() #conv=functional.ConvolveIntegrate(functional.S[j].gradient(image_list[j]),G,j,vector_field_list,zeta_list).copy() for k in range(functional.k_j_list[j]): h[k]-=conv[0][k].copy() eta[k]+=conv[1][k].copy() for k in range(functional.N): h[k]+=2*functional.lamb*vector_field_list[k] eta[k]+=2*functional.tau*zeta_list[k] return [h,eta]
def LDDMM_Beg_solver(template, reference, time_pts, niter, eps, lamb, kernel, callback=None): """ Solver for the shape-based reconstruction using LDDMM_Beg. Notes ----- The model is: .. math:: \min_{v(t) \in V, \phi_{0,1}^v \in G_v} \lambda \int_0^1 \|v(t)\|_V^2 dt + \int_\Omega |(\phi_{0,1}^v.I_0) - I_1|^2 dx Here :math:`I_0` is the template. :math:`v(t)` is the velocity vector. :math:`V` is a reproducing kernel Hilbert space for the velocity vector. :math:`I_1` is the reference. :math:`\lambda` is the regularization parameter. :math:`\phi_{0, 1}^v.I := I \circ \phi_{1, 0}^v` is for geometric deformation, :math:`\phi_{1, 0}^v` is the inverse of :math:`\phi_{0, 1}^v` i.e. the solution at :math:`t=1` of flow of diffeomorphisms. :math:`|D\phi_{1, 0}^v|` is the Jacobian determinant of :math:`\phi_{1, 0}^v`. Parameters ---------- template : `DiscreteLpElement` Fixed template. reference: `DiscreteLpElement` Fixed reference. time_pts : `int` The number of time intervals niter : `int` The given maximum iteration number. eps : `float` The given step size. lamb : `float` The given regularization parameter. It's a weighted value on the regularization-term side. kernel : `function` Kernel function in reproducing kernel Hilbert space. callback : `class`, optional Show the intermediate results of iteration. Returns ------- image_N0 : `ProductSpaceElement` The series of images produced by template and velocity field. E : `numpy.array` Storage of the energy values for iterations. """ # Give the number of time intervals N = time_pts # Get the inverse of time intervals inv_N = 1.0 / N # Create the space of images image_space = template.space #image_space= rec_space # Get the dimension of the space of images dim = image_space.ndim # Fourier transform setting for data matching term # The padded_size is the size of the padded domain padded_size = 2 * image_space.shape[0] # Create operator of Fourier transform composing with padded operator pad_ft_op = _padded_ft_op(image_space, padded_size) # Create vectorial Fourier transform operator # Construct the diagnal element of a matrix operator vectorial_ft_op = DiagonalOperator(*([pad_ft_op] * dim)) # Compute the FT of kernel in fitting term discretized_kernel = _vectorized_kernel(image_space, kernel) ft_kernel_fitting = vectorial_ft_op(discretized_kernel) # Create the space for series deformations and series Jacobian determinant pspace = image_space.tangent_bundle series_pspace = ProductSpace(pspace, N + 1) series_image_space = ProductSpace(image_space, N + 1) # Initialize vector fileds at different time points vector_fields = series_pspace.zero() # Give the initial two series deformations and series Jacobian determinant image_N0 = series_image_space.element() image_N1 = series_image_space.element() detDphi_N1 = series_image_space.element() for i in range(N + 1): image_N0[i] = image_space.element(template) image_N1[i] = image_space.element(reference) detDphi_N1[i] = image_space.one() # Create the gradient operator grad_op = Gradient(domain=image_space, method='forward', pad_mode='symmetric') # Create the divergence operator, which can be obtained from # the adjoint of gradient operator # div_op = Divergence(domain=pspace, method='forward', pad_mode='symmetric') div_op = -grad_op.adjoint # Store energy E = [] kE = len(E) E = np.hstack((E, np.zeros(niter))) # Begin iteration for k in range(niter): # Update the velocity field for i in range(N + 1): #first term term1_tmp1 = 2 * np.abs(image_N0[i] - image_N1[i]) * (detDphi_N1[i]) term1_tmp = grad_op(image_N0[i]) for j in range(dim): term1_tmp[j] *= term1_tmp1 tmp3 = (2 * np.pi)**(dim / 2.0) * vectorial_ft_op.inverse( vectorial_ft_op(term1_tmp) * ft_kernel_fitting) vector_fields[i] = (vector_fields[i] - eps * (lamb * vector_fields[i] - tmp3)) # Update image_N0,image_N1 and detDphi_N0 detDphi_N1 for i in range(N): # Update image_N0[i+1] by image_N0[i] and vector_fields[i+1] image_N0[i + 1] = image_space.element( _linear_deform(image_N0[i], -inv_N * vector_fields[i + 1])) # Update image_N1[N-i-1] by image_N1[N-i] and vector_fields[N-i-1] image_N1[N - i - 1] = image_space.element( _linear_deform(image_N1[N - i], inv_N * vector_fields[N - i - 1])) # # Update detDphi_N1[N-i-1] by detDphi_N1[N-i] # jacobian_det = image_domain.element( # np.exp(inv_N * div_o p(vector_fields[N-i-1]))) jacobian_det = image_space.element( 1.0 + inv_N * div_op(vector_fields[N - i - 1])) detDphi_N1[N - i - 1] = (jacobian_det * image_space.element( _linear_deform(detDphi_N1[N - i], inv_N * vector_fields[N - i - 1]))) # Update the deformed template PhiStarI = image_N0[N] # Show intermediate result if callback is not None: callback(PhiStarI) # Compute the energy of the data fitting term E[k + kE] += np.asarray((PhiStarI - reference)**2).sum() return image_N0, E
def _call(self, X): """ Shooting equations and Integration Args: X = [I0, m] with I0 template image and m vectorvalued momentum Returns: I: deformed Image over time (N+1 timesteps) M: vectorvalued momentum in eulerian coordinates over time V: vectorfields over time """ template = X[0].copy() m0 = X[1].copy() # create spaces for time-dependent Image and vectorfields series_image_space_integration = ProductSpace(template.space, self.N + 1) series_vector_space_integration = ProductSpace(m0.space, self.N + 1) inv_N = 1 / self.N I = series_image_space_integration.element() I[0] = template.copy() M = series_vector_space_integration.element() M[0] = m0.copy() U = series_vector_space_integration.element() # Create the gradient op grad_op = Gradient(domain=self.space, method='forward', pad_mode='symmetric') # Create the divergence op div_op = -grad_op.adjoint for i in range(self.N): m = M[i] # Kernel convolution to obtain vectorfields from momentum # v = K*m U[i] = (2 * np.pi)**( self.dim / 2.0) * self.vectorial_ft_fit_op.inverse( self.vectorial_ft_fit_op(m) * self.ft_kernel_fitting) u = U[i] # shooting equation # d/dt m = - ad*_v m gradu = [grad_op(u[0]), grad_op(u[1])] dtM0 = sum(grad_op(m[0]) * u) + sum( [gradu[i][0] * m[i] for i in range(self.dim)]) + div_op(u) * m[0] dtM1 = sum(grad_op(m[1]) * u) + sum( [gradu[i][1] * m[i] for i in range(self.dim)]) + div_op(u) * m[1] # Integration step M[i + 1] = M[i] - inv_N * m0.space.element([dtM0, dtM1]) I[i + 1] = odl.deform.linear_deform(I[i], -inv_N * u) U[self.N] = (2 * np.pi)**( self.dim / 2.0) * self.vectorial_ft_fit_op.inverse( self.vectorial_ft_fit_op(M[self.N]) * self.ft_kernel_fitting) return I, M, U
def infinitesimal_action_withgrad(v, I): grad_op = Gradient(I.space) grad = grad_op(I) return -sum(v * grad)
def LDDMM_gradient_descent_solver_spatiotemporal(forward_op, noise_proj_data, template, vector_fields, gate_pts, discr_deg, niter, in_niter1, in_inter2, stepsize1, stepsize2, mu_1, mu_2, lamb, kernel, impl1='geom', impl2='least_square', callback=None): """ Solver for spatiotemporal image reconstruction using LDDMM. Notes ----- The model is: .. math:: \min_{v} \lambda * \int_0^1 \|v(t)\|_V^2 dt + \|T(\phi_1.I) - g\|_2^2, where :math:`\phi_1.I := |D\phi_1^{-1}| I(\phi_1^{-1})` is for mass-preserving deformation, instead, :math:`\phi_1.I := I(\phi_1^{-1})` is for geometric deformation. :math:`\phi_1^{-1}` is the inverse of the solution at :math:`t=1` of flow of doffeomorphisms. :math:`|D\phi_1^{-1}|` is the Jacobian determinant of :math:`\phi_1^{-1}`. :math:`T` is the forward operator. If :math:`T` is an identity operator, the above model reduces to image matching. If :math:`T` is a non-identity forward operator, the above model is for shape-based image reconstrction. :math:`g` is the detected data, `data_elem`. :math:`I` is the `template`. :math:`v(t)` is the velocity vector. :math:`V` is a reproducing kernel Hilbert space for velocity vector. :math:`lamb` is the regularization parameter. Parameters ---------- forward_op : `Operator` The forward operator of imaging. data_elem : `DiscreteLpElement` The given data. template : `DiscreteLpElement` Fixed template deformed by the deformation. time_pts : `int` The number of time intervals iter : `int` The given maximum iteration number. eps : `float` The given step size. lamb : `float` The given regularization parameter. It's a weighted value on the regularization-term side. kernel : `function` Kernel function in reproducing kernel Hilbert space. impl1 : {'geom', 'mp'}, optional The given implementation method for group action. The impl1 chooses 'mp' or 'geom', where 'mp' means using mass-preserving method, and 'geom' means using non-mass-preserving geometric method. Its defalt choice is 'geom'. impl2 : {'least square'}, optional The given implementation method for data matching term. Here the implementation only supports the case of least square. callback : `class`, optional Show the intermediate results of iteration. Returns ------- image_N0 : `ProductSpaceElement` The series of images produced by template and velocity field. mp_deformed_image_N0 : `ProductSpaceElement` The series of mass-preserving images produced by template and velocity field. E : `numpy.array` Storage of the energy values for iterations. """ # Max index of discretized points N = gate_pts # Discretized degree M = discr_deg # Compute the max index of discretized points MN = M * N MN1 = MN + 1 # Get the inverse of the number of discretized points inv_MN = 1.0 / MN N1 = N + 1 N2 = 2. / N ss1 = stepsize1 * N2 ss2 = stepsize2 * N2 ss3 = stepsize1 * mu_1 # Create the gradient operator for the squared L2 functional if impl2 == 'least_square': gradS = [forward_op[0].adjoint * (forward_op[0] - noise_proj_data[0])] * N1 for i in range(N): j = i + 1 gradS[j] = forward_op[j].adjoint * (forward_op[j] - noise_proj_data[j]) else: raise NotImplementedError('now only support least square') # Create the space of images image_space = template.space # Get the dimension of the space of images dim = image_space.ndim # Fourier transform setting for data matching term # The padded_size is the size of the padded domain padded_size = 2 * image_space.shape[0] # The pad_ft_op is the operator of Fourier transform # composing with padded operator pad_ft_op = padded_ft_op(image_space, padded_size) # The vectorial_ft_op is a vectorial Fourier transform operator, # which constructs the diagnal element of a matrix. vectorial_ft_op = DiagonalOperator(*([pad_ft_op] * dim)) # Compute the FT of kernel in fitting term discretized_kernel = fitting_kernel(image_space, kernel) ft_kernel_fitting = vectorial_ft_op(discretized_kernel) # Create the space for series deformations and series Jacobian determinant series_image_space = ProductSpace(image_space, MN1) series_backprojection_space = ProductSpace(image_space, N1) series_bp_all = [image_space.element()] * N1 for i in range(N): j = i + 1 series_bp_all[j] = [image_space.element()] * (j * M + 1) # Initialize vector fileds at different time points vector_fields = vector_fields # Initialize two series deformations and series Jacobian determinant image_MN0 = series_image_space.element() if impl1 == 'geom': eta_tt = series_backprojection_space.element() else: raise NotImplementedError('unknown group action') for j in range(MN1): image_MN0[j] = image_space.element(template) eta_tt[0] = gradS[0](image_MN0[0]) series_bp_all[0] = eta_tt[0] for i in range(1, N1): iM = i * M eta_tt[i] = gradS[i](image_MN0[iM]) for j in range(iM + 1): series_bp_all[i][j] = eta_tt[i] # Create the gradient operator grad_op = Gradient(domain=image_space, method='forward', pad_mode='symmetric') # Create the divergence operator, which can be obtained from # the adjoint of gradient operator # div_op = Divergence(domain=pspace, method='forward', pad_mode='symmetric') grad_op_adjoint = grad_op.adjoint div_op = -grad_op.adjoint # Begin iteration for non-mass-preserving case if impl1 == 'geom': print(impl1) # Outer iteration for k in range(niter): print('iter = {!r}'.format(k)) #%%%Setting for getting a proper initial template # Inner iteration for updating template if k == 0: niter1 = 50 # niter1 = in_niter1 else: niter1 = in_niter1 #%%%Solving TV-L2 by Gradient Descent # Store energy E = [] E = np.hstack((E, np.zeros(niter1))) for k1 in range(niter1): image_MN0[0] = template # Update partial of template grad_template = grad_op(template) grad_template_norm = np.sqrt(grad_template[0]**2 + grad_template[1]**2 + 1.0e-12) E[k1] += mu_1 * np.asarray( grad_template_norm).sum() * template.space.cell_volume for i in range(1, N1): E[k1] += 1. / N * np.asarray((forward_op[i](image_MN0[i*M]) - noise_proj_data[i])**2).sum() \ * noise_proj_data[0].space.cell_volume template = template - \ ss3 * grad_op_adjoint(grad_template/grad_template_norm) for j in range(MN): temp1 = j + 1 # Update image_MN0 image_MN0[temp1] = image_space.element( _linear_deform(image_MN0[j], -inv_MN * vector_fields[temp1])) if temp1 % M == 0: temp2 = temp1 // M # print(temp1) # print(temp2) # Update eta_tt eta_tt[temp2] = gradS[temp2](image_MN0[temp1]) # eta_tt[temp2].show('eta_tt[{!r}]'.format(temp2)) series_bp_all[temp2][temp1] = eta_tt[temp2] # the above two lines can be combined into one # series_bp_all[temp2][temp1] = gradS[temp2](image_MN0[temp1]) for l in range(temp1): jacobian_det = image_space.element( 1.0 + inv_MN * div_op(vector_fields[temp1 - l - 1])) # Update eta_tau_tnp series_bp_all[temp2][temp1-l-1] = \ jacobian_det * image_space.element( _linear_deform( series_bp_all[temp2][temp1-l], inv_MN * vector_fields[temp1-l-1])) # Update partial of template template = template - \ ss1 * series_bp_all[temp2][0] for k2 in range(in_inter2): image_MN0[0] = template series_bp_all[0] = gradS[0](image_MN0[0]) for j in range(MN): temp1 = j + 1 # Update image_MN0 image_MN0[temp1] = image_space.element( _linear_deform(image_MN0[j], -inv_MN * vector_fields[temp1])) if temp1 % M == 0: temp2 = temp1 // M # Update eta_tt eta_tt[temp2] = gradS[temp2](image_MN0[temp1]) series_bp_all[temp2][temp1] = eta_tt[temp2] for l in range(temp1): jacobian_det = image_space.element( 1.0 + inv_MN * div_op(vector_fields[temp1 - l - 1])) # Update eta_tau_t series_bp_all[temp2][temp1-l-1] = \ jacobian_det * image_space.element( _linear_deform( series_bp_all[temp2][temp1-l], inv_MN * vector_fields[temp1-l-1])) for j in range(MN1): tmp1 = grad_op(image_MN0[j]) tmp2 = int(np.ceil(j * 1. / M)) tmp0 = tmp2 + 1 # print(tmp2) if tmp2 == 0: tmp3 = image_space.zero() tmp4 = image_space.tangent_bundle.zero() else: tmp3 = series_bp_all[tmp2][j] tmp4 = vector_fields[j] for i in range(tmp0, N1): tmp3 = tmp3 + series_bp_all[i][j] tmp4 = tmp4 + vector_fields[j] for i in range(dim): tmp1[i] *= tmp3 tmp5 = (2 * np.pi)**(dim / 2.0) * vectorial_ft_op.inverse( vectorial_ft_op(tmp1) * ft_kernel_fitting) # Update vector_fields vector_fields[j] = vector_fields[j] + ss2 * (tmp5 - mu_2 * tmp4) return template, vector_fields, image_MN0 else: raise NotImplementedError('unknown group action')