简体   繁体   English

如何使用python中的最小二乘拟合找到圆心?

[英]How to find the center of circle using the least square fit in python?

I'm trying to fit some data points in order to find the center of a circle.我正在尝试拟合一些数据点以找到圆心。 All of the following points are noisy data points around the circumference of the circle:以下所有点都是围绕圆周的噪声数据点:

data = [(2.2176383052987667, 4.218574252410221),
(3.3041214516913033, 5.223500807396272),
(4.280815855023374, 6.461487709813785),
(4.946375258539319, 7.606952538212697),
(5.382428804463699, 9.045717060494576),
(5.752578028217334, 10.613667377465823),
(5.547729017414035, 11.92662513852466),
(5.260208374620305, 13.57722448066025),
(4.642126672822957, 14.88238955729078),
(3.820310290976751, 16.10605425390148),
(2.8099420132544024, 17.225880123445773),
(1.5731539516426183, 18.17052077121059),
(0.31752822350872545, 18.75261434891438),
(-1.2408437559671106, 19.119355580780265),
(-2.680901948575409, 19.15018791257732),
(-4.190406775175328, 19.001321726517297),
(-5.533990404926917, 18.64857428377178),
(-6.903383826792998, 17.730112542165955),
(-8.082883753215347, 16.928080323602334),
(-9.138397388219254, 15.84088004983959),
(-9.92610373064812, 14.380575762984085),
(-10.358670204629814, 13.018017342781242),
(-10.600053524240247, 11.387283417089911),
(-10.463673966507077, 10.107554951600699),
(-10.179820255235496, 8.429558128401448),
(-9.572153386953028, 7.1976672709797676),
(-8.641475289758178, 5.8312286526738175),
(-7.665976739804268, 4.782663065707469),
(-6.493033077746997, 3.8549965442534684),
(-5.092340806635571, 3.384419909199452),
(-3.6530364510489073, 2.992272643733981),
(-2.1522365767310796, 3.020780664301393),
(-0.6855406924835704, 3.0767643753777447),
(0.7848958776292426, 3.6196842530995332),
(2.0614188482646947, 4.32795711960546),
(3.2705467984691508, 5.295836809444288),
(4.359297538484424, 6.378324784240816),
(4.981264502955681, 7.823851404553242)]

I was trying to use some library like Scipy , but I'm having trouble using the available functions.我试图使用一些库,如Scipy ,但我在使用可用函数时遇到问题。

There is for example:有例如:

#  == METHOD 2 ==
from scipy      import optimize

method_2 = "leastsq"

def calc_R(xc, yc):
    """ calculate the distance of each 2D points from the center (xc, yc) """
    return sqrt((x-xc)**2 + (y-yc)**2)

def f_2(c):
    """ calculate the algebraic distance between the data points and the mean circle centered at c=(xc, yc) """
    Ri = calc_R(*c)
    return Ri - Ri.mean()

center_estimate = x_m, y_m
center_2, ier = optimize.leastsq(f_2, center_estimate)

xc_2, yc_2 = center_2
Ri_2       = calc_R(*center_2)
R_2        = Ri_2.mean()
residu_2   = sum((Ri_2 - R_2)**2)

But this seems to be using a single xy?但这似乎使用单个xy? Any ideas on how to plug this function to my data example?关于如何将此功能插入我的数据示例的任何想法?

Your data points seem fairly clean and I see no outliers, so many circle fitting algorithms will work.您的数据点看起来相当干净,我看不到异常值,因此许多圆拟合算法都可以使用。

I recommend you to start with the Coope method, which works by magically linearizing the problem:我建议您从 Coope 方法开始,该方法通过神奇地线性化问题来工作:

(X-Xc)² + (Y-Yc)² = R² is rewritten as (X-Xc)² + (Y-Yc)² = R²重写为

2 Xc X + 2 Yc Y + R² - Xc² - Yc² = X² + Y² , then 2 Xc X + 2 Yc Y + R² - Xc² - Yc² = X² + Y² ,然后

AX + BY + C = X² + Y² , solved by linear least squares. AX + BY + C = X² + Y² ,通过线性最小二乘法求解。

I do not have any experience fitting circles, but I have worked with the more general case of fitting ellipses.我没有任何拟合圆的经验,但我已经处理过拟合椭圆的更一般情况。 Doing this in a correct way with noisy data is not trivial.以正确的方式处理嘈杂的数据并非易事。 For this problem, the algorithm described in Numerically stable direct least squares fitting of ellipses by Halir and Flusser works pretty well.对于这个问题,Halir 和 Flusser在椭圆数值稳定直接最小二乘拟合中描述的算法效果很好。 The paper includes Matlab code, which should be straightforward to translate to Numpy.该论文包括 Matlab 代码,应该可以直接转换为 Numpy。 Maybe you could use this algorithm to fit an ellipse and then take the average of the two axis as the radius or so.也许你可以使用这个算法来拟合一个椭圆,然后取两个轴的平均值作为半径左右。 Some of the references in the paper also mention fitting circles, you might want to look those up.论文中的一些参考文献还提到了拟合圆,您可能需要查找这些。

As a follow up to Bas Swinckels post, I figured I'd post my code implementing the Halir and Flusser method of fitting an ellipse作为 Bas Swinckels 帖子的后续,我想我会发布我的代码,实现了拟合椭圆的 Halir 和 Flusser 方法

https://github.com/bdhammel/least-squares-ellipse-fitting https://github.com/bdhammel/least-squares-ellipse-fitting

Using the above code you can find the center with the following method.使用上面的代码,您可以通过以下方法找到中心。

from ellipses import LSqEllipse
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse

lsqe = LSqEllipse()
lsqe.fit(data)
center, width, height, phi = lsqe.parameters()

plt.close('all')
fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(111)
ax.axis('equal')
ax.plot(data[0], data[1], 'ro', label='test data', zorder=1)

ellipse = Ellipse(xy=center, width=2*width, height=2*height, angle=np.rad2deg(phi),
               edgecolor='b', fc='None', lw=2, label='Fit', zorder = 2)
ax.add_patch(ellipse)

plt.legend()
plt.show()

在此处输入图片说明

I know this is an old question, but in 2019 there's a circle fitting library in python called circle-fit .我知道这是一个老问题,但在 2019 年,python 中有一个圆拟合库,名为circle-fit

pip install circle-fit

you can use one of two algorithms to solve, least_squares_circle or hyper_fit .您可以使用两种算法之一来解决, least_squares_circlehyper_fit

import circle_fit as cf
xc,yc,r,_ = cf.least_squares_circle((data)

then you get xc, yc as the coordinate pair for the solution circle center.然后你得到xc, yc作为解圆中心的坐标对。

Ian Coope's algorithm (paper here ) linearizes the problem using a variable substitution. Ian Coope 的算法(此处为论文)使用变量替换将问题线性化。 It's the most generally robust and fastest algorithm I've come across for circle fitting so far.这是迄今为止我在圆拟合中遇到的最普遍、最快速的算法。 I've implemented the algorithm in python in scikit-guess .我已经在 python 中的scikit-guess 中实现了该算法。 Using the function skg.nsphere_fit for a one-line solution:将函数skg.nsphere_fit用于单行解决方案:

>>> r, c = skg.nsphere_fit(data)
>>> r
8.138962707494084
>>> c
array([-2.45595128, 11.04352796])

Here is a plot of the result:这是结果图:

t = np.linspace(0, 2 * np.pi, 1000, endpoint=True)
plt.scatter(*np.array(data).T, color='r')
plt.plot(r * np.cos(t) + c[0], r * np.sin(t) + c[1])
plt.axis('equal')
plt.show()

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM