[英]How can I fit a curve to a 3d point cloud?
我的目標是通過點雲擬合一條線。 點雲近似圓柱形但可以彎曲,這就是為什么擬合線不應該是直的。 我已經嘗試了幾件事,但這是我目前的方法:
我使用 PyTorch 優化曲面方程並計算每個點的損失。 然而,這似乎不會導致“好的”結果,因為平面/表面不會垂直切割點雲,我希望這會導致最少的錯誤。 由於我是 PyTorch 的新手,我不知道這是我的代碼中的錯誤還是這個想法是否存在數學問題。 這種方法在下面的代碼中。
此外,一旦我安裝了這個表面,我想在上面畫一條線,但我不確定最好的方法。 我的問題是:為什么平面/曲面的擬合不更居中? 那么我怎樣才能獲得最適合結果表面的曲線呢?
我嘗試過的其他方法:
此代碼示例中的數據只是玩具數據,真實數據的形狀會更復雜。 為了驗證我正在裝配平面的想法,但最后我想要一個更復雜的曲面。 代碼生成此圖。
import numpy as np
import torch
import matplotlib.pyplot as plt
from torch import nn
from torch.functional import F
ix = np.random.uniform(0,2, 1000)
iy = np.random.uniform(0,2, 1000)
iz = np.random.uniform(0,100, 1000)
x = torch.tensor(np.array([ ix, iy ]).T).float()
y = torch.tensor(iz).float()
degree =1
class Model(nn.Module):
def __init__(self):
super().__init__()
if degree == 1 :
weights = torch.distributions.Uniform(-1, 1).sample((4,))
if degree == 3 :
weights = torch.distributions.Uniform(-1, 1).sample((8,))
self.weights = nn.Parameter(weights)
def forward(self, X):
if degree ==1 :
a_1, a_2, a_3, a_4 = self.weights
return (a_1 *X[:,0] + a_2*X[:,1] +a_3)
if degree == 3 :
a_1, a_2, a_3, a_4, a_5, a_6, a_7, a_8 = self.weights
return (a_1 * X[:,0]**3 + a_2*X[:,1]**3 + a_3*X[:,0]**2 + a_4 *X[:,1] **2 + a_5 *X[:,0] + a_6 *X[:,1] + a_7)
def training_loop(model, optimizer):
losses = []
loss = 10000
it = 0
if degree == 3 :
lim = 0.1
if degree == 1 :
lim = 0.1
while loss > lim:
it +=1
if it > 5000:
break
preds1= model(x).float()
l1 = torch.nn.L1Loss()
loss = l1(preds1, y)
loss.backward()
optimizer.step()
optimizer.zero_grad()
losses.append(loss.detach().numpy())
print(loss)
return losses
m = Model()
if degree == 1 :
opt = torch.optim.Adam(m.parameters(), lr=0.01)
losses = np.array(training_loop(m, opt))
if degree == 3 :
opt= torch.optim.Adam(m.parameters(), lr=0.001)
losses = np.array(training_loop(m, opt))
params=list(m.parameters())[0].detach().numpy()
X = np.arange(0, 2, 0.1)
Y = np.arange(0, 2, 0.1)
X, Y = np.meshgrid(X, Y)
if degree == 1 :
Z = (params[0] * X + params[1]*Y + params[2])
if degree == 3:
Z = (params[0] * X**3 + params[1]*Y**3 + params[2]*X**2 + params[3]*Y**2 + params[4]*X + params[5]*Y + params[6])
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
surf = ax.plot_surface(X, Y, Z, color='tab:orange', alpha = 0.5,linewidth=0, antialiased=False)
ax.scatter3D(ix,iy,iz, alpha = 0.3, s=2)
plt.show()
編輯:我嘗試了這篇文章中的方法: Fit Curve-Spline to 3D Point Cloud然而,這迫使我為最短路徑指定源和目標。 簡單地選擇最低和最高切片的中心會導致:
您可以使用Delaunay/Voronoi方法來獲得點雲中軸的近似值,並通過它傳遞一條樣條曲線。 請在此處查看我之前的回答,它對在圓柱面上采樣的點執行的操作完全相同。 下圖取自該答案。 如果您的點雲也有來自邊界表面內部的點(而不僅僅是來自外表面的樣本),您可以使用此答案中的代碼計算 3D alpha 形狀,然后只需取外表面上的點並近似我在答案中描述的中軸(或使用不同的方法從三角邊界表面提取中曲線)。
如果你得到一個精確的定義圓柱體的方程,那應該允許你執行笛卡爾 (x,y,z) 到圓柱 (r,theta,z) 空間變換。 圓柱體的曲面應分解為圓柱空間中的平面。 圓柱體的任何特征和/或關鍵位置都可以類似地轉換到這個空間中,並用於繪制直線或樣條曲線或您喜歡的任何其他形狀。 然后簡單地將生成的直線/樣條曲線/形狀向后轉換(圓柱形到笛卡爾),你就有了跟蹤圓柱體表面的彎曲的東西。
請記住,要執行笛卡爾到圓柱的變換,您只需要質心和相關圓柱的主軸變化(通過 pca 或等效方法獲得)。
我還認為專門的 3d 處理庫(例如pcl或open3d )通常更適合此類事情。 (您可以在其中運行優化的圓柱形 ransac)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.