三维点云处理系列----PCA

一、主成分分析

PCA推导的基础数学知识准备
1、矩阵和向量相乘
矩阵和向量相乘实际是矩阵对向量进行旋转加拉伸作用。可以试试例子:矩阵为[(0,1),(2,1)],向量为(1,0)’,相乘之后向量变成了(0,2)’。进行了旋转和拉伸。但是,如果向量刚好是矩阵的特征向量的时候,矩阵只会对向量有缩放的效果,不会有旋转。
2、SVD分解
一个矩阵,可以分解成三个矩阵相乘的形式。
在这里插入图片描述

U和V都是酉矩阵,即
在这里插入图片描述
Σ是一个对角阵。所以也可以从SVD的角度来理解矩阵和向量乘法的一个物理意义。U,V值会对向量进行旋转变换,而Σ会对向量进行缩放变换。
3、瑞利商
瑞利商的简要理解:向量被一个矩阵相乘之后,相当于是进行了拉伸和旋转变换,拉伸变换的倍数是不超过矩阵A的特征值的最大最小值的。
瑞利商被用来求解PCA当中投影方差的最大最小值,从而得出投影主向量。
在这里插入图片描述

PCA的过程:
PCA的主要思想是:对输入的数据进行换基底的操作,使得数据在PCA得到的基底上有更好的表示。比如最大主向量,就是使得数据在最大主向量上分布更加分散,投影的方差最大。
对数据求均值,进行去中心化,得到去中心化之后的矩阵X
假设投影主向量为z,投影的长度大小为X’z,则投影的均值为X’z各个元素加和求均值
投影的方差:因为X先经过了去中心化,在合并化简的时候就等于均值矩阵X在z方向上投影的平方,最后使用瑞利商求方差最大化,发现方差最大的时候,对应的投影方向就是均值矩阵XX’的最大特征值对应的特征向量。

在这里插入图片描述

4、核PCA
PCA针对线性数据可以较好的提取出主向量对数据进行表示,但是对于非线性数据就不能较好的表示。
核PCA主要思想是:把当前的数据升高一个维度,在较高的维度里去表示当前的数据就能清晰的表示出来。核心关键是找出一个变换使得低维空间数据点转换到高维空间。
在这里插入图片描述
上升到高维空间后
在这里插入图片描述
5、PCA应用
法向量计算
基本步骤:
1.随机选取出一个点
2.以改点为中心点,选取邻域中k个点出来
3.以这k个点作为一组数据输入到pca求出数据的主向量
4.选取出特征值最小的特征向量即为法向量方向

6、点云滤波算法
点云滤波voxel filter

出发点问题
对于体素中的点,保留那一个点作为输出比较好?
思路有两种:选取一个voxel中的平均坐标点作为滤波输出;或者进行随机选取一个点作为输出

voxel filter的一般流程:

1.计算点云的整体空间范围,得出点云空间的最大、最小范围
2.设定体素大小r,计算出空间划分的份数索引
D_x=(x_max-x_min)/r
D_y=(y_max-y_min)/r
D_z=(z_max-z_min)/r
3.计算每个点的分组索引
hx = floor((x-x_min)/r)
hy = floor((y-y_min)/r)
hz = floor((y-y_min)/r)
index = hx+hy*D_x+hz*Dx*Dy
4.对每个点的索引进行排序,处在同一个体素的索引相同,相同索引聚集到一起
5.对每个体素中的点进行降采样

另外一种不采用排序的方法:使用哈希表映射,把所有点都映射到哈希表中,对于处于同一个位置的点进行采样输出。
hash(hx,hy,hz) = index % hash_size
但有可能处于不同体素的点会被映射到同一个哈希容器中,这时候就是存在冲突,需要对其进行冲突检测,比如哈希容器大小为100,索引为191,291的两个点不属于同一个体素,但是经过映射都被映射到了91这个容器。

在这里插入图片描述

课后作业代码编写:

# 实现PCA分析和法向量计算,并加载数据集中的文件进行验证
import open3d as o3d 
import os
import numpy as np
from pyntcloud import PyntCloud

# 功能:计算PCA的函数
# 输入:
#     data:点云,NX3的矩阵
#     correlation:区分np的cov和corrcoef,不输入时默认为False
#     sort: 特征值排序,排序是为了其他功能方便使用,不输入时默认为True
# 输出:
#     eigenvalues:特征值
#     eigenvectors:特征向量
def PCA(data, correlation=False, sort=True):
    #1计算数据均值
    data_mean = np.mean(data, axis=0)
    #2对数据进行去中心化
    data = data - data_mean
    #3对去中心化的数据H = X'X矩阵奇异值分解,求特征值和特征向量
    ##注意data中存储的格式是NX3,协方差矩阵描述的是各个维度之间的相关性,所以协方差矩阵大小为3x3.并且协方差矩阵为对称矩阵
    ##对角线上为各个维度内的方差,非对角线上为各个维度间的方差
    #对称矩阵分解出来的U和V是互为转置矩阵
    H = np.dot(data.T, data)
    #H = np.cov(data, rowvar=False)
    eigenvectors, eigenvalues, eigenvectors_t = np.linalg.svd(H)
    #4对特征值和特征向量进行排序
    if sort:
        sort = eigenvalues.argsort()[::-1]
        eigenvalues = eigenvalues[sort]
        #U中的列向量为特征向量
        eigenvectors = eigenvectors[:, sort]

    return eigenvalues, eigenvectors


def main():
    # 加载原始点云
    point_cloud_pynt = PyntCloud.from_file("airplane_0529.ply")
    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])
    size = points.shape[0]
    # 用PCA分析点云主方向
    w, u = PCA(points)
    point_cloud_vector = u[:, 0] #点云主方向对应的向量
    print('the main orientation of this pointcloud is: ', point_cloud_vector ,'and',w[0])
    print('the second orientation of this pointcloud is:',u[:, 1])
    print('the second orientation of this pointcloud is:',u[:, 2])

    # 循环计算每个点的法向量
    pcd_tree = o3d.geometry.KDTreeFlann(point_cloud_o3d)
    normals = []

    # 1.选取一个点,搜索10个邻近点
    for i in range(size):
      [_, idx, _] = pcd_tree.search_knn_vector_3d(point_cloud_o3d.points[i], 10)
      k_nearest_point = np.asarray(point_cloud_o3d.points)[idx, :]  #按照idx取出k个近邻点
    # 2.选取改点的一个邻域,计算该邻域内的点的PCA
      u_1, v_1 = PCA(k_nearest_point)
      
    # 3.选取出特征值最小对应的特征向量作为法向量方向
      normals.append(v_1[:,2])

    normals = np.array(normals, dtype=np.float64)

if __name__ == '__main__':
    main()

python处理点云相关库、函数记录熟悉

numpy库

numpy.mean(a, axis, dtype, out,keepdims )   

经常操作的参数为axis,以m * n矩阵举例:
axis 不设置值,对 mn 个数求均值,返回一个实数
axis = 0:压缩行,对各列求均值,返回 1
n 矩阵
axis =1 :压缩列,对各行求均值,返回 m *1 矩阵

H = np.dot(data.T, data)

点乘运算

eigenvectors, eigenvalues, eigenvectors_t = np.linalg.svd(H)

奇异值分解:
函数:np.linalg.svd(a,full_matrices=1,compute_uv=1)。

参数:
a是一个形如(M,N)矩阵
full_matrices的取值是为0或者1,默认值为1,这时u的大小为(M,M),v的大小为(N,N) 。否则u的大小为(M,K),v的大小为(K,N) ,K=min(M,N)。
compute_uv的取值是为0或者1,默认值为1,表示计算u,s,v。为0的时候只计算s。
返回值:
总共有三个返回值u,s,v
u大小为(M,M),s大小为(M,N),v大小为(N,N)。
A = usv

排序:

sort = eigenvalues.argsort()[::-1]#返回eigenvalues中元素从大到小的索引

x.argsort()
#输出x中元素从小到大排列的对应的index(索引)

[::-1]两个冒号,三个数,起始:结尾(不包括):间隔。
最后为-1代表逆序。

H = np.cov(data, rowvar=False)
def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None,aweights=None)

m:一维或则二维的数组,默认情况下每一行代表一个变量(属性),每一列代表一个观测
y:与m具有一样的形式的一组数据
rowvar:默认为True,此时每一行代表一个变量(属性),每一列代表一个观测(一个点);为False时,则反之

协方差矩阵求解

矩阵拼接:

data_index_point = np.c_[index, point_x, point_y, point_z]
np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。
np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。

python点云数据处理

#读取加载点云
PyntCloud.from_file("airplane_0529.ply")
  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值