三维点云学习(1)上


三维点云学习(1)上

环境安装

1.系统环境 win10 或者 ubuntu

2. Anaconda3+python3.6

使用Anaconda创建的conda虚拟环境进行python的编写
Anaconda界面
创建的python3.6虚拟环境
环境安装主要参考如下网址
安装Anaconda3
Anconda3 安装 open3d

3. 使用conda install 或者 pip install 下载需要的py模块

open3d numpy matplotlib pandas plyfile pyntcloud

1
2
3
4
5
#off_to_ply.py
import os
import numpy as np
from plyfile import PlyData
from plyfile import PlyElement
1
2
3
4
5
#pca_normal.py
import open3d as o3d
import os
import numpy as np
import matplotlib.pyplot as plt
1
2
3
4
5
#voxel_filter.py
import open3d as o3d
import os
import numpy as np
from pyntcloud import PyntCloud

4.数据集下载

为40种物体的三维点云数据集
链接:https://pan.baidu.com/s/1LX9xeiXJ0t-Fne8BCGSjlQ
提取码:es14

5.单独读取数据集,并在open3d中显示原点云图

注意:要把对应的数据集文件 如:"sofa_0001.txt"放在对应的代码路径下

1
2
3
4
5
6
7
8
9
import open3d as o3d    #导入open3d
import numpy as np
import matplotlib as plt
raw_point_cloud_matrix = np.genfromtxt(r"sofa_0001.txt", delimiter=",").reshape((-1,3))
# print(raw_point_cloud_matrix)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(raw_point_cloud_matrix)
print(pcd)
o3d.visualization.draw_geometries([pcd])

原始点云数据在open3d下运行结果如下所示:
在这里插入图片描述

6.便捷的显示模块

摘自enginelong的博客代码片

1
2
3
4
5
6
7
8
9
10
# 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()
1
2
3
4
5
6
7
8
9
10
# 二维点云显示函数
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()

7.PCA 主成分分析法

参考公式网址如下所示
三维点云处理学习笔记
代码参考网址如下所示
点云学习(1)参考网址1
点云学习(1)参考网址2

PCA函数编写

主要流程在这里插入图片描述
1.取均值,去中心化在这里插入图片描述
1
2
    average_data = np.mean(data,axis=0)       #求 NX3 向量的均值
    decentration_matrix = data - average_data   #去中心化
2.求取协方差矩阵H,并用SVD奇异值分解,求解出相应的特征值、特征向量

在这里插入图片描述

1
2
    H = np.dot(decentration_matrix.T,decentration_matrix)  #求解协方差矩阵 H
    eigenvectors,eigenvalues,eigenvectors_T = np.linalg.svd(H)    # SVD求解特征值、特征向量
3.对2步求解出的特征值特征向量进行降序排序,并存放到列表中

在这里插入图片描述

1
2
3
4
    if sort:
        sort = eigenvalues.argsort()[::-1]      #降序排列
        eigenvalues = eigenvalues[sort]         #索引
        eigenvectors = eigenvectors[:, sort]

PCA处理整体代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 功能:计算PCA的函数
# 输入:
#     data:点云,NX3的矩阵
#     correlation:区分np的cov和corrcoef,不输入时默认为False
#     sort: 特征值排序,排序是为了其他功能方便使用,不输入时默认为True
# 输出:
#     eigenvalues:特征值
#     eigenvectors:特征向量
def PCA(data, correlation=False, sort=True):
    # 作业1
    # 屏蔽开始
    average_data = np.mean(data,axis=0)       #求 NX3 向量的均值
    decentration_matrix = data - average_data   #去中心化
    H = np.dot(decentration_matrix.T,decentration_matrix)  #求解协方差矩阵 H
    eigenvectors,eigenvalues,eigenvectors_T = np.linalg.svd(H)    # SVD求解特征值、特征向量
    # 屏蔽结束

    if sort:
        sort = eigenvalues.argsort()[::-1]      #降序排列
        eigenvalues = eigenvalues[sort]         #索引
        eigenvectors = eigenvectors[:, sort]

    return eigenvalues, eigenvectors
4.调用结果

通过调用PCA算法,并显示两个主成分方向,如下图所示黑色线为第一主成分,红色线为第二主成分
在这里插入图片描述

5.PCA的应用
降维(Encoder)

在这里插入图片描述

1
2
3
    #将原数据进行降维度处理
    point_cloud_encode = (np.dot(point_cloud_vector.T,point_cloud_raw.T)).T   #主成分的转置 dot 原数据
    Point_Show(point_cloud_encode)

效果如下所示:
降维后效果图
在这里插入图片描述

升维(Decoder)

在这里插入图片描述

1
2
3
    #使用主方向进行升维
    point_cloud_decode = (np.dot(point_cloud_vector,point_cloud_encode.T)).T
    Point_Cloud_Show(point_cloud_decode)

效果如下所示:
使用两个主方向再次升维后的结果
在这里插入图片描述

8.法向量估计

法向量估计
思想:选取点云中每一点,对其进行临近点的搜索,将包含该点的临近点拟合成曲面,对曲面中的点进行PCA主成分分析,查找特征值最小的对应的特征向量,该特征向量即为该拟合曲面的法向量(摘自:秦乐乐CSDN博客)

1.编码流程

在这里插入图片描述

2.代码展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    # 循环计算每个点的法向量
    pcd_tree = o3d.geometry.KDTreeFlann(point_cloud_o3d)           #将原始点云数据输入到KD,进行近邻取点
    normals = []    #储存曲面的法向量
    # 作业2
    # 屏蔽开始
    print(points.shape[0])      #打印当前点数 20000个点
    for i in range(points.shape[0]):
        # search_knn_vector_3d函数 , 输入值[每一点,x]      返回值 [int, open3d.utility.IntVector, open3d.utility.DoubleVector]
        [_,idx,_] = pcd_tree.search_knn_vector_3d(point_cloud_o3d.points[i],10)      #取10个临近点进行曲线拟合
        # asarray和array 一样 但是array会copy出一个副本,asarray不会,节省内存
        k_nearest_point = np.asarray(point_cloud_o3d.points)[idx, :]  # 找出每一点的10个临近点,类似于拟合成曲面,然后进行PCA找到特征向量最小的值,作为法向量
        w, v = PCA(k_nearest_point)
        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])

3.效果展示

如下图1所示为图2的法向量加粗展示,
法线向量显示:在显示窗口按n 可按 + - 更改点的大小(o3d)
在这里插入图片描述在这里插入图片描述

完整代码(包含PCA算法、PCA应用(升降维)、法向量估计展示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# 实现PCA分析和法向量计算,并加载数据集中的文件进行验证

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

# 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()


# 功能:计算PCA的函数
# 输入:
#     data:点云,NX3的矩阵
#     correlation:区分np的cov和corrcoef,不输入时默认为False
#     sort: 特征值排序,排序是为了其他功能方便使用,不输入时默认为True
# 输出:
#     eigenvalues:特征值
#     eigenvectors:特征向量
def PCA(data, correlation=False, sort=True):
    # 作业1
    # 屏蔽开始
    average_data = np.mean(data,axis=0)       #求 NX3 向量的均值
    decentration_matrix = data - average_data   #去中心化
    H = np.dot(decentration_matrix.T,decentration_matrix)  #求解协方差矩阵 H
    eigenvectors,eigenvalues,eigenvectors_T = np.linalg.svd(H)    # SVD求解特征值、特征向量
    # 屏蔽结束

    if sort:
        sort = eigenvalues.argsort()[::-1]      #降序排列
        eigenvalues = eigenvalues[sort]         #索引
        eigenvectors = eigenvectors[:, sort]

    return eigenvalues, eigenvectors


def main():
    # 指定点云路径
    # 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') # 默认使用第一个点云

    # 加载原始点云,txt处理
    point_cloud_raw = np.genfromtxt(r"sofa_0001.txt", delimiter=",").reshape((-1,3))  #为 xyz的 N*3矩阵
    points = DataFrame(point_cloud_raw[:, 0:3])  # 选取每一列 的 第0个元素到第二个元素   [0,3)
    points.columns = ['x', 'y', 'z']  # 给选取到的数据 附上标题
    point_cloud_o3d  = o3d.geometry.PointCloud()    #实例化
    point_cloud_o3d.points = o3d.utility.Vector3dVector(point_cloud_raw)   #转化为点云数据
    #o3d.visualization.draw_geometries([point_cloud_o3d]) # 显示原始点云

    # 从点云中获取点,只对点进行处理
    print(point_cloud_o3d)              #打印点数

    # 用PCA分析点云主方向
    w, v = PCA(points)        # w为特征值 v为主方向
    point_cloud_vector1 = v[:, 0]   #点云主方向对应的向量,第一主成分
    point_cloud_vector2 = v[:, 1]  # 点云主方向对应的向量,第二主成分
    point_cloud_vector = v[:,0:2]  # 点云主方向与次方向
    print('the main orientation of this pointcloud is: ', point_cloud_vector1)
    print('the main orientation of this pointcloud is: ', point_cloud_vector2)

    #在原点云中画图
    point = [[0,0,0],point_cloud_vector1,point_cloud_vector2]  #画点:原点、第一主成分、第二主成分
    lines = [[0,1],[0,2]]      #画出三点之间两两连线
    colors = [[1,0,0],[0,0,0]]
    #构造open3d中的LineSet对象,用于主成分显示
    line_set = o3d.geometry.LineSet(points=o3d.utility.Vector3dVector(point),lines=o3d.utility.Vector2iVector(lines))
    line_set.colors = o3d.utility.Vector3dVector(colors)
    #o3d.visualization.draw_geometries([point_cloud_o3d,line_set]) # 显示原始点云和PCA后的连线

    #将原数据进行降维度处理
    point_cloud_encode = (np.dot(point_cloud_vector.T,point_cloud_raw.T)).T   #主成分的转置 dot 原数据
    #Point_Show(point_cloud_encode)
    #使用主方向进行升维
    point_cloud_decode = (np.dot(point_cloud_vector,point_cloud_encode.T)).T
    #Point_Cloud_Show(point_cloud_decode)

    # 循环计算每个点的法向量
    pcd_tree = o3d.geometry.KDTreeFlann(point_cloud_o3d)           #将原始点云数据输入到KD,进行近邻取点
    normals = []    #储存曲面的法向量
    # 作业2
    # 屏蔽开始
    print(points.shape[0])      #打印当前点数 20000个点
    for i in range(points.shape[0]):
        # search_knn_vector_3d函数 , 输入值[每一点,x]      返回值 [int, open3d.utility.IntVector, open3d.utility.DoubleVector]
        [_,idx,_] = pcd_tree.search_knn_vector_3d(point_cloud_o3d.points[i],10)      #取10个临近点进行曲线拟合
        # asarray和array 一样 但是array会copy出一个副本,asarray不会,节省内存
        k_nearest_point = np.asarray(point_cloud_o3d.points)[idx, :]  # 找出每一点的10个临近点,类似于拟合成曲面,然后进行PCA找到特征向量最小的值,作为法向量
        w, v = PCA(k_nearest_point)
        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])


if __name__ == '__main__':
    main()