Аппроксимация многоугольника окружностью

ну, аппроксимация круга многоугольником и история Пифагора могут быть хорошо известны. Но как насчет обратного пути?

У меня есть несколько полигонов, которые должны быть на самом деле круги. Однако из-за погрешностей измерений их нет. Итак, я ищу круг, который лучше всего "аппроксимирует" данный многоугольник.

на следующем рисунке мы видим два разных примера.

enter image description here

мой первый Анзац должен был найти максимальное расстояние точек до центра, а также минимальное. Круг, который мы ищем, может быть где-то посередине.

есть ли алгоритм для этой проблемы?

5 ответов


Я хотел бы использовать scipy чтобы лучше всего - "вписать" круг в мои точки. Вы можете получить начальную точку для центра и радиуса простым вычислением центра масс. Это хорошо работает, если точки равномерно распределены по окружности. Если их нет, как в приведенном ниже примере, это все равно лучше, чем ничего!

функция подгонки проста, потому что круг прост. Вам нужно только найти радиальное расстояние от вашего круга до ваших точек в качестве касательной (радиальной) поверхности всегда будет лучше всего подходит.

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()

enter image description here


возможно, простым алгоритмом было бы сначала вычислить центроид точек (при условии, что они обычно расположены примерно равномерно). Это центр круга. После того, как у вас есть, что вы можете рассчитать средний радиус точек, давая радиус круга.

более сложным ответом может быть простая минимизация, где вы минимизируете сумму расстояний точек до края круга (или расстояние в квадрате).


существует два разных алгоритма O(n) для определения наименьшего круга, который вы рисуете, который охватывает ряд точек на странице Википедии задача наименьшего круга. Отсюда должно быть довольно легко нарисовать второй круг, просто определить центр круга, который вы нашли ранее, и найти точку, ближайшую к этой точке. Радиус второго круга таков.

Это может быть не совсем то, что вы хотите, но вот как я бы начал.


эта проблема может быть такой же, как задача наименьшего круга.

но поскольку у вас есть ошибки измерения, которые могут включать выбросы, то RANSAC-хороший вариант. См.http://cs.gmu.edu / ~kosecka / cs482 / lect-fitting.pdf для обзора метода (а также других основных методов), в http://www.asl.ethz.ch/education/master/info-process-rob/Hough-Ransac.pdf больше информации предназначенной к кругу примерка.


довольно легко найти некоторое приближение:

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

объяснено: поместите центр круга в среднее значение x и среднее значение y ваших точек. Затем для каждой точки определите расстояние до центра и возьмите среднее значение по всем точкам. Это твой радиус.

Это полный скрипт:

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()

производит этот заговор:

enter image description here

Это будет работать хорошо, так как у вас есть полигоны с ошибками измерения. Если ваши очки не приблизительно равномерно распределены по углам [0,2pi[, он будет выполнять плохо.

в целом, вы можете использовать оптимизацию.