目录
3.2.3 Farthest Point Sampling(FPS)
3.2.4 Normal Space Sampling(法向量降采样)
3.3.1 Bilateral Filter——Gaussian Filter
1、点云概述
1.1 点云的表达形式
1.2 点云的特点
1、密度不均匀
2、不规则
3、没有纹理信息(三人成车)
4、对深度学习的特点:
无序性
旋转不变性
2、主成分分析在点云中的应用
2.1 PCA主成分分析
- 主成分分析在点云中的作用
- 降维
- 法向量估计
- 分类
- 矩阵的运算:
- PCA :
降维和升维肯定有数据损失,所以要选取重要的主向量
PCA是线性的
-
PCA应用
第一列是输入的图像,例如30*30就是900维的矩阵,把900维的矩阵在l个主向量上投影得到l组a,第二列是计算平均值所以都一样。可以看到由6个主向量来重构图片(最后一列)可以达到识别的效果。对每组a进行聚类,传入新的图片与a对比相似度来分类
2.2 Kernel PCA(升维)
-
点云中点的法向量
点云拟合曲面的切面的法向量,只有定义邻域才有法向量,邻域大相对平滑受个别点的影响小,邻域大比较灵敏但容易受影响。法向量是最没用的向量,所有点投影到这个向量上的值加起来最小,反应最少的特征。
C是中心点
寻找法向量的步骤:
2.3 PCA计算法向量程序
# 实现PCA分析和法向量计算,并加载数据集中的文件进行验证
import open3d as o3d
import os
import numpy as np
import pandas as pd
from pyntcloud import PyntCloud
# 功能:计算PCA的函数
# 输入:
# data:点云,NX3的矩阵
# correlation:区分np的cov和corrcoef,不输入时默认为False
# sort: 特征值排序,排序是为了其他功能方便使用,不输入时默认为True
# 输出:
# eigenvalues:特征值
# eigenvectors:特征向量
def PCA(data, correlation=False, sort=True):
X = np.asarray(data).T #np.array与np.asarray功能是一样的,都是将输入转为矩阵格式,再转置
X_mean = np.mean(X,axis=1).reshape(3,1) #axis=1是每一行的平均值,等于0是没一列的平均值
X_head = X - X_mean
H = X_head.dot(X_head.T) #点乘转置
eigenvalues,eigenvectors = np.linalg.eig(H) #求矩阵的特征值和特征向量
#排序
if sort:
sort = eigenvalues.argsort()[::-1] #.argsort返回数组从小到大排序后对应的索引值
eigenvalues = eigenvalues[sort]
eigenvectors = eigenvectors[:, sort]
return eigenvalues, eigenvectors
#```handlebars
def main():
# 加载txt格式原始点云
points = pd.read_csv("/home/cxl/三维点云课程资源/1点云介绍及降采样/modelnet40_normal_resampled/modelnet40_normal_resampled/car/car_0001.txt") #将读入的数据按照numpyarray的方式存储
points = points.iloc[:,0:3] #iloc遍历的数数字,0:3对应0,1,和2
points.columns = ["x","y","z"] #给选取到的数据 附上标题
point_cloud_pynt = PyntCloud(points) #PyntCloud python的点云处理库
point_cloud_o3d = point_cloud_pynt.to_instance("open3d", mesh=False) #实例化
o3d.visualization.draw_geometries([point_cloud_o3d]) # 显示原始点云
# 从点云中获取点,只对点进行处理
points = point_cloud_pynt.points
print('total points number is:', points.shape[0])
# 用PCA分析点云主方向
w, v = PCA(points)
point_cloud_vector = v[:, 0] #点云主方向对应的向量
print('the main orientation of this pointcloud is: ', point_cloud_vector)
# 绘制三个主方向
line_set = o3d.geometry.LineSet()
line_set.points = o3d.utility.Vector3dVector([np.mean(points, axis=0), np.mean(points, axis=0) + v[:, 2], np.mean(points, axis=0) + v[:, 1], np.mean(points, axis=0) + v[:, 0]])
line_set.lines = o3d.utility.Vector2iVector([[0, 1], [0, 2], [0, 3]])
line_set.colors = o3d.utility.Vector3dVector([[0, 0, 1], [0, 1, 0], [1, 0, 0]])
o3d.visualization.draw_geometries([point_cloud_o3d, line_set])
# 循环计算每个点的法向量
pcd_tree = o3d.geometry.KDTreeFlann(point_cloud_o3d) #建立KD树
normals = []
for point in point_cloud_o3d.points:
[k, idx, _] = pcd_tree.search_knn_vector_3d(point, knn=50)#返回锚点的50个最近邻居的索引列表
w,v = PCA(points.iloc[idx,:])#前面的冒号就是取行数,后面的冒号是取列数
normals.append(v[:,2])
normals = np.array(normals, dtype=np.float64)
# TODO: 此处把法向量存放在了normals中
point_cloud_o3d.normals = o3d.utility.Vector3dVector(normals)
o3d.visualization.draw_geometries([point_cloud_o3d], point_show_normal=True)
# 利用法向量滚球法重建表面
radii = [0.005, 0.01, 0.02, 0.04]
rec_mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting(
point_cloud_o3d, o3d.utility.DoubleVector(radii))
o3d.visualization.draw_geometries([point_cloud_o3d, rec_mesh])
if __name__ == '__main__':
main()
3、点云滤波
3.1 点云去除
划定邻域,少于一定的值就去掉。
3.2 降采样
3.2.1 降采样流程
两种选点方式:1. 一个格子中计算平均点 2. 一个格子中随机选一个点
算法步骤:第6步有四个点在0格中,三个点3格中。每个格子中可以计算平均点,也可以随机选一个点
代码小坑:1、如果小格子过多,对超过int32所表示数的范围。 2. 涉及排序不能用>= <=
# 实现voxel滤波,并加载数据集中的文件进行验证
import sys
import open3d as o3d
import os
import numpy as np
import pandas as pd
from pyntcloud import PyntCloud
# 功能:对点云进行voxel滤波
# 输入:
# point_cloud:输入点云
# leaf_size: voxel尺寸
def voxel_filter(point_cloud, leaf_size):
filtered_points = []
#三个维度最小/大值
x_min, y_min, z_min = np.amin(point_cloud, axis=0)
x_max, y_max, z_max = np.amax(point_cloud, axis=0)
#确定每一个维度的格子数量
Dx = (x_max - x_min)//leaf_size + 1 #保证0-leaf_size 在第一个格子内
Dy = (y_max - y_min)//leaf_size + 1
Dz = (z_max - z_min)//leaf_size + 1
print("Dx x Dy x Dz is {} x {} x {}".format(Dx, Dy, Dz))
dict = { } #建立一个空的字典 放h对应点的数据
index_ = { } #放h对应点的数量
for i in range(len(point_cloud)):
hx = (point_cloud[i, 0] - x_min)//leaf_size + 1
hy = (point_cloud[i, 1] - y_min)//leaf_size + 1
hz = (point_cloud[i, 2] - z_min)//leaf_size + 1
h = hx + hy*Dx + hz*Dx*Dy
# 如果h不相同把点放入,相同则计算平均
if (h not in dict):
dict[h] = point_cloud[i]
index_[h] = 1
else:
val = dict.get(h, 0) #先把字典中的数据取出来
num = index_.get(h, 0)
dict[h] = (val * num + point_cloud[i])/(num + 1) #来一次点就需要求相同h的所有点的平均
index_[h] = num + 1
for key,value in dict.items():#当两个参数时
filtered_points.append(value)
# 把点云格式改成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)
# 加载自己的点云文件
#读取点云txt文件
points = np.genfromtxt("/home/cxl/三维点云课程资源/1点云介绍及降采样/modelnet40_normal_resampled/modelnet40_normal_resampled/car/car_0001.txt", delimiter=",")
points = pd.DataFrame(points[:, 0:3])
points.columns = ['x', 'y', 'z']
point_cloud_pynt = PyntCloud(points)
# 转成open3d能识别的格式
point_cloud_o3d = point_cloud_pynt.to_instance("open3d", mesh=False)
# o3d.visualization.draw_geometries([point_cloud_o3d]) # 显示原始点云
# 调用voxel滤波函数,实现滤波
points = np.asarray(point_cloud_o3d.points)#转为矩阵格式
filtered_cloud = voxel_filter(points, 0.1)
point_cloud_o3d.points = o3d.utility.Vector3dVector(filtered_cloud)#将点云转换成open3d中的数据形式并用point_cloud_o3d来保存
# 显示滤波后的点云
o3d.visualization.draw_geometries([point_cloud_o3d])
if __name__ == '__main__':
main()
3.2.2 哈希函数
如果激光雷达线束过多,这样降采样速度会很慢,为了提升速度,这里用到哈希函数。因为哈希函数不需要排序。
但这样会出现冲突点,即两点在哈希表中一样,但并不是临近点。
3.2.3 Farthest Point Sampling(FPS)
核心思想:挑一个点距离最远的点
3.2.4 Normal Space Sampling(法向量降采样)
核心思想:保留特征突变的点,不会漏掉特征。适用于点云对齐
3.2.5 Learning to Sample
核心思想:传统方法通过几何关系找到降采样点,S-NET是根据语义关系,即经过降采样之后的点云输入网络还是能够得到同样的标签。
设定几何约束,来使降采样的点与原来的点云距离相似。
可以看到通过神经网络,1024个点降到8个点还能达到83.6的准确率,传统方法只有61.6
3.3 上采样
3.3.1 Bilateral Filter——Gaussian Filter
高斯模糊:将该像素值替换成一个范围内像素的均值,参数越大范围越大,越模糊。
3.3.2 双边滤波
为了保留边缘特征,加入了一个权重,该像素与周围像素值差距越大权重越小,差距越小权重越大。这样像素差距小的范围就会被模糊,差距大的会被保留。
3.3.3 点云上采样
激光点云是稀疏的,在传感器融合时,会造成很多像素没有深度信息,所以要对点云进行上采样。运用双边滤波来进行上采样。