pcl colors源码剖析
文章目录
显示点云时,会根据点云某个字段进行配色,那么具体怎么实现的呢?
调用
调用时,可能会这么编写代码:
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/passthrough.h>
using namespace std;
using namespace pcl;
using namespace io;
int main() {
PointCloud<PointXYZI>::Ptr cloud(new PointCloud<PointXYZI>);
if (io::loadPCDFile("/home/randy/sesame.pcd", *cloud) == -1)
{
cerr << "can't read pcd files." << endl;
return -1;
}
shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
viewer->setWindowName("Point clouds are shown by intensity");
// 按照intensity字段进行渲染
pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointXYZI> fieldColor(cloud,"intensity");
viewer->addPointCloud<pcl::PointXYZI>(cloud,fieldColor,"sample cloud");
viewer->addText("Point clouds are shown by intensity", 50, 50,0,1,0, "randy_text");
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
主要代码是
pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointXYZI> fieldColor(cloud,"intensity");
核心思想是设置一个显示颜色的 handler,父类是PointCloudColorHandler
,其子类有
剖析一下源码
PointCloudColorHandlerGenericField
//
/** \brief Generic field handler class for colors. Uses an user given field to extract
* 1D data and display the color at each point using a min-max lookup table.
* \author Radu B. Rusu
* \ingroup visualization
*/
template <typename PointT>
class PointCloudColorHandlerGenericField : public PointCloudColorHandler<PointT>
{
using PointCloud = typename PointCloudColorHandler<PointT>::PointCloud;
using PointCloudPtr = typename PointCloud::Ptr;
using PointCloudConstPtr = typename PointCloud::ConstPtr;
public:
using Ptr = shared_ptr<PointCloudColorHandlerGenericField<PointT> >;
using ConstPtr = shared_ptr<const PointCloudColorHandlerGenericField<PointT> >;
/** \brief Constructor. */
PointCloudColorHandlerGenericField (const std::string &field_name)
: field_name_ (field_name)
{
capable_ = false;
}
/** \brief Constructor. */
PointCloudColorHandlerGenericField (const PointCloudConstPtr &cloud,
const std::string &field_name)
: PointCloudColorHandler<PointT> (cloud)
, field_name_ (field_name)
{
setInputCloud (cloud);
}
/** \brief Destructor. */
virtual ~PointCloudColorHandlerGenericField () {}
/** \brief Get the name of the field used. */
virtual std::string getFieldName () const { return (field_name_); }
vtkSmartPointer<vtkDataArray>
getColor () const override;
/** \brief Set the input cloud to be used.
* \param[in] cloud the input cloud to be used by the handler
*/
virtual void
setInputCloud (const PointCloudConstPtr &cloud);
protected:
/** \brief Class getName method. */
virtual std::string
getName () const { return ("PointCloudColorHandlerGenericField"); }
private:
using PointCloudColorHandler<PointT>::cloud_;
using PointCloudColorHandler<PointT>::capable_;
using PointCloudColorHandler<PointT>::field_idx_;
using PointCloudColorHandler<PointT>::fields_;
/** \brief Name of the field used to create the color handler. */
std::string field_name_;
};
可以看出,函数简介写的很清楚,根据给定字段对每个点使用 最小-最大查询表进行每个点颜色赋值
Generic field handler class for colors. Uses an user given field to extract 1D data and display the color at each point using a min-max lookup table.
查看getColor()
函数
template <typename PointT> vtkSmartPointer<vtkDataArray>
PointCloudColorHandlerGenericField<PointT>::getColor () const
{
if (!capable_ || !cloud_)
return nullptr;
auto scalars = vtkSmartPointer<vtkFloatArray>::New ();
scalars->SetNumberOfComponents (1);
vtkIdType nr_points = cloud_->size ();
scalars->SetNumberOfTuples (nr_points);
using FieldList = typename pcl::traits::fieldList<PointT>::type;
float* colors = new float[nr_points];
float field_data;
int j = 0;
// If XYZ present, check if the points are invalid
int x_idx = -1;
for (std::size_t d = 0; d < fields_.size (); ++d)
if (fields_[d].name == "x")
x_idx = static_cast<int> (d);
if (x_idx != -1)
{
// Color every point
for (vtkIdType cp = 0; cp < nr_points; ++cp)
{
// Copy the value at the specified field
if (!std::isfinite ((*cloud_)[cp].x) || !std::isfinite ((*cloud_)[cp].y) || !std::isfinite ((*cloud_)[cp].z))
continue;
const std::uint8_t* pt_data = reinterpret_cast<const std::uint8_t*> (&(*cloud_)[cp]);
memcpy (&field_data, pt_data + fields_[field_idx_].offset, pcl::getFieldSize (fields_[field_idx_].datatype));
colors[j] = field_data;
j++;
}
}
else
{
// Color every point
for (vtkIdType cp = 0; cp < nr_points; ++cp)
{
const std::uint8_t* pt_data = reinterpret_cast<const std::uint8_t*> (&(*cloud_)[cp]);
memcpy (&field_data, pt_data + fields_[field_idx_].offset, pcl::getFieldSize (fields_[field_idx_].datatype));
if (!std::isfinite (field_data))
continue;
colors[j] = field_data;
j++;
}
}
scalars->SetArray (colors, j, 0, vtkFloatArray::VTK_DATA_ARRAY_DELETE);
return scalars;
}
核心是colors[j] = field_data;
将filed的数值给 colors了
然后加入到点云中viewer->addPointCloud
->再显示到屏幕fromHandlersToScreen
template <typename PointT> bool
pcl::visualization::PCLVisualizer::fromHandlersToScreen (
const PointCloudGeometryHandler<PointT> &geometry_handler,
const PointCloudColorHandler<PointT> &color_handler,
const std::string &id,
int viewport,
const Eigen::Vector4f& sensor_origin,
const Eigen::Quaternion<float>& sensor_orientation)
{
if (!geometry_handler.isCapable ())
{
PCL_WARN ("[fromHandlersToScreen] PointCloud <%s> requested with an invalid geometry handler (%s)!\n", id.c_str (), geometry_handler.getName ().c_str ());
return (false);
}
if (!color_handler.isCapable ())
{
PCL_WARN ("[fromHandlersToScreen] PointCloud <%s> requested with an invalid color handler (%s)!\n", id.c_str (), color_handler.getName ().c_str ());
return (false);
}
vtkSmartPointer<vtkPolyData> polydata;
vtkSmartPointer<vtkIdTypeArray> initcells;
// Convert the PointCloud to VTK PolyData
convertPointCloudToVTKPolyData<PointT> (geometry_handler, polydata, initcells);
// Get the colors from the handler
bool has_colors = false;
double minmax[2];
if (auto scalars = color_handler.getColor ())
{
polydata->GetPointData ()->SetScalars (scalars);
scalars->GetRange (minmax);
has_colors = true;
}
// Create an Actor
vtkSmartPointer<vtkLODActor> actor;
createActorFromVTKDataSet (polydata, actor);
if (has_colors)
actor->GetMapper ()->SetScalarRange (minmax);
// Add it to all renderers
addActorToRenderer (actor, viewport);
// Save the pointer/ID pair to the global actor map
CloudActor& cloud_actor = (*cloud_actor_map_)[id];
cloud_actor.actor = actor;
cloud_actor.cells = initcells;
// Save the viewpoint transformation matrix to the global actor map
vtkSmartPointer<vtkMatrix4x4> transformation = vtkSmartPointer<vtkMatrix4x4>::New();
convertToVtkMatrix (sensor_origin, sensor_orientation, transformation);
cloud_actor.viewpoint_transformation_ = transformation;
cloud_actor.actor->SetUserMatrix (transformation);
cloud_actor.actor->Modified ();
return (true);
}
获取到颜色范围minmax
,然后设置显示区间SetScalerRange
// Get the colors from the handler
bool has_colors = false;
double minmax[2];
if (auto scalars = color_handler.getColor ())
{
polydata->GetPointData ()->SetScalars (scalars);
scalars->GetRange (minmax);
has_colors = true;
}
// Create an Actor
vtkSmartPointer<vtkLODActor> actor;
createActorFromVTKDataSet (polydata, actor);
if (has_colors)
actor->GetMapper ()->SetScalarRange (minmax);
PointCloudColorHandlerLabelField
template <typename PointT> vtkSmartPointer<vtkDataArray>
PointCloudColorHandlerLabelField<PointT>::getColor () const
{
if (!capable_ || !cloud_)
return nullptr;
auto scalars = vtkSmartPointer<vtkUnsignedCharArray>::New ();
scalars->SetNumberOfComponents (3);
vtkIdType nr_points = cloud_->size ();
scalars->SetNumberOfTuples (nr_points);
unsigned char* colors = scalars->GetPointer (0);
std::map<std::uint32_t, pcl::RGB> colormap;
if (!static_mapping_)
{
std::set<std::uint32_t> labels;
// First pass: find unique labels
for (vtkIdType i = 0; i < nr_points; ++i)
labels.insert ((*cloud_)[i].label);
// Assign Glasbey colors in ascending order of labels
std::size_t color = 0;
for (std::set<std::uint32_t>::iterator iter = labels.begin (); iter != labels.end (); ++iter, ++color)
colormap[*iter] = GlasbeyLUT::at (color % GlasbeyLUT::size ());
}
int j = 0;
for (vtkIdType cp = 0; cp < nr_points; ++cp)
{
if (pcl::isFinite ((*cloud_)[cp]))
{
const pcl::RGB& color = static_mapping_ ? GlasbeyLUT::at ((*cloud_)[cp].label % GlasbeyLUT::size ()) : colormap[(*cloud_)[cp].label];
colors[j ] = color.r;
colors[j + 1] = color.g;
colors[j + 2] = color.b;
j += 3;
}
}
return scalars;
}
这段代码是根据label值查询 GlasbeyLUT 表,而GlasbeyLUT 在common/colors.cpp
中
/// Number of colors in Glasbey lookup table
static constexpr std::size_t GLASBEY_LUT_SIZE = GLASBEY_LUT.size() / 3;
/// Number of colors in Viridis lookup table
static constexpr std::size_t VIRIDIS_LUT_SIZE = VIRIDIS_LUT.size() / 3;
static constexpr std::array<const std::array<unsigned char, 256 * 3>*, 2> LUTS = { &GLASBEY_LUT, &VIRIDIS_LUT };
static constexpr std::array<std::size_t, 2> LUT_SIZES = { GLASBEY_LUT_SIZE, VIRIDIS_LUT_SIZE };
template<typename pcl::ColorLUTName T> pcl::RGB
pcl::ColorLUT<T>::at (std::size_t color_id)
{
assert (color_id < LUT_SIZES[T]);
pcl::RGB color;
color.r = (*LUTS[T])[color_id * 3 + 0];
color.g = (*LUTS[T])[color_id * 3 + 1];
color.b = (*LUTS[T])[color_id * 3 + 2];
return color;
}
其中 GLASBEY_LUT
与VIRIDIS_LUT
为 内置的 256 * 3 数组,每行为 RGB颜色值。
GLASBEY颜色值可参考:https://github.com/taketwo/glasbey
PointCloudColorHandlerRGBAField
template <typename PointT> vtkSmartPointer<vtkDataArray>
PointCloudColorHandlerRGBAField<PointT>::getColor () const
{
if (!capable_ || !cloud_)
return nullptr;
auto scalars = vtkSmartPointer<vtkUnsignedCharArray>::New ();
scalars->SetNumberOfComponents (4);
vtkIdType nr_points = cloud_->size ();
scalars->SetNumberOfTuples (nr_points);
unsigned char* colors = scalars->GetPointer (0);
// If XYZ present, check if the points are invalid
int x_idx = -1;
for (std::size_t d = 0; d < fields_.size (); ++d)
if (fields_[d].name == "x")
x_idx = static_cast<int> (d);
if (x_idx != -1)
{
int j = 0;
// Color every point
for (vtkIdType cp = 0; cp < nr_points; ++cp)
{
// Copy the value at the specified field
if (!std::isfinite ((*cloud_)[cp].x) ||
!std::isfinite ((*cloud_)[cp].y) ||
!std::isfinite ((*cloud_)[cp].z))
continue;
colors[j ] = (*cloud_)[cp].r;
colors[j + 1] = (*cloud_)[cp].g;
colors[j + 2] = (*cloud_)[cp].b;
colors[j + 3] = (*cloud_)[cp].a;
j += 4;
}
}
else
{
// Color every point
for (vtkIdType cp = 0; cp < nr_points; ++cp)
{
int idx = static_cast<int> (cp) * 4;
colors[idx ] = (*cloud_)[cp].r;
colors[idx + 1] = (*cloud_)[cp].g;
colors[idx + 2] = (*cloud_)[cp].b;
colors[idx + 3] = (*cloud_)[cp].a;
}
}
return scalars;
}
采用 PointCloudColorHandlerRGBAField
字段的,就是默认点云字段包含rgb的,直接读取每个点的rgb即可。
欢迎关注公众号【三戒纪元】