Я использую минимальный объем окружения для размещения эллипсоида вокруг набора трехмерных точек (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()