disparity.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 * Software License Agreement (BSD License)
00003 * 
00004 *  Copyright (c) 2008, Willow Garage, Inc.
00005 *  All rights reserved.
00006 * 
00007 *  Redistribution and use in source and binary forms, with or without
00008 *  modification, are permitted provided that the following conditions
00009 *  are met:
00010 * 
00011 *   * Redistributions of source code must retain the above copyright
00012 *     notice, this list of conditions and the following disclaimer.
00013 *   * Redistributions in binary form must reproduce the above
00014 *     copyright notice, this list of conditions and the following
00015 *     disclaimer in the documentation and/or other materials provided
00016 *     with the distribution.
00017 *   * Neither the name of the Willow Garage nor the names of its
00018 *     contributors may be used to endorse or promote products derived
00019 *     from this software without specific prior written permission.
00020 * 
00021 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 *  POSSIBILITY OF SUCH DAMAGE.
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/exact_time.h>
00046 #include <message_filters/sync_policies/approximate_time.h>
00047 
00048 #include <image_geometry/stereo_camera_model.h>
00049 #include <opencv2/calib3d/calib3d.hpp>
00050 
00051 #include <sensor_msgs/image_encodings.h>
00052 #include <stereo_msgs/DisparityImage.h>
00053 
00054 #include <stereo_image_proc/DisparityConfig.h>
00055 #include <dynamic_reconfigure/server.h>
00056 
00057 namespace stereo_image_proc {
00058 
00059 using namespace sensor_msgs;
00060 using namespace stereo_msgs;
00061 using namespace message_filters::sync_policies;
00062 
00063 class DisparityNodelet : public nodelet::Nodelet
00064 {
00065   boost::shared_ptr<image_transport::ImageTransport> it_;
00066   
00067   // Subscriptions
00068   image_transport::SubscriberFilter sub_l_image_, sub_r_image_;
00069   message_filters::Subscriber<CameraInfo> sub_l_info_, sub_r_info_;
00070   typedef ExactTime<Image, CameraInfo, Image, CameraInfo> ExactPolicy;
00071   typedef ApproximateTime<Image, CameraInfo, Image, CameraInfo> ApproximatePolicy;
00072   typedef message_filters::Synchronizer<ExactPolicy> ExactSync;
00073   typedef message_filters::Synchronizer<ApproximatePolicy> ApproximateSync;
00074   boost::shared_ptr<ExactSync> exact_sync_;
00075   boost::shared_ptr<ApproximateSync> approximate_sync_;
00076 
00077   // Publications
00078   boost::mutex connect_mutex_;
00079   ros::Publisher pub_disparity_;
00080 
00081   // Dynamic reconfigure
00082   boost::recursive_mutex config_mutex_;
00083   typedef stereo_image_proc::DisparityConfig Config;
00084   typedef dynamic_reconfigure::Server<Config> ReconfigureServer;
00085   boost::shared_ptr<ReconfigureServer> reconfigure_server_;
00086   
00087   // Processing state (note: only safe because we're single-threaded!)
00088   image_geometry::StereoCameraModel model_;
00089   cv::StereoBM block_matcher_; // contains scratch buffers for block matching
00090 
00091   virtual void onInit();
00092 
00093   void connectCb();
00094 
00095   void imageCb(const ImageConstPtr& l_image_msg, const CameraInfoConstPtr& l_info_msg,
00096                const ImageConstPtr& r_image_msg, const CameraInfoConstPtr& r_info_msg);
00097 
00098   void configCb(Config &config, uint32_t level);
00099 };
00100 
00101 void DisparityNodelet::onInit()
00102 {
00103   ros::NodeHandle &nh = getNodeHandle();
00104   ros::NodeHandle &private_nh = getPrivateNodeHandle();
00105 
00106   it_.reset(new image_transport::ImageTransport(nh));
00107 
00108   // Synchronize inputs. Topic subscriptions happen on demand in the connection
00109   // callback. Optionally do approximate synchronization.
00110   int queue_size;
00111   private_nh.param("queue_size", queue_size, 5);
00112   bool approx;
00113   private_nh.param("approximate_sync", approx, false);
00114   if (approx)
00115   {
00116     approximate_sync_.reset( new ApproximateSync(ApproximatePolicy(queue_size),
00117                                                  sub_l_image_, sub_l_info_,
00118                                                  sub_r_image_, sub_r_info_) );
00119     approximate_sync_->registerCallback(boost::bind(&DisparityNodelet::imageCb,
00120                                                     this, _1, _2, _3, _4));
00121   }
00122   else
00123   {
00124     exact_sync_.reset( new ExactSync(ExactPolicy(queue_size),
00125                                      sub_l_image_, sub_l_info_,
00126                                      sub_r_image_, sub_r_info_) );
00127     exact_sync_->registerCallback(boost::bind(&DisparityNodelet::imageCb,
00128                                               this, _1, _2, _3, _4));
00129   }
00130 
00131   // Set up dynamic reconfiguration
00132   ReconfigureServer::CallbackType f = boost::bind(&DisparityNodelet::configCb,
00133                                                   this, _1, _2);
00134   reconfigure_server_.reset(new ReconfigureServer(config_mutex_, private_nh));
00135   reconfigure_server_->setCallback(f);
00136 
00137   // Monitor whether anyone is subscribed to the output
00138   ros::SubscriberStatusCallback connect_cb = boost::bind(&DisparityNodelet::connectCb, this);
00139   // Make sure we don't enter connectCb() between advertising and assigning to pub_disparity_
00140   boost::lock_guard<boost::mutex> lock(connect_mutex_);
00141   pub_disparity_ = nh.advertise<DisparityImage>("disparity", 1, connect_cb, connect_cb);
00142 }
00143 
00144 // Handles (un)subscribing when clients (un)subscribe
00145 void DisparityNodelet::connectCb()
00146 {
00147   boost::lock_guard<boost::mutex> lock(connect_mutex_);
00148   if (pub_disparity_.getNumSubscribers() == 0)
00149   {
00150     sub_l_image_.unsubscribe();
00151     sub_l_info_ .unsubscribe();
00152     sub_r_image_.unsubscribe();
00153     sub_r_info_ .unsubscribe();
00154   }
00155   else if (!sub_l_image_.getSubscriber())
00156   {
00157     ros::NodeHandle &nh = getNodeHandle();
00158     // Queue size 1 should be OK; the one that matters is the synchronizer queue size.
00160     image_transport::TransportHints hints("raw", ros::TransportHints(), getPrivateNodeHandle());
00161     sub_l_image_.subscribe(*it_, "left/image_rect", 1, hints);
00162     sub_l_info_ .subscribe(nh,   "left/camera_info", 1);
00163     sub_r_image_.subscribe(*it_, "right/image_rect", 1, hints);
00164     sub_r_info_ .subscribe(nh,   "right/camera_info", 1);
00165   }
00166 }
00167 
00168 void DisparityNodelet::imageCb(const ImageConstPtr& l_image_msg,
00169                                const CameraInfoConstPtr& l_info_msg,
00170                                const ImageConstPtr& r_image_msg,
00171                                const CameraInfoConstPtr& r_info_msg)
00172 {
00174   assert(l_image_msg->encoding == sensor_msgs::image_encodings::MONO8);
00175   assert(r_image_msg->encoding == sensor_msgs::image_encodings::MONO8);
00176 
00177   // Update the camera model
00178   model_.fromCameraInfo(l_info_msg, r_info_msg);
00179   
00180   // Allocate new disparity image message
00181   DisparityImagePtr disp_msg = boost::make_shared<DisparityImage>();
00182   disp_msg->header         = l_info_msg->header;
00183   disp_msg->image.header   = l_info_msg->header;
00184   disp_msg->image.height   = l_image_msg->height;
00185   disp_msg->image.width    = l_image_msg->width;
00186   disp_msg->image.encoding = sensor_msgs::image_encodings::TYPE_32FC1;
00187   disp_msg->image.step     = disp_msg->image.width * sizeof(float);
00188   disp_msg->image.data.resize(disp_msg->image.height * disp_msg->image.step);
00189 
00190   // Stereo parameters
00191   disp_msg->f = model_.right().fx();
00192   disp_msg->T = model_.baseline();
00193 
00194   // Compute window of (potentially) valid disparities
00195   cv::Ptr<CvStereoBMState> params = block_matcher_.state;
00196   int border   = params->SADWindowSize / 2;
00197   int left   = params->numberOfDisparities + params->minDisparity + border - 1;
00198   int wtf = (params->minDisparity >= 0) ? border + params->minDisparity : std::max(border, -params->minDisparity);
00199   int right  = disp_msg->image.width - 1 - wtf;
00200   int top    = border;
00201   int bottom = disp_msg->image.height - 1 - border;
00202   disp_msg->valid_window.x_offset = left;
00203   disp_msg->valid_window.y_offset = top;
00204   disp_msg->valid_window.width    = right - left;
00205   disp_msg->valid_window.height   = bottom - top;
00206 
00207   // Disparity search range
00208   disp_msg->min_disparity = params->minDisparity;
00209   disp_msg->max_disparity = params->minDisparity + params->numberOfDisparities - 1;
00210   disp_msg->delta_d = 1.0 / 16; // OpenCV uses 16 disparities per pixel
00211 
00212   // Create cv::Mat views onto all buffers
00213   const cv::Mat_<uint8_t> l_image(l_image_msg->height, l_image_msg->width,
00214                                   const_cast<uint8_t*>(&l_image_msg->data[0]),
00215                                   l_image_msg->step);
00216   const cv::Mat_<uint8_t> r_image(r_image_msg->height, r_image_msg->width,
00217                                   const_cast<uint8_t*>(&r_image_msg->data[0]),
00218                                   r_image_msg->step);
00219   cv::Mat_<float> disp_image(disp_msg->image.height, disp_msg->image.width,
00220                              reinterpret_cast<float*>(&disp_msg->image.data[0]),
00221                              disp_msg->image.step);
00222 
00223   // Perform block matching to find the disparities
00224   block_matcher_(l_image, r_image, disp_image, CV_32F);
00225 
00226   // Adjust for any x-offset between the principal points: d' = d - (cx_l - cx_r)
00227   double cx_l = model_.left().cx();
00228   double cx_r = model_.right().cx();
00229   if (cx_l != cx_r)
00230     cv::subtract(disp_image, cv::Scalar(cx_l - cx_r), disp_image);
00231 
00232   pub_disparity_.publish(disp_msg);
00233 }
00234 
00235 void DisparityNodelet::configCb(Config &config, uint32_t level)
00236 {
00237   // Tweak all settings to be valid
00238   config.prefilter_size |= 0x1; // must be odd
00239   config.correlation_window_size |= 0x1; // must be odd
00240   config.disparity_range = (config.disparity_range / 16) * 16; // must be multiple of 16
00241 
00242   // Note: With single-threaded NodeHandle, configCb and imageCb can't be called
00243   // concurrently, so this is thread-safe.
00244   block_matcher_.state->preFilterSize       = config.prefilter_size;
00245   block_matcher_.state->preFilterCap        = config.prefilter_cap;
00246   block_matcher_.state->SADWindowSize       = config.correlation_window_size;
00247   block_matcher_.state->minDisparity        = config.min_disparity;
00248   block_matcher_.state->numberOfDisparities = config.disparity_range;
00249   block_matcher_.state->uniquenessRatio     = config.uniqueness_ratio;
00250   block_matcher_.state->textureThreshold    = config.texture_threshold;
00251   block_matcher_.state->speckleWindowSize   = config.speckle_size;
00252   block_matcher_.state->speckleRange        = config.speckle_range;
00253 }
00254 
00255 } // namespace stereo_image_proc
00256 
00257 // Register nodelet
00258 #include <pluginlib/class_list_macros.h>
00259 PLUGINLIB_EXPORT_CLASS(stereo_image_proc::DisparityNodelet,nodelet::Nodelet)


stereo_image_proc
Author(s): Patrick Mihelich, Kurt Konolige, Jeremy Leibs
autogenerated on Mon Oct 6 2014 00:46:19