最近根据公司的调整开始转向三维重建和三维检测方向。对于三维方向而言,首先应该认识三维数据的存储形式。三维数据的形式多种多样,截止到现在我已经看到了ply格式数据、obj格式数据、pcd格式数据、osgb格式数据、b3dm格式数据等等。相比于二维图像,三维数据的形式更多且复杂。因此,我打算整理出常用的三维数据存储格式总结文章。本篇以ply格式数据作为开篇。
本篇文章简单介绍ply的基础属性,可能有部分的属性没有涉及到,望读者朋友见谅!
参考博客:
1.作者:飞天牛牛, 学习 点云格式(PLY)
相比于二维图像是用像素点构成,三维数据则是由顶点和面构成。在ply格式数据中,既可以包含顶点数据,也可以包含面数据。但是顶点数据是必须存在的。
当数据中只存在顶点数据,没有面数据时,一般称为点云数据。这些顶点数据是由vector
通过Notepad++(或者其他文本查看软件),可以看到ply里面的内容,都是由点构成,如下图所示:
当数据中有面属性时,一般称为网格数据(mesh)。网格数据在我的理解中就是由许多面片(多边形)构成的数据,面片可以是三角面片,四边形面片等,但是常用的是三角面片构成的网格数据。
有面属性的数据,肯定会有点云数据,因为面是由点构成的。
网格数据展示如下图所示:
如果有纹理图映射的话,效果如下:
通过Notepad++(或者其他文本查看软件),可以看到ply里面的内容,可以看到文本中既有点云数据,也有面数据。如下图所示:
ply格式数据是通过文本进行描述的。因此,可以通过Notepad++等文本编辑软件进行打开查看。内容如下图所示:
从上图中可以看出,ply格式数据主要可以分为两个部分,分别是属性和数据。为了方便后续的介绍,我这里先列出一个ply格式框架。如下所示:
属性
|----文本描述属性 (ply, format, comment 等等)
|----数据描述属性
| |----element1:元素声明
| |----property1:元素数据组成声明
| |----element2:元素声明
| |----property2:元素数据组成声明
| |---- …
|—文本描述属性(end_header)
数据
|----element1对应的数据,以property1的声明方式存储。
|----element2对应的数据,以property2的声明方式存储。
|---- …
属性部分主要是由文本描述属性和数据描述属性组成,其中数据描述属性包含vertex(顶点),face(面),camera(相机参数)等元素,文本描述属性则是描述文件的存储格式、存储来源以及文本注释。
第一行,用来表示这是ply数据。
ply
第二行,用来描述数据的存储形式,一般为两种分别是ascii存储和binary_little_endian存储。图片示例中表示是ascii存储。
format ascii 1.0
- 其他情况: binary_little_endian存储(字节流存储)
format binary_little_endian 1.0
第三行,用来描述该ply数据是由什么软件或者库生成的。图片示例中表示是由PCL库生成的。
"comment"为关键字,表示注释,用来声明后面为注释内容。因此,读者可以通过comment自行添加注释内容
comment PCL generated
- 其他情况:
comment VCGLIB generated
…
最后一行,表示文本描述属性结束,是文本描述属性和数据描述属性的分隔符。
end_header
补充:如果包含纹理映射的话,还会有纹理图描述属性。如下图所示:
这个应该是固定格式,读者朋友自己修改后面的图片名就好。
以其中一行为例,内容如下。在示例中纹理图属性以空格为分隔符,将一行内容分隔为三个部分。第一个"comment"表示注释的关键字,"TextureFile"表示这是纹理图属性,"Tile_+082_+121_L22_00001100_0.jpg"则是纹理图的名称。
comment TextureFile Tile_+082_+121_L22_00001100_0.jpg
数据描述属性由很多元素组成,其中每个元素都由element(元素声明)和property(元素数据组成声明)构成。element描述了该元素的名称及数量,property描述了元素数据的存储形式,数据类型以及数据名称。
在图片示例中,从第四行到第十一行描述的是顶点元素。
element vertex 3544221
property float x
property float y
property float z
property float nx
property float ny
property float nz
property float curvature
其中,第四行,"element"是关键字,"vertex"表示这是顶点元素, "3544221"表示顶点的数量。
element vertex 3544221
第五行,用来描述顶点元素中的x坐标,其中"property"是关键字, "float"表示数据类型,"x"表示数据名称。
property float x
第六行,用来描述顶点元素中的y坐标,其中"property"是关键字,"float"表示数据类型,"y"表示数据名称。
property float y
第七行,用来描述顶点元素中的z坐标,其中"property"是关键字, "float"表示数据类型,"z"表示数据名称。
property float z
第八行,用来描述顶点元素中法向量的x坐标,其中"property"是关键字, "float"表示数据类型,"nx"表示数据名称。
property float nx
第九行,用来描述顶点元素中法向量的y坐标,其中"property"是关键字, "float"表示数据类型,"ny"表示数据名称。
property float ny
第十行,用来描述顶点元素中法向量的z坐标,其中"property"是关键字, "float"表示数据类型,"nz"表示数据名称。
property float nz
第十一行,用来描述顶点元素中曲率,其中"property"是关键字, "float"表示数据类型,"curvature"表示数据名称。
property float curvature
除此之外,还有颜色的property,分别是RGB。如下面示例所示:
其中,对于property uint8 blue
而言,"property"是关键字,"uint8"表示为uint8类型,"blue"为数据名称。该property是用来描述顶点的蓝色通道数据。
对于property uint8 green
而言,"property"是关键字,"uint8"表示为uint8类型,"green"为数据名称。该property是用来描述顶点的绿色通道数据。
对于property uint8 red
而言,"property"是关键字,"uint8"表示为uint8类型,"red"为数据名称。该property是用来描述顶点的红色通道数据。
在图片示例中,从第十二行到第十三行描述的是面元素。
element face 6607259
property list uchar int vertex_indices
其中,第十二行,"element"是关键字,"face"表示这是面元素, "6607259"表示面的数量。
element face 6607259
第十三行,用来描述面元素中的各个顶点的索引,其中"property"是关键字,"list"表示使用list类型存储数据,"uchar"表示list中的数据个数,类型为uchar类型,"int"表示list中数据为int类型,"vertex_indices"为数据名称。
property list uchar int vertex_indices
如果有纹理图映射的话,还有两个元素,分别是"texcoord"和"texnumber"。如下面示例所示:
其中,对于property list uchar float texcoord
而言,"property"是关键字, "list"表示使用list类型存储数据,"uchar"表示list中的数据个数,类型为uchar类型,"float"表示list中数据为float类型,"texcoord"为数据名称。"texcoord"表示为顶点数据到二维纹理图上的坐标(归一化后)。
对于property int texnumber
而言,"property"是关键字,"int"表示数据为int类型,"texnumber"为数据名称。"texnumber"表示为每个面片对应的是哪一张纹理图。
这个我不太理解,可以附上例子方便读者参考。相机属性如下图所示:
在属性之后,就是数据部分。数据部分有两种存储形式,分别是ASCII存储形式和binary存储形式,具体展示如下图所示。为了方便介绍,本篇文章将以ASCII存储形式为例子。数据部分的内容分别对应上节的数据描述属性中不同元素的内容。下面,我将分别介绍
在数据描述属性中,顶点元素的内容如下:
element vertex 3544221
property float x
property float y
property float z
property float nx
property float ny
property float nz
property float curvature
其对应的数据存储形式为:
27498 5145 1.7902 -0.050252 -0.90111 -0.43067 0.057233
27499 5144.9 1.3479 -0.038979 -0.80628 -0.59026 0.071784
…
以第一行"27498 5145 1.7902 -0.050252 -0.90111 -0.43067 0.057233"为例,可以看到数据之间使用空格进行分开,并以property声明的数据顺序分别排序,即以x y z nx ny nz curvature的顺序从左往右排序。
这样一行就表示一个顶点数据,该数据数量根据描述属性有3544221个。
如果是带有RGB颜色的数据,还是同样的道理,此处不作过多赘述。如下图所示:
在数据描述属性中,面元素的内容如下:
element face 10501193
property list uchar int vertex_indices
其对应的数据存储形式为:
3 154643 190669 190652
3 154656 154643 190669
…
以第一行"3 154643 190669 190652"为例,可以看到,数据之间使用空格进行分开,以列表的方式进行依次存储,存储的类型为uchar int int int。
这样的一行就表示一个面,该数据数量根据描述属性有10501193个。
如果还有纹理图映射的话,还有两个property,分别是:
element face 10501193
property list uchar int vertex_indices
property list uchar float texcoord
property int texnumber
其对应的数据存储形式为:
3 154643 190669 190652 6 0.021484 0.958008 0.029297 0.999023 0.001953 0.958008 0
这样的一行就表示一个三角面片的纹理贴图。
我通过PCL库在保存法向量计算后的结果中,发现了如下元素。该是元素应该是PCL库保存数据后自动添加的。对于相机元素,我还不是很懂。因此,此处仅仅列出结果,如果后续有涉及,我会重新编辑此处内容。
第一个示例是不包含纹理图映射的,四边形面片组成的立方体网格数据。
该示例引用博客学习 点云格式(PLY)。
示例内容如下(使用时请把注释内容删掉):
ply # 文本描述属性:开头
format ascii 1.0 # ascii存储形式
comment made by Greg Turk # 注释:由Greg Turk创建
comment this file is a cube # 注释:这是一个立方体
element vertex 8 # element:顶点8个
property float x # property:x
property float y # property:y
property float z # property:z
element face 6 # element:面6个
property list uchar int vertex_index # property:vertex_index
end_header # 文本描述属性:结尾
0 0 0 # 第一个点坐标(0,0,0)
0 0 1
0 1 1
0 1 0
1 0 0
1 0 1
1 1 1
1 1 0
4 0 1 2 3 # 第一个四边形面片
4 7 6 5 4
4 0 4 5 1
4 1 5 6 2
4 2 6 7 3
4 3 7 4 0
第二个示例是包含纹理映射的,三角面片构成的方锥。示例如下:
注意:
- 纹理图要和ply数据放在同一目录下。
- 纹理图的坐标原点下在右下角,其中横轴为x,纵轴为y。
ply
format ascii 1.0
comment made by Greg Turk
comment this file is a cube
comment TextureFile 1.png
element vertex 5
property float x
property float y
property float z
element face 4
property list uchar int vertex_index
property list uchar float texcoord
property int texnumber
end_header
0 0 0
0 1 0
1 0 0
1 1 0
0.5 0.5 1
3 0 1 4 6 1 0 0 0 0.5 1 0
3 2 0 4 6 1 0 0 0 0.5 1 0
3 1 3 4 6 1 0 0 0 0.5 1 0
3 3 2 4 6 1 0 0 0 0.5 1 0