点云下采样2

来源:https://blog.csdn.net/weixin_41281151/article/details/107125844

点云体素降采样(Voxel Filter Downsampling)
代码参考网址秦乐乐CSDN博客
理论参考知乎博主:WALL-E

1.方法
Centroid 均值采样
Random select 随机采样


2.伪代码流程

注解:

第4步是计算没一个点的体素索引。
3.代码编写
step1 计算边界值

#step1 计算边界点
x_max, y_max, z_max = np.amax(point_cloud,axis=0) #计算 x,y,z三个维度的最值
x_min, y_min, z_min = np.amin(point_cloud, axis=0)

step2 确定体素的尺寸

#step2 确定体素的尺寸
size_r = leaf_size

step3 计算每个 volex的维度,即在每个维度上的体素的个数。

#step3 计算每个 volex的维度
Dx = (x_max - x_min)/size_r +1
Dy = (y_max - y_min)/size_r +1
Dz = (z_max - z_min)/size_r +1

step4 计算每个点在volex grid内每一个维度的值,即计算每个点属于哪个体素,亦即计算每个点的体素的索引。

#step4 计算每个点在volex grid内每一个维度的值
h = list()
for i in range(len(point_cloud)):
Dx = (x_max - x_min)/size_r
Dy = (y_max - y_min)/size_r
Dz = (z_max - z_min)/size_r
h.append(hx + hy*Dx + hz*Dx*Dy)

step5 对h值进行排序

h = np.array(h)
h_indice = np.argsort(h) #提取索引
h_sorted = h[h_indice] #升序

将h值相同的点放入到同一个grid中,并进行筛选,并区分random和centroid两种滤波方式

count = 0 #用于维度的累计
#将h值相同的点放入到同一个grid中,并进行筛选
for i in range(len(h_sorted)-1): #0-19999个数据点
if h_sorted[i] == h_sorted[i+1]: #当前的点与后面的相同,放在同一个volex grid中
continue
else:
if(filter_mode == "centroid"): #均值滤波
point_idx = h_indice[count: i+1]
filtered_points.append(np.mean(point_cloud[point_idx],axis=0)) #取同一个grid的均值
count = i
elif(filter_mode == "random"): #随机滤波
point_idx = h_indice[count: i+1]
random_points = random.choice(point_cloud[point_idx])
filtered_points.append(random_points)
count = i

调用

# 调用voxel滤波函数,实现滤波
filtered_cloud = voxel_filter(points, 0.05, "random") #centroid or random
point_cloud_o3d_filter.points = o3d.utility.Vector3dVector(filtered_cloud)

效果图

原点云图在open3d中展示如下

random随机降采样效果如下

centroid随机降采样效果如下

整体对比


完整代码
注意:数据集放在相应运行的目录下

# 实现voxel滤波,并加载数据集中的文件进行验证

import open3d as o3d
import os
import numpy as np
from pyntcloud import PyntCloud
import matplotlib.pyplot as plt
import random
from pandas import DataFrame

# matplotlib显示点云函数
def Point_Cloud_Show(points):
fig = plt.figure(dpi=150)
ax = fig.add_subplot(111, projection='3d')
ax.scatter(points[:, 0], points[:, 1], points[:, 2], cmap='spectral', s=2, linewidths=0, alpha=1, marker=".")
plt.title('Point Cloud')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()

# 二维点云显示函数
def Point_Show(pca_point_cloud):
x = []
y = []
pca_point_cloud = np.asarray(pca_point_cloud)
for i in range(10000):
x.append(pca_point_cloud[i][0])
y.append(pca_point_cloud[i][1])
plt.scatter(x, y)
plt.show()

 

# 功能:对点云进行voxel滤波
# 输入:
# point_cloud:输入点云
# leaf_size: voxel尺寸
def voxel_filter(point_cloud, leaf_size,filter_mode):
filtered_points = []
# 作业3
# 屏蔽开始
#step1 计算边界点
x_max, y_max, z_max = np.amax(point_cloud,axis=0) #计算 x,y,z三个维度的最值
x_min, y_min, z_min = np.amin(point_cloud, axis=0)
#step2 确定体素的尺寸
size_r = leaf_size
#step3 计算每个 volex的维度
Dx = (x_max - x_min)/size_r
Dy = (y_max - y_min)/size_r
Dz = (z_max - z_min)/size_r
#step4 计算每个点在volex grid内每一个维度的值
h = list()
for i in range(len(point_cloud)):
hx = np.floor((point_cloud[i][0] - x_min)/size_r)
hy = np.floor((point_cloud[i][1] - y_min)/size_r)
hz = np.floor((point_cloud[i][2] - z_min)/size_r)
h.append(hx + hy*Dx + hz*Dx*Dy)
#step5 对h值进行排序
h = np.array(h)
h_indice = np.argsort(h) #提取索引
h_sorted = h[h_indice] #升序
count = 0 #用于维度的累计
#将h值相同的点放入到同一个grid中,并进行筛选
for i in range(len(h_sorted)-1): #0-19999个数据点
if h_sorted[i] == h_sorted[i+1]: #当前的点与后面的相同,放在同一个volex grid中
continue
else:
if(filter_mode == "centroid"): #均值滤波
point_idx = h_indice[count: i+1]
filtered_points.append(np.mean(point_cloud[point_idx],axis=0)) #取同一个grid的均值
count = i
elif(filter_mode == "random"): #随机滤波
point_idx = h_indice[count: i+1]
random_points = random.choice(point_cloud[point_idx])
filtered_points.append(random_points)
count = i

# 屏蔽结束

# 把点云格式改成array,并对外返回
filtered_points = np.array(filtered_points, dtype=np.float64)
return filtered_points

def main():
# # 从ModelNet数据集文件夹中自动索引路径,加载点云
# cat_index = 10 # 物体编号,范围是0-39,即对应数据集中40个物体
# root_dir = '/Users/renqian/cloud_lesson/ModelNet40/ply_data_points' # 数据集路径
# cat = os.listdir(root_dir)
# filename = os.path.join(root_dir, cat[cat_index],'train', cat[cat_index]+'_0001.ply') # 默认使用第一个点云
# point_cloud_pynt = PyntCloud.from_file(file_name)

# 加载自己的点云文件
point_cloud_raw = np.genfromtxt(r"person_0001.txt", delimiter=",")
point_cloud_raw = DataFrame(point_cloud_raw[:,0:3]) # 为 xyz的 N*3矩阵
point_cloud_raw.columns = ['x', 'y', 'z'] # 给选取到的数据 附上标题
point_cloud_pynt = PyntCloud(point_cloud_raw) # 将points的数据 存到结构体中

point_cloud_o3d_orign = point_cloud_pynt.to_instance("open3d", mesh=False) # to_instance实例化
point_cloud_o3d_filter = o3d.geometry.PointCloud() #实例化

# o3d.visualization.draw_geometries([point_cloud_o3d]) # 显示原始点云

points = np.array(point_cloud_o3d_orign.points)

# 调用voxel滤波函数,实现滤波
filtered_cloud = voxel_filter(points, 0.05, "centroid") #centroid or random
point_cloud_o3d_filter.points = o3d.utility.Vector3dVector(filtered_cloud)
# 显示滤波前后的点云
o3d.visualization.draw_geometries([point_cloud_o3d_orign])
o3d.visualization.draw_geometries([point_cloud_o3d_filter])
if __name__ == '__main__':
main()
原文地址:https://www.cnblogs.com/yibeimingyue/p/15592108.html