保存点云数据_PCL入门系列三——PCL进行数据读写
本節課我們將了解到以下內容:
一、PCL庫基本數據類型
上一節課,我們使用PCL庫在本地寫入了一個名為test_pcd.pcd的文件。我們劃分一下程序的任務步驟:
步驟二和步驟三的內容屬于基本的C++知識。而步驟一中的pcd文件格式是我們所不熟悉的,所以要先看一看pcd原始文件。
在命令行中輸入:
more test_pcd.pcd
可以看到pcd文件的內部結構如下圖所示。
三維點云數據最簡單的形式是x y z的三維點空間坐標。稍微復雜一些可以加上intensity、RGB等屬性。但是這種表達方式有什么缺點呢?
一方面直接以文本形式存儲點云的三個坐標在內存上比較浪費空間,另一方面如果用戶要取得點云的一些基本信息如點云個數,用戶還要自己去查詢。如果有一個文件頭能夠簡潔的描述點云數據信息,那么無論是在存儲效率還是數據信息提取等方面都會有一定幫助。
pcd格式可以視作“文件格式頭+三維點信息”。
請看一個具體pcd文件的例子:
# .PCD v0.7 - Point Cloud Data file format VERSION 0.7 FIELDS x y z SIZE 4 4 4 TYPE F F F COUNT 1 1 1 WIDTH 5 HEIGHT 1 VIEWPOINT 0 0 0 1 0 0 0 POINTS 5 DATA ascii 0.35222197 -0.15188313 -0.10639524 -0.3974061 -0.47310591 0.29260206 ………………………………這個pcd文件里有著不同的字段。常用的有以下幾種。
- VERSION - PCD文件的版本號(通常為0.7);
- FIELDS - 每個點所包含的維度或字段(例如x y z);
- SIZE - 每個數據維度所占據的字節(比如float為4字節)
- TYPE -每個數據維度的數據類型(I= signed,U= unsigned,F=float)
- COUNT - 每個數據維度上包含的元素個數。(比如,x, y, or z的count為1,但是hisogram的count包含N)
- WIDTH - 點云的寬度
- HEIGHT - 點云的高度
- VIEWPOINT - 點云的視角 translation (tx ty tz) +quaternion (qw qx qy qz)
- POINTS -點云的數量
- DATA -具體的數據存儲格式(ascii or binary)
在之前的內容中我們忽略了一個小小的但是相對重要的知識點:待填充的數據怎么表示?請注意這里不能簡單的使用字符串xyz的形式進行填充,如果仍舊使用字符串的格式進行填充,那么pcd文件格式在存儲上的優勢根本無法體現。一種簡單的方法是為點云數據定義一個結構體,詳細的情況后文再敘,這里先羅列一下最常用的數據類型(當然自定義數據類型也可)。
Point Types PointXYZ- float x, y, z PointXYZI- float x, y, z, intensity PointXYZRGB- float x, y, z, rgb PointXYZRGBA- float x, y, z, uint32t rgba Normal- float normal[3], curvature PointNormal- float x, y, z, normal[3], curvature Histogram- float histogram[N] ………………二、利用PCL寫點云文件
現在詳細看一下生成點云寫入文件的過程。
#include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h>intmain (int argc, char** argv) {pcl::PointCloud<pcl::PointXYZ> cloud;// Fill in the cloud datacloud.width = 5;cloud.height = 1;cloud.is_dense = false;cloud.points.resize (cloud.width * cloud.height);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); }關鍵的部分可以分為以下內容:生成點云、寫入文件。
生成點云可以視作一個字段填充的過程,我們來看看具體干了什么事情。
初始化定義一個點云對象,此處我們創建了一個PointXYZ對象
pcl::PointCloud<pcl::PointXYZ> cloud;設置pcd文件字段,將想要填的內容填進去。
// Fill in the cloud datacloud.width = 5;cloud.height = 1;cloud.is_dense = false;cloud.points.resize (cloud.width * cloud.height);填充具體的值
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);三、讀取pcd文件
讀寫不分家。讀文件的過程同樣并不復雜。
#include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h>int main (int argc, char** argv) {pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);if (pcl::io::loadPCDFile<pcl::PointXYZ> ("test_pcd.pcd", *cloud) == -1) //* load the file{PCL_ERROR ("Couldn't read file test_pcd.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); }核心工作就是兩步:
定義一個句柄用來存放待讀取的點云。
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);讀取點云
pcl::io::loadPCDFile<pcl::PointXYZ> ("test_pcd.pcd", *cloud) == -1對!就是這么簡單。
四、xyz文件轉成pcd文件
現在讓我們研究一個小小的案例。有時候,我們的原始點云數據是存放在txt文件里的。也許它長成這樣:
20.623 40.276 -1.999 -1031 127 141 154 20.362 40.375 -2.239 -941 130 141 159 20.36 40.376 -2.402 -1083 139 151 165 20.374 40.367 -2.405 -1122 131 147 163 20.372 40.366 -2.405 -1165 132 145 161 20.375 40.364 -2.404 -1036 133 149 165 20.358 40.371 -2.405 -1137 139 151 165 20.359 40.374 -2.404 -1086 139 151 165 …………前三個字段為xyz,第四個字段為intensity,后三個字段為rgb。如何將這樣的數據轉成pcd文件呢?先上代碼,運行起來再說。將下列代碼復制并保存到xyz2cpp文件。這個程序將xyzirgb數據轉成pcd格式的xyzrgb數據。如果想從xyz的數據轉成pcd數據,那么只需要剔除若干填充字段即可。
#include <pcl/io/pcd_io.h> #include <pcl/console/print.h> #include <pcl/console/parse.h>using namespace std; using namespace pcl; using namespace pcl::io; using namespace pcl::console;void printHelp (int, char **argv) {print_error ("Syntax is: %s input.xyz output.pcdn", argv[0]); }bool loadCloud (const string &filename, PointCloud<PointXYZRGB> &cloud) {ifstream fs;fs.open (filename.c_str (), ios::binary);if (!fs.is_open () || fs.fail ()){PCL_ERROR ("Could not open file '%s'! Error : %sn", filename.c_str (), strerror (errno)); fs.close ();return (false);}string line;vector<string> st;while (!fs.eof ()){getline (fs, line);// Ignore empty linesif (line.empty())continue;// Tokenize the lineboost::trim (line);boost::split (st, line, boost::is_any_of ("tr "), boost::token_compress_on);if (st.size () != 7)continue;pcl::PointXYZRGB point;point.x = float (atof (st[0].c_str ())); point.y = float (atof (st[1].c_str ())); point.z = float (atof (st[2].c_str ()));point.r = uint8_t (atof (st[4].c_str ()));point.g = uint8_t (atof (st[5].c_str ()));point.b = uint8_t (atof (st[6].c_str ()));cloud.push_back (point);}fs.close ();cloud.width = uint32_t (cloud.size ()); cloud.height = 1; cloud.is_dense = true;return (true); }int main (int argc, char** argv) {print_info ("Convert a simple XYZ file to PCD format. For more information, use: %s -hn", argv[0]);if (argc < 3){printHelp (argc, argv);return (-1);}// Parse the command line arguments for .pcd and .ply filesvector<int> pcd_file_indices = parse_file_extension_argument (argc, argv, ".pcd");vector<int> xyz_file_indices = parse_file_extension_argument (argc, argv, ".xyz");if (pcd_file_indices.size () != 1 || xyz_file_indices.size () != 1){print_error ("Need one input XYZ file and one output PCD file.n");return (-1);}// Load the first filePointCloud<PointXYZRGB> cloud;if (!loadCloud (argv[xyz_file_indices[0]], cloud)) return (-1);// Convert to PCD and savepcl::io::savePCDFileASCII (argv[pcd_file_indices[0]], cloud); }創建CMakeList.txt文件
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)project(xyz2pcd)find_package(PCL 1.2 REQUIRED)include_directories(${PCL_INCLUDE_DIRS}) link_directories(${PCL_LIBRARY_DIRS}) add_definitions(${PCL_DEFINITIONS})add_executable (xyz2pcd xyz2pcd.cpp) target_link_libraries (xyz2pcd ${PCL_LIBRARIES})創建build文件夾按照前一節課所講的內容進行編譯。
運行下列命令./xyz2pcd smalldata.xyz smalldata.pcd
其中smalldata.xyz為待轉換的文件,smalldata.pcd為目標文件。如果沒有數據可以將下列數據存為smalldata.xyz與xyz2pcd 放在同一級目錄下。請注意這里intensity這個字段并沒有用到,如果想用intensity字段,請自定義數據類型。
測試數據
20.623 40.276 -1.999 -1031 127 141 154 20.362 40.375 -2.239 -941 130 141 159 20.36 40.376 -2.402 -1083 139 151 165 20.374 40.367 -2.405 -1122 131 147 163 20.372 40.366 -2.405 -1165 132 145 161 20.375 40.364 -2.404 -1036 133 149 165 20.358 40.371 -2.405 -1137 139 151 165 20.359 40.374 -2.404 -1086 139 151 165 20.354 40.375 -2.404 -1106 139 148 163 20.356 40.374 -2.404 -1059 139 151 165 20.359 40.374 -2.399 -1059 138 150 166 20.359 40.374 -2.395 -1014 138 150 166 20.354 40.375 -2.395 -1042 138 150 166 20.356 40.374 -2.396 -1050 138 150 166 20.354 40.374 -2.393 -1031 137 149 165 20.356 40.374 -2.393 -1045 137 149 165 20.359 40.374 -2.392 -957 137 149 165 20.374 40.365 -2.402 -1093 133 149 165 20.375 40.367 -2.401 -1096 133 149 165 20.374 40.367 -2.399 -1158 133 149 165 20.371 40.367 -2.399 -1093 133 146 162 20.365 40.371 -2.405 -1283 133 145 159 20.361 40.371 -2.404 -1211 133 145 159 20.362 40.372 -2.405 -1314 133 145 159 20.36 40.373 -2.405 -1213 139 151 165 20.361 40.375 -2.404 -1116 139 151 165 20.363 40.375 -2.402 -1063 133 145 159 20.367 40.371 -2.405 -1231 133 145 159 20.365 40.375 -2.405 -1311 133 145 159 20.367 40.372 -2.404 -1173 133 145 159至此運行成功,結果如下:
或許大家會感到疑惑為什么RGB轉成了一個大整數?這里為了方便存儲使用了移位拼接的方法。下節課我們繼續討論可視化等更深入的內容。
參考材料:
http://www.pointclouds.org/documentation/tutorials/writing_pcd.php#writing-pcd
http://pointclouds.org/documentation/tutorials/pcd_file_format.php
總結
以上是生活随笔為你收集整理的保存点云数据_PCL入门系列三——PCL进行数据读写的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 短线股票如何确定止损点和止盈点?
- 下一篇: 分数化简_分数应用题七讲 (一) 图示法