簡體   English   中英

如何將曲線擬合到 3d 點雲?

[英]How can I fit a curve to a 3d point cloud?

我的目標是通過點雲擬合一條線。 點雲近似圓柱形但可以彎曲,這就是為什么擬合線不應該是直的。 我已經嘗試了幾件事,但這是我目前的方法:

我使用 PyTorch 優化曲面方程並計算每個點的損失。 然而,這似乎不會導致“好的”結果,因為平面/表面不會垂直切割點雲,我希望這會導致最少的錯誤。 由於我是 PyTorch 的新手,我不知道這是我的代碼中的錯誤還是這個想法是否存在數學問題。 這種方法在下面的代碼中。

此外,一旦我安裝了這個表面,我想在上面畫一條線,但我不確定最好的方法。 我的問題是:為什么平面/曲面的擬合不更居中? 那么我怎樣才能獲得最適合結果表面的曲線呢?

我嘗試過的其他方法:

  • SVD,但就像我說的,我正在尋找一條不直的線
  • 計算 y 和 x 方向上每個 z 切片(即每個水平切片)的平均值,並使用結果點來擬合樣條(使用 python 的 splrep)。 這種方法的問題在於,幾個頂部或底部切片可能由很少的點組成,這些點不一定位於點雲的“中心”(例如,位於頂部切片的最左側),這會強制樣條在那個方向。 這條線是“中心”對於項目來說真的很重要。

此代碼示例中的數據只是玩具數據,真實數據的形狀會更復雜。 為了驗證我正在裝配平面的想法,但最后我想要一個更復雜的曲面。 代碼生成此圖

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 處理庫(例如pclopen3d )通常更適合此類事情。 (您可以在其中運行優化的圓柱形 ransac)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM