示例#1
0
    def fit(src, dst, order=2):
        """function to fit this transform given the corresponding sets
        of points src & dst
        polynomial fit

        Parameters
        ----------
        src : numpy.array
            a Nx2 matrix of source points
        dst : numpy.array
            a Nx2 matrix of destination points
        order : bool
            order of polynomial to fit

        Returns
        -------
        numpy.array
            a [2,(order+1)*(order+2)/2] array with the best fit parameters
        """
        xs = src[:, 0]
        ys = src[:, 1]
        xd = dst[:, 0]
        yd = dst[:, 1]
        rows = src.shape[0]
        no_coeff = (order + 1) * (order + 2)

        if len(src) != len(dst):
            raise EstimationError(
                'source has {} points, but dest has {}!'.format(
                    len(src), len(dst)))
        if no_coeff > len(src):
            raise EstimationError(
                'order {} is too large to fit {} points!'.format(
                    order, len(src)))

        A = np.zeros([rows * 2, no_coeff + 1])
        pidx = 0
        for j in range(order + 1):
            for i in range(j + 1):
                A[:rows, pidx] = xs**(j - i) * ys**i
                A[rows:, pidx + no_coeff // 2] = xs**(j - i) * ys**i
                pidx += 1

        A[:rows, -1] = xd
        A[rows:, -1] = yd

        # right singular vector corresponding to smallest singular value
        _, s, V = svd(A)
        Vsm = V[np.argmin(s), :]  # never trust computers
        return (-Vsm[:-1] / Vsm[-1]).reshape((2, no_coeff // 2))
 def gradient_descent(self,
                      pt,
                      gamma=1.0,
                      precision=0.0001,
                      max_iters=1000):
     """based on https://en.wikipedia.org/wiki/Gradient_descent#Python
     Parameters
     ----------
     pt : numpy array
         [x,y] point for estimating inverse
     gamma : float
         step size is gamma fraction of current gradient
     precision : float
         criteria for stopping for differences between steps
     max_iters : int
         limit for iterations, error if reached
     Returns
     -------
     cur_pt : numpy array
         [x,y] point, estimated inverse of pt
     """
     cur_pt = np.copy(pt)
     prev_pt = np.copy(pt)
     step_size = 1
     iters = 0
     while (step_size > precision) & (iters < max_iters):
         prev_pt[:] = cur_pt[:]
         cur_pt -= gamma * (self.apply(prev_pt) - pt)
         step_size = np.linalg.norm(cur_pt - prev_pt)
         iters += 1
     if iters == max_iters:
         raise EstimationError(
             'gradient descent for inversion of ThinPlateSpline '
             'reached maximum iterations: %d' % max_iters)
     return cur_pt
    def fit(A, B, return_all=False):
        """function to fit this transform given the corresponding sets of points A & B

        Parameters
        ----------
        A : numpy.array
            a Nx2 matrix of source points
        B : numpy.array
            a Nx2 matrix of destination points

        Returns
        -------
        numpy.array
            a 6x1 matrix with the best fit parameters
            ordered M00,M01,M10,M11,B0,B1
        """
        if not all([A.shape[0] == B.shape[0], A.shape[1] == B.shape[1] == 2]):
            raise EstimationError(
                'shape mismatch! A shape: {}, B shape {}'.format(
                    A.shape, B.shape))

        N = A.shape[0]  # total points

        M = np.zeros((2 * N, 6))
        Y = np.zeros((2 * N, 1))
        for i in range(N):
            M[2 * i, :] = [A[i, 0], A[i, 1], 0, 0, 1, 0]
            M[2 * i + 1, :] = [0, 0, A[i, 0], A[i, 1], 0, 1]
            Y[2 * i] = B[i, 0]
            Y[2 * i + 1] = B[i, 1]

        (Tvec, residuals, rank, s) = np.linalg.lstsq(M, Y)
        if return_all:
            return Tvec, residuals, rank, s
        return Tvec
    def fit(src, dst, rigid=True, **kwargs):
        """function to fit this transform given the corresponding
        sets of points src & dst
        Umeyama estimation of similarity transformation

        Parameters
        ----------
        src : numpy.array
            a Nx2 matrix of source points
        dst : numpy.array
            a Nx2 matrix of destination points
        rigid : bool
            whether to constrain this transform to be rigid

        Returns
        -------
        numpy.array
            a 6x1 matrix with the best fit parameters
            ordered M00,M01,M10,M11,B0,B1
        """
        # TODO shape assertion
        num, dim = src.shape
        src_cld = src - src.mean(axis=0)
        dst_cld = dst - dst.mean(axis=0)
        A = np.dot(dst_cld.T, src_cld) / num
        d = np.ones((dim, ), dtype=np.double)
        if np.linalg.det(A) < 0:
            d[dim - 1] = -1
        T = np.eye(dim + 1, dtype=np.double)

        rank = np.linalg.matrix_rank(A)
        if rank == 0:
            raise EstimationError('zero rank matrix A unacceptable -- '
                                  'likely poorly conditioned')

        U, S, V = svd(A)

        if rank == dim - 1:
            if np.linalg.det(U) * np.linalg.det(V) > 0:
                T[:dim, :dim] = np.dot(U, V)
            else:
                s = d[dim - 1]
                d[dim - 1] = -1
                T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V))
                d[dim - 1] = s
        else:
            T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V.T))

        fit_scale = (1.0 if rigid else 1.0 / src_cld.var(axis=0).sum() *
                     np.dot(S, d))

        T[:dim,
          dim] = dst.mean(axis=0) - fit_scale * np.dot(T[:dim, :dim],
                                                       src.mean(axis=0).T)
        T[:dim, :dim] *= fit_scale
        return T
示例#5
0
    def fit(self, A, B):
        """function to fit this transform given the corresponding sets of points A & B
        Parameters
        ----------
        A : numpy.array
            a Nx2 matrix of source points
        B : numpy.array
            a Nx2 matrix of destination points

        Returns
        -------
        beta
            a self.lengthx2 matrix with polynomial factors
        normMean
            a self.length vector of expanded means
        normVar
            a self.length vector of expanded standard deviations
        """
        if not all([A.shape[0] == B.shape[0], A.shape[1] == B.shape[1] == 2]):
            raise EstimationError(
                'shape mismatch! A shape: {}, B shape {}'.format(
                    A.shape, B.shape))

        normMean = np.zeros(self.length).astype('float')
        normVar = np.ones(self.length).astype('float')
        src_exp = self.kernelExpand(A, normMean=normMean, normVar=normVar)
        normMean = src_exp.mean(0)
        normVar = src_exp.std(0)  # poorly named variable
        src_exp = self.kernelExpand(A, normMean=normMean, normVar=normVar)

        xcoeff, xresiduals, xrank, xs = np.linalg.lstsq(src_exp, B[:, 0])
        ycoeff, yresiduals, yrank, ys = np.linalg.lstsq(src_exp, B[:, 1])

        beta = np.zeros((self.length, 2))
        beta[:, 0] = xcoeff
        beta[:, 1] = ycoeff

        return beta, normMean, normVar
示例#6
0
    def estimate(self,
                 src,
                 dst,
                 order=2,
                 test_coords=True,
                 max_tries=100,
                 return_params=True,
                 **kwargs):
        """method for setting this transformation with the
        best fit given the corresponding points src,dst

        Parameters
        ----------
        src : numpy.array
            a Nx2 matrix of source points
        dst : numpy.array
            a Nx2 matrix of destination points
        order : int
            order of polynomial to fit
        test_coords : bool
            whether to test model after fitting to
            make sure it is good (see fitgood)
        max_tries : int
            how many times to attempt to fit the model (see fitgood)
        return_params : bool
            whether to return the parameter matrix
        **kwargs
            dictionary of keyword arguments including those
            that can be passed to fitgood

        Returns
        -------
        numpy.array
            a (2,(order+1)*(order+2)/2) matrix of parameters for this matrix
            (or None if return_params=False)
        """
        def fitgood(src, dst, params, atol=1e-3, rtol=0, **kwargs):
            """check if model produces a 'good' result

            Parameters
            ----------
            src : numpy.array
                a Nx2 matrix of source points
            dst : numpy.array
                a Nx2 matrix of destination points
            params : numpy.array
                a Kx2 matrix of parameters
            atol : float
                absolute tolerance as in numpy.allclose for
                transformed sample points
            rtol : float
                relative tolerance as in numpy.allclose for
                transformed sample points

            Returns
            -------
            bool
                whether the goodness condition is met
            """
            result = Polynomial2DTransform(params=params).tform(src)
            t = np.allclose(result, dst, atol=atol, rtol=rtol)
            return t

        estimated = False
        tries = 0
        while (tries < max_tries and not estimated):
            tries += 1
            try:
                params = Polynomial2DTransform.fit(src, dst, order=order)
            except (LinAlgError, ValueError) as e:
                logger.debug('Encountered error {}'.format(e))
                continue
            estimated = (fitgood(src, dst, params, **kwargs)
                         if test_coords else True)

        if tries == max_tries and not estimated:
            raise EstimationError('Could not fit Polynomial '
                                  'in {} attempts!'.format(tries))
        logger.debug('fit parameters in {} attempts'.format(tries))
        self.params = params
        if return_params:
            return self.params
    def fit(A, B, computeAffine=True):
        """function to fit this transform given the corresponding sets of points A & B

        Parameters
        ----------
        A : numpy.array
            a Nx2 matrix of source points
        B : numpy.array
            a Nx2 matrix of destination points

        Returns
        -------
        dMatrix : numpy.array
            ndims x nLm
        aMatrix : numpy.array
            ndims x ndims, affine matrix
        bVector : numpy.array
            ndims x 1, translation vector
        """

        if not all([A.shape[0] == B.shape[0], A.shape[1] == B.shape[1] == 2]):
            raise EstimationError(
                'shape mismatch! A shape: {}, B shape {}'.format(
                    A.shape, B.shape))

        # build displacements
        ndims = B.shape[1]
        nLm = B.shape[0]
        y = (B - A).flatten()

        # compute K
        # tempting to matricize this, but, nLm x nLm can get big
        # settle for vectorize
        kMatrix = np.zeros((ndims * nLm, ndims * nLm))
        for i in range(nLm):
            r = np.linalg.norm(A[i, :] - A, axis=1)
            nrm = np.zeros_like(r)
            ind = np.argwhere(r > 1e-8)
            nrm[ind] = r[ind] * r[ind] * np.log(r[ind])
            kMatrix[i * ndims, 0::2] = nrm
            kMatrix[(i * ndims + 1)::2, 1::2] = nrm

        # compute L
        lMatrix = kMatrix
        if computeAffine:
            pMatrix = np.tile(np.eye(ndims), (nLm, ndims + 1))
            for d in range(ndims):
                pMatrix[0::2, d * ndims] = A[:, d]
                pMatrix[1::2, d * ndims + 1] = A[:, d]
            lMatrix = np.zeros(
                (ndims * (nLm + ndims + 1), ndims * (nLm + ndims + 1)))
            lMatrix[
                    0: pMatrix.shape[0],
                    kMatrix.shape[1]: kMatrix.shape[1] + pMatrix.shape[1]] = \
                pMatrix
            pMatrix = np.transpose(pMatrix)
            lMatrix[kMatrix.shape[0]:kMatrix.shape[0] + pMatrix.shape[0],
                    0:pMatrix.shape[1]] = pMatrix
            lMatrix[0:ndims * nLm, 0:ndims * nLm] = kMatrix
            y = np.append(y, np.zeros(ndims * (ndims + 1)))

        wMatrix = np.linalg.solve(lMatrix, y)

        dMatrix = np.reshape(wMatrix[0:ndims * nLm], (ndims, nLm), order='F')
        aMatrix = None
        bVector = None
        if computeAffine:
            aMatrix = np.reshape(wMatrix[ndims * nLm:ndims * nLm +
                                         ndims * ndims], (ndims, ndims),
                                 order='F')
            bVector = wMatrix[ndims * nLm + ndims * ndims:]

        return dMatrix, aMatrix, bVector
示例#8
0
    def mesh_refine(new_src,
                    old_src,
                    old_dst,
                    old_tf=None,
                    computeAffine=True,
                    tol=1.0,
                    max_iter=50,
                    nworst=10,
                    niter=0):
        """recursive kernel for adaptive_mesh_estimate()
        Parameters
        ----------
        new_src : numpy.array
            Nx2 array of new control source points. Adapts during recursion.
            Seeded by adaptive_mesh_estimate.
        old_src : numpy.array
            Nx2 array of orignal control source points.
        old_dst : numpy.array
            Nx2 array of orignal control destination points.
        old_tf : ThinPlateSplineTransform
            transform constructed from old_src and old_dst, passed through
            recursion iterations. Created if None.
        computeAffine : boolean
            whether returned transform will have aMtx
        tol : float
            in units of pixels, how close should the points match
        max_iter: int
            some limit on how many recursive attempts
        nworst : int
            per iteration, the nworst matching srcPts will be added
        niter : int
            passed through the recursion for stopping criteria

        Returns
        -------
        ThinPlateSplineTransform
        """

        if old_tf is None:
            old_tf = ThinPlateSplineTransform()
            old_tf.estimate(old_src, old_dst, computeAffine=computeAffine)

        new_tf = ThinPlateSplineTransform()
        new_tf.estimate(new_src,
                        old_tf.tform(new_src),
                        computeAffine=computeAffine)
        new_dst = new_tf.tform(old_src)

        delta = np.linalg.norm(new_dst - old_dst, axis=1)
        ind = np.argwhere(delta > tol).flatten()

        if ind.size == 0:
            return new_tf

        if niter == max_iter:
            raise EstimationError(
                "Max number of iterations ({}) reached in"
                " ThinPlateSplineTransform.mesh_refine()".format(max_iter))

        sortind = np.argsort(delta[ind])
        new_src = np.vstack((new_src, old_src[ind[sortind[0:nworst]]]))

        return ThinPlateSplineTransform.mesh_refine(
            new_src,
            old_src,
            old_dst,
            old_tf=old_tf,
            computeAffine=computeAffine,
            tol=tol,
            max_iter=max_iter,
            nworst=nworst,
            niter=(niter + 1))