Example #1
0
    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)
Example #2
0
    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)
Example #6
0
    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)
Example #7
0
    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]
Example #9
0
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
Example #10
0
    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
Example #11
0
def infinitesimal_action_withgrad(v, I):
    grad_op = Gradient(I.space)
    grad = grad_op(I)
    return -sum(v * grad)
Example #12
0
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')