disparity.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 <boost/version.hpp>
35 #if ((BOOST_VERSION / 100) % 1000) >= 53
36 #include <boost/thread/lock_guard.hpp>
37 #endif
38 
39 #include <ros/ros.h>
40 #include <nodelet/nodelet.h>
47 
49 #include <opencv2/calib3d/calib3d.hpp>
50 #include <cv_bridge/cv_bridge.h>
51 
53 #include <stereo_msgs/DisparityImage.h>
54 
55 #include <stereo_image_proc/DisparityConfig.h>
56 #include <dynamic_reconfigure/server.h>
57 
59 
60 namespace stereo_image_proc {
61 
62 using namespace sensor_msgs;
63 using namespace stereo_msgs;
64 using namespace message_filters::sync_policies;
65 
67 {
69 
70  // Subscriptions
79  // Publications
80  boost::mutex connect_mutex_;
82 
83  // Dynamic reconfigure
84  boost::recursive_mutex config_mutex_;
85  typedef stereo_image_proc::DisparityConfig Config;
86  typedef dynamic_reconfigure::Server<Config> ReconfigureServer;
89 
90  // Processing state (note: only safe because we're single-threaded!)
92  stereo_image_proc::StereoProcessor block_matcher_; // contains scratch buffers for block matching
93 
94  virtual void onInit();
95 
96  void connectCb();
97 
98  void imageCb(const ImageConstPtr& l_image_msg, const CameraInfoConstPtr& l_info_msg,
99  const ImageConstPtr& r_image_msg, const CameraInfoConstPtr& r_info_msg);
100 
101  void configCb(Config &config, uint32_t level);
102 };
103 
105 {
106  ros::NodeHandle &nh = getNodeHandle();
107  ros::NodeHandle &private_nh = getPrivateNodeHandle();
108 
109  it_.reset(new image_transport::ImageTransport(nh));
110 
111  // Synchronize inputs. Topic subscriptions happen on demand in the connection
112  // callback. Optionally do approximate synchronization.
113  int queue_size;
114  private_nh.param("queue_size", queue_size, 5);
115  bool approx;
116  private_nh.param("approximate_sync", approx, false);
117  private_nh.param("downsampling_factor", downsampling_factor_, 1);
118  if (approx)
119  {
120  approximate_sync_.reset( new ApproximateSync(ApproximatePolicy(queue_size),
121  sub_l_image_, sub_l_info_,
122  sub_r_image_, sub_r_info_) );
123  approximate_sync_->registerCallback(boost::bind(&DisparityNodelet::imageCb,
124  this, _1, _2, _3, _4));
125  }
126  else
127  {
128  exact_sync_.reset( new ExactSync(ExactPolicy(queue_size),
129  sub_l_image_, sub_l_info_,
130  sub_r_image_, sub_r_info_) );
131  exact_sync_->registerCallback(boost::bind(&DisparityNodelet::imageCb,
132  this, _1, _2, _3, _4));
133  }
134 
135  // Set up dynamic reconfiguration
136  ReconfigureServer::CallbackType f = boost::bind(&DisparityNodelet::configCb,
137  this, _1, _2);
138  reconfigure_server_.reset(new ReconfigureServer(config_mutex_, private_nh));
139  reconfigure_server_->setCallback(f);
140 
141  // Monitor whether anyone is subscribed to the output
142  ros::SubscriberStatusCallback connect_cb = boost::bind(&DisparityNodelet::connectCb, this);
143  // Make sure we don't enter connectCb() between advertising and assigning to pub_disparity_
144  boost::lock_guard<boost::mutex> lock(connect_mutex_);
145  pub_disparity_ = nh.advertise<DisparityImage>("disparity", 1, connect_cb, connect_cb);
146 }
147 
148 // Handles (un)subscribing when clients (un)subscribe
150 {
151  boost::lock_guard<boost::mutex> lock(connect_mutex_);
152  if (pub_disparity_.getNumSubscribers() == 0)
153  {
154  sub_l_image_.unsubscribe();
155  sub_l_info_ .unsubscribe();
156  sub_r_image_.unsubscribe();
157  sub_r_info_ .unsubscribe();
158  }
159  else if (!sub_l_image_.getSubscriber())
160  {
161  ros::NodeHandle &nh = getNodeHandle();
162  // Queue size 1 should be OK; the one that matters is the synchronizer queue size.
164  image_transport::TransportHints hints("raw", ros::TransportHints(), getPrivateNodeHandle());
165  sub_l_image_.subscribe(*it_, "left/image_rect", 1, hints);
166  sub_l_info_ .subscribe(nh, "left/camera_info", 1);
167  sub_r_image_.subscribe(*it_, "right/image_rect", 1, hints);
168  sub_r_info_ .subscribe(nh, "right/camera_info", 1);
169  }
170 }
171 
173  const cv::Mat& input_image,
174  const uint32_t downsample_factor_per_dimension) {
175  cv::Mat blurred_image;
176  const int32_t kernel_size = 2 * downsample_factor_per_dimension + 1;
177  cv::GaussianBlur(
178  input_image, blurred_image, cv::Size(kernel_size, kernel_size),
179  downsample_factor_per_dimension);
180 
181  // To avoid computational effort of bilinear interpolation, perform
182  // interpolation manually.
183  uint32_t downsampled_height = std::ceil(
184  input_image.size().height /
185  static_cast<double>(downsample_factor_per_dimension));
186  uint32_t downsampled_width = std::ceil(
187  input_image.size().width /
188  static_cast<double>(downsample_factor_per_dimension));
189  cv::Mat downsampled_image(
190  downsampled_height, downsampled_width, input_image.type());
191 
192  for (uint32_t destination_row = 0u; destination_row < downsampled_image.size().height; destination_row++) {
193  for (uint32_t destination_col = 0u; destination_col < downsampled_image.size().width; destination_col++) {
194  downsampled_image.at<uint8_t>(destination_row, destination_col) =
195  blurred_image.at<uint8_t>(destination_row * downsample_factor_per_dimension,
196  destination_col * downsample_factor_per_dimension);
197  }
198  }
199  return downsampled_image;
200 }
201 
203  const cv::Mat& disparity, const cv::Size& destination_size,
204  const uint32_t upsample_factor_per_dimension) {
205  cv::Mat upsampled_disparity(
206  destination_size, disparity.type(), -1.);
207 
208  for (uint32_t destination_row = 0u; destination_row < upsampled_disparity.size().height; destination_row++) {
209  for (uint32_t destination_col = 0u; destination_col < upsampled_disparity.size().width; destination_col++) {
210  upsampled_disparity.at<float>(destination_row, destination_col) = upsample_factor_per_dimension *
211  disparity.at<float>(destination_row / upsample_factor_per_dimension,
212  destination_col / upsample_factor_per_dimension);
213  }
214  }
215  return upsampled_disparity;
216 }
217 
218 void DisparityNodelet::imageCb(const ImageConstPtr& l_image_msg,
219  const CameraInfoConstPtr& l_info_msg,
220  const ImageConstPtr& r_image_msg,
221  const CameraInfoConstPtr& r_info_msg)
222 {
223  // Update the camera model
224  model_.fromCameraInfo(l_info_msg, r_info_msg);
225 
226  // Allocate new disparity image message
227  DisparityImagePtr disp_msg = boost::make_shared<DisparityImage>();
228  disp_msg->header = l_info_msg->header;
229  disp_msg->image.header = l_info_msg->header;
230 
231  // Compute window of (potentially) valid disparities
232  int border = block_matcher_.getCorrelationWindowSize() / 2;
233  int left = block_matcher_.getDisparityRange() + block_matcher_.getMinDisparity() + border - 1;
234  int wtf = (block_matcher_.getMinDisparity() >= 0) ? border + block_matcher_.getMinDisparity() : std::max(border, -block_matcher_.getMinDisparity());
235  int right = disp_msg->image.width - 1 - wtf;
236  int top = border;
237  int bottom = disp_msg->image.height - 1 - border;
238  disp_msg->valid_window.x_offset = left;
239  disp_msg->valid_window.y_offset = top;
240  disp_msg->valid_window.width = right - left;
241  disp_msg->valid_window.height = bottom - top;
242 
243  // Create cv::Mat views onto all buffers
244  const cv::Mat_<uint8_t> l_image = cv_bridge::toCvShare(l_image_msg, sensor_msgs::image_encodings::MONO8)->image;
245  const cv::Mat_<uint8_t> r_image = cv_bridge::toCvShare(r_image_msg, sensor_msgs::image_encodings::MONO8)->image;
246 
247  cv::Mat_<uint8_t> l_sub_image;
248  cv::Mat_<uint8_t> r_sub_image;
249 
250  if (downsampling_factor_ != 1) {
251  l_sub_image = subsampleTheImage(l_image, downsampling_factor_);
252  r_sub_image = subsampleTheImage(r_image, downsampling_factor_);
253  }
254  else {
255  l_sub_image = l_image;
256  r_sub_image = r_image;
257  }
258 
259  // Perform block matching to find the disparities
260  block_matcher_.processDisparity(l_sub_image, r_sub_image, model_, *disp_msg);
261 
262  // Upsampling
263  if (downsampling_factor_ != 1) {
264  const cv::Mat disp_subsampled_image = cv_bridge::toCvShare(disp_msg->image, disp_msg, sensor_msgs::image_encodings::TYPE_32FC1)->image;
265  const cv::Mat disp_upsampled_image =
267  disp_subsampled_image, l_image.size(), downsampling_factor_);
268  const cv_bridge::CvImage disp_image_container =
269  cv_bridge::CvImage(disp_msg->header, sensor_msgs::image_encodings::TYPE_32FC1, disp_upsampled_image);
270  disp_image_container.toImageMsg(disp_msg->image);
271  }
272 
273  // Adjust for any x-offset between the principal points: d' = d - (cx_l - cx_r)
274  double cx_l = model_.left().cx();
275  double cx_r = model_.right().cx();
276  if (cx_l != cx_r) {
277  cv::Mat_<float> disp_image(disp_msg->image.height, disp_msg->image.width,
278  reinterpret_cast<float*>(&disp_msg->image.data[0]),
279  disp_msg->image.step);
280  cv::subtract(disp_image, cv::Scalar(cx_l - cx_r), disp_image);
281  }
282 
283  pub_disparity_.publish(disp_msg);
284 }
285 
286 void DisparityNodelet::configCb(Config &config, uint32_t level)
287 {
288  // Tweak all settings to be valid
289  config.prefilter_size |= 0x1; // must be odd
290  config.correlation_window_size |= 0x1; // must be odd
291  config.disparity_range = (config.disparity_range / 16) * 16; // must be multiple of 16
292 
293  // check stereo method
294  // Note: With single-threaded NodeHandle, configCb and imageCb can't be called
295  // concurrently, so this is thread-safe.
296  block_matcher_.setPreFilterCap(config.prefilter_cap);
297  block_matcher_.setCorrelationWindowSize(config.correlation_window_size);
298  block_matcher_.setMinDisparity(config.min_disparity);
299  block_matcher_.setDisparityRange(config.disparity_range);
300  block_matcher_.setUniquenessRatio(config.uniqueness_ratio);
301  block_matcher_.setSpeckleSize(config.speckle_size);
302  block_matcher_.setSpeckleRange(config.speckle_range);
303  if (config.stereo_algorithm == stereo_image_proc::Disparity_StereoBM) { // StereoBM
304  block_matcher_.setStereoType(StereoProcessor::BM);
305  block_matcher_.setPreFilterSize(config.prefilter_size);
306  block_matcher_.setTextureThreshold(config.texture_threshold);
307  }
308  else if (config.stereo_algorithm == stereo_image_proc::Disparity_StereoSGBM) { // StereoSGBM
309  block_matcher_.setStereoType(StereoProcessor::SGBM);
310  block_matcher_.setSgbmMode(config.fullDP);
311  block_matcher_.setP1(config.P1);
312  block_matcher_.setP2(config.P2);
313  block_matcher_.setDisp12MaxDiff(config.disp12MaxDiff);
314  }
315 }
316 
317 } // namespace stereo_image_proc
318 
319 // Register nodelet
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
ExactTime< Image, CameraInfo, Image, CameraInfo > ExactPolicy
Definition: disparity.cpp:73
boost::shared_ptr< ReconfigureServer > reconfigure_server_
Definition: disparity.cpp:87
boost::function< void(const SingleSubscriberPublisher &)> SubscriberStatusCallback
f
ApproximateTime< Image, CameraInfo, Image, CameraInfo > ApproximatePolicy
Definition: disparity.cpp:74
cv::Mat subsampleTheImage(const cv::Mat &input_image, const uint32_t downsample_factor_per_dimension)
Definition: disparity.cpp:172
void imageCb(const ImageConstPtr &l_image_msg, const CameraInfoConstPtr &l_info_msg, const ImageConstPtr &r_image_msg, const CameraInfoConstPtr &r_info_msg)
Definition: disparity.cpp:218
boost::shared_ptr< ExactSync > exact_sync_
Definition: disparity.cpp:77
boost::shared_ptr< image_transport::ImageTransport > it_
Definition: disparity.cpp:68
image_geometry::StereoCameraModel model_
Definition: disparity.cpp:91
dynamic_reconfigure::Server< Config > ReconfigureServer
Definition: disparity.cpp:86
message_filters::Synchronizer< ExactPolicy > ExactSync
Definition: disparity.cpp:75
boost::recursive_mutex config_mutex_
Definition: disparity.cpp:84
message_filters::Synchronizer< ApproximatePolicy > ApproximateSync
Definition: disparity.cpp:76
bool param(const std::string &param_name, T &param_val, const T &default_val) const
PLUGINLIB_EXPORT_CLASS(image_proc::CropNonZeroNodelet, nodelet::Nodelet)
message_filters::Subscriber< CameraInfo > sub_r_info_
Definition: disparity.cpp:72
cv::Mat upsampleTheDisparityImageWithoutInterpolation(const cv::Mat &disparity, const cv::Size &destination_size, const uint32_t upsample_factor_per_dimension)
Definition: disparity.cpp:202
const std::string TYPE_32FC1
stereo_image_proc::StereoProcessor block_matcher_
Definition: disparity.cpp:92
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
stereo_image_proc::DisparityConfig Config
Definition: disparity.cpp:85
image_transport::SubscriberFilter sub_r_image_
Definition: disparity.cpp:71
boost::shared_ptr< ApproximateSync > approximate_sync_
Definition: disparity.cpp:78
void configCb(Config &config, uint32_t level)
Definition: disparity.cpp:286


stereo_image_proc
Author(s): Patrick Mihelich, Kurt Konolige, Jeremy Leibs
autogenerated on Wed Dec 7 2022 03:25:29