English original web page view.
Add a custom PointT type
This document explains not only how to add your own PointT point types, but also what point types are templated in PCL, why they exist, and how they are described.If you are already familiar with this information, skip to the last part of the document.
Be careful
The current document is only valid for PCL 0.x and 1.x.There will be some changes in PCL 2.x at the time of writing this article.
PCL provides a variety of predefined point types, from the ssi alignment structure of XYZ data to more complex n-dimensional histogram representations, such as PFH (Point Feature Histogram).These types should be sufficient to support all algorithms and methods implemented in PCL.However, in some cases, users want to define new types.This document describes the steps involved in defining your own custom PointT types and ensuring that your project can compile and run successfully.
Why is the PointT type
The PointT type of PCL can be traced back to when it was a development library for ROS.The consensus at that time was that point clouds were a complex n-D structure that required the ability to represent different types of information.However, users should know and understand what types of information need to be passed in order to make the code easier to debug, consider optimization, and so on.
An example is a simple operation on XYZ data.For sse-enabled processors, the most effective method is to store three-dimensional data as floating-point numbers, then fill them with additional floating-point numbers:
struct PointXYZ { float x; float y; float z; float padding; };
However, as an example, adding extra padding can waste memory if users want to compile PCL s on embedded platforms.Therefore, you can use a simpler PointXYZ structure without the last floating point number.
Additionally, if your application requires a PointXYZRGBNormal that contains XYZ 3D data, RGBA information (color), and estimated surface normals for each point, it is easy to define a structure with all of the above.Since all the algorithms in the PCL should be templated, there is no need to make any changes other than the structure definition.
What are the PointT types in PCL?
To cover all the possible scenarios we can think of, we have defined a number of point types in the PCL.The following may be just a snippet, see point_types.hpp Get the full list.
This list is important because before defining your own custom types, you need to understand why you are creating existing types the way you are.In addition, the type you want may already be defined for you.
- PointXYZ - Member: float x, y, z;
This is one of the most common data types because it only represents 3D xyz information.Three float data are populated with an additional float for SSE alignment.Users can access x-coordinate values through points[i].data[0] or points[i].x.
union { float data[4]; struct { float x; float y; float z; }; };
- PointXYZI - Member: float x, y, z, intensity;
Simple XYZ +intensity point type.Ideally, these four members would create a single structure, sse-aligned.However, since most point operations either set data[4] to 0 or 1 (for conversion), we cannot set the intensity to a member of the same structure because its content will be overwritten.For example, the dot product between two points sets its fourth component to zero, otherwise the dot product is meaningless, and so on.
Therefore, for sse alignment, we use three additional floating fill strengths.It's inefficient in storage but good in memory alignment.
union { float data[4]; struct { float x; float y; float z; }; }; union { struct { float intensity; }; float data_c[4]; };
- PointXYZRGBA - Member: float x, y, z; uint32_t rgba;
Similar to PointXYZI, except that rgba contains unsigned 32-bit rgba information.With the union declaration, color channels can also be accessed individually by name.
Be careful
Nested union declarations provide another way to view RGBA data, an exact floating point number.This occurs for historical reasons and should not be used in new code.
union { float data[4]; struct { float x; float y; float z; }; }; union { union { struct { uint8_t b; uint8_t g; uint8_t r; uint8_t a; }; float rgb; }; uint32_t rgba; };
- PointXYZRGB - float x, y, z; uint32_t rgba;
Like PointXYZRGBA. - PointXY - float x, y;
Simple 2D x-y point structure.
struct { float x; float y; };
- InterestPoint - float x, y, z, strength;
Similar to PointXYZI, except that the intensity contains a measure of the strength of the critical point.
union { float data[4]; struct { float x; float y; float z; }; }; union { struct { float strength; }; float data_c[4]; };
- Normal - float normal[3], curvature;
Another of the most commonly used data types, the Normal structure, represents the normal direction and curvature of a given point on a surface (you can derive from solving the characteristics of the surface -, referring to the NormalEstimation class for more information).
Because surface normal operations are common in PCL s, we fill these three components with a fourth component to achieve sse alignment and computational efficiency.The user can access the first coordinate of the normal vector through points[i].data_n[0] or points[i].normal[0] or points[i].normal_x.Similarly, curvature cannot be stored in the same structure because it is overridden by operations on normal data.
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;
A point structure containing XYZ data along with surface normals and curvature.
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, normal[3], curvature; uint32_t rgba;
A point structure containing XYZ data, RGBA colors, along with surface normals and curvatures.
Be careful
In addition to the name, this point type also contains alpha Color channels.
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 { union { union { struct { uint8_t b; uint8_t g; uint8_t r; uint8_t a; }; float rgb; }; uint32_t rgba; }; float curvature; }; float data_c[4]; };
- PointXYZINormal - float x, y, z, intensity, normal[3], curvature;
A point structure consisting of XYZ data, intensity values, surface normals, and curvature.
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;
Similar to PointXYZI, range contains the distance from the collection point to the object point.
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;
Similar to PointXYZI, it also includes vp_x, vp_y, and vp_z, which contain the acquired viewpoint as a 3D point.
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;
Simple point type with three moment invariants on the surface.For more information, see MomentInvariants Estimation.
struct { float j1, j2, j3; };
- PrincipalRadiiRSD - float r_min, r_max;
Simple point type with 2 RSD radii on the surface.For more information, see RSDEstimation.
struct { float r_min, r_max; };
- Boundary - uint8_t boundary_point;
Simple point type that holds information about whether or not it is located on the surface boundary.For more information, see BoundaryEstimation.
struct { uint8_t boundary_point; };
- PrincipalCurvatures - float principal_curvature[3], pc1, pc2;
A simple point type containing the principal curvature of a given point.For more information, see Principal CurvaturesEstimation.
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];
A simple point type that holds the PFH (point feature histogram) of a given point.See PFHEstimation for more information.
struct { float histogram[125]; };
- FPFHSignature33 - float fpfh[33];
Simple point type containing the FPFH (fast point feature histogram) of a given point.For more information, see FPFHEstimation.
struct { float histogram[33]; };
- VFHSignature308 - float vfh[308];
Simple point type of VFH (Viewpoint Feature Histogram) holding a given point.For more information, see VFHEstimation.
struct { float histogram[308]; };
- Narf36 - float x, y, z, roll, pitch, yaw; float descriptor[36];
A simple point type containing the NARF (usually alignment radius characteristic) for a given point.For more information, see NARFEstimation.
struct { float x, y, z, roll, pitch, yaw; float descriptor[36]; };
- BorderDescription - int x, y; BorderTraits traits;
Simple point type, containing the boundary type of a given point.See BorderEstimation for more information.
struct { int x, y; BorderTraits traits; };
- IntensityGradient - float gradient[3];
Simple point type containing the intensity gradient of a given point.For more information, see IntensityGradientEstimation.
struct { union { float gradient[3]; struct { float gradient_x; float gradient_y; float gradient_z; }; }; };
- Histogram - float histogram[N];
Universal n-D histogram placeholder.
template <int N> struct Histogram { float histogram[N]; };
- PointWithScale - float x, y, z, scale;
Similar to PointXYZI, it also includes a scale that considers a point in a geometric operation (for example, calculating the radius of the nearest neighbor's sphere, window size, and so on).
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;
Complex point types that contain XYZ data, surface normals, RGB information, scales, confidence, and curvature of surfaces.
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_t rgba; float radius; float confidence; float curvature; }; float data_c[4]; };
How are point types exposed?
Because PCL is large and a template library, including many PCL algorithms in a source file can slow down the compilation process.At the time of writing this document, most c++ compilers have not been properly optimized to handle a large number of template files, especially when optimization (-O2 or-O3) is involved.
To speed up the inclusion and linking of user code for PCLs, we use explicit template instantiation, including all possible combinations, where all algorithms can be invoked using point types already defined in the PCL.This means that once the PCL is compiled as a library, no user code needs to compile the template code, which speeds up the compilation time.This technique involves separating the template implementation from the header files of forward declared classes and methods and parsing them at link time.Here is an example written:
// 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_
The header file defined above is usually contained in all user code.As we can see, we're defining methods and classes, but we haven't implemented anything yet.
// 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_
The actual template implementation of the Foo::compute method is defined above.This should generally be hidden from user code.
// 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));
Finally, the method of explicit instantiation in PCL is shown above.The macro PCL_INSTANTIATE traverses only a given list of types and creates an explicit instantiation for each type.From 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);
Where PCL_XYZ_POINT_TYPES is (from 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)
Basically, if you only want to explicitly instantiate Foo for pcl::PointXYZ, you don't need to use macros, as simple as the following:
// foo.cpp #include "pcl/point_types.h" #include "pcl/impl/instantiate.hpp" #include "foo.h" #include "impl/foo.hpp" template class Foo<pcl::PointXYZ>;
Be careful
For more information on explicit instantiation, see c++ Templates - The Complete Guide by David Vandervoorde and Nicolai M. Josuttis.
How to add a new PointT type
To add a new point type, you must first define it.For example:
struct MyPointType { float test; };
You then need to add a template header implementation for a specific class/algorithm in the PCL to the new point type MyPointType.For example, suppose you want to use pcl::PassThrough.All you have to do is:
#define PCL_NO_PRECOMPILE #include <pcl/filters/passthrough.h> #include <pcl/filters/impl/passthrough.hpp> // Remaining code fills in secondary
If your code is part of the library and others use it, it makes sense to try to use explicit instantiation for your MyPointType type and any classes you expose (PCLs from outside the PCL).
Be careful
Beginning with PCL-1.7, you need to define PCL_NO_PRECOMPILE before you can include any PCL header files to include the templating algorithm.
Example
The code snippet example below creates a new point type that contains XYZ data (populated with SSE), as well as a test routine.
#define PCL_NO_PRECOMPILE #include <pcl/pcl_macros.h> #include <pcl/point_types.h> #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> struct MyPointType { PCL_ADD_POINT4D; // preferred way of adding a XYZ+padding float test; PCL_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 (MyPointType, // here we assume a XYZ + "test" (as fields) (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); }