【问题标题】:how to extract center coordinates, height, width and phi of an ellipse from SymPy to plot a fitted ellipse?如何从 SymPy 中提取椭圆的中心坐标、高度、宽度和 phi 以绘制拟合椭圆?
【发布时间】:2021-12-13 22:06:11
【问题描述】:

我一直在使用 lsq-ellipse 包,通过以下代码获取椭圆的坐标:

from ellipse import LsqEllipse
from matplotlib.patches import Ellipse
coords_D0 = np.array(coords_D0)
reg = LsqEllipse().fit(coords_D0)
center_D0, width_D0, height_D0, phi_D0 = reg.as_parameters()
print(f'center: {center_D0[0]:.3f}, {center_D0[1]:.3f}')
print(f'width: {width_D0:.3f}')
print(f'height: {height_D0:.3f}')
print(f'phi: {phi_D0:.3f}')

但是,我的 coords_D0 变量由三个坐标组成,导致以下错误:

ValueError: Received too few samplesGot 3 features, 5 or more required.

但是,在查看了一些包和在线后,我发现 sympy 也可以做椭圆,我知道你可以从 centre, vradius and hradius 中提取em>sympy。但是,我想知道如何从 sympy 中获取 width、height 和 phi,它是否与 Ellipse 中使用的 lsq-ellipse 包相同matplotlib?我使用 matplotlib 中 lsq-ellipse 包中的值来形成椭圆部分,它可以在以下代码行中找到:

代码:

ellipse_D0 = Ellipse(xy=center_D0, width=2*width_D0, height=2*height_D0, angle=np.rad2deg(phi_D0),edgecolor='b', fc='None', lw=2, label='Fit', zorder=2)

我的坐标如下:

coords_D0 =
-1.98976     -1.91574
-0.0157721    2.5438
2.00553      -0.628061

# another points
coords_D1 =
-0.195518   0.0273673
-0.655686   -1.45848
-0.447061   -0.168108

# another points
coords_D2 =
-2.28529    0.91896
-2.43207    0.446211
-2.23044    0.200087

附加问题:

有没有办法将椭圆拟合到这些坐标(通常是 3 个坐标或更多)?

【问题讨论】:

  • 你使用fit。两个交叉椭圆可以有 4 个共同点,因此没有唯一的解决方案给出小于 5。您阅读过文档吗?
  • @mikuszefski 你在暗示哪个包?你的意思是适合 Lsq 包我收到错误,因为需要更多的功能超过 5 个。
  • 嗨,如果我做对了,LsqEllipse().fit() 方法会使用(x, y) 的列表来拟合椭圆。要给出正确的唯一结果,您需要 5 个或更多值。所以很自然地你会得到一个错误。
  • @mikuszefski 我明白了。我一直在网上查找这些链接 (stackoverflow.com/questions/39693869/…) (stackoverflow.com/questions/47873759/…) (stackoverflow.com/questions/52818206/…) (stackoverflow.com/questions/39693869/…),但是,我无法使用我的坐标获得正确的中心点、宽度、高度和 phi。
  • 所以我们很清楚你不能拟合提供 3 个点的椭圆。那我们说的是哪个坐标?我想我放了一个易于遵循的代码here

标签: python matplotlib sympy ellipse data-fitting


【解决方案1】:

假设 OP 是关于最小体积封闭椭圆,我建议以下解决方案。

#! /usr/bin/python3
# coding=utf-8
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse

import numpy as np

from mymodules3.mvee import mvee

coords= list()
coords.append( np.array([
    -1.98976,     -1.91574,
    -0.0157721,    2.5438,
    2.00553,      -0.628061
]).reshape(3,-1) )

coords.append(  np.array([
    -0.195518,   0.0273673,
    -0.655686,   -1.4584,8
    -0.447061,   -0.168108,
]).reshape(3,-1)
)

coords.append( np.array([
    -2.28529,    0.91896,
    -2.43207,    0.446211,
    -2.23044,    0.200087
]).reshape(3,-1)
)


fig = plt.figure()
ax = fig.add_subplot( 1, 1, 1 )

for i, col in enumerate( ['k', 'g', 'm'] ):
    sol = mvee( coords[i] )

    e =Ellipse(
        sol[0],
        width=2 * sol[1][0],
        height=2 * sol[1][1],
        angle=sol[2] *  180/3.1415926,
        color=col, alpha=0.5,
        zorder = -1000 + i
    )
    ax.scatter( coords[i][:,0], coords[i][:,1], c=col, zorder=10 * i )
    ax.add_artist( e )
plt.show()

提供

mvee 是基于 SE answer 的类似问题。

"""
NMI : 2021-11-11 
Minimum Volume Enclosing Ellipsoids, see e.g.
NIMA MOSHTAGH : MINIMUM VOLUME ENCLOSING ELLIPSOIDS
or 
Linus Källberg : Minimum_Enclosing_Balls_and_Ellipsoids (Thesis)
"""
from warnings import warn

from numpy import pi
from numpy import sqrt
from numpy import arccos
from numpy import dot, outer
from numpy import diag, transpose
from numpy import append
from numpy import asarray
from numpy import ones
from numpy import argmax

from numpy.linalg import inv
from numpy.linalg import norm
from numpy.linalg import eig


def mvee( data, tolerance=1e-4, maxcnt=1000 ):
    """
    param data: list of xy data points
    param tolerance: termination condition for iterative approximation
    param maxcnt: maximum number of iterations
    type data: iterable of float
    type tolerance: float
    return: (offset, semiaxis, angle)
    return type: ( (float, float), (float, float), float )
    """
    locdata = asarray( data )
    N = len( locdata )
    if not locdata.shape == ( N, 2):
        raise ValueError ( " data must be of shape( n, 2 )" )
    if tolerance >= 1 or tolerance <= 0:
        raise ValueError (" 0 < tolerance < 1 required")
    if not isinstance( maxcnt, int ):
        raise TypeError
    if not maxcnt > 0:
        raise ValueError
    count = 1
    err = 1
    d = 2
    d1 = d + 1
    u = ones( N ) / N
    P = transpose( locdata )
    Q = append( P, ones( N ) ).reshape( 3, -1 )
    while ( err > tolerance):
        X = dot( Q, dot( diag( u ), transpose( Q ) ) )
        M = diag( 
            dot( 
                transpose( Q ),
                dot(
                    inv( X ),
                    Q
                )
            )
        )
        maximum = max( M )
        j = argmax( M )
        step_size = ( maximum - d1 ) / ( d1 * ( maximum - 1 ) )
        new_u = ( 1 - step_size ) * u
        new_u[ j ] += step_size
        err = norm( new_u - u )
        count = count + 1
        u = new_u
        if count > maxcnt:
            warn(
                "Process did not converge in {} steps".format(
                    count - 1
                ),
                UserWarning
            )
            break
    U = diag( u )
    c = dot( P,  u )
    A = inv(
        dot(
            P,
            dot( U, transpose( P ) )
        ) - outer( c, c )
    ) / d
    E, V = eig( A )
    phiopt = arccos( V[ 0, 0 ] )
    if V[ 0, 1 ] < 0: 
        phiopt = 2 * pi - phiopt
    ### cw vs ccw and periodicity of pi
    phiopt = -phiopt % pi
    sol =  (  c, sqrt( 1.0 / E ), phiopt)
    return sol

【讨论】:

  • 感谢您的回答。我查看了代码、链接并设法让它工作。我只是有一个关于容差和maxcnt的问题。我可以使用哪些参数将椭圆拟合到数据点?
  • @WDpad159 如您所见,我的图像使用标准设置。 maxcnt 不应该是您期望收敛的问题,并且循环应该在达到此之前终止。我没有完全理解这个理论,所以我对这种宽容在细节上的含义没有很好的感觉。当然,这是u 向量中长度的变化。它以某种方式对A 的特征值进行编码。由于这是一种梯度方法,它正在测试梯度变化的程度。如果你对极值的形状有一个很好的假设,可以暗示你离你有多远......
  • ...如果您现在有线索,它可能是一个非常浅的极值,即使梯度非常小,您也可以在几英里之外。 tldr;我不知道。玩它。
猜你喜欢
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 2018-01-23
  • 1970-01-01
  • 1970-01-01
  • 2018-02-05
  • 2015-12-01
  • 2014-03-29
相关资源
最近更新 更多