Open3d系列 | 1. Open3d实现点云数据读写、点云配准、点云法向量计算

简介: Open3d系列 | 1. Open3d实现点云数据读写、点云配准、点云法向量计算

1. 点云格式介绍


常见点云存储方式有pcd、ply、txt、bin文件,下面分别对其进行介绍。


1.1 pcd文件

pcd点云格式是pcl库种常常使用的点云文件格式。一个pcd文件中通常由两部分组成:分别是文件说明和点云数据。 文件说明由11行组成,如下所示。


.PCD v0.7 - Point Cloud Data file format #点云文件说明
VERSION 0.7 #版本说明
FIELDS x y z #每个点组成,参考第一部分点云组成
SIZE 4 4 4 #Fileds种每个数据占用的字节数
TYPE F F F #Fileds数据对应的类型,F表示浮点类型,U表示无符号整型,I表示整型
COUNT 1 1 1 #Fields数据对应的维度
WIDTH 35947 #对于无序点云为点的数量,对于有序点云为一圈点的数量。
HEIGHT 1 #对于无序点云取值默认为1,对于有序点云为垂直方向上的点数,比如多少线雷达
VIEWPOINT 0 0 0 1 0 0 0 #点云获取的视点,用于坐标变换
POINTS 35947 #点的数量
DATA ascii #点云数据的存储类型,0.7版本支持两种存储方式:ascii和binary。


从第12行开始为点云数据,每个点与上面的FIELDS相对应。一般来说,就的点云的x,y,z坐标。pcd格式的点云可视化由于其组成就是xyz坐标,所以可以使用mayavi.mlab来直接显示:mlab.points3d(x, y, z,y, mode="point")


ps:pcd文件的读取方式如下所示
def pcd_read(file_path):
    lines = []
    with open(file_path, 'r') as f:
        lines = f.readlines()
    return lines
points = pcd_read(file_path)


1.2 ply文件

一个ply文件中通常由两部分组成:分别是文件说明和点云数据。这与pcd文件很像。 文件说明如下组成,如下所示。前3行与最后1行是描述性语句,中间部分主要说明每一行存储的是什么样的数据、数据的数量、属性等。定义元素的组成需要用到element和property两部分,第一部分element定义元素名称和数量,第二部分property逐一列举元素组成和类型。


下面的示例中定义了点的存储名称为vertex,共35947个。后面存储每一个点含5个属性,即每一行由5个数组成;然后定义了面的存储名称是face,共69451个。后面存储每一个面含1个属性,这个属性是一个列表,存储的是每一个点的索引。


文件从end_header结束后为点云数据,每一行存储的数据与element对应,描述结束。随后开始逐一按行列举上述两种元素,第一种元素是35947个点,每行有5个属性,共35947行。第二中元素是69451个面,每行1个属性,共69451行。


ply#声明是ply文件
format ascii 1.0 #存储方式
comment zipper output#备注说明,解释性描述
element vertex 35947 #表示第一种元素构成是顶点,共35947个,下面的property对应点的组成。
property float x #点的第一个元素,x,浮点型
property float y #点的第二个元素,y,浮点型
property float z #点的第三个元素,z,浮点型
property float confidence #点的第四个元素,置信度,浮点型
property float intensity #点的第五个元素,强度,浮点型
element face 69451 #表示第二种元素构成是面,共69451个,下面的property对应面的组成
property list uchar int vertex_indices #list uchar 表示面类型,int vertex_indices面中对应上述点的索引
end_header #描述结束,下面开始逐一按行列举上述两种元素,第一种元素是35947个点,每行有5个属性,共35947行。同样地,然后开始按行列举上述第二种元素。
-0.0378297 0.12794 0.00447467 0.850855 0.5 
-0.0447794 0.128887 0.00190497 0.900159 0.5 
-0.0680095 0.151244 0.0371953 0.398443 0.5 
-0.00228741 0.13015 0.0232201 0.85268 0.5


对于ply文件合适内容,点云的每个点由5个属性构成,前三个属性也是xyz的坐标位置。将xyz坐标提取出来,即可利用mlab进行3d绘图。而描述面属性的数据在可视化操作中是不需要使用到的,所以直接提取描述点的35947行数据即可。如下代码所示:points = pcd_read(file_path)[12:(12+35947)],这里的第12行开始,到之后的35947+12行结束,都是描述点的数据。


ply格式的文件读取方式同pcd格式,不能直接使用numpy.fromfile来读取,只可以通过open函数读取,随后进行一定的数据处理。读取方式:


def pcd_read(file_path):
    lines = []
    with open(file_path, 'r') as f:
        lines = f.readlines()
    return lines
points = pcd_read(file_path)


1.3 txt文件

txt文件可以说是最朴素简单的点云描述文件了。 txt点云文件与前两篇介绍的pcd和ply点云格式的区别在于,其通常只含点云信息,不含文件说明部分。txt格式的点云文件中的每一行代表一个点,文件中行数即为点的数量。每行数据全部是点的数据,不包含任何的描述信息。行的取值可以是以下几种形式数据的排列组合。

(1)x、y、z:点云的空间坐标。

(2)i:强度值,强度反应了点的密集成度。

(3)r、g、b:rgb色彩信息。

(4)a:a代表alpha(透明度)。

(5)nx、ny、nz:n代表Normal,点云的法向量。


以Pointnet的modelnet40为例,其点云表示方式如下所示x、y、z、normal_x、normal_y、normal_z。样例如下所示:


# 每一行代表一个点的数据
0.208700,0.221100,0.565600,0.837600,-0.019280,0.545900
0.404700,0.080140,-0.002638,0.002952,0.973500,-0.228800
-0.043890,-0.115500,-0.412900,-0.672800,0.679900,-0.291600
-0.525000,0.158200,0.303300,0.000000,-0.413200,0.910600
-0.191700,-0.160900,0.657400,0.228400,-0.071910,0.970900


同样的,由于是txt文件,所以直接使用简单的numpy即可读取txt文件,而上述的pcd,ply需要用open函数的系统操作读取。txt文件的同样只需要读取其xyz坐标信息即可。

读取方式:np.loadtxt()


txt_file = r"E:\workspace\PointCloud\Pointnet2\data\modelnet40_normal_resampled\airplane\airplane_0001.txt"
point = np.loadtxt(txt_file, delimiter=',')


1.4 bin文件

bin文件的存储方式是以二进制形式存储,这带来的好处是读写的速度快,精度不丢失。与txt文件相同,其没有文件的描述信息,只包含点云数据,没有文件的说明部分。但是,txt文件按行存储点的信息,而bin则是将全部数据合并为一个序列,也可以理解为一行。也就是说,读取一个bin文件的输出是一个长串的序列信息,一般需要将其reshape一下,每4个数据为一个点云数据,所以需要reshape成N行4列。样例如下所示:


49.52  22.668  2.051  0.    49.428 22.814  2.05   0.    48.096 22.658
  2.007  0.05  47.813 22.709  1.999  0.    48.079 23.021  2.012  0.
 48.211 23.271  2.019  0.    46.677 22.71   1.964  0.    46.437 22.774
  1.958  0.12  46.075 22.776  1.947  0.18  45.994 22.826  1.945  0.18
 45.881 22.95   1.944  0.23  45.721 23.05   1.941  0.    45.763 23.252
  1.945  0.12  45.617 23.358  1.942  0.12  45.667 23.565  1.947  0.16
 45.65  23.738  1.949  0.28  45.586 23.796  1.948  0.11  45.591 23.981
  1.951  0.    48.238 25.569  2.055  0.05  43.789 23.385  1.888  0.14
 43.779 23.556  1.89   0.    43.784 23.737  1.893  0.    43.719 23.88
  1.894  0.    43.987 24.116  1.905  0.    43.024 23.763  1.871  0.


bin文件reshape之后的4列依次是x,y,z的坐标位置,以及反映强度s。所以,直接提取前3列即可用mlab来实现点云的可视化操作。读取方式:np.fromfile()


kitti_file = r'E:\Study\Machine Learning\Dataset3d\kitti\training\velodyne\000100.bin'
points = np.fromfile(file=kitti_file, dtype=np.float32, count=-1).reshape([-1, 4])


总结:以上既是点云数据的4中常见的数据格式,pcd和ply格式具有说明文档,txt和bin格式没有说明文档更加直观。


参考资料:


1. 点云格式介绍(一)

2. 点云格式介绍(二)

3. 点云格式介绍(三)

4. 点云格式介绍(四)


2. Open3d读写点云数据


2.1 open3d处理pcd格式数据

Open3d读取到的点云通常存储到PointCloud类中,如下图所示。下图中points存储了全部的点云坐标,可以用numpy.array转换成矩阵形式。

image.png


  • numpy转pcd格式

对于点云矩阵,通常要转换为PointCloud格式才能被Open3d处理,包括存储和点云处理等。


import numpy as np
import open3d
points = np.random.randn(35947, 3)
pcd = open3d.geometry.PointCloud()
pcd.points = open3d.utility.Vector3dVector(points)


  • pcd格式转numpy

Open3d读取pcd格式点云文件的函数为o3d.io.read_point_cloud,读取的点云存储为上图所示的PointCloud类


import open3d as o3d
import numpy as np
pcd = o3d.io.read_point_cloud(path)
points = np.array(pcd.points) #转为矩阵


  • 保存为pcd格式

保存点云文件的函数为o3d.io.write_point_cloud


import open3d as o3d
o3d.io.write_point_cloud(path, pcd)
o3d.io.write_point_cloud(path, pcd , write_ascii=True)  # 指定编码方式减少错误,也可以直接打开查看


2.2 Open3d处理ply格式数据

open3d处理ply数据与处理pcd数据是类似的,只是在open3d处理ply数据是转化为TriangleMesh格式,包括存储和点云处理等。也就是说,对于ply点云文件,Open3d读取到的点云通常存储到TriangleMesh类中,vertices存储了全部的点云坐标。一些ply测试文件的下载地址:http://graphics.stanford.edu/data/3Dscanrep/

image.png


  • numpy转ply格式
import open3d as o3d
ply = o3d.geometry.TriangleMesh()
ply.vertices = o3d.utility.Vector3dVector(points_array)


  • ply格式转numpy
import open3d
import numpy as np
ply_path = r"E:\Study\Machine Learning\Dataset3d\points_ply\lucy\lucy.ply"
ply = open3d.io.read_triangle_mesh(ply_path)
points = np.array(ply.vertices)


  • 保存为ply格式
import open3d as o3d
o3d.io.write_triangle_mesh(path, ply)
o3d.io.write_triangle_mesh (path, ply, write_ascii=True)   # 指定编码方式减少错误,也可以直接打开查看


2.3 点云格式的转换

在mmdetection3d中提供了一些点云格式转换的工具代码。


  • ply转bin格式
import numpy as np
import pandas as pd
from plyfile import PlyData
def convert_ply(input_path, output_path):
    plydata = PlyData.read(input_path)  # read file
    data = plydata.elements[0].data  # read data
    data_pd = pd.DataFrame(data)  # convert to DataFrame
    data_np = np.zeros(data_pd.shape, dtype=np.float)  # initialize array to store data
    property_names = data[0].dtype.names  # read names of properties
    for i, name in enumerate(
            property_names):  # read data by property
        data_np[:, i] = data_pd[name]
    data_np.astype(np.float32).tofile(output_path)
convert_ply('./test.ply', './test.bin')


  • off、obj转ply格式

如果你有其他格式的点云文件 (例:off, obj), 你可以使用 trimesh 将它们转化成 ply.


import trimesh
def to_ply(input_path, output_path, original_type):
    mesh = trimesh.load(input_path, file_type=original_type)  # read file
    mesh.export(output_path, file_type='ply')  # convert to ply
to_ply('./test.obj', './test.ply', 'obj')


参考资料:


1. Open3d读写pcd点云文件

2. Open3d读写ply点云文件

3. mmdetection3d官方文档


3. Open3d配准点云数据


3.1 点云配准原理

  • 配准原理

点云配准本质上是将点云从一个坐标系变换到另一个坐标系。点云配准通常会需要用到两个点云数据。第一类点云数据称为原始点云,用S(source)来表示。第二类点云数据称为目标点云,用T(Target)来表示。配准就是希望这里的原始数据可以对其目标数据的坐标系,让原始点云S在目标点云T的坐标上进行显示。以实现数据的补充。我们可以通过找到点云中具有相似特征的点云来确定坐标的变换关系。例如,同一个物体的点云同时出现在原始点云和目标点云中,并且在两个点云中有特征相似的部分点云,根据这些相似的点云信息来计算出变换关系。


假设原始点云到目标点云发生的是刚体变换,即原始点云通过旋转和平移即可得到目标点云。这里的旋转和平移过程用旋转变换矩阵R和平移变换矩阵T来表示。我们用P(S)表示原始点云中的点,P(T)表示原始点云在目标点云坐标系中的点。那么这种变换关系可以表示为:

image.png


因此,点云配准的主要任务是计算出旋转矩阵R和平移矩阵T。


  • 迭代最近点算法(Iterative Closest Point, ICP)

第一步:初始化R、T矩阵,根据R、T矩阵可以得到P(T),即原始点云在目标点云坐标系下的坐标。

第二步:在目标点云中寻找与P(T)最近的点,并且距离小于规定的阈值,这个阈值可以自己定义。

第三步:对第二步中匹配到的点计算欧式距离误差,并且通过最小二乘法来优化R、T矩阵。

第四步:将第三步优化后的R、T矩阵带回第一步中,重新进行迭代,直到迭代满足要求后,得到最终优化的R、T矩阵。


  • ICP方法分类

ICP方法可分为点到点(PointToPoint)和点到平面(PointToPlane)两类。


1)PointToPoint:计算P(t)和目标点云T的距离采用点到点之间的距离形式。

2)PointToPlane:计算P(t)中点到目标点云T的点所在平面的距离,这里通常需要用到目标点云的法向量。


3.2 Open3d配准实现

在Open3d的open3d.pipelines.registration中,提供了几种点云配准的方法,这里给出代码的参数说明。


def registration_icp(source, target, max_correspondence_distance, init, *args, **kwargs): # real signature unknown; NOTE: unreliably restored from __doc__ 
    """
    registration_icp(source, target, max_correspondence_distance, init=(with default value), estimation_method=TransformationEstimationPointToPoint without scaling., criteria=ICPConvergenceCriteria class with relative_fitness=1.000000e-06, relative_rmse=1.000000e-06, and max_iteration=30)
    Function for ICP registration
    Args:
        source (open3d.geometry.PointCloud): The source point cloud.
        target (open3d.geometry.PointCloud): The target point cloud.
        max_correspondence_distance (float): Maximum correspondence points-pair distance.
        init (numpy.ndarray[numpy.float64[4, 4]], optional): Initial transformation estimation Default value:
            array([[1., 0., 0., 0.],
            [0., 1., 0., 0.],
            [0., 0., 1., 0.],
            [0., 0., 0., 1.]])
        estimation_method (open3d.pipelines.registration.TransformationEstimation, optional, default=TransformationEstimationPointToPoint without scaling.): Estimation method. 
                           One of (``TransformationEstimationPointToPoint``, ``TransformationEstimationPointToPlane``, ``TransformationEstimationForGeneralizedICP``, ``TransformationEstimationForColoredICP``)
        criteria (open3d.pipelines.registration.ICPConvergenceCriteria, optional, default=ICPConvergenceCriteria class with relative_fitness=1.000000e-06, relative_rmse=1.000000e-06, and max_iteration=30): Convergence criteria
    Returns:
        open3d.pipelines.registration.RegistrationResult
    """


在下面的参考代码中,就是使用了open3d.pipelines.registration这个函数来实现原始点云到目标点云上的配准。参考代码中的happyStandRight_0.ply作为原始点云,对应代码中的point_s1,结果中显示绿色部分点云;happyStandRight_24.ply作为目标点云,对应代码中的point_s2,结果中显示红色部分;原始点云在目标点云上的配准是显示蓝色部分。(以上的颜色是通过控制参数color来实现的)


参考代码:


import open3d
import numpy as np
import mayavi.mlab as mlab
# 对两个ply格式的点云数据进行配准
def open3d_registration():
    ply_path = r"E:\Study\Machine Learning\Dataset3d\points_ply\happy_stand\happyStandRight_0.ply"
    ply = open3d.io.read_triangle_mesh(ply_path)
    point_s1 = np.array(ply.vertices)
    ply_path = r"E:\Study\Machine Learning\Dataset3d\points_ply\happy_stand\happyStandRight_24.ply"
    ply = open3d.io.read_triangle_mesh(ply_path)
    point_s2 = np.array(ply.vertices)
    print(point_s1.shape, point_s2.shape)
    source = open3d.geometry.PointCloud()
    source.points = open3d.utility.Vector3dVector(point_s1)
    target = open3d.geometry.PointCloud()
    target.points = open3d.utility.Vector3dVector(point_s2)
    icp = open3d.pipelines.registration.registration_icp(
        source=source,
        target=target,
        max_correspondence_distance=0.2,    # 距离阈值
        estimation_method=open3d.pipelines.registration.TransformationEstimationPointToPoint()
    )
    print(icp)
    source.transform(icp.transformation)
    points = np.array(source.points)
    x = points[:, 0]
    y = points[:, 1]
    z = points[:, 2]
    fig = mlab.figure(bgcolor=(0, 0, 0), size=(640, 640))
    mlab.points3d(x, y, z, x, mode="point", color=(0, 0, 1), figure=fig)   # 蓝色
    x = point_s1[:, 0]
    y = point_s1[:, 1]
    z = point_s1[:, 2]
    mlab.points3d(x, y, z, x, mode="point", color=(0, 1, 0), figure=fig)   # 绿色
    x = point_s2[:, 0]
    y = point_s2[:, 1]
    z = point_s2[:, 2]
    mlab.points3d(x, y, z, x, mode="point", color=(1, 0, 0), figure=fig)   # 红色
    mlab.show()
if __name__ == '__main__':
    open3d_registration()


ps: 这里的 print(icp) 会计算两个重要指标

1)fitness计算重叠区域(内点对应关系/目标点数),越高越好。

2)inlier_rmse计算所有内在对应关系的均方根误差RMSE,越低越好(可以作为最小二乘法的优化指标)。


结果显示:

image.png


3.3 Numpy拼接实现

点云拼接主要是把不同的点云拼接到一起。通常,为了获得一个完整物体的三维点云,我们可能会在不同的视点进行数据采集,然后把采集的点云数据拼接到一起。由于视点的不同,所采集到的多个点云的坐标系也会不一致。为了解决坐标系不一致的问题,最可能用到点云配准技术,或者提前知道视点间的坐标关系。


对于上述已经配准好的数据,这里就可以直接将配准好的数据与目标点云进行拼接,实现视角的补充。这里使用最普通的Numpy将两个数据矩阵进行拼接,既两个点云叠加。这会导致点云数据量显著增加。因此可以用一些下采样的方法,或者将距离相近的点合并为一个点。(点云下采样的方法可以有效的减少点云数据,又不至于影响整体效果)


参考代码:


def open3d_registration():
    ply_path = r"E:\Study\Machine Learning\Dataset3d\points_ply\happy_stand\happyStandRight_0.ply"
    ply = open3d.io.read_triangle_mesh(ply_path)
    point_s1 = np.array(ply.vertices)
    ply_path = r"E:\Study\Machine Learning\Dataset3d\points_ply\happy_stand\happyStandRight_24.ply"
    ply = open3d.io.read_triangle_mesh(ply_path)
    point_s2 = np.array(ply.vertices)
    print(point_s1.shape, point_s2.shape)
    source = open3d.geometry.PointCloud()
    source.points = open3d.utility.Vector3dVector(point_s1)
    target = open3d.geometry.PointCloud()
    target.points = open3d.utility.Vector3dVector(point_s2)
    icp = open3d.pipelines.registration.registration_icp(
        source=source,
        target=target,
        max_correspondence_distance=0.2,    # 距离阈值
        estimation_method=open3d.pipelines.registration.TransformationEstimationPointToPoint()
    )
    print(icp)
    source.transform(icp.transformation)
    points = np.array(source.points)
    # 配准可视化
    # x = points[:, 0]
    # y = points[:, 1]
    # z = points[:, 2]
    # fig = mlab.figure(bgcolor=(0, 0, 0), size=(640, 640))
    # mlab.points3d(x, y, z, x, mode="point", color=(0, 0, 1), figure=fig)   # 蓝色
    #
    # x = point_s1[:, 0]
    # y = point_s1[:, 1]
    # z = point_s1[:, 2]
    # mlab.points3d(x, y, z, x, mode="point", color=(0, 1, 0), figure=fig)   # 绿色
    #
    # x = point_s2[:, 0]
    # y = point_s2[:, 1]
    # z = point_s2[:, 2]
    # mlab.points3d(x, y, z, x, mode="point", color=(1, 0, 0), figure=fig)   # 红色
    # mlab.show()
    # 拼接可视化
    points = np.concatenate([points, point_s2], axis=0)
    print(points.shape)
    x = points[:, 0]
    y = points[:, 1]
    z = points[:, 2]
    fig = mlab.figure(bgcolor=(0, 0, 0), size=(640, 640))
    mlab.points3d(x, y, z, y, mode="point", figure=fig)  
    mlab.show()
if __name__ == '__main__':
    open3d_registration()


结果显示:

image.png


参考资料:


1. 点云配准(一)— ICP方法

2. 点云配准(二)— python open3d ICP方法


4. Open3d点云法向量计算


上述讲到的ICP中,有一个PointToPlane的方法是计算P(t)中点到目标点云T的点所在平面的距离,就需要用到目标点云的法向量。那么法向量是针对平面而言的,即垂直于平面的向量。因此,对于点云来说,需要先拟合出一个平面,然后才能求出相应的法向量。对于一个点云数据,可以用周围邻近的点来拟合平面。


Open3d使用estimate_normals函数来计算法向量。其参数设置Open3d提供了3中参数搜索的方法(所有计算的法向量模长为1):


open3d.geometry.KDTreeSearchParamKNN(knn=20)                        # 计算近邻的20个点
open3d.geometry.KDTreeSearchParamRadius(radius=0.01)                # 计算指定半径内的点
open3d.geometry.KDTreeSearchParamHybrid(radius=0.01, max_nn=20)     # 同时考虑搜索半径和近邻点个数


Open3d绘制点云draw_geometries的参数说明:


def draw_geometries(*args, **kwargs): # real signature unknown; restored from __doc__
    """
    draw_geometries(*args, **kwargs)
    Overloaded function.
    1. draw_geometries(geometry_list, window_name='Open3D', width=1920, height=1080, left=50, top=50, point_show_normal=False, mesh_show_wireframe=False, mesh_show_back_face=False)
        Function to draw a list of geometry.Geometry objects
    Args:
        geometry_list (List[open3d.geometry.Geometry]): List of geometries to be visualized.
        window_name (str, optional, default='Open3D'): The displayed title of the visualization window.
        width (int, optional, default=1920): The width of the visualization window.
        height (int, optional, default=1080): The height of the visualization window.
        left (int, optional, default=50): The left margin of the visualization window.
        top (int, optional, default=50): The top margin of the visualization window.
        point_show_normal (bool, optional, default=False): Visualize point normals if set to true.
        mesh_show_wireframe (bool, optional, default=False): Visualize mesh wireframe if set to true.
        mesh_show_back_face (bool, optional, default=False): Visualize also the back face of the mesh triangles.
    Returns:
        None
    2. draw_geometries(geometry_list, window_name='Open3D', width=1920, height=1080, left=50, top=50, point_show_normal=False, mesh_show_wireframe=False, mesh_show_back_face=False, lookat, up, front, zoom)
        Function to draw a list of geometry.Geometry objects
    Args:
        geometry_list (List[open3d.geometry.Geometry]): List of geometries to be visualized.
        window_name (str, optional, default='Open3D'): The displayed title of the visualization window.
        width (int, optional, default=1920): The width of the visualization window.
        height (int, optional, default=1080): The height of the visualization window.
        left (int, optional, default=50): The left margin of the visualization window.
        top (int, optional, default=50): The top margin of the visualization window.
        point_show_normal (bool, optional, default=False): Visualize point normals if set to true.
        mesh_show_wireframe (bool, optional, default=False): Visualize mesh wireframe if set to true.
        mesh_show_back_face (bool, optional, default=False): Visualize also the back face of the mesh triangles.
        lookat (numpy.ndarray[numpy.float64[3, 1]]): The lookat vector of the camera.
        up (numpy.ndarray[numpy.float64[3, 1]]): The up vector of the camera.
        front (numpy.ndarray[numpy.float64[3, 1]]): The front vector of the camera.
        zoom (float): The zoom of the camera.
    Returns:
        None
    """
    pass


法向量计算与可视化代码:


import open3d
import numpy as np
import mayavi.mlab as mlab
# 4. 法向量的计算
def open3d_vector_compute():
    pcd_path = r"E:\Study\Machine Learning\Dataset3d\points_pcd\rabbit.pcd"
    pcd = open3d.io.read_point_cloud(pcd_path)
    pcd.estimate_normals(
        search_param=open3d.geometry.KDTreeSearchParamHybrid(radius=0.01, max_nn=30)
    )
    normals = np.array(pcd.normals)    # 法向量结果与点云维度一致(N, 3)
    points = np.array(pcd.points)
    print(normals.shape, points.shape)
    # 验证法向量模长为1(模长会有一定的偏差,不完全为1)
    normals_length = np.sum(normals**2, axis=1)
    flag = np.equal(np.ones(normals_length.shape, dtype=float), normals_length).all()
    print('all equal 1:', flag)
    # 法向量可视化
    open3d.visualization.draw_geometries([pcd],
                                         window_name="Open3d",
                                         point_show_normal=True,
                                         width=800,   # 窗口宽度
                                         height=600)  # 窗口高度
if __name__ == '__main__':
    open3d_vector_compute()


结果展示:

image.png


ps:这里的法向量一半是向外,一半是向里的。同时, 点云格式必须设置为pcd格式才能进行法向量计算,ply格式不支持法向量计算。


参考资料:点云法向量


相关实践学习
使用CLup和iSCSI共享盘快速体验PolarDB for PostgtreSQL
在Clup云管控平台中快速体验创建与管理在iSCSI共享盘上的PolarDB for PostgtreSQL。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
目录
相关文章
|
前端开发 rax Python
Open3d系列 | 2. Open3d实现点云数据增强
Open3d系列 | 2. Open3d实现点云数据增强
1938 1
Open3d系列 | 2. Open3d实现点云数据增强
|
4月前
|
存储 关系型数据库 MySQL
云数据仓库ADB的 热数据存储空间 在哪里看?热数据存储空间 的计费是怎么计算的?
云数据仓库ADB的 热数据存储空间 在哪里看?热数据存储空间 的计费是怎么计算的?
39 0
|
存储 消息中间件 缓存
云数据仓库的未来趋势:计算存储分离
随着云时代的到来,数据库也开始拥抱云数据库时代,各类数据库系统在各内外云平台百花齐放,有开源的MySQL、PostgreSQL、MongoDB,传统数据库厂商的SQLServer、Oracle,云厂商自研的Aurora、Redshift、PolarDB、AnalyticDB、AzureSQL等。
云数据仓库的未来趋势:计算存储分离
|
监控 大数据 数据挖掘
阿里云MVP Meetup 《云数据·大计算:海量日志数据分析与应用》之《数据应用:数据服务》篇
本文主要阐述在使用DataWorks(数据工场)的过程中如何使用数据服务模块,快速将已生成的数据给用起来。此功能可用于快速将数仓中的结果表生成API,通过API给下游应用使用,或者通过API服务完成数据共享等。
1358 0
|
人工智能 分布式计算 安全
大数据计算杭州高端峰会—探寻真正的“云数据,大计算”
你的业务数据还在沉睡吗? 如何让数据发挥更大的价值? 数据是企业无价之宝,上云真能保证安全吗? 如何省去自建环境、省去运维,快速实现大数据平台落地,更多聚焦于业务? 我的业务离数据智能有多远?
2817 0
|
数据采集 监控 大数据
阿里云MVP Meetup 《云数据·大计算:海量日志数据分析与应用》之《数据质量监控》篇
本手册为阿里云MVP Meetup Workshop《云计算·大数据:海量日志数据分析与应用》的《数据质量监控》篇而准备。主要阐述在使用大数据开发套件过程中如何将已经采集至MaxCompute上的日志数据质量进行监控,学员可以根据本实验手册,去学习如何创建表的监控规则,如何去订阅表等。
3836 0
|
存储 NoSQL 物联网
北京云栖大会 Tech Insight 爆场论坛-云数据·大计算:快速搭建互联网在线运营分析平台
2月19日上午9点,Tech Insight·北京峰会,在2017北京云栖大会国家会议中心准时拉开帷幕。做为年内压轴之作, 吸引了大量技术决策者,和一线的工程师、运维工程师等前来交流学习。
2279 0
|
分布式计算 NoSQL 大数据
北京Workshop准备条件:《云数据·大计算:快速搭建互联网在线运营分析平台》
本手册为云栖大会Workshop之《云计算·大数据:海量日志数据分析与应用》场的前提准备条件所需。主要为保障各位学员在workshop当天能够顺畅进行动手实操,那么本场需要各位学员再参加之前确保自己云账号已免费开通表格存储TableStore、大数据计算服务MaxCompute、DataWorks和Quick BI。
5889 0
北京Workshop准备条件:《云数据·大计算:快速搭建互联网在线运营分析平台》
|
存储 大数据
北京:《云数据·大计算:快速搭建互联网在线运营分析平台》Workshop-入口
该课程是基于大数据时代APP日志分析的基础需求的基础上,给开发者从数据高并发写入及实时在线访问存储到便捷高效的数据加工处理最终再到数据分析与展示的完整链路解决方案。不仅可以了解并实操到阿里云的产品,同时可以自己可以跟着讲师动手完成在线运营分析平台的搭建。
4700 0
|
分布式计算 监控 大数据
【转载】大数据workshop:《云数据·大计算:海量日志数据分析与应用》
本手册为云栖大会Workshop之《云计算·大数据:海量日志数据分析与应用》场的前提准备条件所需。主要为保障各位学员在workshop当天能够顺畅进行动手实操,那么本场需要各位学员再参加之前确保自己云账号已免费开通MaxCompute、Data IDE和Quick BI。
2167 0

热门文章

最新文章