Fitting an ellipse to a set of 2-D points

落花浮王杯 提交于 2021-01-28 03:58:06

问题


I'm trying to fit an ellipse to a set of points described by x and y coordinates.

I found a detailed explanation of how to do it here http://nicky.vanforeest.com/misc/fitEllipse/fitEllipse.html and tried out the code, but it doesn't seem to work.

It finds the center correctly, but the angle and axes are completely wrong, as you can see in this image: https://i.imgur.com/VLEeNKQ.png

The red points are my data points and the blue aptch is an ellipse drawn from the obtained parameters. Now, the data isn't a perfect ellipse, but the fit is nowhere near it. I'd like to get the fit closer to the actual data.

Here's the code in question.

import numpy as np
from numpy.linalg import eig, inv

def fitEllipse(x,y):
    x = x[:,np.newaxis]
    y = y[:,np.newaxis]
    D =  np.hstack((x*x, x*y, y*y, x, y, np.ones_like(x)))
    S = np.dot(D.T,D)
    C = np.zeros([6,6])
    C[0,2] = C[2,0] = 2; C[1,1] = -1
    E, V =  eig(np.dot(inv(S), C))
    n = np.argmax(np.abs(E))
    a = V[:,n]
    return a

def ellipse_center(a):
    b,c,d,f,g,a = a[1]/2, a[2], a[3]/2, a[4]/2, a[5], a[0]
    num = b*b-a*c
    x0=(c*d-b*f)/num
    y0=(a*f-b*d)/num
    return np.array([x0,y0])


def ellipse_angle_of_rotation( a ):
    b,c,d,f,g,a = a[1]/2, a[2], a[3]/2, a[4]/2, a[5], a[0]
    return 0.5*np.arctan(2*b/(a-c))


def ellipse_axis_length( a ):
    b,c,d,f,g,a = a[1]/2, a[2], a[3]/2, a[4]/2, a[5], a[0]
    up = 2*(a*f*f+c*d*d+g*b*b-2*b*d*f-a*c*g)
    down1=(b*b-a*c)*( (c-a)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a))
    down2=(b*b-a*c)*( (a-c)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a))
    res1=np.sqrt(up/down1)
    res2=np.sqrt(up/down2)
    return np.array([res1, res2])

def ellipse_angle_of_rotation2( a ):
    b,c,d,f,g,a = a[1]/2, a[2], a[3]/2, a[4]/2, a[5], a[0]
    if b == 0:
        if a > c:
            return 0
        else:
            return np.pi/2
    else: 
        if a > c:
            return np.arctan(2*b/(a-c))/2
        else:
            return np.pi/2 + np.arctan(2*b/(a-c))/2

And here's my full dataset. Does anyone have any idea why it's not being fitted correctly?

# --------------------------------------------------------------------------
x = np.array([ 5727.53135,  7147.62235, 10330.93573,  8711.17228, 7630.40262,
        4777.24983,  4828.27655,  9449.94416,  5203.81323,  6299.44811,
        6494.21906])

y = np.array([67157.77567 , 66568.50068 , 55922.56257 , 54887.47348 ,
       65150.14064 , 66529.91705 , 65934.25548 , 55351.57612 ,
       63123.5103  , 67181.141725, 56321.36025 ])
# -----------------------------------------------------------------------------


a = fitEllipse(x,y)
center = ellipse_center(a)
#phi = ellipse_angle_of_rotation(a)
phi = ellipse_angle_of_rotation2(a)
axes = ellipse_axis_length(a)

# get the individual axes
a, b = axes

from matplotlib.patches import Ellipse
import matplotlib.pyplot as plt

ell = Ellipse(center, a, b, phi)

fig, ax = plt.subplots(subplot_kw={'aspect': 'equal'})
ax.add_artist(ell)
ell.set_clip_box(ax.bbox)
ax.set_xlim(0, 100000)
ax.set_ylim(0, 100000)
plt.show()
scat = plt.scatter(x, y, c = "r")

回答1:


Your code is absolutely fine, it is the patch definition that makes the trouble here. a and b of Ellipse are the full width. So you have to multiply the result of your fit by 2. Moreover the angle is in degree so you have to multiply by 180/np.pi. Finally, the zero is not at the same position, so you have to add 90.

Long story short change

Ellipse(center, a, b, phi)

to

ell = Ellipse(center, 2 * a, 2 * b,  phi * 180 / np.pi + 90 ) 

and you are good.



来源:https://stackoverflow.com/questions/52818206/fitting-an-ellipse-to-a-set-of-2-d-points

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!