web-dev-qa-db-ja.com

python)の他の2つの行の間の行を補間する方法

注:以前にこの質問をしましたが、重複して閉じられましたが、他のいくつかの人と一緒に、過度に閉じられたと信じているので、元の編集で理由を説明します post 。ですから、ここでもう一度この質問をしたいと思います。

2本の線の間を補間できるpythonライブラリを知っている人はいますか。たとえば、下の2本の実線を考えて、真ん中に破線を作成したいと思います。つまり、I中心線を取得したい。入力は、それぞれサイズN x 2M x 2の座標の2つのnumpy配列です。

enter image description here

さらに、誰かが最適化されたpythonライブラリでこのための関数を書いたかどうか知りたいのですが、最適化は必ずしも必要ではありません。

これは私が持っているかもしれない2本の線の例です。それらは互いに重なっておらず、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]])

これを視覚化すると、次のようになります。 enter image description here

そのため、これを試みたのは、最初に座標を塗りつぶされたポリゴンにラスター化することにより、skimage.morphologyライブラリのskeletonize関数を使用することでした。しかし、私はこのように最後に分岐します:

enter image description here

12
Justin Liang

「2本の線の間の線」はあまり明確に定義されていません。 2つの曲線の間を三角測量することで、まともな、しかし単純な解決策を得ることができます(頂点から頂点に進み、斜めの少ない三角形を生成する対角線を選択することで三角測量できます)。

次に、補間された曲線が辺の中央を結合します。

enter image description here

9
Yves Daoust

まず第一に、やり過ぎを許してください。私はあなたの質問を楽しんだ。説明が長すぎる場合は、下にスキップしてください。説明するすべてを実行する関数を定義しました。

配列が同じ長さの場合、問題は比較的簡単です。その場合、あなたがしなければならないのは、各配列の対応するx値と各配列の対応するy値の間の平均を見つけることだけです。

したがって、私たちにできることは、同じ長さのcreate配列です。これは、元の配列のほぼ適切な見積もりです。あなたが持っている配列に多項式を当てはめることによってこれを行うことができます。コメントやその他の回答に記載されているように、元の配列の正中線は具体的に定義されていないため、適切な見積もりでニーズを満たす必要があります。

注:これらすべての例では、先に進んで、投稿した2つのアレイにa1a2という名前を付けました。

ステップ1:古い行を推定する新しい配列を作成します

あなたが投稿したデータを見てください:

the data

これらは特に複雑な関数ではなく、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)

結果:

Fitted Arrays

それは悪くないです!より複雑な関数がある場合は、より高次の多項式に適合させるか、データに適合する他の適切な関数を見つける必要があります。

これで、同じ長さの配列が2セットあります(長さ100を選択しました。中点線をどの程度滑らかにするかに応じて、多かれ少なかれ実行できます)。これらのセットは、元の配列の推定値のx座標とy座標を表します。上記の例では、これらにnew_a1_xnew_a1_ynew_a2_x、およびnew_a2_yという名前を付けました。

ステップ2:新しい配列の各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()

そしてvoilà:

final product

この方法は、より複雑でノイズの多いデータでも機能します(ただし、関数を慎重に適合させる必要があります)。

noisy data

関数として:

上記のコードを関数に組み込んだので、簡単に使用できます。元の配列があった形式で、推定された中点の配列を返します。

引数: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()

enter image description here

22
sacuL