假設(shè)你有一組 3D 中的 n 個(gè)點(diǎn),并且想要為它們擬合一個(gè)平面。 在本文中,我將推導(dǎo)出一個(gè)簡(jiǎn)單的、數(shù)值穩(wěn)定的方法,并提供它的源代碼。 聽(tīng)起來(lái)很好玩? 我們開(kāi)始吧!
NSDT工具推薦:?Three.js AI紋理開(kāi)發(fā)包?-?YOLO合成數(shù)據(jù)生成器?-?GLTF/GLB在線編輯?-?3D模型格式在線轉(zhuǎn)換?-?可編程3D場(chǎng)景編輯器?-?REVIT導(dǎo)出3D模型插件?-?3D模型語(yǔ)義搜索引擎
首先,如果在網(wǎng)上尋找答案,你將得到的答案包括對(duì)協(xié)方差矩陣進(jìn)行奇異值分解以找到最小特征值的特征向量。 然而事實(shí)證明,這讓事情變得比他們需要的更加復(fù)雜。 讓我們從基礎(chǔ)開(kāi)始:
平面通常由法向量 n = [a, b, c]? 和距離 d 描述,因此對(duì)于平面 n · p + d = 0 上的點(diǎn) p = [x, y, z]?。我們可以 將其寫(xiě)為:
但請(qǐng)注意,這是超定的 - 解空間(平面)是三維的,但上面的描述使用了四個(gè)值。 因此,讓我們首先通過(guò)限制解決方案空間來(lái)刪除一個(gè)組件。 我們通過(guò)任意指定 c = 1 來(lái)實(shí)現(xiàn)這一點(diǎn),即平面法線的 z 分量始終為 1(請(qǐng)注意,法線的長(zhǎng)度不需要為 1)。 如果你認(rèn)為這是一個(gè)潛在有問(wèn)題的假設(shè),那么你是對(duì)的——我們稍后會(huì)再討論這個(gè)問(wèn)題。 現(xiàn)在,讓我們定義:
并求解a、b、d。 矩陣形式:
接下來(lái),我們將這個(gè)矩陣轉(zhuǎn)置,然后從左側(cè)相乘以執(zhí)行線性最小二乘:
轉(zhuǎn)置后相乘:
其中 N 是點(diǎn)數(shù)。 現(xiàn)在這是聰明的部分:讓我們定義上面的 x,y,z 相對(duì)于點(diǎn)云的質(zhì)心(平均值)。 現(xiàn)在 Σx = Σy = Σz = 0 所以我們可以簡(jiǎn)化為:
從最后一行(N·d = 0)我們可以得出結(jié)論:d = 0。這意味著如果所有點(diǎn)都相對(duì)于點(diǎn)云的質(zhì)心,則平面穿過(guò)原點(diǎn)。 換句話說(shuō):平面始終穿過(guò)輸入點(diǎn)的平均值。 我們現(xiàn)在可以去掉一個(gè)維度:
克萊默規(guī)則告訴我們:
我們可以通過(guò)將 n 乘以 D 來(lái)簡(jiǎn)化它(無(wú)論如何我們都需要對(duì) n 進(jìn)行歸一化),這給我們:
就是這樣! 但請(qǐng)記住我們的假設(shè):平面法線的 z 分量不為零。 如果為零怎么辦? 那么可以證明上面的行列式 D 變?yōu)榱悴⑶椅覀兛梢猿粤恪?即使它不完全為零,但很接近,我們?nèi)匀粫?huì)得到不好的條件,從而得到不好的結(jié)果。 所以,我們能做些什么? 好吧,如果這些點(diǎn)跨越一個(gè)平面,則法線的至少一個(gè)分量必須非零。 因此,讓我們對(duì)三個(gè)單獨(dú)的假設(shè)進(jìn)行上述計(jì)算,其中哪個(gè)分量不為零。 然后我們簡(jiǎn)單地選擇表現(xiàn)最好的一個(gè),即具有最大行列式的那個(gè)。
注意:此方法將最小化垂直于主軸的殘差的平方,而不是垂直于平面的殘差的平方。 如果殘差很小(即你的點(diǎn)都靠近生成的平面),那么這種方法可能就足夠了。 但是,如果你的點(diǎn)分布比較分散,那么此方法可能不是最合適的。
這是 Rust 代碼:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-784394.html
// Constructs a plane from a collection of points
// so that the summed squared distance to all points is minimzized
fn plane_from_points(points: &[Vec3]) -> Option<Plane> {
if points.len() < 3 {
return None; // At least three points required
}
let mut sum = Vec3{x:0.0, y:0.0, z:0.0};
for p in points {
sum += p;
}
let centroid = sum * (1.0 / (points.len() as f64));
// Calc full 3x3 covariance matrix, excluding symmetries:
let mut xx = 0.0; let mut xy = 0.0; let mut xz = 0.0;
let mut yy = 0.0; let mut yz = 0.0; let mut zz = 0.0;
for p in points {
let r = p - centroid;
xx += r.x * r.x;
xy += r.x * r.y;
xz += r.x * r.z;
yy += r.y * r.y;
yz += r.y * r.z;
zz += r.z * r.z;
}
let det_x = yy*zz - yz*yz;
let det_y = xx*zz - xz*xz;
let det_z = xx*yy - xy*xy;
let det_max = max3(det_x, det_y, det_z);
if det_max <= 0.0 {
return None; // The points don't span a plane
}
// Pick path with best conditioning:
let dir =
if det_max == det_x {
Vec3{
x: det_x,
y: xz*yz - xy*zz,
z: xy*yz - xz*yy,
}
} else if det_max == det_y {
Vec3{
x: xz*yz - xy*zz,
y: det_y,
z: xy*xz - yz*xx,
}
} else {
Vec3{
x: xy*yz - xz*yy,
y: xy*xz - yz*xx,
z: det_z,
}
};
Some(plane_from_point_and_normal(centroid, normalize(dir)))
}
原文鏈接:3D點(diǎn)云平面擬合 - BimAnt文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-784394.html
到了這里,關(guān)于3D點(diǎn)云平面擬合算法的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!