【问题标题】:How to interpolate a line between two other lines in python如何在python中的其他两条线之间插入一条线
【发布时间】:2018-02-28 20:21:59
【问题描述】:

注意:我之前问过这个问题,但它作为副本被关闭了,但是,我和其他几个人认为它被不当关闭了,我在我原来的 post 的编辑中解释了为什么。所以我想在这里再次提出这个问题。

有谁知道可以在两行之间插入的python库。例如,给定下面的两条实线,我想在中间生成虚线。换句话说,我想得到中心线。输入只是两个numpy 坐标数组,大小分别为N x 2M x 2

此外,我想知道是否有人在一些优化的 python 库中为此编写了一个函数。虽然优化并不是完全必要的。

这是我可能有两条线的示例,您可以假设它们不相互重叠,并且 x/y 可以有多个 y/x 坐标。

array([[ 1233.87375018,  1230.07095987],
       [ 1237.63559365,  1253.90749041],
       [ 1240.87500801,  1264.43925132],
       [ 1245.30875975,  1274.63795396],
       [ 1256.1449357 ,  1294.48254424],
       [ 1264.33600095,  1304.47893299],
       [ 1273.38192911,  1313.71468591],
       [ 1283.12411536,  1322.35942538],
       [ 1293.2559388 ,  1330.55873344],
       [ 1309.4817002 ,  1342.53074698],
       [ 1325.7074616 ,  1354.50276051],
       [ 1341.93322301,  1366.47477405],
       [ 1358.15898441,  1378.44678759],
       [ 1394.38474581,  1390.41880113]])

array([[ 1152.27115094,  1281.52899302],
       [ 1155.53345506,  1295.30515742],
       [ 1163.56506781,  1318.41642169],
       [ 1168.03497425,  1330.03181319],
       [ 1173.26135672,  1341.30559949],
       [ 1184.07110925,  1356.54121651],
       [ 1194.88086178,  1371.77683353],
       [ 1202.58908737,  1381.41765447],
       [ 1210.72465255,  1390.65097106],
       [ 1227.81309742,  1403.2904646 ],
       [ 1244.90154229,  1415.92995815],
       [ 1261.98998716,  1428.56945169],
       [ 1275.89219696,  1438.21626352],
       [ 1289.79440676,  1447.86307535],
       [ 1303.69661656,  1457.50988719],
       [ 1323.80994319,  1470.41028655],
       [ 1343.92326983,  1488.31068591],
       [ 1354.31738934,  1499.33260989],
       [ 1374.48879779,  1516.93734053],
       [ 1394.66020624,  1534.54207116]])

可视化我们有:

所以我在这方面的尝试是使用skimage.morphology 库中的skeletonize 函数,首先将坐标栅格化为填充的多边形。但是,我在末端会出现这样的分支:

【问题讨论】:

  • 这可能是一个未充分说明的问题 - 即这里如何定义“中间”? (或者更糟的是,当曲线像这样不是很好和凸的时候。)
  • 线条是否相交?任何 x 坐标都会有多个 y 坐标吗?
  • 我们可以使用纯直线吗?
  • 我已更新问题以回答您的一些问题。至于“中”的定义,我自己也不太清楚如何定义。是否有多种解释方式?

标签: python algorithm math geometry interpolation


【解决方案1】:

首先,请原谅我的矫枉过正;我对你的问题很感兴趣。如果描述太长,请随意跳到底部,我定义了一个函数来完成我所描述的一切。

如果您的数组长度相同,您的问题会相对简单。在这种情况下,您所要做的就是找到每个数组中对应的 x 值和每个数组中对应的 y 值之间的平均值。

所以我们可以做的是创建 相同长度的数组,这或多或少是对原始数组的良好估计。我们可以通过将多项式拟合到您拥有的数组来做到这一点。正如 cmets 和其他答案中所述,原始数组的中线没有具体定义,因此一个好的估计应该可以满足您的需求。

注意:在所有这些示例中,我已将您发布的两个数组命名为 a1a2

第一步:创建估计旧线的新数组

查看您发布的数据:

这些并不是特别复杂的函数,看起来 3 次多项式非常适合它们。我们可以使用numpy 创建那些:

import numpy as np

# Find the range of x values in a1
min_a1_x, max_a1_x = min(a1[:,0]), max(a1[:,0])
# Create an evenly spaced array that ranges from the minimum to the maximum
# I used 100 elements, but you can use more or fewer. 
# This will be used as your new x coordinates
new_a1_x = np.linspace(min_a1_x, max_a1_x, 100)
# Fit a 3rd degree polynomial to your data
a1_coefs = np.polyfit(a1[:,0],a1[:,1], 3)
# Get your new y coordinates from the coefficients of the above polynomial
new_a1_y = np.polyval(a1_coefs, new_a1_x)

# Repeat for array 2:
min_a2_x, max_a2_x = min(a2[:,0]), max(a2[:,0])
new_a2_x = np.linspace(min_a2_x, max_a2_x, 100)
a2_coefs = np.polyfit(a2[:,0],a2[:,1], 3)
new_a2_y = np.polyval(a2_coefs, new_a2_x)

结果:

这还不错,太糟糕了!如果您有更复杂的函数,则必须拟合更高次的多项式,或者找到一些其他合适的函数来拟合您的数据。

现在,您有两组长度相同的数组(我选择了长度为 100 的数组,您可以根据自己希望中点线的平滑程度做更多或更少)。这些集合代表原始数组的 估计值 的 x 和 y 坐标。在上面的示例中,我将它们命名为 new_a1_xnew_a1_ynew_a2_xnew_a2_y

第二步:计算新数组中每个 x 和每个 y 的平均值

然后,我们想要找到每个估计数组的平均 x 和平均 y 值。只需使用np.mean

midx = [np.mean([new_a1_x[i], new_a2_x[i]]) for i in range(100)]
midy = [np.mean([new_a1_y[i], new_a2_y[i]]) for i in range(100)]

midxmidy 现在代表我们 2 个估计数组之间的中点。现在,只需在中点数组旁边绘制原始(而非估计)数组:

plt.plot(a1[:,0], a1[:,1],c='black')
plt.plot(a2[:,0], a2[:,1],c='black')
plt.plot(midx, midy, '--', c='black')
plt.show()

然后瞧:

这种方法仍然适用于更复杂、嘈杂的数据(但你必须仔细地拟合函数):

作为一个函数:

我已经把上面的代码放在了一个函数中,所以你可以很容易地使用它。它以原始数组的格式返回您估计的中点数组。

参数:a1a2 是您的 2 个输入数组,poly_deg 是您想要拟合的次数多项式,n_points 是您想要在中点数组中的点数,plot是一个布尔值,不管你想不想绘制它。

import matplotlib.pyplot as plt
import numpy as np

def interpolate(a1, a2, poly_deg=3, n_points=100, plot=True):

    min_a1_x, max_a1_x = min(a1[:,0]), max(a1[:,0])
    new_a1_x = np.linspace(min_a1_x, max_a1_x, n_points)
    a1_coefs = np.polyfit(a1[:,0],a1[:,1], poly_deg)
    new_a1_y = np.polyval(a1_coefs, new_a1_x)

    min_a2_x, max_a2_x = min(a2[:,0]), max(a2[:,0])
    new_a2_x = np.linspace(min_a2_x, max_a2_x, n_points)
    a2_coefs = np.polyfit(a2[:,0],a2[:,1], poly_deg)
    new_a2_y = np.polyval(a2_coefs, new_a2_x)

    midx = [np.mean([new_a1_x[i], new_a2_x[i]]) for i in range(n_points)]
    midy = [np.mean([new_a1_y[i], new_a2_y[i]]) for i in range(n_points)]

    if plot:
        plt.plot(a1[:,0], a1[:,1],c='black')
        plt.plot(a2[:,0], a2[:,1],c='black')
        plt.plot(midx, midy, '--', c='black')
        plt.show()

    return np.array([[x, y] for x, y in zip(midx, midy)])

[编辑]:

我正在回想这个问题,但我忽略了一种更简单的方法,即使用np.interp 将两个数组“密集化”到相同数量的点。此方法遵循与上面的线拟合方法相同的基本思想,但不是使用polyfit / polyval 来近似线,它只是致密化:

min_a1_x, max_a1_x = min(a1[:,0]), max(a1[:,0])
min_a2_x, max_a2_x = min(a2[:,0]), max(a2[:,0])

new_a1_x = np.linspace(min_a1_x, max_a1_x, 100)
new_a2_x = np.linspace(min_a2_x, max_a2_x, 100)

new_a1_y = np.interp(new_a1_x, a1[:,0], a1[:,1])
new_a2_y = np.interp(new_a2_x, a2[:,0], a2[:,1])

midx = [np.mean([new_a1_x[i], new_a2_x[i]]) for i in range(100)]
midy = [np.mean([new_a1_y[i], new_a2_y[i]]) for i in range(100)]

plt.plot(a1[:,0], a1[:,1],c='black')
plt.plot(a2[:,0], a2[:,1],c='black')
plt.plot(midx, midy, '--', c='black')
plt.show()

【讨论】:

  • @sacul 你有没有机会知道 C# 的等价物?我发现 Math.NET 包拥有大部分(如果不是全部)功能,但它们并不完全相同,所以我不确定哪个会转换。
  • 对不起,我的 C# 不太好,但仅从 Math.NET 文档来看,MathNet.Numerics 中的 Fit 类型的 PolynomialFunc 可能能够处理很多函数拟合,特别是 Polynomial 函数,它基本上将替换我的代码中的 np.polyfit。其余的更简单,但我不会是一个很好的帮助来源......
  • 好的。这是一个巨大的帮助!谢谢。
【解决方案2】:

“两条线之间的线”没有很好的定义。您可以通过在两条曲线之间进行三角剖分来获得一个不错但简单的解决方案(您可以通过从一个顶点到另一个顶点进行三角剖分,选择产生较少倾斜三角形的对角线)。

然后插值曲线连接边的中间。

【讨论】:

  • 嗯,这可行,我可以使每行中的点变密以获得更准确的解决方案。 “倾斜度较小的三角形”是什么意思?
  • @JustinLiang:如果曲线是由方程已知的,答案会有所不同。
  • @JustinLiang “我可以使点变密”:中值曲线不必比给定曲线更准确/更平滑。所以不需要密化。
【解决方案3】:

我在河流方面工作,所以这是一个常见问题。我的解决方案之一与您在问题中显示的解决方案完全相同 - 即骨架化blob。你看到边界有问题,所以我所做的似乎工作良好的只是简单地反映边界。要使这种方法起作用,blob 不得与图像的角相交。

你可以在RivGraph找到我的实现;这个特定的算法在rivers/river_utils.py 中称为“mask_to_centerline”。

这是一个示例输出,显示了中心线的末端如何延伸到对象的所需边缘:

【讨论】:

    猜你喜欢
    • 2013-09-27
    • 1970-01-01
    • 1970-01-01
    • 2014-06-23
    • 1970-01-01
    • 1970-01-01
    • 2020-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多