我正在做一个练习以熟悉 scipy.optimize
的python least_squares
。
练习试图将椭圆拟合到2D点的列表中,最小化点和椭圆之间的平方距离之和。
也许数学方法不是正确的方法,但让我们假装很好,因为我认为我的困难在其他地方。
这个想法首先要编写一个计算点和椭圆之间距离的函数,然后在优化器中使用此功能。
i还编程了此距离函数作为最小化问题:给定查询点和椭圆的参数方程式,我寻找连接查询点的最小长度线段和属于椭圆的点,其长度是其长度所需的距离。
import math
import numpy as np
from scipy.optimize import least_squares
# I would like to fit an ellipse to these points (a point in each row):
p=[
[614.0471123474172,289.51195416538405],
[404.85868232180786,509.3183970173126],
[166.5322099316754,335.6006010213824],
[302.6076456817051,71.14357043842081],
[614.094939200562,285.48762845572804]
]
# This is the x of the parametric equation of an ellipse
# centered at (C_x,C_y), with axis R_x and R_y and angle
# of rotation theta. alpha is the parameter that describe
# the ellipse when going from 0 to 2pi.
def x_e(alpha,R_x,R_y,theta,C_x):
return R_x*math.cos(alpha)*math.cos(theta)-R_y*math.sin(alpha)*math.sin(theta)+C_x
# This is the y
def y_e(alpha,R_x,R_y,theta,C_y):
return R_x*math.cos(alpha)*math.sin(theta)+R_y*math.sin(alpha)*math.cos(theta)+C_y
points = np.array(p)
x=points[:,0]
y=points[:,1]
def residual_for_distance(params,x_q,y_q,R_x,R_y,theta,C_x,C_y):
alpha = params[0]
return (x_q-x_e(alpha,R_x,R_y,theta,C_x))**2+(y_q-y_e(alpha,R_x,R_y,theta,C_y))**2
def ellipse_point_distance(x_q,y_q,R_x,R_y,C_x,C_y,theta):
params_0 = np.array([math.atan2(y_q-C_y,x_q-C_x)])
result = least_squares(residual_for_distance,params_0,args=(x_q,y_q,R_x,R_y,theta,C_x,C_y))
d=math.sqrt(residual_for_distance(result.x,x_q,y_q,R_x,R_y,theta,C_x,C_y))
return d
现在我在一个简单的情况下测试ellipse_point_distance
:
x_q=1
y_q=1
R_x=1
R_y=1
C_x=0
C_y=0
theta=0
print(ellipse_point_distance(x_q,y_q,R_x,R_y,C_x,C_y,theta))
我得到了0.414213562373
,看起来不错,所以让我们继续解决最小化问题:
def residual_for_fit(params,x,y):
R_x = params[0]
R_y = params[1]
C_x = params[2]
C_y = params[3]
theta = params[4]
return ellipse_point_distance(x,y,R_x,R_y,C_x,C_y,theta)
params_0 = np.array([227,227,x.mean(),y.mean(),0])
result = least_squares(residual_for_fit,params_0,args=(x,y),verbose=1)
我得到此错误:
Traceback (most recent call last):
File "fit_ellipse.py", line 57, in <module>
result = least_squares(residual_for_fit,params_0,args=(x,y),verbose=1)
File "/home/aj/anaconda2/lib/python2.7/site-packages/scipy/optimize/_lsq/least_squares.py", line 799, in least_squares
f0 = fun_wrapped(x0)
File "/home/aj/anaconda2/lib/python2.7/site-packages/scipy/optimize/_lsq/least_squares.py", line 794, in fun_wrapped
return np.atleast_1d(fun(x, *args, **kwargs))
File "fit_ellipse.py", line 54, in residual_for_fit
return ellipse_point_distance(x,y,R_x,R_y,C_x,C_y,theta)
File "fit_ellipse.py", line 33, in ellipse_point_distance
params_0 = np.array([math.atan2(y_q-C_y,x_q-C_x)])
TypeError: only size-1 arrays can be converted to Python scalars
快速查看TypeError:在试图拟合指数数据的同时,只能将Length-1数组阵列转换为Python标量,我认为我解决了问题:
def ellipse_point_distance_2(x_q,y_q,R_x,R_y,C_x,C_y,theta):
params_0 = np.array([np.arctan2(y_q-C_y,x_q-C_x)])
result = least_squares(residual_for_distance,params_0,args=(x_q,y_q,R_x,R_y,theta,C_x,C_y))
d=math.sqrt(residual_for_distance(result.x,x_q,y_q,R_x,R_y,theta,C_x,C_y))
return d
我刚刚用np.arctan2
代替math.atan2
,希望获得最佳:
print(ellipse_point_distance_2(x_q,y_q,R_x,R_y,C_x,C_y,theta))
ellipse_point_distance_2
仍然很好(给出0.414213562373
),所以我们在这里:
def residual_for_fit_2(params,x,y):
R_x = params[0]
R_y = params[1]
C_x = params[2]
C_y = params[3]
theta = params[4]
return ellipse_point_distance_2(x,y,R_x,R_y,C_x,C_y,theta)
params_0 = np.array([227,227,x.mean(),y.mean(),0])
result = least_squares(residual_for_fit_2,params_0,args=(x,y),verbose=1)
但是现在我有一个不同的错误:
Traceback (most recent call last):
File "fit_ellipse.py", line 76, in <module>
result = least_squares(residual_for_fit_2,params_0,args=(x,y),verbose=1)
File "/home/aj/anaconda2/lib/python2.7/site-packages/scipy/optimize/_lsq/least_squares.py", line 799, in least_squares
f0 = fun_wrapped(x0)
File "/home/aj/anaconda2/lib/python2.7/site-packages/scipy/optimize/_lsq/least_squares.py", line 794, in fun_wrapped
return np.atleast_1d(fun(x, *args, **kwargs))
File "fit_ellipse.py", line 73, in residual_for_fit_2
return ellipse_point_distance_2(x,y,R_x,R_y,C_x,C_y,theta)
File "fit_ellipse.py", line 61, in ellipse_point_distance_2
result = least_squares(residual_for_distance,params_0,args=(x_q,y_q,R_x,R_y,theta,C_x,C_y))
File "/home/aj/anaconda2/lib/python2.7/site-packages/scipy/optimize/_lsq/least_squares.py", line 772, in least_squares
raise ValueError("`x0` must have at most 1 dimension.")
ValueError: `x0` must have at most 1 dimension.
现在我有点困惑...我认为我的问题与矢量化问题有关,但我无法解决。
在此功能中,您需要更改两行:
def ellipse_point_distance(x_q,y_q,R_x,R_y,C_x,C_y,theta):
# params_0 = np.array([math.atan2(y_q-C_y,x_q-C_x)])
params_0 = np.array(math.atan2(y_q-C_y,x_q-C_x)) # removed inner square brackets
result = least_squares(residual_for_distance,params_0,args=(x_q,y_q,R_x,R_y,theta,C_x,C_y))
# d=math.sqrt(residual_for_distance(result.x,x_q,y_q,R_x,R_y,theta,C_x,C_y))
d=np.sqrt(residual_for_distance(result.x,x_q,y_q,R_x,R_y,theta,C_x,C_y)) # changed from math.sqrt to np.sqrt
return d
我认为您的代码仍然不起作用,但是现在它对我来说没有任何错误。如果您难以获得least_squares
做您想做的事,您可能想发布另一个问题。