三维点云课程—Voxel Grid Downsamping
1.选取点的方案
1.质心法:计算所有满足条件点的平均值,速度慢,但相对准确
2.随机法:从满足条件的点中随机选择一个点,速度块,但误差大
2.步骤
1.计算三维点集
p
1
,
p
2
,
.
.
.
,
p
N
{p_1,p_2,...,p_N}
p1,p2,...,pN的最小和最大值
x
max
=
max
(
x
1
,
x
2
,
⋯
,
x
N
)
,
x
min
=
min
(
x
1
,
x
2
,
⋯
,
x
N
)
,
y
max
=
⋯
⋯
x_{\max }=\max \left(x_{1}, x_{2}, \cdots, x_{N}\right), x_{\min }=\min \left(x_{1}, x_{2}, \cdots, x_{N}\right), y_{\max }=\cdots \cdots
xmax=max(x1,x2,⋯,xN),xmin=min(x1,x2,⋯,xN),ymax=⋯⋯
2.设置一个voxel grid的大小r
3.计算x,y,z关于voxel grid的维数
D
x
=
(
x
max
−
x
min
)
/
r
D
y
=
(
y
max
−
y
min
)
/
r
D
z
=
(
z
max
−
z
min
)
/
r
\begin{aligned} D_{x} &=\left(x_{\max }-x_{\min }\right) / r \\ D_{y} &=\left(y_{\max }-y_{\min }\right) / r \\ D_{z} &=\left(z_{\max }-z_{\min }\right) / r \end{aligned}
DxDyDz=(xmax−xmin)/r=(ymax−ymin)/r=(zmax−zmin)/r
4.计算每个点关于voxel的索引
h
x
=
⌊
(
x
−
x
min
)
/
r
⌋
h
y
=
⌊
(
y
−
y
min
)
/
r
⌋
h
z
=
⌊
(
z
−
z
min
)
/
r
⌋
h
=
h
x
+
h
y
∗
D
x
+
h
z
∗
D
x
∗
D
y
\begin{aligned} h_{x} &=\left\lfloor\left(x-x_{\min }\right) / r\right\rfloor \\ h_{y} &=\left\lfloor\left(y-y_{\min }\right) / r\right\rfloor \\ h_{z} &=\left\lfloor\left(z-z_{\min }\right) / r\right\rfloor \\ h &=h_{x}+h_{y} * D_{x}+h_{z} * D_{x} * D_{y} \end{aligned}
hxhyhzh=⌊(x−xmin)/r⌋=⌊(y−ymin)/r⌋=⌊(z−zmin)/r⌋=hx+hy∗Dx+hz∗Dx∗Dy
其中
⌊
⌋
\left\lfloor\right\rfloor
⌊⌋符号表示取整的意思
5.对第四步得到的h的索引进行排序,编程中注意:原始三维坐标点也要跟着一起变化
6.选择一种选取点的方案(质心法还是随机法),进行筛选。
例如排序后h的索引为
0
,
0
,
0
,
0
,
3
,
3
,
3
,
8
,
8
,
8
,
8
,
8
,
8
,
.
.
.
.
0,0,0,0,3,3,3,8,8,8,8,8,8,....
0,0,0,0,3,3,3,8,8,8,8,8,8,....
那么质心法就是在所有0的索引所对应的原始点云中进行求均值,随机法就是在所有0的索引中随机选择一个索引,再通过这个索引得到原始点云的数据。对于3或者8也是这样。可能表述的并不是很清楚,具体参见代码,代码中有详细的解释
3.Voxel Grid Downsamping 代码
# 实现voxel滤波,并加载数据集中的文件进行验证
import open3d as o3d
import os
import numpy as np
from pyntcloud import PyntCloud
# 采用均值法获取所有相关的索引
def get_all_index(lst,item):
return [i for i in range(len(lst)) if lst[i]==item]
## 采用随机法获取其中的一个索引
def get_one_index(lst,item):
for i in range(len(lst)):
if lst[i]==item:
return i
# 功能:对点云进行voxel滤波
# 输入:point_cloud:输入点云
# leaf_size: voxel尺寸
# 输出:filtered_points:滤波后的点云
def voxel_filter(point_cloud, leaf_size,method="centroid"):
filtered_points = []
filtered_idx=[]
minPC=np.min(point_cloud)
maxPC=np.max(point_cloud)
Dx=(maxPC[0]-minPC[0])/leaf_size
Dy=(maxPC[1]-minPC[1])/leaf_size
#Dz=(maxPC[2]-minPC(2))/leaf_size
N=point_cloud.shape[0]
for i in range(N):
hx=(np.asarray(point_cloud)[i][0] - minPC[0] )//leaf_size
hy=(np.asarray(point_cloud)[i][1] - minPC[1] ) //leaf_size
hz=(np.asarray(point_cloud)[i][2] - minPC[2] )//leaf_size
idx=hx+hy*Dx+hz*Dx*Dy
filtered_idx.append(idx)
#argsort函数返回的是数组值从小到大的索引值,加上[::-1]表示从大到小
sort_idx=np.array(filtered_idx).argsort()[::-1]
#filtered_idx_ok里面由相等的数,且按照从大到小的顺序进行
filtered_idx_ok=np.array(filtered_idx)[sort_idx]
#point_cloud_ok是将原始点云数据point_cloud按照sort_idx进行排列
point_cloud_ok=np.asarray(point_cloud)[sort_idx]
#set()函数产生一个无序不重复元素集,给下面的for循环使用
idx_set=set(filtered_idx_ok)
if method=="centroid":
print("method is centroid")
for i in idx_set:
all_idx=get_all_index(filtered_idx_ok,i)
#axis = 0:压缩行,对各列求均值,返回 1* n 矩阵
xm=np.mean(np.asarray(point_cloud_ok)[all_idx],axis=0)
filtered_points.append(xm)
elif method=="random":
print("method is random")
for i in idx_set:
one_idx=get_one_index(filtered_idx_ok,i)
xm=np.asarray(point_cloud_ok)[one_idx]
filtered_points.append(xm)
# 把点云格式改成array,并对外返回
filtered_points = np.array(filtered_points, dtype=np.float64)
return filtered_points
def main():
# 加载自己的点云文件
file_name = r"D:\ply_data\airplane\train\airplane_0001.ply"
point_cloud_pynt = PyntCloud.from_file(file_name)
#显示原图
point_cloud_o3d = point_cloud_pynt.to_instance("open3d", mesh=False)
o3d.visualization.draw_geometries([point_cloud_o3d]) # 显示原始点云
# 调用voxel滤波函数,实现滤波
filtered_cloud = voxel_filter(point_cloud_pynt.points, 10,method="random")
point_cloud_o3d.points = o3d.utility.Vector3dVector(filtered_cloud)
# 显示滤波后的点云
o3d.visualization.draw_geometries([point_cloud_o3d])
if __name__ == '__main__':
main()
由于数据集比较大,需要的话请加我QQ:2822902808,备注:点云数据集
4.仿真结果
原图:
滤波后