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 <boost/version.hpp>
00035 #if ((BOOST_VERSION / 100) % 1000) >= 53
00036 #include <boost/thread/lock_guard.hpp>
00037 #endif
00038
00039 #include <ros/ros.h>
00040 #include <nodelet/nodelet.h>
00041 #include <image_transport/image_transport.h>
00042 #include <image_transport/subscriber_filter.h>
00043 #include <message_filters/subscriber.h>
00044 #include <message_filters/synchronizer.h>
00045 #include <message_filters/sync_policies/approximate_time.h>
00046 #include <sensor_msgs/image_encodings.h>
00047 #include <sensor_msgs/point_cloud2_iterator.h>
00048 #include <sensor_msgs/PointCloud2.h>
00049 #include <image_geometry/pinhole_camera_model.h>
00050 #include <depth_image_proc/depth_traits.h>
00051 #include <cv_bridge/cv_bridge.h>
00052 #include <opencv2/imgproc/imgproc.hpp>
00053
00054 namespace depth_image_proc {
00055
00056 using namespace message_filters::sync_policies;
00057 namespace enc = sensor_msgs::image_encodings;
00058
00059 class PointCloudXyziNodelet : public nodelet::Nodelet
00060 {
00061 ros::NodeHandlePtr intensity_nh_;
00062 boost::shared_ptr<image_transport::ImageTransport> intensity_it_, depth_it_;
00063
00064
00065 image_transport::SubscriberFilter sub_depth_, sub_intensity_;
00066 message_filters::Subscriber<sensor_msgs::CameraInfo> sub_info_;
00067 typedef ApproximateTime<sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::CameraInfo> SyncPolicy;
00068 typedef message_filters::Synchronizer<SyncPolicy> Synchronizer;
00069 boost::shared_ptr<Synchronizer> sync_;
00070
00071
00072 boost::mutex connect_mutex_;
00073 typedef sensor_msgs::PointCloud2 PointCloud;
00074 ros::Publisher pub_point_cloud_;
00075
00076 image_geometry::PinholeCameraModel model_;
00077
00078 virtual void onInit();
00079
00080 void connectCb();
00081
00082 void imageCb(const sensor_msgs::ImageConstPtr& depth_msg,
00083 const sensor_msgs::ImageConstPtr& intensity_msg,
00084 const sensor_msgs::CameraInfoConstPtr& info_msg);
00085
00086 template<typename T, typename T2>
00087 void convert(const sensor_msgs::ImageConstPtr& depth_msg,
00088 const sensor_msgs::ImageConstPtr& intensity_msg,
00089 const PointCloud::Ptr& cloud_msg);
00090 };
00091
00092 void PointCloudXyziNodelet::onInit()
00093 {
00094 ros::NodeHandle& nh = getNodeHandle();
00095 ros::NodeHandle& private_nh = getPrivateNodeHandle();
00096 intensity_nh_.reset( new ros::NodeHandle(nh, "intensity") );
00097 ros::NodeHandle depth_nh(nh, "depth");
00098 intensity_it_ .reset( new image_transport::ImageTransport(*intensity_nh_) );
00099 depth_it_.reset( new image_transport::ImageTransport(depth_nh) );
00100
00101
00102 int queue_size;
00103 private_nh.param("queue_size", queue_size, 5);
00104
00105
00106 sync_.reset( new Synchronizer(SyncPolicy(queue_size), sub_depth_, sub_intensity_, sub_info_) );
00107 sync_->registerCallback(boost::bind(&PointCloudXyziNodelet::imageCb, this, _1, _2, _3));
00108
00109
00110 ros::SubscriberStatusCallback connect_cb = boost::bind(&PointCloudXyziNodelet::connectCb, this);
00111
00112 boost::lock_guard<boost::mutex> lock(connect_mutex_);
00113 pub_point_cloud_ = depth_nh.advertise<PointCloud>("points", 1, connect_cb, connect_cb);
00114 }
00115
00116
00117 void PointCloudXyziNodelet::connectCb()
00118 {
00119 boost::lock_guard<boost::mutex> lock(connect_mutex_);
00120 if (pub_point_cloud_.getNumSubscribers() == 0)
00121 {
00122 sub_depth_.unsubscribe();
00123 sub_intensity_ .unsubscribe();
00124 sub_info_ .unsubscribe();
00125 }
00126 else if (!sub_depth_.getSubscriber())
00127 {
00128 ros::NodeHandle& private_nh = getPrivateNodeHandle();
00129
00130 std::string depth_image_transport_param = "depth_image_transport";
00131
00132
00133 image_transport::TransportHints depth_hints("raw",ros::TransportHints(), private_nh, depth_image_transport_param);
00134 sub_depth_.subscribe(*depth_it_, "image_rect", 1, depth_hints);
00135
00136
00137 image_transport::TransportHints hints("raw", ros::TransportHints(), private_nh);
00138 sub_intensity_.subscribe(*intensity_it_, "image_rect", 1, hints);
00139 sub_info_.subscribe(*intensity_nh_, "camera_info", 1);
00140 }
00141 }
00142
00143 void PointCloudXyziNodelet::imageCb(const sensor_msgs::ImageConstPtr& depth_msg,
00144 const sensor_msgs::ImageConstPtr& intensity_msg_in,
00145 const sensor_msgs::CameraInfoConstPtr& info_msg)
00146 {
00147
00148 if (depth_msg->header.frame_id != intensity_msg_in->header.frame_id)
00149 {
00150 NODELET_ERROR_THROTTLE(5, "Depth image frame id [%s] doesn't match image frame id [%s]",
00151 depth_msg->header.frame_id.c_str(), intensity_msg_in->header.frame_id.c_str());
00152 return;
00153 }
00154
00155
00156 model_.fromCameraInfo(info_msg);
00157
00158
00159 sensor_msgs::ImageConstPtr intensity_msg = intensity_msg_in;
00160 if (depth_msg->width != intensity_msg->width || depth_msg->height != intensity_msg->height)
00161 {
00162 sensor_msgs::CameraInfo info_msg_tmp = *info_msg;
00163 info_msg_tmp.width = depth_msg->width;
00164 info_msg_tmp.height = depth_msg->height;
00165 float ratio = float(depth_msg->width)/float(intensity_msg->width);
00166 info_msg_tmp.K[0] *= ratio;
00167 info_msg_tmp.K[2] *= ratio;
00168 info_msg_tmp.K[4] *= ratio;
00169 info_msg_tmp.K[5] *= ratio;
00170 info_msg_tmp.P[0] *= ratio;
00171 info_msg_tmp.P[2] *= ratio;
00172 info_msg_tmp.P[5] *= ratio;
00173 info_msg_tmp.P[6] *= ratio;
00174 model_.fromCameraInfo(info_msg_tmp);
00175
00176 cv_bridge::CvImageConstPtr cv_ptr;
00177 try
00178 {
00179 cv_ptr = cv_bridge::toCvShare(intensity_msg, intensity_msg->encoding);
00180 }
00181 catch (cv_bridge::Exception& e)
00182 {
00183 ROS_ERROR("cv_bridge exception: %s", e.what());
00184 return;
00185 }
00186 cv_bridge::CvImage cv_rsz;
00187 cv_rsz.header = cv_ptr->header;
00188 cv_rsz.encoding = cv_ptr->encoding;
00189 cv::resize(cv_ptr->image.rowRange(0,depth_msg->height/ratio), cv_rsz.image, cv::Size(depth_msg->width, depth_msg->height));
00190 if ((intensity_msg->encoding == enc::MONO8) || (intensity_msg->encoding == enc::MONO16))
00191 intensity_msg = cv_rsz.toImageMsg();
00192 else
00193 intensity_msg = cv_bridge::toCvCopy(cv_rsz.toImageMsg(), enc::MONO8)->toImageMsg();
00194
00195
00196
00197
00198 } else
00199 intensity_msg = intensity_msg_in;
00200
00201
00202 if (intensity_msg->encoding != enc::MONO8 || intensity_msg->encoding != enc::MONO16)
00203 {
00204 try
00205 {
00206 intensity_msg = cv_bridge::toCvCopy(intensity_msg, enc::MONO8)->toImageMsg();
00207 }
00208 catch (cv_bridge::Exception& e)
00209 {
00210 NODELET_ERROR_THROTTLE(5, "Unsupported encoding [%s]: %s", intensity_msg->encoding.c_str(), e.what());
00211 return;
00212 }
00213 }
00214
00215
00216 PointCloud::Ptr cloud_msg (new PointCloud);
00217 cloud_msg->header = depth_msg->header;
00218 cloud_msg->height = depth_msg->height;
00219 cloud_msg->width = depth_msg->width;
00220 cloud_msg->is_dense = false;
00221 cloud_msg->is_bigendian = false;
00222
00223 sensor_msgs::PointCloud2Modifier pcd_modifier(*cloud_msg);
00224
00225 pcd_modifier.setPointCloud2Fields(4,
00226 "x", 1, sensor_msgs::PointField::FLOAT32,
00227 "y", 1, sensor_msgs::PointField::FLOAT32,
00228 "z", 1, sensor_msgs::PointField::FLOAT32,
00229 "intensity", 1, sensor_msgs::PointField::FLOAT32);
00230
00231
00232 if (depth_msg->encoding == enc::TYPE_16UC1 &&
00233 intensity_msg->encoding == enc::MONO8)
00234 {
00235 convert<uint16_t, uint8_t>(depth_msg, intensity_msg, cloud_msg);
00236 }
00237 else if (depth_msg->encoding == enc::TYPE_16UC1 &&
00238 intensity_msg->encoding == enc::MONO16)
00239 {
00240 convert<uint16_t, uint16_t>(depth_msg, intensity_msg, cloud_msg);
00241 }
00242 else if (depth_msg->encoding == enc::TYPE_32FC1 &&
00243 intensity_msg->encoding == enc::MONO8)
00244 {
00245 convert<float, uint8_t>(depth_msg, intensity_msg, cloud_msg);
00246 }
00247 else if (depth_msg->encoding == enc::TYPE_32FC1 &&
00248 intensity_msg->encoding == enc::MONO16)
00249 {
00250 convert<float, uint16_t>(depth_msg, intensity_msg, cloud_msg);
00251 }
00252 else
00253 {
00254 NODELET_ERROR_THROTTLE(5, "Depth image has unsupported encoding [%s]", depth_msg->encoding.c_str());
00255 return;
00256 }
00257
00258 pub_point_cloud_.publish (cloud_msg);
00259 }
00260
00261 template<typename T, typename T2>
00262 void PointCloudXyziNodelet::convert(const sensor_msgs::ImageConstPtr& depth_msg,
00263 const sensor_msgs::ImageConstPtr& intensity_msg,
00264 const PointCloud::Ptr& cloud_msg)
00265 {
00266
00267 float center_x = model_.cx();
00268 float center_y = model_.cy();
00269
00270
00271 double unit_scaling = DepthTraits<T>::toMeters( T(1) );
00272 float constant_x = unit_scaling / model_.fx();
00273 float constant_y = unit_scaling / model_.fy();
00274 float bad_point = std::numeric_limits<float>::quiet_NaN ();
00275
00276 const T* depth_row = reinterpret_cast<const T*>(&depth_msg->data[0]);
00277 int row_step = depth_msg->step / sizeof(T);
00278
00279 const T2* inten_row = reinterpret_cast<const T2*>(&intensity_msg->data[0]);
00280 int inten_row_step = intensity_msg->step / sizeof(T2);
00281
00282 sensor_msgs::PointCloud2Iterator<float> iter_x(*cloud_msg, "x");
00283 sensor_msgs::PointCloud2Iterator<float> iter_y(*cloud_msg, "y");
00284 sensor_msgs::PointCloud2Iterator<float> iter_z(*cloud_msg, "z");
00285 sensor_msgs::PointCloud2Iterator<float> iter_i(*cloud_msg, "intensity");
00286
00287 for (int v = 0; v < int(cloud_msg->height); ++v, depth_row += row_step, inten_row += inten_row_step)
00288 {
00289 for (int u = 0; u < int(cloud_msg->width); ++u, ++iter_x, ++iter_y, ++iter_z, ++iter_i)
00290 {
00291 T depth = depth_row[u];
00292 T2 inten = inten_row[u];
00293
00294 if (!DepthTraits<T>::valid(depth))
00295 {
00296 *iter_x = *iter_y = *iter_z = bad_point;
00297 }
00298 else
00299 {
00300
00301 *iter_x = (u - center_x) * depth * constant_x;
00302 *iter_y = (v - center_y) * depth * constant_y;
00303 *iter_z = DepthTraits<T>::toMeters(depth);
00304 }
00305
00306
00307 *iter_i = inten;
00308 }
00309 }
00310 }
00311
00312 }
00313
00314
00315 #include <pluginlib/class_list_macros.h>
00316 PLUGINLIB_EXPORT_CLASS(depth_image_proc::PointCloudXyziNodelet,nodelet::Nodelet);