PCL已有点类型介绍和增加自定义的点类型

本小节不仅解释如何增加你自己的PointT点类型,也介绍了PCL中的模板point类型,以及它们的用处和定义。PCL从开始就伴随着各种预定义的point类型,从用于XYZ数据到更复杂的n维直方图表示法,例如PFH(点特征直方图)。这些类型应该足够支持在PCL中应用的算法及方法,然而,也有情况下用户希望定义新的类型。

注意:由于PCL的快速更新,本节内容仅仅对PCL 0.x和1.x版本兼容,撰写本文档的时候预期在PCL 2.X中会有新的改变,但大的架构不变,只是添加些新定义的描述子等点类型之类的。

为什么用PointT类型
PCL的PointT可以追溯到它在ROS中作为开源库被开发的时候,大家一致认为,点云是复杂的n维结构,它需要能表示不同类型的信息,然而用户应该知道并理解需要传送什么样的信息,为了使代码更易于调试,考虑优化等等,下面给出一个例子,是对XYZ数据的简单操作,对带SSE功能的处理器,最高效的方法是存储3维坐标为浮点型,紧跟着一个浮点型数据作为填补位数以满足存储对齐要求:

struct PointXYZ
{
float x;
float y;
float z;
float padding;
};

然而,例如当用户在嵌入式平台上寻找编译PCL的时候,增加额外的填补就是浪费存储空间了,因此,可以用一个简单的不带最后浮点数的PointXYZ结构来替代。此外,如果你的应用程序需要一个包含XYZ三维数据、RGB信息(颜色)和每个点的估计法线的PointXYZRGBNormal类型,定义包含以上所有内容的结构是很简单的,由于PCL中所有的算法都是模板化的,除了更改的自定义结构之外,不需要做其他的更改,增加了代码的重用性和可读性。

为了涵盖能想到的所有可能的情况,PCL中定义了大量的point类型。下面是一小段,在point_types.hpp中有完整目录,这个列表很重要,因为在定义你自己的类型之前,需要了解已有的类型,如果你需要的类型,已经存在于PCL,那么就不需要重复定义了。

PointXYZ–成员变量: float x, y, z;
PointXYZ是使用最常见的一个点数据类型,因为它只包含三维xyz坐标信息,这三个浮点数附加一个浮点数来满足存储对齐,用户可利用points[i].data[0],或者points[i].x访问点的x坐标值。

union
{
float data[4];
struct
{
	float x;
	float y;
	float z;
};
};

PointXYZI–成员变量: float x, y, z, intensity;
PointXYZI是一个简单的XYZ坐标加intensity的point类型,理想情况下,这四个变量将新建单独一个结构体,并且满足存储对齐,然而,由于point的大部分操作会把data[4]元素设置成0或1(用于变换),不能让intensity与xyz在同一个结构体中,如果这样的话其内容将会被覆盖。例如,两个点的点积会把他们的第四个元素设置成0,否则该点积没有意义,等等。因此,对于兼容存储对齐,用三个额外的浮点数来填补intensity,这样在存储方面效率较低,但是符合存储对齐要求,运行效率较高。

union
{
float data[4];
struct
{
	float x;
	float y;
	float z;
};
};
union
{
struct
{
	float intensity;
};
float data_c[4];
};
}

PointXYZRGBA–成员变量: float x, y, z; uint32_t rgba;
除了rgba信息被包含在一个整型变量中,其它的和PointXYZI类似。

union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
struct
{
uint32_t rgba;
};
float data_c[4];
};

PointXYZRGB - float x, y, z, rgb;
除了rgb信息被包含在一个浮点型变量中,其它和PointXYZRGB类似。rgb数据被压缩到一个浮点数里的原因在于早期PCL是作为ROS项目的一部分来开发的,那里RGB数据是用浮点数来传送的,PCL设计者希望所有遗留代码会重新更改(在PCL 2.x中很可能这样做),可能取消此数据类型。

union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
struct
{
float rgb;
};
float data_c[4];
};

PointXY-float x, y;
简单的二维x-y point结构

struct
{
float x;
float y;
};

InterestPoint-float x, y, z, strength;
除了strength表示关键点的强度的测量值,其它的和PointXYZI类似。

union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
struct
{
float strength;
};
float data_c[4];
};

Normal - float normal[3], curvature;
另一个最常用的数据类型,Normal结构体表示给定点所在样本曲面上的法线方向,以及对应曲率的测量值(通过曲面块特征值之间关系获得——查看NormalEstimation类API以便获得更多信息,后续章节有介绍),由于在PCL中对曲面法线的操作很普遍,还是用第四个元素来占位,这样就兼容SSE和高效计算,例如,用户访问法向量的第一个坐标,可以通过points[i].data_n[0]或者points[i].normal[0]或者points[i].normal_x,再一次强调,曲率不能被存储在同一个结构体中,因为它会被普通的数据操作覆盖掉。

union
{
float data_n[4];
float normal[3];
struct
{
float normal_x;
float normal_y;
float normal_z;
};
}
union
{
struct
{
float curvature;
};
float data_c[4];
};

PointNormal - float x, y, z; float normal[3], curvature;
PointNormal是存储XYZ数据的point结构体,并且包括采样点对应法线和曲率。

union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
float data_n[4];
float normal[3];
struct
{
float normal_x;
float normal_y;
float normal_z;
};
};
union
{
struct
{
float curvature;
};
float data_c[4];
};

PointXYZRGBNormal - float x, y, z, rgb, normal[3], curvature;
PointXYZRGBNormal存储XYZ数据和RGB颜色的point结构体,并且包括曲面法线和曲率。

union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
float data_n[4];
float normal[3];
struct
{
float normal_x;
float normal_y;
float normal_z;
};
}
union
{
struct
{
float rgb;
float curvature;
};
float data_c[4];
};

PointXYZINormal - float x, y, z, intensity, normal[3], curvature;
PointXYZINormal存储XYZ数据和强度值的point结构体,并且包括曲面法线和曲率。

union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
float data_n[4];
float normal[3];
struct
{
float normal_x;
float normal_y;
float normal_z;
};
}
union
{
struct
{
float intensity;
float curvature;
};
float data_c[4];
};

PointWithRange - float x, y, z (union with float point[4]), range;
PointWithRange除了range包含从所获得的视点到采样点的距离测量值之外,其它与PointXYZI类似。

union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
struct
{
float range;
};
float data_c[4];
};

PointWithViewpoint - float x, y, z, vp_x, vp_y, vp_z;
PointWithViewpoint除了vp_x、vp_y和vp_z以三维点表示所获得的视点之外,其它与PointXYZI一样。

union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
struct
{
float vp_x;
float vp_y;
float vp_z;
};
float data_c[4];
};

MomentInvariants - float j1, j2, j3;
MomentInvariants是一个包含采样曲面上面片的三个不变矩的point类型,描述面片上质量的分布情况。查看MomentInvariantsEstimation以获得更多信息。

struct
{
float j1,j2,j3;
};

PrincipalRadiiRSD - float r_min, r_max;
PrincipalRadiiRSD是一个包含曲面块上两个RSD半径的point类型,查看RSDEstimation以获得更多信息。

struct
{
float r_min,r_max;
};

Boundary - uint8_t boundary_point;
Boundary存储一个点是否位于曲面边界上的简单point类型,查看BoundaryEstimation以获得更多信息。

struct
{
uint8_t boundary_point;
};

PrincipalCurvatures - float principal_curvature[3], pc1, pc2;
PrincipalCurvatures包含给定点主曲率的简单point类型。查看PrincipalCurvaturesEstimation以获得更多信息。

struct
{
union
{
float principal_curvature[3];
struct
{
float principal_curvature_x;
float principal_curvature_y;
float principal_curvature_z;
};
};
float pc1;
float pc2;
};

PFHSignature125 - float pfh[125];
PFHSignature125包含给定点的PFH(点特征直方图)的简单point类型,查看PFHEstimation以获得更多信息

struct
{
float histogram[125];
};

FPFHSignature33 - float fpfh[33];
FPFHSignature33包含给定点的FPFH(快速点特征直方图)的简单point类型,查看FPFHEstimation以获得更多信息。

struct
{
float histogram[33];
};

VFHSignature308 - float vfh[308];
VFHSignature308包含给定点VFH(视点特征直方图)的简单point类型,查看VFHEstimation以获得更多信息。

struct
{
float histogram[308];
};

Narf36 - float x, y, z, roll, pitch, yaw; float descriptor[36];
Narf36包含给定点NARF(归一化对齐半径特征)的简单point类型,查看NARFEstimation以获得更多信息。

struct
{
float x,y,z,roll,pitch,yaw;
float descriptor[36];
};

BorderDescription - int x, y; BorderTraits traits;
BorderDescription包含给定点边界类型的简单point类型,看BorderEstimation以获得更多信息。

struct
{
int x,y;
BorderTraitstraits;
};

IntensityGradient - float gradient[3];
IntensityGradient包含给定点强度的梯度point类型,查看IntensityGradientEstimation以获得更多信息。

struct
{
union
{
float gradient[3];
struct
{
float gradient_x;
float gradient_y;
float gradient_z;
};
};
};

Histogram - float histogram[N];
Histogram用来存储一般用途的n维直方图。

template<int N>
struct Histogram
{
float histogram[N];
};

PointWithScale - float x, y, z, scale;
PointWithScale除了scale表示某点用于几何操作的尺度(例如,计算最近邻所用的球体半径,窗口尺寸等等),其它的和PointXYZI一样。

struct
{
union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
float scale;
}

PointSurfel - float x, y, z, normal[3], rgba, radius, confidence, curvature;
PointSurfel存储XYZ坐标、曲面法线、RGB信息、半径、可信度和曲面曲率的复杂point类型。

union
{
float data[4];
struct
{
float x;
float y;
float z;
};
};
union
{
float data_n[4];
float normal[3];
struct
{
float normal_x;
float normal_y;
float normal_z;
};
};
union
{
struct
{
uint32_trgba;
float radius;
float confidence;
float curvature;
};
float data_c[4];
};

如何在模板类中使用这些point类型?
由于PCL模块较多,并且是一个模板库,在一个源文件里包含很多PCL算法会减慢编译过程,在撰写本文档的时候,大多数C++编译器仍然没有很好的优化来处理大量模板文件,尤其是涉及优化(-O2或者-O3)问题的时候。为了使包含和链接到PCL库的用户代码编译速度提高,我们使用显示的模板实例化,最终编译链接的库包括了所有可能的模板实例——在这些组合中使PCL中已经定义的point类型所有的模板类都能够直接调用,不需要重新编译,这意味着一旦PCL编译成库,任何用户代码都不需要编译模板化代码,这样就加速了用户编译过程。这个是通过在头文件中首先声明了我们的类和方法,再在模板类实现头文件中进行实现,配置在源文件中进行显示的实例化,最后在编译链接时分别实例化。举一个例子:

// foo.h
#ifndef PCL_FOO_
#define PCL_FOO_
template<typename PointT>
class Foo
{
public:
void compute(const pcl::PointCloud<PointT> &input,
pcl::PointCloud<PointT> &output);
}
#endif // PCL_FOO_

上面定义了模板类Foo,可以看到,它定义了方法和类,但是还没有实现任何功能。

// impl/foo.hpp
#ifndef PCL_IMPL_FOO_
#define PCL_IMPL_FOO_
#include "foo.h"
template<typename PointT>
void Foo::compute(const pcl::PointCloud<PointT> &input,
pcl::PointCloud<PointT> &output)
{
	output=input;
}
#endif // PCL_IMPL_FOO_

上面定义了Foo::compute方法的模板实现, 这种定义通常不与用户代码混合。

// foo.cpp
#include "pcl/point_types.h"
#include "pcl/impl/instantiate.hpp"
#include "foo.h"
#include "impl/foo.hpp"
// Instantiations of specific point types
PCL_INSTANTIATE(Foo,PCL_XYZ_POINT_TYPES));

最后,上面展示了在PCL中是如何进行显示实例化的,宏PCL_INSTANTIATE仅仅检查给定的类型清单并为每一个类型创建对应一个类实例。pcl/include/pcl/impl/instantiate.hpp中有如下代码:

// PCL_INSTANTIATE: call to instantiate template TEMPLATE for all
// POINT_TYPES
#define PCL_INSTANTIATE_IMPL(r, TEMPLATE, POINT_TYPE) \
  BOOST_PP_CAT(PCL_INSTANTIATE_, TEMPLATE)(POINT_TYPE)
#define PCL_INSTANTIATE(TEMPLATE, POINT_TYPES)        \
  BOOST_PP_SEQ_FOR_EACH(PCL_INSTANTIATE_IMPL, TEMPLATE, POINT_TYPES);

PCL_XYZ_POINT_TYPES在这里(在pcl/include/pcl/impl/point_types.hpp中):

// Define all point types that include XYZ data
#define PCL_XYZ_POINT_TYPES   \
  (pcl::PointXYZ)             \
  (pcl::PointXYZI)            \
  (pcl::PointXYZRGBA)         \
  (pcl::PointXYZRGB)          \
  (pcl::InterestPoint)        \
  (pcl::PointNormal)          \
  (pcl::PointXYZRGBNormal)    \
  (pcl::PointXYZINormal)      \
  (pcl::PointWithRange)       \
  (pcl::PointWithViewpoint)   \
  (pcl::PointWithScale)

实际上,如果你只是想给pcl::PointXYZ类型实例化Foo这一个实例类,你不需要使用宏,而只需要像下面这样简单地做:
注意:查看David Vandervoorde和Nicolai M. Josuttis所著的C++ Templates - The Complete Guide,可获得关于显示实例化的更多信息。

// foo.cpp
#include "pcl/point_types.h"
#include "pcl/impl/instantiate.hpp"
#include "foo.h"
#include "impl/foo.hpp"
template class Foo<pcl::PointXYZ>;

如何增加新的PointT类型
为了增加新的point类型,首先需要进行定义,例如:

struct MyPointType
{
float test;
};

然后,你得确保你的代码包含了PCL中特定的类/算法的模板头文件的实现,它将和你的新point类型MyPointType共同使用,例如,你想使用pcl::PassThrough。你只需要使用下面的代码即可:

#include <pcl/filters/passthrough.h>
#include <pcl/filters/impl/passthrough.hpp>
// the rest of the code goes here

如果你的代码是库的一部分,可以被他人使用,需要为你自己的MyPointType类型进行显示实例化。
实例
下面的代码段创建了包含XYZ数据的新point类型,连同一个的test的浮点型数据,这样满足存储对齐。

#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>
 
struct MyPointType              //定义点类型结构
{
PCL_ADD_POINT4D;                // 该点类型有4个元素
float test;
EIGEN_MAKE_ALIGNED_OPERATOR_NEW// 确保new操作符对齐操作
}EIGEN_ALIGN16;// 强制SSE对齐
 
POINT_CLOUD_REGISTER_POINT_STRUCT(MyPointType,// 注册点类型宏
(float,x,x)
(float,y,y)
(float,z,z)
(float,test,test)
)
int
main(int argc,char** argv)
{
pcl::PointCloud<MyPointType> cloud;
cloud.points.resize(2);
cloud.width=2;
cloud.height=1;
 
cloud.points[0].test=1;
cloud.points[1].test=2;
cloud.points[0].x=cloud.points[0].y=cloud.points[0].z=0;
cloud.points[1].x=cloud.points[1].y=cloud.points[1].z=3;
 
pcl::io::savePCDFile("test.pcd",cloud);
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yhwang-hub

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值