目录

1、点云概述

1.1 点云的表达形式

1.2 ​​​​​​​点云的特点

2、主成分分析在点云中的应用

2.1 PCA主成分分析

2.2 Kernel PCA(升维)

2.3 PCA计算法向量程序

3、点云滤波

3.1 点云去除

3.2 降采样

3.2.1 降采样流程

3.2.2 哈希函数

3.2.3 Farthest Point Sampling(FPS)

3.2.4 Normal Space Sampling(法向量降采样) 

​编辑3.2.5 Learning to Sample

3.3 上采样

3.3.1 Bilateral Filter——Gaussian Filter

3.3.2 双边滤波

3.3.3 点云上采样

1、点云概述

1.1 点云的表达形式

1.2 ​​​​​​​点云的特点

1、密度不均匀

2、不规则

3、没有纹理信息(三人成车)

4、对深度学习的特点:

        无序性

        旋转不变性

2、主成分分析在点云中的应用

2.1 PCA主成分分析

  • 主成分分析在点云中的作用
  1. 降维
  2. 法向量估计
  3. 分类
  •  矩阵的运算:

  • 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 点云上采样

激光点云是稀疏的,在传感器融合时,会造成很多像素没有深度信息,所以要对点云进行上采样。运用双边滤波来进行上采样。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐