If you have used PCL in C++, you’ll notice that a point cloud is a templated type (pcl::PointCloud<PointT>). To handle this with our Python wrappers, we have defined:
- ecto::pcl::PointCloud - a data type that stores a pcl::PointCloud, hiding the PointT type. ecto::pcl::PointCloud will always have X, Y, and Z components and may also have things such as RGB, Alpha, etc.
- ecto::pcl::FeatureCloud - a data type that stores a pcl::PointCloud, hiding the PointT type. This is similar to ecto::pcl::PointCloud, however it is intended for clouds of features, such as Normals, VPFH, etc.
There are additional common data types defined:
- ecto::pcl::Indices
- ecto::pcl::Clusters
- ecto::pcl::ModelCoefficients
Each of the above data types is implemented with the help of boost variants. To ease the difficulty in using them, we have created a number of stubs for typical ecto_pcl cells:
- ecto::pcl::PclCell (pcl_cell.hpp) - for cells that take a single PointCloud input.
- ecto::pcl::PclCellDualInputs (pcl_cell_dual_inputs.hpp) - for cells with two PointCloud inputs.
- ecto::pcl::PclCellWithNormals (pcl_cell_with_normals.hpp) - for cells that need a PointCloud and Normals (a FeatureCloud).
Using each of these is similar. You include the header file and declare your cell. The only difference from a typical ecto cell is that the process() callback will be templated on the point type and have extra parameters corresponding to the extracted pcl::PointClouds<PointT>:
/*
* Example of how to create a cell with PclCell
*/
#include <ecto_pcl/ecto_pcl.hpp>
#include <ecto_pcl/pcl_cell.hpp>
// other pcl includes!
struct ExampleFilter
{
static void declare_params(ecto::tendrils& params)
{
// put declarations here as usual!
params.declare<int> ("a_param", "Description of params.", 0);
}
static void declare_io(const tendrils& params, tendrils& inputs, tendrils& outputs)
{
/*
* A single "ecto::pcl::PointCloud" input is already defined... any others should be here
* If you need 2 input PointClouds, use the ecto::pcl::PclCellDualInputs
*/
// Most cells will output a ecto::pcl::PointCloud
outputs.declare<ecto::pcl::PointCloud> ("output", "Cloud after my stufz has run.");
}
void configure(const tendrils& params, const tendrils& inputs, const tendrils& outputs)
{
// Store params in spores
my_param_ = params["a_param"];
// Same for inputs/outputs
output_ = outputs["output"];
}
template <typename Point>
int process(const tendrils& inputs, const tendrils& outputs,
boost::shared_ptr<const pcl::PointCloud<Point> >& input)
{
// cloud to store our output in
pcl::PointCloud<Point>::Ptr cloud(new pcl::PointCloud<Point>);
// do something with our params/clouds
*cloud = input;
// We have to use this variant to create an ecto::pcl::PointCloud
*output_ = ecto::pcl::xyz_cloud_variant_t(cloud);
return ecto::OK;
}
// Store params/inputs/outputs in spores
ecto::spore<int> my_param_;
ecto::spore<ecto::pcl::PointCloud> output_;
};
ECTO_CELL(ecto_pcl, ecto::pcl::PclCell<ExampleFilter>, "ExampleFilter", "Example of creating a custom filter.");
The key aspects here are:
- Our cell is declared in the ECTO_CELL as ecto::pcl::PclCell<ExampleFilter>.
- We declare parameters and IO as usual.
- ecto::pcl::PclCell already declares an input PointCloud called “input”.
- Our process callback is templated on point type.
- Our process callback has an extra parameter to a templated pcl::PointCloud (held in a shared pointer).
- To set the value of our output spore we have to use: ecto::pcl::xyz_cloud_variant_t (boost_shared pointer to our cloud);