00001 #include <ros/ros.h>
00002 #include <nodelet/nodelet.h>
00003 #include <image_transport/image_transport.h>
00004 #include <image_transport/subscriber_filter.h>
00005 #include <message_filters/subscriber.h>
00006 #include <message_filters/synchronizer.h>
00007 #include <message_filters/sync_policies/exact_time.h>
00008 #include <message_filters/sync_policies/approximate_time.h>
00009 #include <image_proc/advertisement_checker.h>
00010
00011 #include <image_geometry/stereo_camera_model.h>
00012
00013 #include <stereo_msgs/DisparityImage.h>
00014 #include <sensor_msgs/PointCloud.h>
00015 #include <sensor_msgs/image_encodings.h>
00016
00017 namespace stereo_image_proc {
00018
00019 using namespace sensor_msgs;
00020 using namespace stereo_msgs;
00021 using namespace message_filters::sync_policies;
00022
00023 class PointCloudNodelet : public nodelet::Nodelet
00024 {
00025 boost::shared_ptr<image_transport::ImageTransport> it_;
00026
00027
00028 image_transport::SubscriberFilter sub_l_image_;
00029 message_filters::Subscriber<CameraInfo> sub_l_info_, sub_r_info_;
00030 message_filters::Subscriber<DisparityImage> sub_disparity_;
00031 typedef ExactTime<Image, CameraInfo, CameraInfo, DisparityImage> ExactPolicy;
00032 typedef ApproximateTime<Image, CameraInfo, CameraInfo, DisparityImage> ApproximatePolicy;
00033 typedef message_filters::Synchronizer<ExactPolicy> ExactSync;
00034 typedef message_filters::Synchronizer<ApproximatePolicy> ApproximateSync;
00035 boost::shared_ptr<ExactSync> exact_sync_;
00036 boost::shared_ptr<ApproximateSync> approximate_sync_;
00037 bool subscribed_;
00038
00039
00040 ros::Publisher pub_points_;
00041
00042
00043 image_geometry::StereoCameraModel model_;
00044 cv::Mat_<cv::Vec3f> points_mat_;
00045
00046
00047 boost::shared_ptr<image_proc::AdvertisementChecker> check_inputs_;
00048
00049
00050 virtual void onInit();
00051
00052 void connectCb();
00053
00054 void imageCb(const ImageConstPtr& l_image_msg,
00055 const CameraInfoConstPtr& l_info_msg,
00056 const CameraInfoConstPtr& r_info_msg,
00057 const DisparityImageConstPtr& disp_msg);
00058 };
00059
00060 void PointCloudNodelet::onInit()
00061 {
00062 ros::NodeHandle &nh = getNodeHandle();
00063 ros::NodeHandle &private_nh = getPrivateNodeHandle();
00064 it_.reset(new image_transport::ImageTransport(nh));
00065
00066
00067 subscribed_ = false;
00068 ros::SubscriberStatusCallback connect_cb = boost::bind(&PointCloudNodelet::connectCb, this);
00069 pub_points_ = nh.advertise<PointCloud>("points", 1, connect_cb, connect_cb);
00070
00071
00072
00073 int queue_size;
00074 private_nh.param("queue_size", queue_size, 5);
00075 bool approx;
00076 private_nh.param("approximate_sync", approx, false);
00077 if (approx)
00078 {
00079 approximate_sync_.reset( new ApproximateSync(ApproximatePolicy(queue_size),
00080 sub_l_image_, sub_l_info_,
00081 sub_r_info_, sub_disparity_) );
00082 approximate_sync_->registerCallback(boost::bind(&PointCloudNodelet::imageCb,
00083 this, _1, _2, _3, _4));
00084 }
00085 else
00086 {
00087 exact_sync_.reset( new ExactSync(ExactPolicy(queue_size),
00088 sub_l_image_, sub_l_info_,
00089 sub_r_info_, sub_disparity_) );
00090 exact_sync_->registerCallback(boost::bind(&PointCloudNodelet::imageCb,
00091 this, _1, _2, _3, _4));
00092 }
00093
00094
00095 const std::vector<std::string>& argv = getMyArgv();
00096 bool do_input_checks = std::find(argv.begin(), argv.end(),
00097 "--no-input-checks") == argv.end();
00098
00099
00100 if (do_input_checks)
00101 {
00102 ros::V_string topics;
00103 topics.push_back("left/image_rect_color");
00104 topics.push_back("left/camera_info");
00105 topics.push_back("right/camera_info");
00106 topics.push_back("disparity");
00107 check_inputs_.reset( new image_proc::AdvertisementChecker(nh, getName()) );
00108 check_inputs_->start(topics, 60.0);
00109 }
00110 }
00111
00112
00113 void PointCloudNodelet::connectCb()
00114 {
00115 if (pub_points_.getNumSubscribers() == 0)
00116 {
00117 sub_l_image_ .unsubscribe();
00118 sub_l_info_ .unsubscribe();
00119 sub_r_info_ .unsubscribe();
00120 sub_disparity_.unsubscribe();
00121 subscribed_ = false;
00122 }
00123 else if (!subscribed_)
00124 {
00125 ros::NodeHandle &nh = getNodeHandle();
00126
00127 sub_l_image_ .subscribe(*it_, "left/image_rect_color", 1);
00128 sub_l_info_ .subscribe(nh, "left/camera_info", 1);
00129 sub_r_info_ .subscribe(nh, "right/camera_info", 1);
00130 sub_disparity_.subscribe(nh, "disparity", 1);
00131 subscribed_ = true;
00132 }
00133 }
00134
00135 inline bool isValidPoint(const cv::Vec3f& pt)
00136 {
00137
00138
00139 return pt[2] != image_geometry::StereoCameraModel::MISSING_Z && !std::isinf(pt[2]);
00140 }
00141
00142 void PointCloudNodelet::imageCb(const ImageConstPtr& l_image_msg,
00143 const CameraInfoConstPtr& l_info_msg,
00144 const CameraInfoConstPtr& r_info_msg,
00145 const DisparityImageConstPtr& disp_msg)
00146 {
00147
00148 model_.fromCameraInfo(l_info_msg, r_info_msg);
00149
00150
00151 const Image& dimage = disp_msg->image;
00152 const cv::Mat_<float> dmat(dimage.height, dimage.width, (float*)&dimage.data[0], dimage.step);
00153 model_.projectDisparityImageTo3d(dmat, points_mat_, true);
00154 cv::Mat_<cv::Vec3f> mat = points_mat_;
00155
00156
00157 PointCloudPtr points_msg = boost::make_shared<PointCloud>();
00158 points_msg->header = disp_msg->header;
00159
00160 points_msg->channels.resize(3);
00161 points_msg->channels[0].name = "rgb";
00162 points_msg->channels[0].values.resize(0);
00163 points_msg->channels[1].name = "u";
00164 points_msg->channels[1].values.resize(0);
00165 points_msg->channels[2].name = "v";
00166 points_msg->channels[2].values.resize(0);
00167
00168
00169
00170 for (int32_t u = 0; u < mat.rows; ++u) {
00171 for (int32_t v = 0; v < mat.cols; ++v) {
00172 if (isValidPoint(mat(u,v))) {
00173
00174 geometry_msgs::Point32 pt;
00175 pt.x = mat(u,v)[0];
00176 pt.y = mat(u,v)[1];
00177 pt.z = mat(u,v)[2];
00178 points_msg->points.push_back(pt);
00179
00180 points_msg->channels[1].values.push_back(u);
00181 points_msg->channels[2].values.push_back(v);
00182 }
00183 }
00184 }
00185
00186
00187 namespace enc = sensor_msgs::image_encodings;
00188 const std::string& encoding = l_image_msg->encoding;
00189 points_msg->channels[0].values.reserve(points_msg->points.size());
00190 if (encoding == enc::MONO8) {
00191 const cv::Mat_<uint8_t> color(l_image_msg->height, l_image_msg->width,
00192 (uint8_t*)&l_image_msg->data[0],
00193 l_image_msg->step);
00194 for (int32_t u = 0; u < mat.rows; ++u) {
00195 for (int32_t v = 0; v < mat.cols; ++v) {
00196 if (isValidPoint(mat(u,v))) {
00197 uint8_t g = color(u,v);
00198 int32_t rgb = (g << 16) | (g << 8) | g;
00199 points_msg->channels[0].values.push_back(*(float*)(&rgb));
00200 }
00201 }
00202 }
00203 }
00204 else if (encoding == enc::RGB8) {
00205 const cv::Mat_<cv::Vec3b> color(l_image_msg->height, l_image_msg->width,
00206 (cv::Vec3b*)&l_image_msg->data[0],
00207 l_image_msg->step);
00208 for (int32_t u = 0; u < mat.rows; ++u) {
00209 for (int32_t v = 0; v < mat.cols; ++v) {
00210 if (isValidPoint(mat(u,v))) {
00211 const cv::Vec3b& rgb = color(u,v);
00212 int32_t rgb_packed = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
00213 points_msg->channels[0].values.push_back(*(float*)(&rgb_packed));
00214 }
00215 }
00216 }
00217 }
00218 else if (encoding == enc::BGR8) {
00219 const cv::Mat_<cv::Vec3b> color(l_image_msg->height, l_image_msg->width,
00220 (cv::Vec3b*)&l_image_msg->data[0],
00221 l_image_msg->step);
00222 for (int32_t u = 0; u < mat.rows; ++u) {
00223 for (int32_t v = 0; v < mat.cols; ++v) {
00224 if (isValidPoint(mat(u,v))) {
00225 const cv::Vec3b& bgr = color(u,v);
00226 int32_t rgb_packed = (bgr[2] << 16) | (bgr[1] << 8) | bgr[0];
00227 points_msg->channels[0].values.push_back(*(float*)(&rgb_packed));
00228 }
00229 }
00230 }
00231 }
00232 else {
00233 NODELET_WARN_THROTTLE(30, "Could not fill color channel of the point cloud, "
00234 "unsupported encoding '%s'", encoding.c_str());
00235 }
00236
00237 pub_points_.publish(points_msg);
00238 }
00239
00240 }
00241
00242
00243 #include <pluginlib/class_list_macros.h>
00244 PLUGINLIB_DECLARE_CLASS(stereo_image_proc, point_cloud,
00245 stereo_image_proc::PointCloudNodelet, nodelet::Nodelet)