point_cloud_xyzrgb_radial.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2008, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 #include <ros/ros.h>
35 #include <nodelet/nodelet.h>
36 #include <cv_bridge/cv_bridge.h>
45 #include <opencv2/calib3d/calib3d.hpp>
46 #include <boost/thread.hpp>
48 
50 
51 namespace depth_image_proc {
52 
53  using namespace message_filters::sync_policies;
57 
58  class PointCloudXyzRgbRadialNodelet : public nodelet::Nodelet
59  {
60  ros::NodeHandlePtr rgb_nh_;
61 
62  // Subscriptions
64  image_transport::SubscriberFilter sub_depth_, sub_rgb_;
66 
67  int queue_size_;
68 
69  // Publications
70  boost::mutex connect_mutex_;
71  typedef sensor_msgs::PointCloud2 PointCloud;
72  ros::Publisher pub_point_cloud_;
73 
74 
75  typedef message_filters::Synchronizer<SyncPolicy> Synchronizer;
76  typedef message_filters::Synchronizer<ExactSyncPolicy> ExactSynchronizer;
79 
80  std::vector<double> D_;
81  boost::array<double, 9> K_;
82 
83  int width_;
84  int height_;
85 
86  cv::Mat transform_;
88 
89  virtual void onInit();
90 
91  void connectCb();
92 
93  void imageCb(const sensor_msgs::ImageConstPtr& depth_msg,
94  const sensor_msgs::ImageConstPtr& rgb_msg_in,
95  const sensor_msgs::CameraInfoConstPtr& info_msg);
96 
97  // Handles float or uint16 depths
98  template<typename T>
99  void convert_depth(const sensor_msgs::ImageConstPtr& depth_msg, PointCloud::Ptr& cloud_msg);
100 
101  void convert_rgb(const sensor_msgs::ImageConstPtr &rgb_msg, PointCloud::Ptr& cloud_msg,
102  int red_offset, int green_offset, int blue_offset, int color_step);
103 
104  cv::Mat initMatrix(cv::Mat cameraMatrix, cv::Mat distCoeffs, int width, int height, bool radial);
105 
106  };
107 
108  cv::Mat PointCloudXyzRgbRadialNodelet::initMatrix(cv::Mat cameraMatrix, cv::Mat distCoeffs, int width, int height, bool radial)
109  {
110  int i,j;
111  int totalsize = width*height;
112  cv::Mat pixelVectors(1,totalsize,CV_32FC3);
113  cv::Mat dst(1,totalsize,CV_32FC3);
114 
115  cv::Mat sensorPoints(cv::Size(height,width), CV_32FC2);
116  cv::Mat undistortedSensorPoints(1,totalsize, CV_32FC2);
117 
118  std::vector<cv::Mat> ch;
119  for(j = 0; j < height; j++)
120  {
121  for(i = 0; i < width; i++)
122  {
123  cv::Vec2f &p = sensorPoints.at<cv::Vec2f>(i,j);
124  p[0] = i;
125  p[1] = j;
126  }
127  }
128 
129  sensorPoints = sensorPoints.reshape(2,1);
130 
131  cv::undistortPoints(sensorPoints, undistortedSensorPoints, cameraMatrix, distCoeffs);
132 
133  ch.push_back(undistortedSensorPoints);
134  ch.push_back(cv::Mat::ones(1,totalsize,CV_32FC1));
135  cv::merge(ch,pixelVectors);
136 
137  if(radial)
138  {
139  for(i = 0; i < totalsize; i++)
140  {
141  normalize(pixelVectors.at<cv::Vec3f>(i),
142  dst.at<cv::Vec3f>(i));
143  }
144  pixelVectors = dst;
145  }
146  return pixelVectors.reshape(3,width);
147  }
148 
149 
151  {
152  NODELET_INFO("INIT XYZRGB RADIAL");
153  ros::NodeHandle& nh = getNodeHandle();
154  ros::NodeHandle& private_nh = getPrivateNodeHandle();
155 
156  rgb_nh_.reset( new ros::NodeHandle(nh, "rgb") );
157  ros::NodeHandle depth_nh(nh, "depth_registered");
158  rgb_it_ .reset( new image_transport::ImageTransport(*rgb_nh_) );
159  depth_it_.reset( new image_transport::ImageTransport(depth_nh) );
160 
161  // Read parameters
162  private_nh.param("queue_size", queue_size_, 5);
163  bool use_exact_sync;
164  private_nh.param("exact_sync", use_exact_sync, false);
165 
166  // Synchronize inputs. Topic subscriptions happen on demand in the connection callback.
167  if(use_exact_sync) {
168  exact_sync_.reset( new ExactSynchronizer(ExactSyncPolicy(queue_size_), sub_depth_, sub_rgb_, sub_info_) );
169  exact_sync_->registerCallback(
170  boost::bind(&PointCloudXyzRgbRadialNodelet::imageCb, this, boost::placeholders::_1, boost::placeholders::_2, boost::placeholders::_3));
171  } else {
172  sync_.reset( new Synchronizer(SyncPolicy(queue_size_), sub_depth_, sub_rgb_, sub_info_) );
173  sync_->registerCallback(
174  boost::bind(&PointCloudXyzRgbRadialNodelet::imageCb, this, boost::placeholders::_1, boost::placeholders::_2, boost::placeholders::_3));
175  }
176  // Monitor whether anyone is subscribed to the output
177  ros::SubscriberStatusCallback connect_cb =
178  boost::bind(&PointCloudXyzRgbRadialNodelet::connectCb, this);
179  // Make sure we don't enter connectCb() between advertising and assigning to pub_point_cloud_
180  boost::lock_guard<boost::mutex> lock(connect_mutex_);
181  pub_point_cloud_ = depth_nh.advertise<PointCloud>("points", 20, connect_cb, connect_cb);
182  }
183 
184  // Handles (un)subscribing when clients (un)subscribe
186  {
187  boost::lock_guard<boost::mutex> lock(connect_mutex_);
188 
189  if (pub_point_cloud_.getNumSubscribers() == 0)
190  {
191  sub_depth_.unsubscribe();
192  sub_rgb_.unsubscribe();
193  sub_info_.unsubscribe();
194  }
195  else if (!sub_depth_.getSubscriber())
196  {
197  ros::NodeHandle& private_nh = getPrivateNodeHandle();
198  // parameter for depth_image_transport hint
199  std::string depth_image_transport_param = "depth_image_transport";
200 
201  // depth image can use different transport.(e.g. compressedDepth)
202  image_transport::TransportHints depth_hints("raw",ros::TransportHints(), private_nh, depth_image_transport_param);
203  sub_depth_.subscribe(*depth_it_, "image_rect", 1, depth_hints);
204 
205  // intensity uses normal ros transport hints.
206  image_transport::TransportHints hints("raw", ros::TransportHints(), private_nh);
207  sub_rgb_.subscribe(*rgb_it_, "image_rect_color", 1, hints);
208  sub_info_.subscribe(*rgb_nh_, "camera_info", 1);
209 
210  NODELET_INFO(" subscribed to: %s", sub_rgb_.getTopic().c_str());
211  NODELET_INFO(" subscribed to: %s", sub_depth_.getTopic().c_str());
212  NODELET_INFO(" subscribed to: %s", sub_info_.getTopic().c_str());
213  }
214  }
215 
216  void PointCloudXyzRgbRadialNodelet::imageCb(const sensor_msgs::ImageConstPtr& depth_msg,
217  const sensor_msgs::ImageConstPtr& rgb_msg_in,
218  const sensor_msgs::CameraInfoConstPtr& info_msg)
219  {
220  PointCloud::Ptr cloud_msg(new PointCloud);
221  cloud_msg->header = depth_msg->header;
222  cloud_msg->height = depth_msg->height;
223  cloud_msg->width = depth_msg->width;
224  cloud_msg->is_dense = false;
225  cloud_msg->is_bigendian = false;
226 
227  sensor_msgs::PointCloud2Modifier pcd_modifier(*cloud_msg);
228  pcd_modifier.setPointCloud2FieldsByString(2, "xyz", "rgb");
229 // pcd_modifier.setPointCloud2Fields(6,
230 // "x", 1, sensor_msgs::PointField::FLOAT32,
231 // "y", 1, sensor_msgs::PointField::FLOAT32,
232 // "z", 1, sensor_msgs::PointField::FLOAT32,
233 // "r", 1, sensor_msgs::PointField::UINT8,
234 // "g", 1, sensor_msgs::PointField::UINT8,
235 // "b", 1, sensor_msgs::PointField::UINT8);
236  // Check for bad inputs
237  if (depth_msg->header.frame_id != rgb_msg_in->header.frame_id)
238  {
239  NODELET_ERROR_THROTTLE(5, "Depth image frame id [%s] doesn't match RGB image frame id [%s]",
240  depth_msg->header.frame_id.c_str(), rgb_msg_in->header.frame_id.c_str());
241  return;
242  }
243 
244  // Update camera model
245  model_.fromCameraInfo(info_msg);
246 
247  // Check if the input image has to be resized
248  sensor_msgs::ImageConstPtr rgb_msg = rgb_msg_in;
249  if (depth_msg->width != rgb_msg->width || depth_msg->height != rgb_msg->height)
250  {
251  sensor_msgs::CameraInfo info_msg_tmp = *info_msg;
252  info_msg_tmp.width = depth_msg->width;
253  info_msg_tmp.height = depth_msg->height;
254  float ratio = float(depth_msg->width)/float(rgb_msg->width);
255  info_msg_tmp.K[0] *= ratio;
256  info_msg_tmp.K[2] *= ratio;
257  info_msg_tmp.K[4] *= ratio;
258  info_msg_tmp.K[5] *= ratio;
259  info_msg_tmp.P[0] *= ratio;
260  info_msg_tmp.P[2] *= ratio;
261  info_msg_tmp.P[5] *= ratio;
262  info_msg_tmp.P[6] *= ratio;
263  model_.fromCameraInfo(info_msg_tmp);
264 
265 
267  try
268  {
269  cv_ptr = cv_bridge::toCvShare(rgb_msg, rgb_msg->encoding);
270  }
271  catch (cv_bridge::Exception& e)
272  {
273  ROS_ERROR("cv_bridge exception: %s", e.what());
274  return;
275  }
276  cv_bridge::CvImage cv_rsz;
277  cv_rsz.header = cv_ptr->header;
278  cv_rsz.encoding = cv_ptr->encoding;
279  cv::resize(cv_ptr->image.rowRange(0,depth_msg->height/ratio), cv_rsz.image, cv::Size(depth_msg->width, depth_msg->height));
280  if ((rgb_msg->encoding == enc::RGB8) || (rgb_msg->encoding == enc::BGR8) || (rgb_msg->encoding == enc::MONO8))
281  rgb_msg = cv_rsz.toImageMsg();
282  else
283  rgb_msg = cv_bridge::toCvCopy(cv_rsz.toImageMsg(), enc::RGB8)->toImageMsg();
284 
285  //NODELET_ERROR_THROTTLE(5, "Depth resolution (%ux%u) does not match RGB resolution (%ux%u)",
286  // depth_msg->width, depth_msg->height, rgb_msg->width, rgb_msg->height);
287  //return;
288  } else {
289  rgb_msg = rgb_msg;
290  }
291 
292 
293  if(info_msg->D != D_ || info_msg->K != K_ || width_ != info_msg->width ||
294  height_ != info_msg->height)
295  {
296  D_ = info_msg->D;
297  K_ = info_msg->K;
298  width_ = info_msg->width;
299  height_ = info_msg->height;
300  transform_ = initMatrix(cv::Mat_<double>(3, 3, &K_[0]),cv::Mat(D_),width_,height_,true);
301  }
302 
303  if (depth_msg->encoding == enc::TYPE_16UC1)
304  {
305  convert_depth<uint16_t>(depth_msg, cloud_msg);
306  }
307  else if (depth_msg->encoding == enc::TYPE_32FC1)
308  {
309  convert_depth<float>(depth_msg, cloud_msg);
310  }
311  else
312  {
313  NODELET_ERROR_THROTTLE(5, "Depth image has unsupported encoding [%s]", depth_msg->encoding.c_str());
314  return;
315  }
316 
317  int red_offset, green_offset, blue_offset, color_step;
318  if(rgb_msg->encoding == enc::RGB8)
319  {
320  red_offset = 0;
321  green_offset = 1;
322  blue_offset = 2;
323  color_step = 3;
324  convert_rgb(rgb_msg, cloud_msg, red_offset, green_offset, blue_offset, color_step);
325 
326  }
327  if(rgb_msg->encoding == enc::RGBA8)
328  {
329  red_offset = 0;
330  green_offset = 1;
331  blue_offset = 2;
332  color_step = 4;
333  convert_rgb(rgb_msg, cloud_msg, red_offset, green_offset, blue_offset, color_step);
334  }
335  else if(rgb_msg->encoding == enc::BGR8)
336  {
337  red_offset = 2;
338  green_offset = 1;
339  blue_offset = 0;
340  color_step = 3;
341  convert_rgb(rgb_msg, cloud_msg, red_offset, green_offset, blue_offset, color_step);
342  }
343  else if(rgb_msg->encoding == enc::BGRA8)
344  {
345  red_offset = 2;
346  green_offset = 1;
347  blue_offset = 0;
348  color_step = 4;
349  convert_rgb(rgb_msg, cloud_msg, red_offset, green_offset, blue_offset, color_step);
350  }
351  else if(rgb_msg->encoding == enc::MONO8)
352  {
353  red_offset = 0;
354  green_offset = 0;
355  blue_offset = 0;
356  color_step = 1;
357  convert_rgb(rgb_msg, cloud_msg, red_offset, green_offset, blue_offset, color_step);
358  }
359  else
360  {
361  NODELET_ERROR_THROTTLE(5, "RGB image has unsupported encoding [%s]", rgb_msg->encoding.c_str());
362  return;
363  }
364 
365  pub_point_cloud_.publish (cloud_msg);
366  }
367 
368  template<typename T>
369  void PointCloudXyzRgbRadialNodelet::convert_depth(const sensor_msgs::ImageConstPtr& depth_msg,
370  PointCloud::Ptr& cloud_msg)
371  {
372  // Combine unit conversion (if necessary) with scaling by focal length for computing (X,Y)
373  double unit_scaling = DepthTraits<T>::toMeters( T(1) );
374  float bad_point = std::numeric_limits<float>::quiet_NaN();
375 
376  sensor_msgs::PointCloud2Iterator<float> iter_x(*cloud_msg, "x");
377  sensor_msgs::PointCloud2Iterator<float> iter_y(*cloud_msg, "y");
378  sensor_msgs::PointCloud2Iterator<float> iter_z(*cloud_msg, "z");
379  const T* depth_row = reinterpret_cast<const T*>(&depth_msg->data[0]);
380 
381  int row_step = depth_msg->step / sizeof(T);
382  for (int v = 0; v < (int)cloud_msg->height; ++v, depth_row += row_step)
383  {
384  for (int u = 0; u < (int)cloud_msg->width; ++u, ++iter_x, ++iter_y, ++iter_z)
385  {
386  T depth = depth_row[u];
387 
388  // Missing points denoted by NaNs
389  if (!DepthTraits<T>::valid(depth))
390  {
391  *iter_x = *iter_y = *iter_z = bad_point;
392  continue;
393  }
394  const cv::Vec3f &cvPoint = transform_.at<cv::Vec3f>(u,v) * DepthTraits<T>::toMeters(depth);
395  // Fill in XYZ
396  *iter_x = cvPoint(0);
397  *iter_y = cvPoint(1);
398  *iter_z = cvPoint(2);
399  }
400  }
401  }
402 
403  void PointCloudXyzRgbRadialNodelet::convert_rgb(const sensor_msgs::ImageConstPtr& rgb_msg,
404  PointCloud::Ptr& cloud_msg,
405  int red_offset, int green_offset, int blue_offset, int color_step)
406  {
407  sensor_msgs::PointCloud2Iterator<uint8_t> iter_r(*cloud_msg, "r");
408  sensor_msgs::PointCloud2Iterator<uint8_t> iter_g(*cloud_msg, "g");
409  sensor_msgs::PointCloud2Iterator<uint8_t> iter_b(*cloud_msg, "b");
410 
411  const uint8_t* rgb = &rgb_msg->data[0];
412  int rgb_skip = rgb_msg->step - rgb_msg->width * color_step;
413 
414  for (int v = 0; v < (int)cloud_msg->height; ++v, rgb += rgb_skip)
415  {
416  for (int u = 0; u < (int)cloud_msg->width; ++u, rgb += color_step, ++iter_r, ++iter_g, ++iter_b)
417  {
418  *iter_r = rgb[red_offset];
419  *iter_g = rgb[green_offset];
420  *iter_b = rgb[blue_offset];
421  }
422  }
423  }
424 
425 } // namespace depth_image_proc
426 
427 // Register as nodelet
image_transport::SubscriberFilter
sensor_msgs::image_encodings
point_cloud2_iterator.h
depth_traits.h
ros::Publisher
image_encodings.h
image_transport::ImageTransport
message_filters::Synchronizer
boost::shared_ptr< NodeHandle >
cv_bridge::CvImage::toImageMsg
sensor_msgs::ImagePtr toImageMsg() const
pinhole_camera_model.h
NODELET_ERROR_THROTTLE
#define NODELET_ERROR_THROTTLE(rate,...)
ros.h
ros::TransportHints
cv_bridge::Exception
ros::SubscriberStatusCallback
boost::function< void(const SingleSubscriberPublisher &)> SubscriberStatusCallback
depth_image_proc::PointCloudXyzRgbRadialNodelet::imageCb
void imageCb(const sensor_msgs::ImageConstPtr &depth_msg, const sensor_msgs::ImageConstPtr &rgb_msg_in, const sensor_msgs::CameraInfoConstPtr &info_msg)
Definition: point_cloud_xyzrgb_radial.cpp:248
message_filters::Subscriber< sensor_msgs::CameraInfo >
depth_image_proc::PointCloudXyzRgbRadialNodelet::connectCb
void connectCb()
Definition: point_cloud_xyzrgb_radial.cpp:217
depth_image_proc::PointCloudXyzRgbRadialNodelet
Definition: point_cloud_xyzrgb_radial.cpp:90
message_filters::sync_policies::ApproximateTime
sensor_msgs::PointCloud2Iterator
subscriber_filter.h
cv_bridge::toCvCopy
CvImagePtr toCvCopy(const sensor_msgs::CompressedImage &source, const std::string &encoding=std::string())
cv_bridge::CvImage::header
std_msgs::Header header
subscriber.h
depth_image_proc::ExactSyncPolicy
ExactTime< sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::CameraInfo > ExactSyncPolicy
Definition: point_cloud_xyzrgb_radial.cpp:88
depth_image_proc::SyncPolicy
ExactTime< sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::CameraInfo > SyncPolicy
Definition: point_cloud_xyzi_radial.cpp:85
depth_image_proc
Definition: depth_conversions.h:44
image_transport.h
depth_image_proc::PointCloudXyzRgbRadialNodelet::initMatrix
cv::Mat initMatrix(cv::Mat cameraMatrix, cv::Mat distCoeffs, int width, int height, bool radial)
Definition: point_cloud_xyzrgb_radial.cpp:140
exact_time.h
NODELET_INFO
#define NODELET_INFO(...)
depth_image_proc::PointCloud
sensor_msgs::PointCloud2 PointCloud
Definition: depth_conversions.h:78
message_filters::sync_policies
nodelet::Nodelet
cv_bridge::CvImage::encoding
std::string encoding
image_geometry::PinholeCameraModel
nodelet.h
sensor_msgs::PointCloud2Modifier
cv_bridge.h
ROS_ERROR
#define ROS_ERROR(...)
cv_bridge::CvImage
class_list_macros.hpp
ros::NodeHandle::param
T param(const std::string &param_name, const T &default_val) const
synchronizer.h
approximate_time.h
image_transport::TransportHints
cv_bridge::toCvShare
CvImageConstPtr toCvShare(const sensor_msgs::Image &source, const boost::shared_ptr< void const > &tracked_object, const std::string &encoding=std::string())
depth_image_proc::initMatrix
cv::Mat initMatrix(cv::Mat cameraMatrix, cv::Mat distCoeffs, int width, int height, bool radial)
Definition: point_cloud_xyz_radial.cpp:114
message_filters::sync_policies::ExactTime
PLUGINLIB_EXPORT_CLASS
PLUGINLIB_EXPORT_CLASS(depth_image_proc::PointCloudXyzRgbRadialNodelet, nodelet::Nodelet)
depth_image_proc::PointCloudXyzRgbRadialNodelet::convert_rgb
void convert_rgb(const sensor_msgs::ImageConstPtr &rgb_msg, PointCloud::Ptr &cloud_msg, int red_offset, int green_offset, int blue_offset, int color_step)
Definition: point_cloud_xyzrgb_radial.cpp:435
cv_bridge::CvImage::image
cv::Mat image
depth_image_proc::PointCloudXyzRgbRadialNodelet::PointCloud
sensor_msgs::PointCloud2 PointCloud
Definition: point_cloud_xyzrgb_radial.cpp:103
depth_image_proc::PointCloudXyzRgbRadialNodelet::onInit
virtual void onInit()
Definition: point_cloud_xyzrgb_radial.cpp:182
sensor_msgs::PointCloud2Modifier::setPointCloud2FieldsByString
void setPointCloud2FieldsByString(int n_fields,...)
ros::NodeHandle
depth_image_proc::PointCloudXyzRgbRadialNodelet::convert_depth
void convert_depth(const sensor_msgs::ImageConstPtr &depth_msg, PointCloud::Ptr &cloud_msg)
Definition: point_cloud_xyzrgb_radial.cpp:401


depth_image_proc
Author(s): Patrick Mihelich
autogenerated on Wed Jan 24 2024 03:57:15