Beispiel #1
0
def plot_reg_open_curve(beta1, beta2n):
    """
    plots registration between two open curves using matplotlib

    :param beta: numpy ndarray of shape (2,M) of M samples
    :param beta: numpy ndarray of shape (2,M) of M samples

    :return fig: figure definition
    :return ax: axes definition

    """
    T = beta1.shape[1]
    centroid1 = cf.calculatecentroid(beta1)
    beta1 = beta1 - tile(centroid1, [T, 1]).T
    centroid2 = cf.calculatecentroid(beta2n)
    beta2n = beta2n - tile(centroid2, [T, 1]).T
    beta2n[0, :] = beta2n[0, :] + 1.3
    beta2n[1, :] = beta2n[1, :] - 0.1

    fig, ax = plt.subplots()
    ax.plot(beta1[0, :], beta1[1, :], "r", linewidth=2)
    fig.hold()
    ax.plot(beta2n[0, :], beta2n[1, :], "b-o", linewidth=2)

    for j in range(0, int(T / 5)):
        i = j * 5
        ax.plot(array([beta1[0, i], beta2n[0, i]]), array([beta1[1, i], beta2n[1, i]]), "k", linewidth=1)

    ax.set_aspect("equal")
    ax.axis("off")
    fig.hold()

    return fig, ax
Beispiel #2
0
def plot_reg_open_curve(beta1, beta2n):
    """
    plots registration between two open curves using matplotlib

    :param beta: numpy ndarray of shape (2,M) of M samples
    :param beta: numpy ndarray of shape (2,M) of M samples

    :return fig: figure definition
    :return ax: axes definition

    """
    T = beta1.shape[1]
    centroid1 = cf.calculatecentroid(beta1)
    beta1 = beta1 - tile(centroid1, [T, 1]).T
    centroid2 = cf.calculatecentroid(beta2n)
    beta2n = beta2n - tile(centroid2, [T, 1]).T
    beta2n[0, :] = beta2n[0, :] + 1.3
    beta2n[1, :] = beta2n[1, :] - 0.1

    fig, ax = plt.subplots()
    ax.plot(beta1[0, :], beta1[1, :], 'r', linewidth=2)
    fig.hold()
    ax.plot(beta2n[0, :], beta2n[1, :], 'b-o', linewidth=2)

    for j in range(0, int(T/5)):
        i = j*5
        ax.plot(array([beta1[0, i], beta2n[0, i]]),
                array([beta1[1, i], beta2n[1, i]]), 'k', linewidth=1)

    ax.set_aspect('equal')
    ax.axis('off')
    fig.hold()

    return fig, ax
Beispiel #3
0
def oc_srvf_align(beta, mode='O'):
    """
    This claculates the mean of a set of curves and aligns them
    :param beta: numpy ndarray of shape (n, M, N) describing N curves
    in R^M
    :param mode: Open ('O') or closed curve ('C') (default 'O')

    :rtype: tuple of numpy array
    :return betan: aligned curves
    :return qn: aligned srvf
    :return betamean: mean curve
    :return mu: mean srvf
    """
    n, T, N = beta.shape
    # find mean
    mu, betamean, v, q = curve_karcher_mean(beta, mode=mode)

    qn = zeros((n, T, N))
    betan = zeros((n, T, N))
    centroid2 = cf.calculatecentroid(betamean)
    betamean = betamean - tile(centroid2, [T, 1]).T
    q_mu = cf.curve_to_q(betamean)
    # align to mean
    for ii in range(0, N):
        beta1 = beta[:, :, ii]
        centroid1 = cf.calculatecentroid(beta1)
        beta1 = beta1 - tile(centroid1, [T, 1]).T

        # Iteratively optimize over SO(n) x Gamma
        for i in range(0, 1):
            # Optimize over SO(n)
            beta1, O_hat, tau = cf.find_rotation_and_seed_coord(betamean,
                                                                beta1)
            q1 = cf.curve_to_q(beta1)

            # Optimize over Gamma
            gam = cf.optimum_reparam_curve(q1, q_mu, 0.0)
            gamI = uf.invertGamma(gam)
            # Applying optimal re-parameterization to the second curve
            beta1 = cf.group_action_by_gamma_coord(beta1, gamI)

        # Optimize over SO(n)
        beta1, O_hat, tau = cf.find_rotation_and_seed_coord(betamean, beta1)
        qn[:, :, ii] = cf.curve_to_q(beta1)
        betan[:, :, ii] = beta1

    align_results = collections.namedtuple('align', ['betan', 'qn', 'betamean', 'mu'])
    out = align_results(betan, qn, betamean, q_mu)
    return out
def oc_srvf_align(beta, mode='O'):
    """
    This claculates the mean of a set of curves and aligns them
    :param beta: numpy ndarray of shape (n, M, N) describing N curves
    in R^M
    :param mode: Open ('O') or closed curve ('C') (default 'O')

    :rtype: tuple of numpy array
    :return betan: aligned curves
    :return qn: aligned srvf
    :return betamean: mean curve
    :return mu: mean srvf
    """
    n, T, N = beta.shape
    # find mean
    mu, betamean, v, q = curve_karcher_mean(beta, mode=mode)

    qn = zeros((n, T, N))
    betan = zeros((n, T, N))
    centroid2 = cf.calculatecentroid(betamean)
    betamean = betamean - tile(centroid2, [T, 1]).T
    q_mu = cf.curve_to_q(betamean)
    # align to mean
    for ii in range(0, N):
        beta1 = beta[:, :, ii]
        centroid1 = cf.calculatecentroid(beta1)
        beta1 = beta1 - tile(centroid1, [T, 1]).T

        # Iteratively optimize over SO(n) x Gamma
        for i in range(0, 1):
            # Optimize over SO(n)
            beta1, O_hat, tau = cf.find_rotation_and_seed_coord(betamean,
                                                                beta1)
            q1 = cf.curve_to_q(beta1)

            # Optimize over Gamma
            gam = cf.optimum_reparam_curve(q1, q_mu, 0.0)
            gamI = uf.invertGamma(gam)
            # Applying optimal re-parameterization to the second curve
            beta1 = cf.group_action_by_gamma_coord(beta1, gamI)

        # Optimize over SO(n)
        beta1, O_hat, tau = cf.find_rotation_and_seed_coord(betamean, beta1)
        qn[:, :, ii] = cf.curve_to_q(beta1)
        betan[:, :, ii] = beta1

    align_results = collections.namedtuple('align', ['betan', 'qn', 'betamean', 'mu'])
    out = align_results(betan, qn, betamean, q_mu)
    return out
Beispiel #5
0
def init_path_geod(beta1, beta2, T=100, k=5):
    """
    Initializes a path in \cal{C}. beta1, beta2 are already
    standardized curves. Creates a path from beta1 to beta2 in
    shape space, then projects to the closed shape manifold.

    :param beta1: numpy ndarray of shape (2,M) of M samples (first curve)
    :param beta2: numpy ndarray of shape (2,M) of M samples (end curve)
    :param T: Number of samples of curve (Default = 100)
    :param k: number of samples along path (Default = 5)

    :rtype: numpy ndarray
    :return alpha: a path between two q-functions
    :return beta:  a path between two curves
    :return O: rotation matrix

    """
    alpha = zeros((2, T, k))
    beta = zeros((2, T, k))

    dist, pathq, O = geod_sphere(beta1, beta2, k)

    for tau in range(0, k):
        alpha[:, :, tau] = cf.project_curve(pathq[:, :, tau])
        x = cf.q_to_curve(alpha[:, :, tau])
        a = -1 * cf.calculatecentroid(x)
        beta[:, :, tau] = x + tile(a, [T, 1]).T

    return (alpha, beta, O)
Beispiel #6
0
def init_path_geod(beta1, beta2, T=100, k=5):
    r"""
    Initializes a path in :math:`\cal{C}`. beta1, beta2 are already
    standardized curves. Creates a path from beta1 to beta2 in
    shape space, then projects to the closed shape manifold.

    :param beta1: numpy ndarray of shape (2,M) of M samples (first curve)
    :param beta2: numpy ndarray of shape (2,M) of M samples (end curve)
    :param T: Number of samples of curve (Default = 100)
    :param k: number of samples along path (Default = 5)

    :rtype: numpy ndarray
    :return alpha: a path between two q-functions
    :return beta:  a path between two curves
    :return O: rotation matrix

    """
    alpha = zeros((2, T, k))
    beta = zeros((2, T, k))

    dist, pathq, O = geod_sphere(beta1, beta2, k)

    for tau in range(0, k):
        alpha[:, :, tau] = cf.project_curve(pathq[:, :, tau])
        x = cf.q_to_curve(alpha[:, :, tau])
        a = -1*cf.calculatecentroid(x)
        beta[:, :, tau] = x + tile(a, [T, 1]).T

    return(alpha, beta, O)
Beispiel #7
0
    def __init__(self, beta, mode='O', N=200, scale=True):
        """
        fdacurve Construct an instance of this class
        :param beta: (n,T,K) matrix defining n dimensional curve on T samples with K curves
        :param mode:  Open ('O') or closed curve ('C') (default 'O')
        :param N: resample curve to N points
        :param scale: scale curve to length 1 (true/false)
        """
        self.mode = mode
        self.scale = scale

        K = beta.shape[2]
        n = beta.shape[0]
        q = zeros((n, N, K))
        beta1 = zeros((n, N, K))
        cent1 = zeros((n, K))
        for ii in range(0, K):
            beta1[:, :, ii] = cf.resamplecurve(beta[:, :, ii], N, mode)
            a = -cf.calculatecentroid(beta1[:, :, ii])
            beta1[:, :, ii] += tile(a, (N, 1)).T
            q[:, :, ii] = cf.curve_to_q(beta1[:, :, ii], self.scale, self.mode)
            cent1[:, ii] = -a

        self.q = q
        self.beta = beta1
        self.cent = cent1
Beispiel #8
0
    def srvf_align(self, parallel=False, cores=-1):
        """
        This aligns a set of curves to the mean and computes mean if not computed
        :param parallel: run in parallel (default = F)
        :param cores: number of cores for parallel (default = -1 (all))
        """
        n, T, N = self.beta.shape
        # find mean
        if not hasattr(self, 'beta_mean'):
            self.karcher_mean()

        self.qn = zeros((n, T, N))
        self.betan = zeros((n, T, N))
        self.gams = zeros((T, N))
        C = zeros((T, N))
        centroid2 = cf.calculatecentroid(self.beta_mean)
        self.beta_mean = self.beta_mean - tile(centroid2, [T, 1]).T
        q_mu = cf.curve_to_q(self.beta_mean)
        # align to mean

        out = Parallel(n_jobs=-1)(
            delayed(align_sub)(self.beta_mean, q_mu, self.beta[:, :, n])
            for n in range(N))
        for ii in range(0, N):
            self.gams[:, ii] = out[ii][2]
            self.qn[:, :, ii] = cf.curve_to_q(out[ii][0])
            self.betan[:, :, ii] = out[ii][0]

        return
def preproc_open_curve(beta, T=100):
    n, M, k = beta.shape

    q = np.zeros((n, T, k))
    beta2 = np.zeros((n, T, k))
    for i in range(0, k):
        beta1 = beta[:, :, i]
        beta1, scale = cf.scale_curve(beta1)
        beta1 = cf.resamplecurve(beta1, T)
        centroid1 = cf.calculatecentroid(beta1)
        beta1 = beta1 - np.tile(centroid1, [T, 1]).T
        beta2[:, :, i] = beta1
        q[:, :, i] = cf.curve_to_q(beta1)

    return (q, beta2)
Beispiel #10
0
    def srvf_align(self, rotation=True, parallel=False, cores=-1, method="DP"):
        """
        This aligns a set of curves to the mean and computes mean if not computed
        :param rotation: compute optimal rotation (default = T)
        :param parallel: run in parallel (default = F)
        :param cores: number of cores for parallel (default = -1 (all))
        :param method: method to apply optimization (default="DP") options are "DP" or "RBFGS"
        """
        n, T, N = self.beta.shape

        modes = ['O', 'C']
        mode = [i for i, x in enumerate(modes) if x == self.mode]
        if len(mode) == 0:
            mode = 0
        else:
            mode = mode[0]

        # find mean
        if not hasattr(self, 'beta_mean'):
            self.karcher_mean()

        self.qn = zeros((n, T, N))
        self.betan = zeros((n, T, N))
        self.gams = zeros((T, N))
        centroid2 = cf.calculatecentroid(self.beta_mean)
        self.beta_mean = self.beta_mean - tile(centroid2, [T, 1]).T

        # align to mean
        out = Parallel(n_jobs=-1)(delayed(cf.find_rotation_and_seed_unique)(
            self.q_mean, self.q[:, :, n], mode, rotation, method)
                                  for n in range(N))
        for ii in range(0, N):
            self.gams[:, ii] = out[ii][2]
            self.qn[:, :, ii] = out[ii][0]
            btmp = out[ii][1].dot(self.beta[:, :, ii])
            self.betan[:, :,
                       ii] = cf.group_action_by_gamma_coord(btmp, out[ii][2])

        return
Beispiel #11
0
def update_path(alpha, beta, gradE, delta, T=100, k=5):
    """
    Update the path along the direction -gradE

    :param alpha: numpy ndarray of shape (2,M) of M samples
    :param beta: numpy ndarray of shape (2,M) of M samples
    :param gradE: numpy ndarray of shape (2,M) of M samples
    :param delta: gradient paramenter
    :param T: Number of samples of curve (Default = 100)
    :param k: number of samples along path (Default = 5)

    :rtype: numpy scalar
    :return alpha: updated path of srvfs
    :return beta: updated path of curves

    """
    for tau in range(1, k - 1):
        alpha_new = alpha[:, :, tau] - delta * gradE[:, :, tau]
        alpha[:, :, tau] = cf.project_curve(alpha_new)
        x = cf.q_to_curve(alpha[:, :, tau])
        a = -1 * cf.calculatecentroid(x)
        beta[:, :, tau] = x + tile(a, [T, 1]).T

    return (alpha, beta)
Beispiel #12
0
    def srvf_align(self, parallel=False, cores=-1):
        """
        This aligns a set of curves to the mean and computes mean if not computed
        :param parallel: run in parallel (default = F)
        :param cores: number of cores for parallel (default = -1 (all))
        """
        n, T, N = self.beta.shape

        modes = ['O', 'C']
        mode = [i for i, x in enumerate(modes) if x == self.mode]
        if len(mode) == 0:
            mode = 0
        else:
            mode = mode[0]

        # find mean
        if not hasattr(self, 'beta_mean'):
            self.karcher_mean()

        self.qn = zeros((n, T, N))
        self.betan = zeros((n, T, N))
        self.gams = zeros((T, N))
        C = zeros((T, N))
        centroid2 = cf.calculatecentroid(self.beta_mean)
        self.beta_mean = self.beta_mean - tile(centroid2, [T, 1]).T
        q_mu = cf.curve_to_q(self.beta_mean)
        # align to mean

        out = Parallel(n_jobs=-1)(delayed(cf.find_rotation_and_seed_coord)(
            self.beta_mean, self.beta[:, :, n], mode) for n in range(N))
        for ii in range(0, N):
            self.gams[:, ii] = out[ii][3]
            self.qn[:, :, ii] = out[ii][1]
            self.betan[:, :, ii] = out[ii][0]

        return
Beispiel #13
0
def update_path(alpha, beta, gradE, delta, T=100, k=5):
    """
    Update the path along the direction -gradE

    :param alpha: numpy ndarray of shape (2,M) of M samples
    :param beta: numpy ndarray of shape (2,M) of M samples
    :param gradE: numpy ndarray of shape (2,M) of M samples
    :param delta: gradient paramenter
    :param T: Number of samples of curve (Default = 100)
    :param k: number of samples along path (Default = 5)

    :rtype: numpy scalar
    :return alpha: updated path of srvfs
    :return beta: updated path of curves

    """
    for tau in range(1, k-1):
        alpha_new = alpha[:, :, tau] - delta*gradE[:, :, tau]
        alpha[:, :, tau] = cf.project_curve(alpha_new)
        x = cf.q_to_curve(alpha[:, :, tau])
        a = -1*cf.calculatecentroid(x)
        beta[:, :, tau] = x + tile(a, [T, 1]).T

    return(alpha, beta)
Beispiel #14
0
    def karcher_mean(self, parallel=False, cores=-1):
        """
        This calculates the mean of a set of curves
        :param parallel: run in parallel (default = F)
        :param cores: number of cores for parallel (default = -1 (all))
        """
        n, T, N = self.beta.shape

        modes = ['O', 'C']
        mode = [i for i, x in enumerate(modes) if x == self.mode]
        if len(mode) == 0:
            mode = 0
        else:
            mode = mode[0]

        # Initialize mu as one of the shapes
        mu = self.q[:, :, 0]
        betamean = self.beta[:, :, 0]
        itr = 0

        gamma = zeros((T, N))
        maxit = 20

        sumd = zeros(maxit + 1)
        v = zeros((n, T, N))
        normvbar = zeros(maxit + 1)

        delta = 0.5
        tolv = 1e-4
        told = 5 * 1e-3

        print("Computing Karcher Mean of %d curves in SRVF space.." % N)
        while itr < maxit:
            print("updating step: %d" % (itr + 1))

            if iter == maxit:
                print("maximal number of iterations reached")

            mu = mu / sqrt(cf.innerprod_q2(mu, mu))
            if mode == 1:
                self.basis = cf.find_basis_normal(mu)
            else:
                self.basis = []

            sumv = zeros((n, T))
            sumd[0] = inf
            sumd[itr + 1] = 0
            out = Parallel(n_jobs=cores)(delayed(karcher_calc)(
                self.beta[:, :, n], self.q[:, :,
                                           n], betamean, mu, self.basis, mode)
                                         for n in range(N))
            v = zeros((n, T, N))
            for i in range(0, N):
                v[:, :, i] = out[i][0]
                sumd[itr + 1] = sumd[itr + 1] + out[i][1]**2

            sumv = v.sum(axis=2)

            # Compute average direction of tangent vectors v_i
            vbar = sumv / float(N)

            normvbar[itr] = sqrt(cf.innerprod_q2(vbar, vbar))
            normv = normvbar[itr]

            if normv > tolv and fabs(sumd[itr + 1] - sumd[itr]) > told:
                # Update mu in direction of vbar
                mu = cos(delta * normvbar[itr]) * mu + sin(
                    delta * normvbar[itr]) * vbar / normvbar[itr]

                if mode == 1:
                    mu = cf.project_curve(mu)

                x = cf.q_to_curve(mu)
                a = -1 * cf.calculatecentroid(x)
                betamean = x + tile(a, [T, 1]).T
            else:
                break

            itr += 1

        self.q_mean = mu
        self.beta_mean = betamean
        self.v = v
        self.qun = sumd[0:(itr + 1)]
        self.E = normvbar[0:(itr + 1)]

        return
def curve_karcher_mean(beta, mode='O'):
    """
    This claculates the mean of a set of curves
    :param beta: numpy ndarray of shape (n, M, N) describing N curves
    in R^M
    :param mode: Open ('O') or closed curve ('C') (default 'O')

    :rtype: tuple of numpy array
    :return mu: mean srvf
    :return betamean: mean curve
    :return v: shooting vectors
    :return q: srvfs

    """
    n, T, N = beta.shape
    q = zeros((n, T, N))
    for ii in range(0, N):
        q[:, :, ii] = cf.curve_to_q(beta[:, :, ii])

    modes = ['O', 'C']
    mode = [i for i, x in enumerate(modes) if x == mode]
    if len(mode) == 0:
        mode = 0
    else:
        mode = mode[0]

    # Initialize mu as one of the shapes
    mu = q[:, :, 0]
    betamean = beta[:, :, 0]

    delta = 0.5
    tolv = 1e-4
    told = 5*1e-3
    maxit = 20
    itr = 0
    sumd = zeros(maxit+1)
    v = zeros((n, T, N))
    normvbar = zeros(maxit+1)

    while itr < maxit:
        print("Iteration: %d" % itr)

        mu = mu / sqrt(cf.innerprod_q2(mu, mu))

        sumv = zeros((2, T))
        sumd[itr+1] = 0
        out = Parallel(n_jobs=-1)(delayed(karcher_calc)(beta[:, :, n],
                                  q[:, :, n], betamean, mu, mode) for n in range(N))
        v = zeros((n, T, N))
        for i in range(0, N):
            v[:, :, i] = out[i][0]
            sumd[itr+1] = sumd[itr+1] + out[i][1]**2

        sumv = v.sum(axis=2)

        # Compute average direction of tangent vectors v_i
        vbar = sumv/float(N)

        normvbar[itr] = sqrt(cf.innerprod_q2(vbar, vbar))
        normv = normvbar[itr]

        if normv > tolv and fabs(sumd[itr+1]-sumd[itr]) > told:
            # Update mu in direction of vbar
            mu = cos(delta*normvbar[itr])*mu + sin(delta*normvbar[itr]) * vbar/normvbar[itr]

            if mode == 1:
                mu = cf.project_curve(mu)

            x = cf.q_to_curve(mu)
            a = -1*cf.calculatecentroid(x)
            betamean = x + tile(a, [T, 1]).T
        else:
            break

        itr += 1

    return(mu, betamean, v, q)
Beispiel #16
0
def geod_sphere(beta1, beta2, k=5):
    """
    This function calculates the geodesics between open curves beta1 and
    beta2 with k steps along path

    :param beta1: numpy ndarray of shape (2,M) of M samples
    :param beta2: numpy ndarray of shape (2,M) of M samples
    :param k: number of samples along path (Default = 5)

    :rtype: numpy ndarray
    :return dist: geodesic distance
    :return path: geodesic path
    :return O: rotation matrix

    """
    lam = 0.0
    elastic = 1
    rotation = 1
    returnpath = 1
    n, T = beta1.shape

    beta1 = cf.resamplecurve(beta1, T)
    beta2 = cf.resamplecurve(beta2, T)

    centroid1 = cf.calculatecentroid(beta1)
    beta1 = beta1 - tile(centroid1, [T, 1]).T
    centroid2 = cf.calculatecentroid(beta2)
    beta2 = beta2 - tile(centroid2, [T, 1]).T

    q1 = cf.curve_to_q(beta1)
    if rotation:
        beta2, O1, tau = cf.find_rotation_and_seed_coord(beta1, beta2)
        q2 = cf.curve_to_q(beta2)
    else:
        O1 = eye(2)
        q2 = cf.curve_to_q(beta2)

    if elastic:
        # Find the optimal coorespondence
        gam = cf.optimum_reparam_curve(q2, q1, lam)
        gamI = uf.invertGamma(gam)
        # Applying optimal re-parameterization to the second curve
        beta2n = cf.group_action_by_gamma_coord(beta2, gamI)
        q2n = cf.curve_to_q(beta2n)

        if rotation:
            beta2n, O2, tau = cf.find_rotation_and_seed_coord(beta1, beta2n)
            centroid2 = cf.calculatecentroid(beta2n)
            beta2n = beta2n - tile(centroid2, [T, 1]).T
            q2n = cf.curve_to_q(beta2n)
            O = O1.dot(O2)
    else:
        q2n = q2
        O = O1

    # Forming geodesic between the registered curves
    dist = arccos(cf.innerprod_q2(q1, q2n))

    if returnpath:
        PsiQ = zeros((n, T, k))
        PsiX = zeros((n, T, k))
        for tau in range(0, k):
            s = dist * tau / (k - 1.)
            PsiQ[:, :, tau] = (sin(dist-s)*q1+sin(s)*q2n)/sin(dist)
            PsiX[:, :, tau] = cf.q_to_curve(PsiQ[:, :, tau])

        path = PsiQ
    else:
        path = 0

    return(dist, path, O)
Beispiel #17
0
def init_path_rand(beta1, beta_mid, beta2, T=100, k=5):
    r"""
    Initializes a path in :math:`\cal{C}`. beta1, beta_mid beta2 are already
    standardized curves. Creates a path from beta1 to beta_mid to beta2 in
    shape space, then projects to the closed shape manifold.

    :param beta1: numpy ndarray of shape (2,M) of M samples (first curve)
    :param betamid: numpy ndarray of shape (2,M) of M samples (mid curve)
    :param beta2: numpy ndarray of shape (2,M) of M samples (end curve)
    :param T: Number of samples of curve (Default = 100)
    :param k: number of samples along path (Default = 5)

    :rtype: numpy ndarray
    :return alpha: a path between two q-functions
    :return beta:  a path between two curves
    :return O: rotation matrix

    """
    alpha = zeros((2, T, k))
    beta = zeros((2, T, k))

    q1 = cf.curve_to_q(beta1)
    q_mid = cf.curve_to_q(beta_mid)

    # find optimal rotation of q2
    beta2, O1, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2)
    q2 = cf.curve_to_q(beta2)

    # find the optimal coorespondence
    gam = cf.optimum_reparam_curve(q2, q1)
    gamI = uf.invertGamma(gam)

    # apply optimal reparametrization
    beta2n = cf.group_action_by_gamma_coord(beta2, gamI)

    # find optimal rotation of q2
    beta2n, O2, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2n)
    centroid2 = cf.calculatecentroid(beta2n)
    beta2n = beta2n - tile(centroid2, [T, 1]).T
    q2n = cf.curve_to_q(beta2n)
    O = O1.dot(O2)

    # Initialize a path as a geodesic through q1 --- q_mid --- q2
    theta1 = arccos(cf.innerprod_q2(q1, q_mid))
    theta2 = arccos(cf.innerprod_q2(q_mid, q2n))
    tmp = arange(2, int((k-1)/2)+1)
    t = zeros(tmp.size)
    alpha[:, :, 0] = q1
    beta[:, :, 0] = beta1

    i = 0
    for tau in range(2, int((k-1)/2)+1):
        t[i] = (tau-1.)/((k-1)/2.)
        qnew = (1/sin(theta1))*(sin((1-t[i])*theta1)*q1+sin(t[i]*theta1)*q_mid)
        alpha[:, :, tau-1] = cf.project_curve(qnew)
        x = cf.q_to_curve(alpha[:, :, tau-1])
        a = -1*cf.calculatecentroid(x)
        beta[:, :, tau-1] = x + tile(a, [T, 1]).T
        i += 1

    alpha[:, :, int((k-1)/2)] = q_mid
    beta[:, :, int((k-1)/2)] = beta_mid

    i = 0
    for tau in range(int((k-1)/2)+1, k-1):
        qnew = (1/sin(theta2))*(sin((1-t[i])*theta2)*q_mid
                                + sin(t[i]*theta2)*q2n)
        alpha[:, :, tau] = cf.project_curve(qnew)
        x = cf.q_to_curve(alpha[:, :, tau])
        a = -1*cf.calculatecentroid(x)
        beta[:, :, tau] = x + tile(a, [T, 1]).T
        i += 1

    alpha[:, :, k-1] = q2n
    beta[:, :, k-1] = beta2n

    return(alpha, beta, O)
Beispiel #18
0
def geod_sphere(beta1, beta2, k=5):
    """
    This function caluclates the geodecis between open curves beta1 and
    beta2 with k steps along path

    :param beta1: numpy ndarray of shape (2,M) of M samples
    :param beta2: numpy ndarray of shape (2,M) of M samples
    :param k: number of samples along path (Default = 5)

    :rtype: numpy ndarray
    :return dist: geodesic distance
    :return path: geodesic path
    :return O: rotation matrix

    """
    lam = 0.0
    elastic = 1
    rotation = 1
    returnpath = 1
    n, T = beta1.shape

    beta1 = cf.resamplecurve(beta1, T)
    beta2 = cf.resamplecurve(beta2, T)

    centroid1 = cf.calculatecentroid(beta1)
    beta1 = beta1 - tile(centroid1, [T, 1]).T
    centroid2 = cf.calculatecentroid(beta2)
    beta2 = beta2 - tile(centroid2, [T, 1]).T

    q1 = cf.curve_to_q(beta1)
    if rotation:
        beta2, O1, tau = cf.find_rotation_and_seed_coord(beta1, beta2)
        q2 = cf.curve_to_q(beta2)
    else:
        O1 = eye(2)
        q2 = cf.curve_to_q(beta2)

    if elastic:
        # Find the optimal coorespondence
        gam = cf.optimum_reparam_curve(q2, q1, lam)
        gamI = uf.invertGamma(gam)
        # Applying optimal re-parameterization to the second curve
        beta2n = cf.group_action_by_gamma_coord(beta2, gamI)
        q2n = cf.curve_to_q(beta2n)

        if rotation:
            beta2n, O2, tau = cf.find_rotation_and_seed_coord(beta1, beta2n)
            centroid2 = cf.calculatecentroid(beta2n)
            beta2n = beta2n - tile(centroid2, [T, 1]).T
            q2n = cf.curve_to_q(beta2n)
            O = O1.dot(O2)
    else:
        q2n = q2
        O = O1

    # Forming geodesic between the registered curves
    dist = arccos(cf.innerprod_q2(q1, q2n))

    if returnpath:
        PsiQ = zeros((n, T, k))
        PsiX = zeros((n, T, k))
        for tau in range(0, k):
            s = dist * tau / (k - 1.0)
            PsiQ[:, :, tau] = (sin(dist - s) * q1 + sin(s) * q2n) / sin(dist)
            PsiX[:, :, tau] = cf.q_to_curve(PsiQ[:, :, tau])

        path = PsiQ
    else:
        path = 0

    return (dist, path, O)
    def predict(self, newdata=None):
        """
        This function performs prediction on regression model on new data if available or current stored data in object
        Usage:  obj.predict()
                obj.predict(newdata)

        :param newdata: dict containing new data for prediction (needs the keys below, if None predicts on training data)
        :type newdata: dict
        :param beta: (n, M,N) matrix of curves
        :param y: truth if available
        """

        T = self.warp_data.beta_mean.shape[1]
        if newdata != None:
            beta = newdata['beta']
            y = newdata['y']
            n = beta.shape[2]
            beta1 = np.zeros(beta.shape)
            q = np.zeros(beta.shape)
            for ii in range(0,n):
                if (beta.shape[1] != T):
                    beta1[:,:,ii] = cf.resamplecurve(beta[:,:,ii],T)
                else:
                    beta1[:,:,ii] = beta[:,:,ii]
                a = -cf.calculatecentroid(beta1[:,:,ii])
                beta1[:,:,ii] += np.tile(a, (T,1)).T
                q[:,:,ii] = cf.curve_to_q(beta1[:,:,ii])[0]
            
            mu = self.warp_data.q_mean

            v = np.zeros(q.shape)
            for ii in range(0,n):
                qn_t, R, gamI = cf.find_rotation_and_seed_unique(mu, q[:,:,ii], 0, self.rotation)
                qn_t = qn_t / np.sqrt(cf.innerprod_q2(qn_t,qn_t))

                q1dotq2 = cf.innerprod_q2(mu,qn_t)

                if (q1dotq2 > 1):
                    q1dotq2 = 1
                
                d = np.arccos(q1dotq2)

                u = qn_t - q1dotq2*mu
                normu = np.sqrt(cf.innerprod_q2(u,u))
                if (normu>1e-4):
                    v[:,:,ii] = u*np.arccos(q1dotq2)/normu
                else:
                    v[:,:,ii] = np.zeros(qn_t.shape)

            
            Utmp = self.warp_data.U.T
            no = self.warp_data.U.shape[1]
            VM = np.mean(self.warp_data.v,2)
            VM = VM.flatten()

            x = np.zeros((no,n))
            for i in range(0,n):
                tmp = v[:,:,i]
                tmpv1 = tmp.flatten()
                x[:,i] = Utmp.dot((tmpv1- VM))

            self.y_pred = np.zeros(n)
            for ii in range(0,n):
                self.y_pred[ii] = self.alpha + np.dot(x[:,ii],self.b)
            
            if y is None:
                self.SSE = np.nan
            else:
                self.SSE = np.sum((y-self.y_pred)**2)
        else:
            n = self.warp_data.coef.shape[1]
            self.y_pred = np.zeros(n)
            for ii in range(0,n):
                self.y_pred[ii] = self.alpha + np.dot(self.warp_data.coef[:,ii],self.b)
            
            self.SSE = np.sum((self.y-self.y_pred)**2)

        return
Beispiel #20
0
def init_path_rand(beta1, beta_mid, beta2, T=100, k=5):
    """
    Initializes a path in \cal{C}. beta1, beta_mid beta2 are already
    standardized curves. Creates a path from beta1 to beta_mid to beta2 in
    shape space, then projects to the closed shape manifold.

    :param beta1: numpy ndarray of shape (2,M) of M samples (first curve)
    :param betamid: numpy ndarray of shape (2,M) of M samples (mid curve)
    :param beta2: numpy ndarray of shape (2,M) of M samples (end curve)
    :param T: Number of samples of curve (Default = 100)
    :param k: number of samples along path (Default = 5)

    :rtype: numpy ndarray
    :return alpha: a path between two q-functions
    :return beta:  a path between two curves
    :return O: rotation matrix

    """
    alpha = zeros((2, T, k))
    beta = zeros((2, T, k))

    q1 = cf.curve_to_q(beta1)
    q_mid = cf.curve_to_q(beta_mid)

    # find optimal rotation of q2
    beta2, O1, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2)
    q2 = cf.curve_to_q(beta2)

    # find the optimal coorespondence
    gam = cf.optimum_reparam_curve(q2, q1)
    gamI = uf.invertGamma(gam)

    # apply optimal reparametrization
    beta2n = cf.group_action_by_gamma_coord(beta2, gamI)

    # find optimal rotation of q2
    beta2n, O2, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2n)
    centroid2 = cf.calculatecentroid(beta2n)
    beta2n = beta2n - tile(centroid2, [T, 1]).T
    q2n = cf.curve_to_q(beta2n)
    O = O1.dot(O2)

    # Initialize a path as a geodesic through q1 --- q_mid --- q2
    theta1 = arccos(cf.innerprod_q2(q1, q_mid))
    theta2 = arccos(cf.innerprod_q2(q_mid, q2n))
    tmp = arange(2, int((k - 1) / 2) + 1)
    t = zeros(tmp.size)
    alpha[:, :, 0] = q1
    beta[:, :, 0] = beta1

    i = 0
    for tau in range(2, int((k - 1) / 2) + 1):
        t[i] = (tau - 1.0) / ((k - 1) / 2.0)
        qnew = (1 / sin(theta1)) * (sin((1 - t[i]) * theta1) * q1 + sin(t[i] * theta1) * q_mid)
        alpha[:, :, tau - 1] = cf.project_curve(qnew)
        x = cf.q_to_curve(alpha[:, :, tau - 1])
        a = -1 * cf.calculatecentroid(x)
        beta[:, :, tau - 1] = x + tile(a, [T, 1]).T
        i += 1

    alpha[:, :, int((k - 1) / 2)] = q_mid
    beta[:, :, int((k - 1) / 2)] = beta_mid

    i = 0
    for tau in range(int((k - 1) / 2) + 1, k - 1):
        qnew = (1 / sin(theta2)) * (sin((1 - t[i]) * theta2) * q_mid + sin(t[i] * theta2) * q2n)
        alpha[:, :, tau] = cf.project_curve(qnew)
        x = cf.q_to_curve(alpha[:, :, tau])
        a = -1 * cf.calculatecentroid(x)
        beta[:, :, tau] = x + tile(a, [T, 1]).T
        i += 1

    alpha[:, :, k - 1] = q2n
    beta[:, :, k - 1] = beta2n

    return (alpha, beta, O)
Beispiel #21
0
def curve_principal_directions(betamean, mu, K, mode='O', no=3, N=5):
    """
    Computes principal direction of variation specified by no. N is
    Number of shapes away from mean. Creates 2*N+1 shape sequence

    :param betamean: numpy ndarray of shape (n, M) describing the mean curve
    :param mu: numpy ndarray of shape (n, M) describing the mean srvf
    :param K: numpy ndarray of shape (M, M) describing the covariance
    :param mode: Open ('O') or closed curve ('C') (default 'O')
    :param no: number of direction (default 3)
    :param N: number of shapes (2*N+1) (default 5)

    :rtype: tuple of numpy array
    :return pd: principal directions

    """
    n, T = betamean.shape
    modes = ['O', 'C']
    mode = [i for i, x in enumerate(modes) if x == mode]
    if len(mode) == 0:
        mode = 0
    else:
        mode = mode[0]

    U, s, V = svd(K)

    qarray = empty((no, 2*N+1), dtype=object)
    qarray1 = empty(N, dtype=object)
    qarray2 = empty(N, dtype=object)
    pd = empty((no, 2*N+1), dtype=object)
    pd1 = empty(N, dtype=object)
    pd2 = empty(N, dtype=object)
    for m in range(0, no):
        princDir = vstack((U[0:T, m], U[T:2*T, m]))
        v = sqrt(s[m]) * princDir
        q1 = mu
        epsilon = 2./N

        # Forward direction from mean
        for i in range(0, N):
            normv = sqrt(cf.innerprod_q2(v, v))

            if normv < 1e-4:
                q2 = mu
            else:
                q2 = cos(epsilon*normv)*q1 + sin(epsilon*normv)*v/normv
                if mode == 1:
                    q2 = cf.project_curve(q2)

            qarray1[i] = q2
            p = cf.q_to_curve(q2)
            centroid1 = -1*cf.calculatecentroid(p)
            beta_scaled, scale = cf.scale_curve(p + tile(centroid1, [T, 1]).T)
            pd1[i] = beta_scaled

            # Parallel translate tangent vector
            basis2 = cf.find_basis_normal(q2)
            v = cf.parallel_translate(v, q1, q2, basis2, mode)

            q1 = q2

        # Backward direction from mean
        v = -sqrt(s[m])*princDir
        q1 = mu
        for i in range(0, N):
            normv = sqrt(cf.innerprod_q2(v, v))

            if normv < 1e-4:
                q2 = mu
            else:
                q2 = cos(epsilon*normv)*q1+sin(epsilon*normv)*v/normv
                if mode == 1:
                    q2 = cf.project_curve(q2)

            qarray2[i] = q2
            p = cf.q_to_curve(q2)
            centroid1 = -1*cf.calculatecentroid(p)
            beta_scaled, scale = cf.scale_curve(p + tile(centroid1, [T, 1]).T)
            pd2[i] = beta_scaled

            # Parallel translate tangent vector
            basis2 = cf.find_basis_normal(q2)
            v = cf.parallel_translate(v, q1, q2, basis2, mode)

            q1 = q2

        for i in range(0, N):
            qarray[m, i] = qarray2[(N-1)-i]
            pd[m, i] = pd2[(N-1)-i]

        qarray[m, N] = mu
        centroid1 = -1*cf.calculatecentroid(betamean)
        beta_scaled, scale = cf.scale_curve(betamean +
                                            tile(centroid1, [T, 1]).T)
        pd[m, N] = beta_scaled

        for i in range(N+1, 2*N+1):
            qarray[m, i] = qarray1[i-(N+1)]
            pd[m, i] = pd1[i-(N+1)]

    return(pd)
Beispiel #22
0
def curve_karcher_mean(beta, mode='O'):
    """
    This claculates the mean of a set of curves
    :param beta: numpy ndarray of shape (n, M, N) describing N curves
    in R^M
    :param mode: Open ('O') or closed curve ('C') (default 'O')

    :rtype: tuple of numpy array
    :return mu: mean srvf
    :return betamean: mean curve
    :return v: shooting vectors
    :return q: srvfs

    """
    n, T, N = beta.shape
    q = zeros((n, T, N))
    for ii in range(0, N):
        q[:, :, ii] = cf.curve_to_q(beta[:, :, ii])

    modes = ['O', 'C']
    mode = [i for i, x in enumerate(modes) if x == mode]
    if len(mode) == 0:
        mode = 0
    else:
        mode = mode[0]

    # Initialize mu as one of the shapes
    mu = q[:, :, 0]
    betamean = beta[:, :, 0]

    delta = 0.5
    tolv = 1e-4
    told = 5*1e-3
    maxit = 20
    itr = 0
    sumd = zeros(maxit+1)
    v = zeros((n, T, N))
    normvbar = zeros(maxit+1)

    while itr < maxit:
        print("Iteration: %d" % itr)

        mu = mu / sqrt(cf.innerprod_q2(mu, mu))

        sumv = zeros((2, T))
        sumd[itr+1] = 0
        out = Parallel(n_jobs=-1)(delayed(karcher_calc)(beta[:, :, n],
                                  q[:, :, n], betamean, mu, mode) for n in range(N))
        v = zeros((n, T, N))
        for i in range(0, N):
            v[:, :, i] = out[i][0]
            sumd[itr+1] = sumd[itr+1] + out[i][1]**2

        sumv = v.sum(axis=2)

        # Compute average direction of tangent vectors v_i
        vbar = sumv/float(N)

        normvbar[itr] = sqrt(cf.innerprod_q2(vbar, vbar))
        normv = normvbar[itr]

        if normv > tolv and fabs(sumd[itr+1]-sumd[itr]) > told:
            # Update mu in direction of vbar
            mu = cos(delta*normvbar[itr])*mu + sin(delta*normvbar[itr]) * vbar/normvbar[itr]

            if mode == 1:
                mu = cf.project_curve(mu)

            x = cf.q_to_curve(mu)
            a = -1*cf.calculatecentroid(x)
            betamean = x + tile(a, [T, 1]).T
        else:
            break

        itr += 1

    return(mu, betamean, v, q)
Beispiel #23
0
def geod_sphere(beta1, beta2, k=5, scale=False, rotation=True, center=True):
    """
    This function calculates the geodesics between open curves beta1 and
    beta2 with k steps along path

    :param beta1: numpy ndarray of shape (2,M) of M samples
    :param beta2: numpy ndarray of shape (2,M) of M samples
    :param k: number of samples along path (Default = 5)
    :param scale: include length (Default = False)
    :param rotation: include rotation (Default = True)
    :param center: center curves at origin (Default = True)

    :rtype: numpy ndarray
    :return dist: geodesic distance
    :return path: geodesic path
    :return O: rotation matrix

    """
    lam = 0.0
    returnpath = 1
    n, T = beta1.shape

    if center:
        centroid1 = cf.calculatecentroid(beta1)
        beta1 = beta1 - tile(centroid1, [T, 1]).T
        centroid2 = cf.calculatecentroid(beta2)
        beta2 = beta2 - tile(centroid2, [T, 1]).T

    q1, len1, lenq1 = cf.curve_to_q(beta1)
    if scale:
        q2, len2, lenq2 = cf.curve_to_q(beta2)
    beta2, q2n, O1, gamI = cf.find_rotation_and_seed_coord(beta1,
                                                           beta2,
                                                           rotation=rotation)

    # Forming geodesic between the registered curves
    val = cf.innerprod_q2(q1, q2n)
    if val > 1:
        if val < 1.0001:  # assume numerical error
            import warnings
            warnings.warn(
                f"Corrected a numerical error in geod_sphere: rounded {val} to 1"
            )
            val = 1
        else:
            raise Exception(
                f"innerpod_q2 computed an inner product of {val} which is much greater than 1"
            )
    elif val < -1:
        if val > -1.0001:  # assume numerical error
            import warnings
            warnings.warn(
                f"Corrected a numerical error in geod_sphere: rounded {val} to -1"
            )
            val = -1
        else:
            raise Exception(
                f"innerpod_q2 computed an inner product of {val} which is much less than -1"
            )

    dist = arccos(val)
    if isnan(dist):
        raise Exception("geod_sphere computed a dist value which is NaN")

    if returnpath:
        PsiQ = zeros((n, T, k))
        PsiX = zeros((n, T, k))
        for tau in range(0, k):
            if tau == 0:
                tau1 = 0
            else:
                tau1 = tau / (k - 1.)

            s = dist * tau1
            if dist > 0:
                PsiQ[:, :,
                     tau] = (sin(dist - s) * q1 + sin(s) * q2n) / sin(dist)
            elif dist == 0:
                PsiQ[:, :, tau] = (1 - tau1) * q1 + (tau1) * q2n
            else:
                raise Exception("geod_sphere computed a negative distance")

            if scale:
                scl = len1**(1 - tau1) * len2**(tau1)
            else:
                scl = 1
            beta = scl * cf.q_to_curve(PsiQ[:, :, tau])
            if center:
                centroid = cf.calculatecentroid(beta)
                beta = beta - tile(centroid, [T, 1]).T
            PsiX[:, :, tau] = beta

        path = PsiX
    else:
        path = 0

    return (dist, path, PsiQ)
def curve_principal_directions(betamean, mu, K, mode='O', no=3, N=5):
    """
    Computes principal direction of variation specified by no. N is
    Number of shapes away from mean. Creates 2*N+1 shape sequence

    :param betamean: numpy ndarray of shape (n, M) describing the mean curve
    :param mu: numpy ndarray of shape (n, M) describing the mean srvf
    :param K: numpy ndarray of shape (M, M) describing the covariance
    :param mode: Open ('O') or closed curve ('C') (default 'O')
    :param no: number of direction (default 3)
    :param N: number of shapes (2*N+1) (default 5)

    :rtype: tuple of numpy array
    :return pd: principal directions

    """
    n, T = betamean.shape
    modes = ['O', 'C']
    mode = [i for i, x in enumerate(modes) if x == mode]
    if len(mode) == 0:
        mode = 0
    else:
        mode = mode[0]

    U, s, V = svd(K)

    qarray = empty((no, 2*N+1), dtype=object)
    qarray1 = empty(N, dtype=object)
    qarray2 = empty(N, dtype=object)
    pd = empty((no, 2*N+1), dtype=object)
    pd1 = empty(N, dtype=object)
    pd2 = empty(N, dtype=object)
    for m in range(0, no):
        princDir = vstack((U[0:T, m], U[T:2*T, m]))
        v = sqrt(s[m]) * princDir
        q1 = mu
        epsilon = 2./N

        # Forward direction from mean
        for i in range(0, N):
            normv = sqrt(cf.innerprod_q2(v, v))

            if normv < 1e-4:
                q2 = mu
            else:
                q2 = cos(epsilon*normv)*q1 + sin(epsilon*normv)*v/normv
                if mode == 1:
                    q2 = cf.project_curve(q2)

            qarray1[i] = q2
            p = cf.q_to_curve(q2)
            centroid1 = -1*cf.calculatecentroid(p)
            beta_scaled, scale = cf.scale_curve(p + tile(centroid1, [T, 1]).T)
            pd1[i] = beta_scaled

            # Parallel translate tangent vector
            basis2 = cf.find_basis_normal(q2)
            v = cf.parallel_translate(v, q1, q2, basis2, mode)

            q1 = q2

        # Backward direction from mean
        v = -sqrt(s[m])*princDir
        q1 = mu
        for i in range(0, N):
            normv = sqrt(cf.innerprod_q2(v, v))

            if normv < 1e-4:
                q2 = mu
            else:
                q2 = cos(epsilon*normv)*q1+sin(epsilon*normv)*v/normv
                if mode == 1:
                    q2 = cf.project_curve(q2)

            qarray2[i] = q2
            p = cf.q_to_curve(q2)
            centroid1 = -1*cf.calculatecentroid(p)
            beta_scaled, scale = cf.scale_curve(p + tile(centroid1, [T, 1]).T)
            pd2[i] = beta_scaled

            # Parallel translate tangent vector
            basis2 = cf.find_basis_normal(q2)
            v = cf.parallel_translate(v, q1, q2, basis2, mode)

            q1 = q2

        for i in range(0, N):
            qarray[m, i] = qarray2[(N-1)-i]
            pd[m, i] = pd2[(N-1)-i]

        qarray[m, N] = mu
        centroid1 = -1*cf.calculatecentroid(betamean)
        beta_scaled, scale = cf.scale_curve(betamean +
                                            tile(centroid1, [T, 1]).T)
        pd[m, N] = beta_scaled

        for i in range(N+1, 2*N+1):
            qarray[m, i] = qarray1[i-(N+1)]
            pd[m, i] = pd1[i-(N+1)]

    return(pd)