问题
Well, approximating a circle with a polygon and Pythagoras' story may be well known. But what about the other way around?
I have some polygons, that should be in fact circles. However, due to measurement errors they are not. So, what I'm looking for is the circle that best "approximates" the given polygon.
In the following figure we can see two different examples.

My first Ansatz was to find the maximum distance of the points to the center as well as the minimum. The circle we are looking for is maybe somewhere in between.
Is there any algorithm out there for this problem?
回答1:
I would use scipy
to best-"fit" a circle onto my points. You can get a starting point for the center and radius by a simple center-of-mass calculation. This works well if the points are uniformly distributed over the circle. If they are not, as in the example below, it is still better than nothing!
The fitting function is simple because a circle is simple. You only need to find the radial distance from your fit circle to your points as the tangent (radial) surface will always be the best fit.
import numpy as np
from scipy.spatial.distance import cdist
from scipy.optimize import fmin
import scipy
# Draw a fuzzy circle to test
N = 15
THETA = np.random.random(15)*2*np.pi
R = 1.5 + (.1*np.random.random(15) - .05)
X = R*np.cos(THETA) + 5
Y = R*np.sin(THETA) - 2
# Choose the inital center of fit circle as the CM
xm = X.mean()
ym = Y.mean()
# Choose the inital radius as the average distance to the CM
cm = np.array([xm,ym]).reshape(1,2)
rm = cdist(cm, np.array([X,Y]).T).mean()
# Best fit a circle to these points
def err((w,v,r)):
pts = [np.linalg.norm([x-w,y-v])-r for x,y in zip(X,Y)]
return (np.array(pts)**2).sum()
xf,yf,rf = scipy.optimize.fmin(err,[xm,ym,rm])
# Viszualize the results
import pylab as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# Show the inital guess circle
circ = plt.Circle((xm, ym), radius=rm, color='y',lw=2,alpha=.5)
ax.add_patch(circ)
# Show the fit circle
circ = plt.Circle((xf, yf), radius=rf, color='b',lw=2,alpha=.5)
ax.add_patch(circ)
plt.axis('equal')
plt.scatter(X,Y)
plt.show()

回答2:
Perhaps a simple algorithm would be firstly to calculate the centroid of the points (providing they are usually roughly regularly spaced). This is the circle centre. Once you have that you can calculate the mean radius of the points, giving the radius of the circle.
A more sophisticated answer might be to do a simple minimisation, where you minimise the sum of the distances of the points to the edge of the circle (or distance squared).
回答3:
There are two different O(n) algorithms for determining the smallest circle you draw that encompasses a series of points on the wikipedia page smallest-circle problem. From here it should be fairly easy to draw the second circle, simply determine the center of the circle you found previously, and find the point closest to that point. The radius of the second circle is that.
This may not be exactly what you want, but this is how I would start.
回答4:
That problem might be the same as the Smallest-circle problem.
But since you have measurement errors which could include outliers, then RANSAC is a good option instead. See http://cs.gmu.edu/~kosecka/cs482/lect-fitting.pdf for a overview of the method (as well other basic techniques), in http://www.asl.ethz.ch/education/master/info-process-rob/Hough-Ransac.pdf there is more information dedicated to circle fitting.
回答5:
It's quite easy to find some approximation:
def find_circle_deterministically(x,y):
center = x.mean(), y.mean()
radius = np.sqrt((x-center[0])**2 + (y-center[1])**2).mean()
return center, radius
Explained: put the center of the circle to the mean x and mean y of your points. Then, for each point, determine the distance to the center and take the mean over all points. That's your radius.
This complete script:
import numpy as np
import matplotlib.pyplot as plt
n_points = 10
radius = 4
noise_std = 0.3
angles = np.linspace(0,2*np.pi,n_points,False)
x = np.cos(angles) * radius
y = np.sin(angles) * radius
x += np.random.normal(0,noise_std,x.shape)
y += np.random.normal(0,noise_std,y.shape)
plt.axes(aspect="equal")
plt.plot(x,y,"bx")
def find_circle_deterministically(x,y):
center = x.mean(), y.mean()
radius = np.sqrt((x-center[0])**2 + (y-center[1])**2).mean()
return center, radius
center, radius2 = find_circle_deterministically(x,y)
angles2 = np.linspace(0,2*np.pi,100,True)
x2 = center[0] + np.cos(angles2) * radius2
y2 = center[1] + np.sin(angles2) * radius2
plt.plot(x2,y2,"r-")
plt.show()
produces this plot:

This will work good as you have polygons with measurement errors. If your points are not approximately equally distributed over the angles [0,2pi[
, it will perform poorly.
More generally, you could use optimization.
来源:https://stackoverflow.com/questions/14834693/approximating-a-polygon-with-a-circle