如何在python中的其他两条线之间插入一条线
问题描述
注意:我之前问过这个问题,但它被关闭为重复,但是,我和其他几个人认为它被不当关闭了,我在我原来的编辑中解释了为什么
此外,我想知道是否有人在一些优化的 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]])数组([[ 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 值之间的平均值.
所以我们可以做的是创建 相同长度的数组,这或多或少是对原始数组的良好估计.我们可以通过将多项式拟合到您拥有的数组来做到这一点.正如评论和其他答案中所述,原始数组的中线没有具体定义,因此一个好的估计应该可以满足您的需求.
注意:在所有这些示例中,我已将您发布的两个数组命名为 a1
和 a2
.
第一步:创建估计旧线的新数组
查看您发布的数据:
这些并不是特别复杂的函数,看起来 3 次多项式非常适合它们.我们可以使用 numpy
创建那些:
将 numpy 导入为 np# 查找 a1 中 x 值的范围min_a1_x, max_a1_x = min(a1[:,0]), max(a1[:,0])# 创建一个从最小值到最大值的等间距数组# 我使用了 100 个元素,但你可以使用更多或更少.# 这将用作新的 x 坐标new_a1_x = np.linspace(min_a1_x, max_a1_x, 100)# 为您的数据拟合一个 3 次多项式a1_coefs = np.polyfit(a1[:,0],a1[:,1], 3)# 从上述多项式的系数中获取新的 y 坐标new_a1_y = np.polyval(a1_coefs, new_a1_x)# 对数组 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_x
、new_a1_y
、new_a2_x
和 new_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)]
midx
和 midy
现在表示我们的 2 个估计数组之间的中点.现在,只需在中点数组旁边绘制原始(而非估计)数组:
plt.plot(a1[:,0], a1[:,1],c='black')plt.plot(a2[:,0], a2[:,1],c='黑色')plt.plot(midx, midy, '--', c='black')plt.show()
然后瞧:
这种方法仍然适用于更复杂、嘈杂的数据(但你必须仔细地拟合函数):
作为一个函数:
我已经把上面的代码放在了一个函数中,所以你可以很容易地使用它.它以原始数组的格式返回您估计的中点数组.
参数:a1
和 a2
是您的 2 个输入数组,poly_deg
是您要拟合的度多项式,n_points
是你想要在中点数组中的点数,而 plot
是一个布尔值,不管你想不想绘制它.
import matplotlib.pyplot as plt将 numpy 导入为 npdef 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)]如果情节:plt.plot(a1[:,0], a1[:,1],c='黑色')plt.plot(a2[:,0], a2[:,1],c='黑色')plt.plot(midx, midy, '--', c='black')plt.show()return np.array([[x, y] for x, y in zip(midx, midy)])
:
我正在回想这个问题,但我忽略了一种更简单的方法,即使用
Note: I asked this question before but it was closed as a duplicate, however, I, along with several others believe it was unduely closed, I explain why in an edit in my original post. So I would like to re-ask this question here again.
Does anyone know of a python library that can interpolate between two lines. For example, given the two solid lines below, I would like to produce the dashed line in the middle. In other words, I'd like to get the centreline. The input is a just two numpy
arrays of coordinates with size N x 2
and M x 2
respectively.
Furthermore, I'd like to know if someone has written a function for this in some optimized python library. Although optimization isn't exactly a necessary.
Here is an example of two lines that I might have, you can assume they do not overlap with each other and an x/y can have multiple y/x coordinates.
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]])
Visualizing this we have:
So my attempt at this has been using the skeletonize
function in the skimage.morphology
library by first rasterizing the coordinates into a filled in polygon. However, I get branching at the ends like this:
First of all, pardon the overkill; I had fun with your question. If the description is too long, feel free to skip to the bottom, I defined a function that does everything I describe.
Your problem would be relatively straightforward if your arrays were the same length. In that case, all you would have to do is find the average between the corresponding x values in each array, and the corresponding y values in each array.
So what we can do is create arrays of the same length, that are more or less good estimates of your original arrays. We can do this by fitting a polynomial to the arrays you have. As noted in comments and other answers, the midline of your original arrays is not specifically defined, so a good estimate should fulfill your needs.
Note: In all of these examples, I've gone ahead and named the two arrays that you posted a1
and a2
.
Step one: Create new arrays that estimate your old lines
Looking at the data you posted:
These aren't particularly complicated functions, it looks like a 3rd degree polynomial would fit them pretty well. We can create those using 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)
The result:
That's not bad so bad! If you have more complicated functions, you'll have to fit a higher degree polynomial, or find some other adequate function to fit to your data.
Now, you've got two sets of arrays of the same length (I chose a length of 100, you can do more or less depending on how smooth you want your midpoint line to be). These sets represent the x and y coordinates of the estimates of your original arrays. In the example above, I named these new_a1_x
, new_a1_y
, new_a2_x
and new_a2_y
.
Step two: calculate the average between each x and each y in your new arrays
Then, we want to find the average x and average y value for each of our estimate arrays. Just use 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)]
midx
and midy
now represent the midpoint between our 2 estimate arrays. Now, just plot your original (not estimate) arrays, alongside your midpoint array:
plt.plot(a1[:,0], a1[:,1],c='black')
plt.plot(a2[:,0], a2[:,1],c='black')
plt.plot(midx, midy, '--', c='black')
plt.show()
And voilà:
This method still works with more complex, noisy data (but you have to fit the function thoughtfully):
As a function:
I've put the above code in a function, so you can use it easily. It returns an array of your estimated midpoints, in the format you had your original arrays in.
The arguments: a1
and a2
are your 2 input arrays, poly_deg
is the degree polynomial you want to fit, n_points
is the number of points you want in your midpoint array, and plot
is a boolean, whether you want to plot it or not.
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)])
[EDIT]:
I was thinking back on this question, and I overlooked a simpler way to do this, by "densifying" both arrays to the same number of points using np.interp
. This method follows the same basic idea as the line-fitting method above, but instead of approximating lines using polyfit
/ polyval
, it just densifies:
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()
相关文章