在众多存储点云的文件格式中,有些格式是为点云数据“量身打造”的,也有一些文件格式(如计算机图形学和计算机和学领域的3D模型或通讯数据文件)具备表示和存储点云的能力,应用于点云信息的存储。
点云文件除了包含基本的点的笛卡尔坐标信息之外,有些文件格式还可能包含点之间的连接关系(拓扑结构),以及法线等其他信息。
PCL库官方指定格式,典型的为点云量身定制的格式。优点是支持n维点类型扩展机制,能够更好的发挥PCL库的点云处理性能。文件格式有文本和二进制两种格式。
pcd格式具有文件头,用于描绘点云的整体信息。数据本体部分由点的笛卡尔坐标构成,文本模式下以空格做分隔符。
# .PCD v.5 - Point Cloud Data file format
VERSION .5
FIELDS x y z
SIZE 4 4 4
TYPE F F F
COUNT 1 1 1
WIDTH 397
HEIGHT 1
POINTS 397
DATA ascii
0.0054216 0.11349 0.040749
-0.0017447 0.11425 0.041273
-0.010661 0.11338 0.040916
0.026422 0.11499 0.032623
0.024545 0.12284 0.024255
0.034137 0.11316 0.02507
VERSION .5 指定PCS文件版本
FIELDS x y z 指定一个点的每一个维度和字段名字,例如
FIELDS x y z # XYZ data
FIELDS x y z rgb # XYZ + colors
FIELDS x y z normal_x normal_y normal_z # XYZ + surface normals
SIZE 4 4 4 指定每一个维度的字节数大小
TYPE F F F 指定每一个维度的类型,I表示int,U表示uint,F表示浮点
COUNT 1 1 1 指定每一个维度包含的元素数,如果没有COUNT,默认都为1
WIDTH 397 点云数据集的宽度
HEIGHT 1 点云数据集的高度
VIEWPOINT 0 0 0 1 0 0 0 指定点云获取的视点和角度,在不同坐标系之间转换时使用(由3个平移+4个四元数构成)
POINTS 397 总共的点数(显得多余)
DATA ascii 存储点云数据的数据类型,ASCII和binary
LAS格式是一种二进制文件格式,其目的是提供一种开放的格式标准,允许不同的硬件和软件提供商输出可互操作的统一格式。LAS格式文件已成为LiDAR数据的工业标准格式。
LAS文件按每条扫描线排列方式存放数据,包括激光点的三维坐标、多次回波信息、强度信息、扫描角度、分类信息、飞行航带信息、飞行姿态信息、项目信息、GPS信息、数据点颜色信息等。LAS格式定义中用到的数据类型遵循1999年ANSI(AmericanNationalStandardsInstitute,美国国家标准化协会)C语言标准。
LAS文件包含以下信息:
C–class(所属类)
F一flight(航线号)
T一time(GPS时间)
I一intensity(回波强度)
R一return(第几次回波)
N一number of return(回波次数)
A一scan angle(扫描角)
RGB一red green blue(RGB颜色值)
一种由斯坦福大学的Turk等人设计开发的多边形文件格式,因而也被成为斯坦福三角格式。文件格式有文本和二进制两种格式。典型的PLY对象定义仅仅是顶点的(x,y,z)三元组列表和由顶点列表中的索引描述的面的列表。
文件结构如下:
· Header (头部)
· Vertex List (顶点列表)
· Face List (面列表)
· (lists of other elements) (其它元素列表)
示例:
ply
format ascii1.0 { ascii/binary, formatversion number }
comment made byGreg Turk { comments keyword specified,like all lines }
comment thisfile is a cube
element vertex8 { define "vertex"element, 8 of them in file }
property floatx { vertex contains float"x" coordinate }
property floaty { y coordinate is also avertex property }
property floatz { z coordinate, too }
element face6 { there are 6"face" elements in the file }
property listuchar int vertex_index { "vertex_indices" is a list of ints }
end_header { delimits the end of theheader }
0 0 0 { start of vertex list }
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 { start of face list }
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
序列化和反序列化是将数据结构或对象转换为字节流(序列化)以及将字节流还原为数据结构或对象(反序列化)的过程。
当需要将数据从内存中保存到磁盘或通过网络传输时,通常需要进行序列化操作。序列化将数据结构或对象转换为一个字节流的形式,以便能够进行持久化存储或传输。
反序列化则是将之前序列化过的字节流重新转换为原始的数据结构或对象,使其可以被系统再次使用。
在点云领域中,序列化和反序列化操作允许我们将点云数据保存到文件中或通过网络传输,并在需要时重新加载点云数据。这对于点云处理、共享和传输非常有用。
对一个点云进行反序列化操作,将之保存到PointCloud对象中:load_pcd.cpp
#include
#include
#include
int
main(int argc, char **argv) {
// 准备pcl::PointXYZ类型的点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 将pcd中的数据加载到cloud中
if (pcl::io::loadPCDFile<pcl::PointXYZ>("./data/bunny.pcd", *cloud) == -1)
{
PCL_ERROR ("Couldn't read file bunny.pcd \n");
return (-1);
}
std::cout << "Loaded "
<< cloud->width * cloud->height
<< " data points from test_pcd.pcd with the following fields: "
<< std::endl;
for (size_t i = 0; i < cloud->points.size(); ++i)
std::cout << " " << cloud->points[i].x
<< " " << cloud->points[i].y
<< " " << cloud->points[i].z << std::endl;
return (0);
}
以下代码,随机生成了5个点,并将之以ASCII形式保存(序列化)在test_pcd.pcd文件中:save_pcd.cpp
#include
#include
#include
int
main(int argc, char **argv) {
pcl::PointCloud<pcl::PointXYZ> cloud;
// Fill in the cloud data
cloud.width = 5;
cloud.height = 1;
cloud.is_dense = false;
cloud.points.resize(cloud.width * cloud.height);
std::cout << rand() << std::endl;
std::cout << rand() / (RAND_MAX + 1.0f) << std::endl;
std::cout << 1024 * rand() / (RAND_MAX + 1.0f) << std::endl;
// 随机生成5个点
for (size_t i = 0; i < cloud.points.size(); ++i) {
cloud.points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
cloud.points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
cloud.points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
}
pcl::io::savePCDFileASCII("test_pcd.pcd", cloud);
std::cerr << "Saved " << cloud.points.size() << " data points to test_pcd.pcd." << std::endl;
for (size_t i = 0; i < cloud.points.size(); ++i)
std::cerr << " " << cloud.points[i].x << " " << cloud.points[i].y << " " << cloud.points[i].z << std::endl;
return (0);
}
点云Point Cloud是三维空间中,表达目标空间分布和目标表面特性的点的集合,点云通常可以从深度相机中直接获取,也可以从CAD等软件中生成。点云是用于表示多维点集合的数据结构,通常用于表示三维数据。在3D点云中,这些点通常代表采样表面的X,Y和Z几何坐标。当存在颜色信息时(请参见下图),点云变为4D。
三维图像有以下几种表现形式:深度图(描述物体与相机的距离信息),几何模型(由CAD等软件生成),点云模型(逆向工程设备采集生成)。
PCL(Point Cloud Library)是用于2D / 3D图像和点云处理的大型开源跨平台的C++编程库。 PCL框架实现了大量点云相关的通用算法和高效的数据结构。内容涉及了点云的获取、滤波、分割、配准、检索、特征提取、特征估计,表面重建、识别、模型拟合、追踪、曲面重建、可视化等等,这些算法可用于从嘈杂的数据中过滤出异常值,将3D点云缝合在一起,分割场景的部分区域,提取关键点并计算描述符,根据物体的几何外观识别实际物体,创建表面点云并将其可视化。
Step1. 配置根目录CMakeLists.txt:
cmake_minimum_required(VERSION 3.14)
project(PCLDemo)
set(CMAKE_CXX_STANDARD 14)
# 设置输出根目录为build/Debug
set(OUTPUT_DIRECTORY_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/build/${CMAKE_BUILD_TYPE})
# 设置可执行程序输出到build/Debug/bin目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY_ROOT}/bin" CACHE PATH "Runtime directory" FORCE)
# 设置库文件输出到build/Debug/lib目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY_ROOT}/lib" CACHE PATH "Library directory" FORCE)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY_ROOT}/lib" CACHE PATH "Archive directory" FORCE)
find_package(PCL REQUIRED)
# 包含头文件目录
include_directories(${PCL_INCLUDE_DIRS})
# 设置依赖库链接目录
link_directories(${PCL_LIBRARY_DIRS})
# 添加预处理器和编译器标记
add_definitions(${PCL_DEFINITIONS})
add_executable(PCLDemo main.cpp)
target_link_libraries(PCLDemo ${PCL_LIBRARIES})
Step2. 创建主函数文件:
PclDemo.cpp
#include
#include
#include
#include
int main(int argc, char **argv) {
// 创建PointCloud的智能指针
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBA>);
// 加载pcd文件到cloud
pcl::io::loadPCDFile("C:\\....", *cloud);
pcl::visualization::CloudViewer viewer("Cloud Viewer");
//这里会一直阻塞直到点云被渲染
viewer.showCloud(cloud);
// 循环判断是否退出
while (!viewer.wasStopped()) {
// 你可以在这里对点云做很多处理
}
return 0;
}
PCL的基本数据类型是PointCloud
,一个PointCloud
是一个C++的模板类,它包含了以下字段:
width(int):指定点云数据集的宽度。 对于无组织格式的数据集,width代表了所有点的总数; 对于有组织格式的数据集,width代表了一行中的总点数
height(int):制定点云数据集的高度。对于无组织格式的数据集,值为1;对于有组织格式的数据集,表示总行数
is_dense 于指示点云是否密集(dense);是否是有限的。 当 is_dense = false 时,表示点云是非密集的;点云中可能存在一些缺失或无效的点,例如由于传感器噪声、遮挡或其他原因导致的点云数据不完整,某些点的位置信息可能会丢失或不准确。当 is_dense = true 时,表示点云是密集的;点云中的每个点都有有效和准确的位置信息。
points(std::vector):包含所是有PointT类型的点的数据列表
PointXYZ - float x, y, z
PointXYZI - float x, y, z, intensity
PointXYZRGB - float x, y, z, rgb
PointXYZRGBA - float x, y, z, uint32_t rgba
Normal - float normal[3], curvature 法线方向,对应的曲率的测量值
PointNormal - float x, y, z, normal[3], curvature 采样点,法线和曲率
Histogram - float histogram[N] 用于存储一般用途的n维直方图
科学计算可视化(Visualization in Scientific Computing)能够把科学数据(包括测量获得的数值、图像或者计算中设计、产生的数字信息)变为直观的、可以用以图形图像信息表示的、随时间和空间变化的物理现象或物理量。进而呈现在研究者面前,使他们可以方便的观察、模拟和计算。
基本使用> 进入:pcl_viewer xxxxx.pcd
帮助:在界面中输入h,可以在控制台看到帮助信息
退出:界面中输入q
放大缩小:鼠标滚轮 或 Alt + [+/-]
平移:Shift+鼠标拖拽
旋转:Ctrl+鼠标拖拽
其他技巧
修改点颜色:数字1,2,3,4,5…9,重复按1可切换不同颜色方便观察
放大缩小点:放大Ctrl+Shift+加号,缩小> Ctrl+减号
保存截图:j
显示颜色尺寸:u
显示比例尺寸:g
在控制列出所有几何和颜色信息:l
鼠标选点打印坐标
选点模式进入:pcl_viewer -use_point_picking bunny.pcd
选择指定点:shift+鼠标左键
#include
#include
#include
#include
int user_data;
void viewerOneOff(pcl::visualization::PCLVisualizer &viewer){
//设置背景颜色
viewer.setBackgroundColor(1.1,0.5,1.0);
pcl::PointXYZ o;
o.x = 1.0;
o.z = 0;
o.y = 0;
//添加一个圆心为o,半径为0.25m的球体
viewer.addSphere(o, 0.25, "sphere", 0.2);
std::cout << "i only run once" << std::endl;
}
void viewerPsycho(pcl::visualization::PCLVisualizer &viewer){
static unsigned count = 0;
std::stringstream ss;
ss << "Once per viewer loop: " << count++;
//每次刷新时,移除text,添加新的text
viewer.removeShape("text", 0);
viewer.addText(ss.str(), 200, 300, "text", 0);
user_data++;
}
int main(){
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBA>);
pcl::io::loadPCDFile("C:\\0APractice\\PclLearning\\CloudViewer\\bunny.pcd", *cloud);
pcl::visualization::CloudViewer viewer("Cloud Viewer");
//这里会一直阻塞直到点云被渲染
viewer.showCloud(cloud);
//只会调用一次(非必须)
viewer.runOnVisualizationThreadOnce(viewerOneOff);
//每次可视化迭代都会调用一次
viewer.runOnVisualizationThread(viewerPsycho);
while(!viewer.wasStopped()){
user_data++;
}
return 0;
}
#include
#include
#include
#include
int main(int argc, char **argv){
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("C:\\0APractice\\PclLearning\\PCLVisualizer\\bunny.pcd", *cloud);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_desk(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::io::loadPLYFile("C:\\0APractice\\PclLearning\\PCLVisualizer\\Desk1.ply", *cloud_desk);
//创建PCLVisualizer
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer ("3D Viewer"));
//设置背景颜色为灰色(非必须)
viewer->setBackgroundColor(0.05, 0.05, 0.05, 0);
//添加一个普通点云(可以设置指定颜色,也可以去掉single_color参数不设置)
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color(cloud, 0, 255, 0);
viewer->addPointCloud<pcl::PointXYZ> (cloud, single_color, "sample cloud" );
viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud");
//再添加一个彩色点云及配置
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(cloud_desk);
viewer->addPointCloud<pcl::PointXYZRGB> (cloud_desk, rgb, "sample cloud milk");
viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud milk");
//添加一个0.5倍缩放的坐标系(非必须)
viewer->addCoordinateSystem(0.5);
//直到窗口关闭才结束循环
while(!viewer->wasStopped()){
//每次循环调用内部的重绘函数
viewer->spinOnce();
}
return 0;
}
#include
#include
#include
#include
int user_data;
typedef pcl::PointXYZ PointType;
int main(int argc, char **argv){
//Create point cloud class and load the cloud
pcl::PointCloud<PointType>::Ptr cloud1(new pcl::PointCloud<PointType>);
pcl::PointCloud<PointType>::Ptr cloud2(new pcl::PointCloud<PointType>);
pcl::PointCloud<PointType>::Ptr cloud3(new pcl::PointCloud<PointType>);
/*pcl::io::loadPLYFile("C:\\0APractice\\PclLearning\\PCLMultiWindow\\20230712-05.ply", *cloud1);
pcl::io::loadPLYFile("C:\\0APractice\\PclLearning\\PCLMultiWindow\\Desk1.ply", *cloud1);*/
pcl::io::loadPCDFile<PointType>("C:\\0APractice\\PclLearning\\PCLMultiWindow\\bunny.pcd", *cloud2);
pcl::io::loadPCDFile<PointType>("C:\\0APractice\\PclLearning\\PCLMultiWindow\\bunny.pcd", *cloud1);
pcl::io::loadPCDFile<PointType>("C:\\0APractice\\PclLearning\\PCLMultiWindow\\bunny.pcd", *cloud3);
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D viewer"));
pcl::visualization::PointCloudColorHandlerCustom <PointType> cloud1_color(cloud1, 0, 255, 0);
pcl::visualization::PointCloudColorHandlerCustom <PointType> cloud2_color(cloud2, 255, 0, 0);
pcl::visualization::PointCloudColorHandlerCustom <PointType> cloud3_color(cloud3, 0, 0, 255);
viewer->initCameraParameters();
int V1(0);
viewer->createViewPort(0.0, 0.0, 0.3, 1.0, V1);
int V2(0);
viewer->createViewPort(0.3, 0.0, 0.7, 1.0, V2);
int V3(0);
viewer->createViewPort(0.7, 0.0, 1.0, 1.0, V3);
viewer->setBackgroundColor(255, 0, 0, V1);
viewer->setBackgroundColor(0, 0, 255, V2);
viewer->setBackgroundColor(0, 255, 0, V3);
viewer->addCoordinateSystem(0.5);
viewer->addPointCloud<PointType>(cloud1, cloud1_color, "cloud1", V1);
viewer->addPointCloud<PointType>(cloud2, cloud2_color, "cloud2", V2);
viewer->addPointCloud<PointType>(cloud3, cloud3_color, "cloud3", V3);
viewer->spin();
while (!viewer->wasStopped()) {
user_data++;
}
return 0;
}
注意:在设置每个窗口的左上角和右下角时要注意,x、y坐标的最大值不能超过1,否则该窗口无法显示出来!
// 创建仿射变换对象
Eigen::Affine3f transform_2 = Eigen::Affine3f::Identity();
// 在x轴平移0.8m
transform_2.translation() << 0.8, 0.0, 0.0;
// 绕Z轴先转45度(逆时针)
transform_2.rotate(Eigen::AngleAxisf(M_PI / 4;, Eigen::Vector3f::UnitZ()));
// 打印仿射变换矩阵
printf("\nMethod #2: using an Affine3f\n");
std::cout << transform_2.matrix() << std::endl;
// 创建变换后的点云
pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_cloud(new pcl::PointCloud<pcl::PointXYZ>());
// 执行变换, source_cloud为变换前的点云
pcl::transformPointCloud(*source_cloud, *transformed_cloud, transform_2);
#include
#include
#include
#include
#include
#include
#include
/**
* 旋转并平移
* @param argc
* @param argv
* @return
*/
int
main(int argc, char **argv) {
// Load file | Works with PCD and PLY files
pcl::PointCloud<pcl::PointXYZ>::Ptr source_cloud(new pcl::PointCloud<pcl::PointXYZ>());
pcl::io::loadPLYFile("C:\\0APractice\\PclLearning\\PCLTransform\\Desk1.ply", *source_cloud);
/*if (pcl::io::loadPCDFile(argv[1], *source_cloud) < 0) {
std::cout << "Error loading point cloud" << argv[1] << std::endl << std::endl;
return -1;
}*/
/* Reminder: how transformation matrices work :
|-------> This column is the translation
| 1 0 0 x | \
| 0 1 0 y | }-> The identity 3x3 matrix (no rotation) on the left
| 0 0 1 z | /
| 0 0 0 1 | -> We do not use this line (and it has to stay 0,0,0,1)
METHOD #1: Using a Matrix4f
This is the "manual" method, perfect to understand but error prone !
*/
Eigen::Matrix4f transform_1 = Eigen::Matrix4f::Identity();
// Define a rotation matrix (see https://en.wikipedia.org/wiki/Rotation_matrix)
float theta = M_PI / 4; // The angle of rotation in radians
transform_1(0, 0) = cos(theta);
transform_1(0, 1) = -sin(theta);
transform_1(1, 0) = sin(theta);
transform_1(1, 1) = cos(theta);
// (row, column)
// Define a translation of 2.5 meters on the x axis.
transform_1(0, 3) = 2.5;
// Print the transformation
printf("Method #1: using a Matrix4f\n");
std::cout << transform_1 << std::endl;
/* METHOD #2: Using a Affine3f
This method is easier and less error prone
*/
Eigen::Affine3f transform_2 = Eigen::Affine3f::Identity();
// Define a translation of 0.8 meters on the x axis.
transform_2.translation() << 0.8, 0.0, 0.0;
// The same rotation matrix as before; theta radians arround Z axis
transform_2.rotate(Eigen::AngleAxisf(theta, Eigen::Vector3f::UnitZ()));
// Print the transformation
printf("\nMethod #2: using an Affine3f\n");
std::cout << transform_2.matrix() << std::endl;
// Executing the transformation
pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_cloud(new pcl::PointCloud<pcl::PointXYZ>());
// You can either apply transform_1 or transform_2; they are the same
pcl::transformPointCloud(*source_cloud, *transformed_cloud, transform_2);
// Visualization
printf("\nPoint cloud colors : white = original point cloud\n"
" red = transformed point cloud\n");
pcl::visualization::PCLVisualizer viewer("Matrix transformation example");
// Define R,G,B colors for the point cloud
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> source_cloud_color_handler(source_cloud, 255, 255,
255);
// We add the point cloud to the viewer and pass the color handler
viewer.addPointCloud(source_cloud, source_cloud_color_handler, "original_cloud");
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> transformed_cloud_color_handler(transformed_cloud,
230, 20, 20); // Red
viewer.addPointCloud(transformed_cloud, transformed_cloud_color_handler, "transformed_cloud");
// 设置坐标系系统
viewer.addCoordinateSystem(0.5, "cloud", 0);
// 设置背景色
viewer.setBackgroundColor(0.05, 0.05, 0.05, 0); // Setting background to a dark grey
// 设置渲染属性(点大小)
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "original_cloud");
// 设置渲染属性(点大小)
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "transformed_cloud");
//viewer.setPosition(800, 400); // Setting visualiser window position
while (!viewer.wasStopped()) { // Display the visualiser until 'q' key is pressed
viewer.spinOnce();
}
return 0;
}
https://www.yuque.com/huangzhongqing/pcl/habl9h
https://robot.czxy.com/docs/pcl/chapter01/io/
https://blog.csdn.net/qq_44324181/article/details/120969705