1

我有一个代表闭合轮廓(带噪声)的数据:

contour = [(x1, y1), (x2, y2), ...]

有没有简单的方法来拟合轮廓?有numpy.polyfit。但是如果 x 值重复并且需要一些努力来确定多项式的适当次数,它就会失败。

4

2 回答 2

3

从一个点到您要拟合的轮廓的距离是极坐标中以该点为中心的角度的周期函数。该函数可以表示为正弦(或余弦)函数的组合,可以通过傅里叶变换精确计算。实际上,根据Parseval 定理,通过截断到前 N 个函数的傅立叶变换计算的线性组合适合那些 N 个函数。

要在实践中使用它,请选择一个中心点(可能是轮廓的重心),将轮廓转换为极坐标,并计算距中心点的距离的傅里叶变换。拟合轮廓由前几个傅立叶系数给出。

剩下的一个问题是转换为极坐标的轮廓在等距角处没有距离值。这就是不规则采样问题。由于您可能有相当高的样本密度,因此您可以通过使用两个最近点之间的线性插值到均匀间隔的角度,或者(取决于您的数据)用一个小窗口进行平均来非常简单地解决这个问题。大多数其他不规则采样的解决方案在这里要复杂得多且不必要。

编辑:示例代码,作品:

import numpy, scipy, scipy.ndimage, scipy.interpolate, numpy.fft, math

# create simple square
img = numpy.zeros( (10, 10) )
img[1:9, 1:9] = 1
img[2:8, 2:8] = 0

# find contour
x, y = numpy.nonzero(img)

# find center point and conver to polar coords
x0, y0 = numpy.mean(x), numpy.mean(y)
C = (x - x0) + 1j * (y - y0)
angles = numpy.angle(C)
distances = numpy.absolute(C)
sortidx = numpy.argsort( angles )
angles = angles[ sortidx ]
distances = distances[ sortidx ]

# copy first and last elements with angles wrapped around
# this is needed so can interpolate over full range -pi to pi
angles = numpy.hstack(([ angles[-1] - 2*math.pi ], angles, [ angles[0] + 2*math.pi ]))
distances = numpy.hstack(([distances[-1]], distances, [distances[0]]))

# interpolate to evenly spaced angles
f = scipy.interpolate.interp1d(angles, distances)
angles_uniform = scipy.linspace(-math.pi, math.pi, num=100, endpoint=False) 
distances_uniform = f(angles_uniform)

# fft and inverse fft
fft_coeffs = numpy.fft.rfft(distances_uniform)
# zero out all but lowest 10 coefficients
fft_coeffs[11:] = 0
distances_fit = numpy.fft.irfft(fft_coeffs)

# plot results
import matplotlib.pyplot as plt
plt.polar(angles, distances)
plt.polar(angles_uniform, distances_uniform)
plt.polar(angles_uniform, distances_fit)
plt.show()

PS 有一种特殊情况可能需要注意,当轮廓非凸(重入)的程度足够大时,沿某个角度穿过所选中心点的某些光线与其相交两次。在这种情况下,选择不同的中心点可能会有所帮助。在极端情况下,可能没有不具有此属性的中心点(如果您的轮廓看起来像这样)。在这种情况下,您仍然可以使用上述方法来标记或限制您拥有的形状,但这不是适合其本身的合适方法。这种方法适用于拟合像土豆一样的“块状”椭圆,而不是像椒盐卷饼这样的“扭曲”椭圆:)

于 2012-11-28T23:11:55.647 回答
1

如果您修复多项式次数,您可以简单地使用scipy.optimize中的minimumsq函数

假设您生成了一个简单的圆圈。我将它分成它的 x 和 y 分量

data = [ [cos(t)+0.1*randn(),sin(t)+0.1*randn()] for t in rand(100)*2*np.pi ]
contour = array(data)
x,y = contour.T

编写一个简单的函数,在给定多项式系数的情况下评估每个点与 0 的差异。我们将曲线拟合为以原点为中心的圆。

def f(coef):
    a = coef
    return a*x**2+a*y**2-1

我们可以简单地使用 leastsq 函数来找到最佳系数。

from scipy.optimize import leastsq
initial_guess = [0.1,0.1]
coef = leastsq(f,initial_guess)[0]
# coef = array([ 0.92811554])

我只取返回元组的第一个元素,因为 minimumsq 返回了很多我们不需要的其他信息。

如果您需要拟合更复杂的多项式,例如具有通用中心的椭圆,您可以简单地使用更复杂的函数:

def f(coef):
    a,b,cx,cy = coef
    return a*(x-cx)**2+b*(y-cy)**2-1

initial_guess = [0.1,0.1,0.0,0.0]
coef = leastsq(f,initial_guess)[0]
# coef = array([ 0.92624664,  0.93672577,  0.00531   ,  0.01269507])

编辑:

如果由于某种原因您需要估计拟合参数的不确定性,您可以从结果的协方差矩阵中获取此信息:

res = leastsq(f,initial_guess,full_output=True)
coef = res[0]
cov  = res[1]
#cov = array([[ 0.02537329, -0.00970796, -0.00065069,  0.00045027],
#             [-0.00970796,  0.03157025,  0.0006394 ,  0.00207787],
#             [-0.00065069,  0.0006394 ,  0.00535228, -0.00053483],
#             [ 0.00045027,  0.00207787, -0.00053483,  0.00618327]])

uncert = sqrt(diag(cov))
# uncert = array([ 0.15928997,  0.17768018,  0.07315927,  0.07863377])

协方差矩阵的对角线是每个参数的方差,所以不确定性是它的平方根

查看http://www.scipy.org/Cookbook/FittingData以获取有关拟合程序的更多信息。

我使用了 leastsq 而不是更容易使用的 curve_fit 函数的原因是,curve_fit 需要形式为显式的函数y = f(x),并且并非每个隐式多项式都可以转换为该形式(或者更好的是,几乎没有有趣的隐式多项式完全)

于 2012-11-28T12:41:43.980 回答