來源:https://zhuanlan.zhihu.com/p/353761080
本文介紹python點雲數據處理中的點雲下采樣算法和關鍵點算法以及在點雲工具箱軟件中的實現。由於點雲的海量和無序性,直接處理的方式在對鄰域進行搜索時需要較高的計算成本。一個常用的解決方式就是對點雲進行下采樣,將對全部點雲的操作轉換到下采樣所得到的點上,降低計算量。
一、均勻下采樣
均勻下采樣有多種不同的采樣方式,其中最遠點采樣是較為簡單的一種,首先需要選取一個種子點,並設置一個內點集合,每次從點雲中不屬於內點的集合找出一點距離內點最遠的點,如下圖,這里的距離計算方式為該點至內點所有點的最小距離。這種方式的下采樣點雲分布均勻,但是算法復雜度較高效率低。
點雲工具箱中該功能實現依賴於Open3d庫,代碼如下:
import open3d as o3d # 讀取文件 pcd = o3d.io.read_point_cloud(path) # path為文件路徑 pcd_new = o3d.geometry.PointCloud.uniform_down_sample(pcd, value) # value值的設定為整數,即value個點中選取一個點
二、體素下采樣
體素下采樣就是把三維空間體素化,然后在每個體素里采樣一個點,通常可用中心點或最靠近中心的點作為采樣點。具體方法如下:
1. 創建體素:計算點雲的包圍盒,然后把包圍盒離散成小體素。體素的長寬高可以用戶設定,也可以通過設定包圍盒三個方向的格點數來求得。
2. 每個小體素包含了若干個點,取中心點或離中心點最近的點為采樣點。
體素采樣的特點:
- 效率非常高
- 采樣點分布比較均勻,但是均勻性沒有均勻采樣高
- 可以通過體素的尺寸控制點間距
- 不能精確控制采樣點個數
點雲工具箱中該功能實現依賴於Open3d庫,代碼如下:
pcd_new = o3d.geometry.PointCloud.voxel_down_sample(pcd, value)
# value為體素的大小
三、曲率下采樣
曲率下采樣就是在點雲曲率越大的地方,采樣點個數越多。一種簡單有效的曲率下采樣實現方法如下:
1.通過該方法近似達到曲率的效果同時提高計算效率:計算每個點K鄰域,然后計算點到鄰域點的法線夾角值。曲率越大的地方,這個夾角值就越大。
2.設置一個角度閾值,比如5度。點的鄰域夾角值大於這個閾值的點被認為是特征明顯的區域,其余的為不明顯區域。
3.均勻采樣特征明顯區域和不明顯區域,采樣數分別為S * (1 - U),S * U。S為目標采樣數,U為采樣均勻性。
該采樣方法的特點:
- 幾何特征越明顯的區域,采樣點個數分布越多
- 計算效率高
- 采樣點局部分布是均勻的
- 穩定性高:通過幾何特征區域的划分,使得采樣結果抗噪性更強
點雲工具箱中該功能實現代碼如下:
def vector_angle(x, y): Lx = np.sqrt(x.dot(x)) Ly = (np.sum(y ** 2, axis=1)) ** (0.5) cos_angle = np.sum(x * y, axis=1) / (Lx * Ly) angle = np.arccos(cos_angle) angle2 = angle * 360 / 2 / np.pi return angle2 knn_num = 10 # 自定義參數值(鄰域點數) angle_thre = 30 # 自定義參數值(角度值) N = 5 # 自定義參數值(每N個點采樣一次) C = 10 # 自定義參數值(采樣均勻性>N) pcd = o3d.io.read_point_cloud(path) point = np.asarray(pcd.points) point_size = point.shape[0] tree = o3d.geometry.KDTreeFlann(pcd) o3d.geometry.PointCloud.estimate_normals( pcd, search_param=o3d.geometry.KDTreeSearchParamKNN(knn=knn_num)) normal = np.asarray(pcd.normals) normal_angle = np.zeros((point_size)) for i in range(point_size): [_, idx, dis] = tree.search_knn_vector_3d(point[i], knn_num + 1) current_normal = normal[i] knn_normal = normal[idx[1:]] normal_angle[i] = np.mean(vector_angle(current_normal, knn_normal)) point_high = point[np.where(normal_angle >= angle_thre)] point_low = point[np.where(normal_angle < angle_thre)] pcd_high = o3d.geometry.PointCloud() pcd_high.points = o3d.utility.Vector3dVector(point_high) pcd_low = o3d.geometry.PointCloud() pcd_low.points = o3d.utility.Vector3dVector(point_low) pcd_high_down = o3d.geometry.PointCloud.uniform_down_sample(pcd_high, N) pcd_low_down = o3d.geometry.PointCloud.uniform_down_sample(pcd_low, C) pcd_finl = o3d.geometry.PointCloud() pcd_finl.points = o3d.utility.Vector3dVector(np.concatenate((np.asarray(pcd_high_down.points), np.asarray(pcd_low_down.points))))
四、曲面均勻采樣
曲面均勻采樣的算法實現依賴於Open3d庫,從三角網格曲面數據均勻采樣得到點雲。
點雲工具箱中該功能實現依賴於Open3d庫,代碼如下:
ply = o3d.io.read_triangle_mesh(path)
pcd = ply.sample_points_uniformly(number_of_points = value) # number_of_points參數為采樣點數
五、泊松磁盤采樣
1. 泊松磁盤采樣(Poisson-Disk Sampling)性質
一個理想的 Poisson 圓盤采樣點集需要滿足 3 個條件:
(1) 無偏差采樣性質 (采樣區域的每個沒有被覆蓋的點都有相同的概率接受一個新的采樣點);
(2) 最小距離性質 (任意兩個采樣點之間的距離大於給定的采樣半徑);
(3) 最大化性質 (采樣區域被所有的采樣圓盤完全覆蓋).
滿足這 3 個條件的采樣方法稱為最大化 Poisson 圓盤采樣 (maximal Poisson-disk sampling, MPS).
2.Poisson-Disk Sampling偽代碼如下:
function WeightedSamplingElim(samples) Build a K-Dtree for each samples Assign weights wi to each sample si Build a heap for si using weights wi While number of samples > desired sj <- pull the top sample from heap For each sample si around sj Remove wij from wi Update the heap position of si
每個sample都會被分配一個對應的權重,其權重的參數如下:
w[index] += weightFunction(point, p, d2, d_max)
其中point為每個泊松圓盤固定點,p為point周圍的點,d2為周圍點與point之間的距離
,d_max為設定Poisson radius距離。
點雲工具箱中該功能實現依賴於Open3d庫,代碼如下:
pcd = tri_mesh.sample_points_poisson_disk(number_of_points=value) # number_of_points參數為采樣點數