介紹了球面體素化的過程,包括重要的類和方法,如ConvertToSphericalVoxel和spherical_voxel_optimized,詳細(xì)解釋了參數(shù)及其作用。球面體素化通過將點云轉(zhuǎn)換為球面坐標(biāo)系,利用自適應(yīng)采樣權(quán)重來平衡不同緯度區(qū)域的點密度,從而有效捕捉幾何特征。文中還提到C++綁定的s
參考鏈接:
代碼組成:
ConvertToSphericalVoxel類:最高接口,實例化一個converter類,調(diào)用convert轉(zhuǎn)換局部點云
↓
spherical_voxel_optimized方法:在convert中調(diào)用,實現(xiàn)轉(zhuǎn)換,先轉(zhuǎn)換到球面坐標(biāo)系,然后進(jìn)行體素化
↓
spherical_voxel.compute方法:最終實現(xiàn)體素化,用pybind綁定C++代碼最終實現(xiàn)
from utils import geometry as ug
class ConvertToSphericalVoxel():
"""
Convert point cloud to spherical voxel [beta = 2 * bandwidth, alfa = 2 * bandwidth, num_radial_division].
Alfa in [0, 2pi], Beta in [0, pi]
"""
def __init__(self, bandwidth, radius_support, num_radial_division, num_points, random_sampling):
self.bandwidth = bandwidth
self.radius_support = radius_support
self.num_radial_division = num_radial_division
self.num_points = num_points
self.random_sampling = random_sampling
def __call__(self, point_cloud):
features, pts_normed = ug.spherical_voxel_optimized(points=point_cloud,
size_bandwidth=self.bandwidth,
size_radial_divisions=self.num_radial_division,
radius_support=self.radius_support,
do_random_sampling=self.random_sampling,
num_random_points=self.num_points)
return features, pts_normed
……
True
使得在局部區(qū)域內(nèi)的點采樣更加多樣化,避免由于局部密度過高或過低而導(dǎo)致的信息丟失。隨機(jī)采樣可以讓網(wǎng)絡(luò)更具魯棒性,適應(yīng)不同點云的分布。
def spherical_voxel_optimized(points: np.ndarray, size_bandwidth: int, size_radial_divisions: int,
radius_support: float, do_random_sampling: bool, num_random_points: int) \
-> Tuple[np.ndarray, np.ndarray]:
"""Compute spherical voxel using the C++ code.
Compute Spherical Voxel signal as defined in:
Pointwise Rotation-Invariant Network withAdaptive Sampling and 3D Spherical Voxel Convolution.
Yang You, Yujing Lou, Qi Liu, Yu-Wing Tai, Weiming Wang, Lizhuang Ma and Cewu Lu.
AAAI 2020.
:param points: the points to convert.
:param size_bandwidth: alpha and beta bandwidth.
:param size_radial_divisions: the number of bins along radial dimension.
:param radius_support: the radius used to compute the points in the support.
:param do_random_sampling: if true a subset of random points will be used to compute the spherical voxel.
:param num_random_points: the number of points to keep if do_random_sampling is true.
:return: A tuple containing:
The spherical voxel, shape(size_radial_divisions, 2 * size_bandwidth, 2 * size_bandwidth).
The points used to compute the signal normalized according the the farthest point.
"""
if do_random_sampling:
min_limit = 1 if points.shape[0] > 1 else 0
indices_random = np.random.randint(min_limit, points.shape[0], num_random_points)
points = points[indices_random]
pts_norm = np.linalg.norm(points, axis=1)
# Scale points to fit unit sphere
pts_normed = points / pts_norm[:, None]
pts_normed = np.clip(pts_normed, -1, 1)
pts_s2_coord = S2.change_coordinates(pts_normed, p_from='C', p_to='S')
# Convert to spherical voxel indices
pts_s2_coord[:, 0] *= 2 * size_bandwidth / np.pi # [0, pi]
pts_s2_coord[:, 1] *= size_bandwidth / np.pi # raw 2*size_bandwidth/2*np.pi
pts_s2_coord[:, 1][pts_s2_coord[:, 1] < 0] += 2 * size_bandwidth
# Adaptive sampling factor sin{pi*[(1/2,..., 2*size_bandwidth+1/2)/(2*size_bandwidth)]}
# 能更好的聚合點云信息,但是也會導(dǎo)致更多的形變,有得必有失
daas_weights = np.sin(np.pi * (2 * np.arange(2 * size_bandwidth) + 1) / 4 / size_bandwidth).astype(np.float32)
voxel = np.asarray(sv.compute(pts_on_s2=pts_s2_coord,
pts_norm=pts_norm,
size_bandwidth=size_bandwidth,
size_radial_divisions=size_radial_divisions,
radius_support=radius_support,
daas_weights=daas_weights))
pts_normed = points / np.max(pts_norm)
return voxel.astype(np.float32), pts_normed.astype(np.float32)
pts_norm
是local patch的點云徑向距離,所以
local patch輸入的時候最好經(jīng)過對于關(guān)鍵點的中心化操作
,不然徑向距離會是關(guān)于坐標(biāo)系原點的。
S2.change_coordinates
用于將點云從笛卡爾坐標(biāo)系轉(zhuǎn)換成球面坐標(biāo)系,球面坐標(biāo)系解釋見WIKI,簡單來說就是兩個坐標(biāo),維度角度坐標(biāo)\beta,和經(jīng)度角度坐標(biāo)\alpha
β
表示)不同區(qū)域的面積差異,不同區(qū)域的點密度會有所不同。例如,在球面的極地區(qū)域(緯度接近
0
或
π
的區(qū)域),同樣的角度變化可能覆蓋的球面面積較小,而在赤道區(qū)域,面積較大。為了避免在這些區(qū)域中出現(xiàn)過度或不足的采樣,自適應(yīng)采樣權(quán)重用于平衡不同緯度區(qū)域的影響。
sv.compute
用于體素轉(zhuǎn)換。
該函數(shù)是用pybind綁定的C++方法,文件為
spherical_voxel.cc
,代碼解釋如下:
const float interval = radius_support / (size_radial_divisions);
std::vector > > > > grids;
std::vector > > features;
grids.resize(size_radial_divisions);
features.resize(size_radial_divisions);
for (auto &beta: grids) {
beta.resize(2 * size_bandwidth);
for (auto &alpha: beta) {
alpha.resize(2 * size_bandwidth);
}
}
for (auto &beta: features) {
beta.resize(2 * size_bandwidth);
for (auto &alpha: beta) {
alpha.resize(2 * size_bandwidth, 0);
}
}
// mapping the points to the voxel grid
for (size_t i = 0; i < pts_on_s2.size(); i++) {
int r_idx = int(pts_norm[i] / interval);
// except for the points radius larger than radius_support
if (r_idx > size_radial_divisions - 1) r_idx = size_radial_divisions - 1;
int beta_idx = int(pts_on_s2[i][0] + 0.5f);
if (beta_idx > 2 * size_bandwidth - 1) beta_idx = 2 * size_bandwidth - 1;
int alpha_idx = int(pts_on_s2[i][1] + 0.5f);
if (alpha_idx > 2 * size_bandwidth - 1) alpha_idx = 2 * size_bandwidth - 1;
grids[r_idx][beta_idx][alpha_idx].emplace_back(std::vector{pts_norm[i], pts_on_s2[i][0], pts_on_s2[i][1]});
}
這里會遍歷每個點,計算每個點的徑向體素所用
r_idx
,緯度體素索引
beta_idx
,經(jīng)度體素索引
alpha_idx
,然后push到對應(yīng)的體素里面。
首先計算每個體素的經(jīng)度左右特征計算邊界
left
、
right
(也就是說每個體素的特征計算并不僅僅只考慮本體素內(nèi)部,還有一些可能出現(xiàn)的相鄰體素),這里計算左右邊界就用到自適應(yīng)權(quán)重,維度高的,左右邊界會寬一些。
之后根據(jù)左右邊界訪問對應(yīng)體素,并取出體素中所有點,基于徑向距離確定點是否靠近本體素中心,越靠近該點的特征權(quán)重越大([0, 1])。
然后考慮徑向相鄰體素內(nèi)部的點,用于本體素的特征計算,因為從徑向考慮,點分布相對連續(xù),需要補(bǔ)充這樣的信息。
最后計算本體素的特征(密度特征(加過權(quán)的點個數(shù)))
// compute the feature of each voxel
for (size_t i = 0; i < size_radial_divisions; i++) {
for (size_t j = 0; j < 2 * size_bandwidth; j++) {
for (size_t k = 0; k < 2 * size_bandwidth; k++) {
const float left = std::max(0.f, k - 0.5f / daas_weights[j]);
const float right = std::min(2.f * size_bandwidth, k + 0.5f / daas_weights[j]);
float sum = 0.f;
int cnt = 0;
for (int m = int(left + 0.5f); m < int(right + 0.5f); m++) {
for (int n = 0; n < grids[i][j][m].size(); n++) {
if (grids[i][j][m][n][2] > left && grids[i][j][m][n][2] < right) {
sum += 1.f - std::abs(grids[i][j][m][n][0] / interval - (i + 1)); // radial feature weight
cnt++;
}
}
// 在實際情況中,點云數(shù)據(jù)可能分布在兩個相鄰的徑向分割之間,
// 尤其是當(dāng)點的徑向距離位于兩個徑向分割的邊界附近時。
// 為了防止因單純考慮當(dāng)前徑向分割而導(dǎo)致信息的丟失,
// 代碼會查找相鄰徑向分割中滿足條件的點,并將它們的貢獻(xiàn)也加到當(dāng)前體素單元的特征值中。
if (i < size_radial_divisions - 1) {
for (int n = 0; n < grids[i + 1][j][m].size(); n++) {
if (grids[i + 1][j][m][n][2] > left && grids[i + 1][j][m][n][2] < right) {
sum += 1.f - std::abs(grids[i + 1][j][m][n][0] / interval - (i + 1));
cnt++;
}
}
}
}
// 與徑向分割不同,緯度分割(即 beta 方向)代表的是球面坐標(biāo)中的角度,
// 分割的區(qū)域代表不同的“環(huán)”或“帶”。
// 在這種情況下,每個緯度分割對應(yīng)的球面區(qū)域是明確的,
// 且這些分割區(qū)域之間沒有交叉,因此點不會“跨越”到另一個緯度分割。
if (cnt > 0) {
features[i][j][k] = sum / cnt;
}
}
}
}
機(jī)器學(xué)習(xí):神經(jīng)網(wǎng)絡(luò)構(gòu)建(下)
閱讀華為Mate品牌盛典:HarmonyOS NEXT加持下游戲性能得到充分釋放
閱讀實現(xiàn)對象集合與DataTable的相互轉(zhuǎn)換
閱讀算法與數(shù)據(jù)結(jié)構(gòu) 1 - 模擬
閱讀5. Spring Cloud OpenFeign 聲明式 WebService 客戶端的超詳細(xì)使用
閱讀Java代理模式:靜態(tài)代理和動態(tài)代理的對比分析
閱讀Win11筆記本“自動管理應(yīng)用的顏色”顯示規(guī)則
閱讀本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請發(fā)郵件[email protected]
湘ICP備2022002427號-10 湘公網(wǎng)安備:43070202000427號© 2013~2025 haote.com 好特網(wǎng)