在应用3D检测时,项目中总会需要采集数据,来离线分析问题,而点云数据往往较为庞大,本文主要通过数据类型的转换来保存点云数据,达到数据压缩的效果。本博客的点云压缩方法较为简单切实用,向大家分享一下。本文仅对PCL中的pcl::PointXYZI进行数据压缩,其他类型可以仿照参考即可完善。
首先我们来了解一下pcl::PointXYZI的数据类型定义:
/** \brief A point structure representing Euclidean xyz coordinates, and the intensity value.
* \ingroup common
*/
struct EIGEN_ALIGN16 _PointXYZI
{
PCL_ADD_POINT4D; // This adds the members x,y,z which can also be accessed using the point (which is float[4])
union
{
struct
{
float intensity;
};
float data_c[4];
};
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
PCL_EXPORTS std::ostream& operator << (std::ostream& os, const PointXYZI& p);
struct PointXYZI : public _PointXYZI
{
inline PointXYZI (const _PointXYZI &p)
{
x = p.x; y = p.y; z = p.z; data[3] = 1.0f;
intensity = p.intensity;
}
inline PointXYZI ()
{
x = y = z = 0.0f;
data[3] = 1.0f;
intensity = 0.0f;
}
inline PointXYZI (float _intensity)
{
x = y = z = 0.0f;
data[3] = 1.0f;
intensity = _intensity;
}
friend std::ostream& operator << (std::ostream& os, const PointXYZI& p);
};
可以很直观看出,其数据结构主要有 f l o a t float float类型组成,而实际点云应用场景则是用米为单位,因此,很多场景仅需要毫米级别的点云数据即可。因此,思路就简单起来了:
我们考虑把点云的 f l o a t float float数据类型转成 i n t int int,即可将64字节的数据转换成8字节的数据,数据大小也极大的降低。
首先是自定义点云数据结构。
struct PointXYZId {
int x;
int y;
int z;
int intensity;
EIGEN_MAKE_ALIGNED_OPERATOR_NEW // make sure our new allocators are aligned
} EIGEN_ALIGN16; // enforce SSE padding for correct memory alignment
POINT_CLOUD_REGISTER_POINT_STRUCT(PointXYZId,
(uint8_t, x, x)
(uint8_t, y, y)
(uint8_t, z, z)
(uint8_t, intensity, intensity))
确定以 i n t int int为主的点云结构以后,则需要将点云的单位由米转换成毫米。
for (size_t i = 0; i < input_size; ++i){
src_cloud.points[i].x = static_cast<int>(input_cloud.points[i].x * 1000);
src_cloud.points[i].y = static_cast<int>(input_cloud.points[i].y * 1000);
src_cloud.points[i].z = static_cast<int>(input_cloud.points[i].z * 1000);
src_cloud.points[i].intensity = input_cloud.points[i].intensity;
}
而解压操作无非就是将毫米级别的点云数据转换成以米为单位的点云。
for (size_t i = 0; i < input_size; ++i){
src_cloud.points[i].x = static_cast<float>(input_cloud.points[i].x )/ 1000 ;
src_cloud.points[i].y = static_cast<float>(input_cloud.points[i].y )/ 1000;
src_cloud.points[i].z = static_cast<float>(input_cloud.points[i].z )/ 1000;
src_cloud.points[i].intensity = input_cloud.points[i].intensity;
}
#define PCL_NO_PRECOMPILE
#pragma once
#include
#include
#include
struct PointXYZId {
int x;
int y;
int z;
int intensity;
EIGEN_MAKE_ALIGNED_OPERATOR_NEW // make sure our new allocators are aligned
} EIGEN_ALIGN16; // enforce SSE padding for correct memory alignment
POINT_CLOUD_REGISTER_POINT_STRUCT(PointXYZId,
(uint8_t, x, x)
(uint8_t, y, y)
(uint8_t, z, z)
(uint8_t, intensity, intensity))
class PointCloudCompression
{
public:
PointCloudCompression(/* args */){};
~PointCloudCompression(){};
/**
* @brief Compress the imported point cloud
* @param[in] input_cloud Import the point cloud to be compressed
* @param[out] output_cloud Export compressed point cloud
*/
void GetCompressionData(const pcl::PointCloud<pcl::PointXYZI> &input_cloud,
pcl::PointCloud<PointXYZId> &output_cloud);
/**
* @brief Decompress the imported point cloud
* @param[in] input_cloud Import the point cloud to be decompressed
* @param[out] output_cloud Export decompressed point cloud
*/
void GetDecompressionData(const pcl::PointCloud<PointXYZId> &input_cloud,
pcl::PointCloud<pcl::PointXYZI> &output_cloud);
};
void PointCloudCompression::GetCompressionData(const pcl::PointCloud<pcl::PointXYZI> &input_cloud,
pcl::PointCloud<PointXYZId> & output_cloud){
if (input_cloud.empty()) return;
int input_size = input_cloud.points.size();
pcl::PointCloud<PointXYZId> src_cloud;
src_cloud.resize(input_size);
for (size_t i = 0; i < input_size; ++i){
src_cloud.points[i].x = static_cast<int>(input_cloud.points[i].x * 1000);
src_cloud.points[i].y = static_cast<int>(input_cloud.points[i].y * 1000);
src_cloud.points[i].z = static_cast<int>(input_cloud.points[i].z * 1000);
src_cloud.points[i].intensity = input_cloud.points[i].intensity;
}
src_cloud.is_dense = false;
src_cloud.height = 1;
src_cloud.width = src_cloud.points.size();
pcl::copyPointCloud(src_cloud,output_cloud);
return;
}
void PointCloudCompression::GetDecompressionData(const pcl::PointCloud<PointXYZId> &input_cloud,
pcl::PointCloud<pcl::PointXYZI> &output_cloud){
if (input_cloud.empty()) return;
int input_size = input_cloud.points.size();
pcl::PointCloud<pcl::PointXYZI> src_cloud;
src_cloud.resize(input_size);
for (size_t i = 0; i < input_size; ++i){
src_cloud.points[i].x = static_cast<float>(input_cloud.points[i].x )/ 1000 ;
src_cloud.points[i].y = static_cast<float>(input_cloud.points[i].y )/ 1000;
src_cloud.points[i].z = static_cast<float>(input_cloud.points[i].z )/ 1000;
src_cloud.points[i].intensity = input_cloud.points[i].intensity;
}
src_cloud.is_dense = false;
src_cloud.height = 1;
src_cloud.width = src_cloud.points.size();
pcl::copyPointCloud(src_cloud,output_cloud);
return;
}
int main(int argc, char **argv){
pcl::PointCloud<pcl::PointXYZI>::Ptr src_lidar_cloud(new pcl::PointCloud<pcl::PointXYZI>);
pcl::PointCloud<pcl::PointXYZI>::Ptr out_lidar_cloud(new pcl::PointCloud<pcl::PointXYZI>);
pcl::io::loadPCDFile(argv[1], *src_lidar_cloud);
PointCloudCompression pcc;
pcl::PointCloud<PointXYZId> compression_data;
pcc.GetCompressionData(*src_lidar_cloud,compression_data);
pcl::io::savePCDFileASCII("compression_data.pcd",compression_data);
pcc.GetDecompressionData(compression_data,*out_lidar_cloud);
pcl::io::savePCDFileASCII("decompression_data.pcd",*out_lidar_cloud);
return 0;
}
至此,就可以自由的对点云数据进行压缩,解压处理了,可以压缩约1/2左右的空间。