От 3D-эллипсоида до 2D-эллипса с использованием Python - PullRequest
0 голосов
/ 18 февраля 2020

Я использую минимальный объем окружения для размещения эллипсоида вокруг набора трехмерных точек (x, y, z). Я хочу перейти от трехмерного вида, для которого я использую ax.wireframe, к двухмерному виду в заданной точке на оси z. В идеале у меня будет ползунок, который отображает точки и эллипс при его перемещении вдоль z-axis. Пока у меня есть:

  • центроид (x, y, z)
  • матрица, описывающая эллипсоид
  • радиусы (rx, ry, rz)

Думаю, мне не хватает угла поворота в 2D. Вот несколько вариантов построения моего эллипса: matplotlib.patches.Ellipse https://matplotlib.org/3.1.3/api/_as_gen/matplotlib.patches.Ellipse.html, cv2.ellipse и scikit.draw.Ellipse https://scikit-image.org/docs/dev/api/skimage.draw.html#skimage .draw.ellipse .

Код пока:

from matplotlib.patches import Ellipse
import os
from skimage.draw import ellipsoid, ellipsoid_stats, ellipse
from os import walk
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import numpy.linalg as la

def mvee(points, tol=0.0001, flag='outer'):
    """
    Find the minimum volume ellipse.
    Return A, c where the equation for the ellipse given in "center form" is
    (x-c).T * A * (x-c) = 1
    """
    points = np.asmatrix(points)
    N, d = points.shape
    Q = np.column_stack((points, np.ones(N))).T
    err = tol + 1.0
    u = np.ones(N) / N
    #  inner ellipse: if d < 1+tol_dist
    #  outer ellipse : while err > tol:
    if flag == 'inner':
        while err < tol:
            # assert u.sum() == 1 # invariant
            X = Q * np.diag(u) * Q.T
            M = np.diag(Q.T * la.inv(X) * Q)
            jdx = np.argmax(M)
            step_size = (M[jdx] - d - 1.0) / ((d + 1) * (M[jdx] - 1.0))
            new_u = (1 - step_size) * u
            new_u[jdx] += step_size
            err = la.norm(new_u - u)
            u = new_u
    elif flag == 'outer':
        while err > tol:
            # assert u.sum() == 1 # invariant
            X = Q * np.diag(u) * Q.T
            M = np.diag(Q.T * la.inv(X) * Q)
            jdx = np.argmax(M)
            step_size = (M[jdx] - d - 1.0) / ((d + 1) * (M[jdx] - 1.0))
            new_u = (1 - step_size) * u
            new_u[jdx] += step_size
            err = la.norm(new_u - u)
            u = new_u
    c = u * points
    A = la.inv(points.T * np.diag(u) * points - c.T * c) / d
    return np.asarray(A), np.squeeze(np.asarray(c))

def plot_3D_ellipsoid(A, centroid, color):
    U, D, V = la.svd(A)
    rx, ry, rz = 1. / np.sqrt(D)
    print(rx, ry, rz)
    u, v = np.mgrid[0:2 * pi:20j, -pi / 2:pi / 2:10j]
    x = rx * cos(u) * cos(v)
    y = ry * sin(u) * cos(v)
    z = rz * sin(v)
    E = np.dstack((x, y, z))
    E = np.dot(E, V) + centroid
    x, y, z = np.rollaxis(E, axis=-1)

    ax.plot_wireframe(x, y, z, cstride=1, rstride=1, color=color, alpha=0.2)


if __name__ == '__main__':
    points = np.array([[0.53135758, -0.25818091, -0.32382715],
                       [0.58368177, -0.3286576, -0.23854156, ],
                       [0.28741533, -0.03066228, -0.94294771],
                       [0.65685862, -0.09220681, -0.60347573],
                       [0.63137604, -0.22978685, -0.27479238],
                       [0.59683195, -0.15111101, -0.40536606],
                       [0.68646128, -0.046802, -0.68407367],
                       [0.62311759, -0.0101013, -0.1863324],
                       [0.62311759, -0.2101013, -0.1863324]])
 A_outer, centroid_outer = mvee(points, flag='outer')
    A_inner, centroid_inner = mvee(points, flag='inner')

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.scatter(points[:, 0], points[:, 1], points[:, 2], c='g')
    plot_ellipsoid(A_outer, centroid_outer, 'blue')
    plot_ellipsoid(A_inner, centroid_inner, 'red')
    plt.show()

enter image description here

...