00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include <ros/ros.h>
00035 #include <nodelet/nodelet.h>
00036 #include <image_transport/image_transport.h>
00037 #include <sensor_msgs/image_encodings.h>
00038 #include <image_transport/subscriber_filter.h>
00039 #include <message_filters/subscriber.h>
00040 #include <message_filters/synchronizer.h>
00041 #include <message_filters/sync_policies/exact_time.h>
00042 #include <image_geometry/pinhole_camera_model.h>
00043 #include <boost/thread.hpp>
00044 #include <depth_image_proc/depth_traits.h>
00045
00046 #include <sensor_msgs/point_cloud2_iterator.h>
00047
00048 namespace depth_image_proc {
00049
00050 using namespace message_filters::sync_policies;
00051 namespace enc = sensor_msgs::image_encodings;
00052 typedef ExactTime<sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::CameraInfo> SyncPolicy;
00053
00054 class PointCloudXyziRadialNodelet : public nodelet::Nodelet
00055 {
00056 ros::NodeHandlePtr intensity_nh_;
00057
00058
00059 boost::shared_ptr<image_transport::ImageTransport> intensity_it_, depth_it_;
00060 image_transport::SubscriberFilter sub_depth_, sub_intensity_;
00061 message_filters::Subscriber<sensor_msgs::CameraInfo> sub_info_;
00062
00063 int queue_size_;
00064
00065
00066 boost::mutex connect_mutex_;
00067 typedef sensor_msgs::PointCloud2 PointCloud;
00068 ros::Publisher pub_point_cloud_;
00069
00070
00071 typedef message_filters::Synchronizer<SyncPolicy> Synchronizer;
00072 boost::shared_ptr<Synchronizer> sync_;
00073
00074 std::vector<double> D_;
00075 boost::array<double, 9> K_;
00076
00077 int width_;
00078 int height_;
00079
00080 cv::Mat transform_;
00081
00082 virtual void onInit();
00083
00084 void connectCb();
00085
00086 void imageCb(const sensor_msgs::ImageConstPtr& depth_msg,
00087 const sensor_msgs::ImageConstPtr& intensity_msg_in,
00088 const sensor_msgs::CameraInfoConstPtr& info_msg);
00089
00090
00091 template<typename T>
00092 void convert_depth(const sensor_msgs::ImageConstPtr& depth_msg, PointCloud::Ptr& cloud_msg);
00093
00094 template<typename T>
00095 void convert_intensity(const sensor_msgs::ImageConstPtr &inten_msg, PointCloud::Ptr& cloud_msg);
00096
00097 cv::Mat initMatrix(cv::Mat cameraMatrix, cv::Mat distCoeffs, int width, int height, bool radial);
00098
00099 };
00100
00101 cv::Mat PointCloudXyziRadialNodelet::initMatrix(cv::Mat cameraMatrix, cv::Mat distCoeffs, int width, int height, bool radial)
00102 {
00103 int i,j;
00104 int totalsize = width*height;
00105 cv::Mat pixelVectors(1,totalsize,CV_32FC3);
00106 cv::Mat dst(1,totalsize,CV_32FC3);
00107
00108 cv::Mat sensorPoints(cv::Size(height,width), CV_32FC2);
00109 cv::Mat undistortedSensorPoints(1,totalsize, CV_32FC2);
00110
00111 std::vector<cv::Mat> ch;
00112 for(j = 0; j < height; j++)
00113 {
00114 for(i = 0; i < width; i++)
00115 {
00116 cv::Vec2f &p = sensorPoints.at<cv::Vec2f>(i,j);
00117 p[0] = i;
00118 p[1] = j;
00119 }
00120 }
00121
00122 sensorPoints = sensorPoints.reshape(2,1);
00123
00124 cv::undistortPoints(sensorPoints, undistortedSensorPoints, cameraMatrix, distCoeffs);
00125
00126 ch.push_back(undistortedSensorPoints);
00127 ch.push_back(cv::Mat::ones(1,totalsize,CV_32FC1));
00128 cv::merge(ch,pixelVectors);
00129
00130 if(radial)
00131 {
00132 for(i = 0; i < totalsize; i++)
00133 {
00134 normalize(pixelVectors.at<cv::Vec3f>(i),
00135 dst.at<cv::Vec3f>(i));
00136 }
00137 pixelVectors = dst;
00138 }
00139 return pixelVectors.reshape(3,width);
00140 }
00141
00142
00143 void PointCloudXyziRadialNodelet::onInit()
00144 {
00145 ros::NodeHandle& nh = getNodeHandle();
00146 ros::NodeHandle& private_nh = getPrivateNodeHandle();
00147
00148 intensity_nh_.reset( new ros::NodeHandle(nh, "intensity") );
00149 ros::NodeHandle depth_nh(nh, "depth");
00150 intensity_it_ .reset( new image_transport::ImageTransport(*intensity_nh_) );
00151 depth_it_.reset( new image_transport::ImageTransport(depth_nh) );
00152
00153
00154 private_nh.param("queue_size", queue_size_, 5);
00155
00156
00157 sync_.reset( new Synchronizer(SyncPolicy(queue_size_), sub_depth_, sub_intensity_, sub_info_) );
00158 sync_->registerCallback(boost::bind(&PointCloudXyziRadialNodelet::imageCb, this, _1, _2, _3));
00159
00160
00161 ros::SubscriberStatusCallback connect_cb =
00162 boost::bind(&PointCloudXyziRadialNodelet::connectCb, this);
00163
00164 boost::lock_guard<boost::mutex> lock(connect_mutex_);
00165 pub_point_cloud_ = nh.advertise<PointCloud>("points", 20, connect_cb, connect_cb);
00166 }
00167
00168
00169 void PointCloudXyziRadialNodelet::connectCb()
00170 {
00171 boost::lock_guard<boost::mutex> lock(connect_mutex_);
00172
00173 if (pub_point_cloud_.getNumSubscribers() == 0)
00174 {
00175 sub_depth_.unsubscribe();
00176 sub_intensity_.unsubscribe();
00177 sub_info_.unsubscribe();
00178 }
00179 else if (!sub_depth_.getSubscriber())
00180 {
00181 ros::NodeHandle& private_nh = getPrivateNodeHandle();
00182
00183 std::string depth_image_transport_param = "depth_image_transport";
00184
00185
00186 image_transport::TransportHints depth_hints("raw",ros::TransportHints(), private_nh, depth_image_transport_param);
00187 sub_depth_.subscribe(*depth_it_, "image_raw", 5, depth_hints);
00188
00189
00190 image_transport::TransportHints hints("raw", ros::TransportHints(), private_nh);
00191 sub_intensity_.subscribe(*intensity_it_, "image_raw", 5, hints);
00192 sub_info_.subscribe(*intensity_nh_, "camera_info", 5);
00193 }
00194 }
00195
00196 void PointCloudXyziRadialNodelet::imageCb(const sensor_msgs::ImageConstPtr& depth_msg,
00197 const sensor_msgs::ImageConstPtr& intensity_msg,
00198 const sensor_msgs::CameraInfoConstPtr& info_msg)
00199 {
00200 PointCloud::Ptr cloud_msg(new PointCloud);
00201 cloud_msg->header = depth_msg->header;
00202 cloud_msg->height = depth_msg->height;
00203 cloud_msg->width = depth_msg->width;
00204 cloud_msg->is_dense = false;
00205 cloud_msg->is_bigendian = false;
00206
00207 sensor_msgs::PointCloud2Modifier pcd_modifier(*cloud_msg);
00208 pcd_modifier.setPointCloud2Fields(4,
00209 "x", 1, sensor_msgs::PointField::FLOAT32,
00210 "y", 1, sensor_msgs::PointField::FLOAT32,
00211 "z", 1, sensor_msgs::PointField::FLOAT32,
00212 "intensity", 1, sensor_msgs::PointField::FLOAT32);
00213
00214
00215 if(info_msg->D != D_ || info_msg->K != K_ || width_ != info_msg->width ||
00216 height_ != info_msg->height)
00217 {
00218 D_ = info_msg->D;
00219 K_ = info_msg->K;
00220 width_ = info_msg->width;
00221 height_ = info_msg->height;
00222 transform_ = initMatrix(cv::Mat_<double>(3, 3, &K_[0]),cv::Mat(D_),width_,height_,true);
00223 }
00224
00225 if (depth_msg->encoding == enc::TYPE_16UC1)
00226 {
00227 convert_depth<uint16_t>(depth_msg, cloud_msg);
00228 }
00229 else if (depth_msg->encoding == enc::TYPE_32FC1)
00230 {
00231 convert_depth<float>(depth_msg, cloud_msg);
00232 }
00233 else
00234 {
00235 NODELET_ERROR_THROTTLE(5, "Depth image has unsupported encoding [%s]", depth_msg->encoding.c_str());
00236 return;
00237 }
00238
00239 if(intensity_msg->encoding == enc::TYPE_16UC1)
00240 {
00241 convert_intensity<uint16_t>(intensity_msg, cloud_msg);
00242
00243 }
00244 else if(intensity_msg->encoding == enc::MONO8)
00245 {
00246 convert_intensity<uint8_t>(intensity_msg, cloud_msg);
00247 }
00248 else
00249 {
00250 NODELET_ERROR_THROTTLE(5, "Intensity image has unsupported encoding [%s]", intensity_msg->encoding.c_str());
00251 return;
00252 }
00253
00254 pub_point_cloud_.publish (cloud_msg);
00255 }
00256
00257 template<typename T>
00258 void PointCloudXyziRadialNodelet::convert_depth(const sensor_msgs::ImageConstPtr& depth_msg,
00259 PointCloud::Ptr& cloud_msg)
00260 {
00261
00262 double unit_scaling = DepthTraits<T>::toMeters( T(1) );
00263 float bad_point = std::numeric_limits<float>::quiet_NaN();
00264
00265 sensor_msgs::PointCloud2Iterator<float> iter_x(*cloud_msg, "x");
00266 sensor_msgs::PointCloud2Iterator<float> iter_y(*cloud_msg, "y");
00267 sensor_msgs::PointCloud2Iterator<float> iter_z(*cloud_msg, "z");
00268 const T* depth_row = reinterpret_cast<const T*>(&depth_msg->data[0]);
00269
00270 int row_step = depth_msg->step / sizeof(T);
00271 for (int v = 0; v < (int)cloud_msg->height; ++v, depth_row += row_step)
00272 {
00273 for (int u = 0; u < (int)cloud_msg->width; ++u, ++iter_x, ++iter_y, ++iter_z)
00274 {
00275 T depth = depth_row[u];
00276
00277
00278 if (!DepthTraits<T>::valid(depth))
00279 {
00280 *iter_x = *iter_y = *iter_z = bad_point;
00281 continue;
00282 }
00283 const cv::Vec3f &cvPoint = transform_.at<cv::Vec3f>(u,v) * DepthTraits<T>::toMeters(depth);
00284
00285 *iter_x = cvPoint(0);
00286 *iter_y = cvPoint(1);
00287 *iter_z = cvPoint(2);
00288 }
00289 }
00290 }
00291
00292 template<typename T>
00293 void PointCloudXyziRadialNodelet::convert_intensity(const sensor_msgs::ImageConstPtr& intensity_msg,
00294 PointCloud::Ptr& cloud_msg)
00295 {
00296 sensor_msgs::PointCloud2Iterator<float> iter_i(*cloud_msg, "intensity");
00297 const T* inten_row = reinterpret_cast<const T*>(&intensity_msg->data[0]);
00298
00299 const int i_row_step = intensity_msg->step/sizeof(T);
00300 for (int v = 0; v < (int)cloud_msg->height; ++v, inten_row += i_row_step)
00301 {
00302 for (int u = 0; u < (int)cloud_msg->width; ++u, ++iter_i)
00303 {
00304 *iter_i = inten_row[u];
00305 }
00306 }
00307 }
00308
00309 }
00310
00311
00312 #include <pluginlib/class_list_macros.h>
00313 PLUGINLIB_EXPORT_CLASS(depth_image_proc::PointCloudXyziRadialNodelet,nodelet::Nodelet);