pcl colors源码剖析

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_LUTVIRIDIS_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即可。


欢迎关注公众号【三戒纪元】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值