fuse_images.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2017, Kentaro Wada and JSK Lab
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 Kentaro Wada and JSK Lab 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 
35 #include <limits>
36 #include <string>
37 #include <vector>
38 
39 #include <opencv2/opencv.hpp>
40 
41 #include <cv_bridge/cv_bridge.h>
44 
45 
46 namespace jsk_pcl_ros
47 {
48  void FuseImages::onInit()
49  {
50  DiagnosticNodelet::onInit();
51  pub_out_ = advertise<sensor_msgs::Image>(*pnh_, "output", 1);
52  onInitPostProcess();
53  }
54 
56  // message_filters::Synchronizer needs to be called reset
57  // before message_filters::Subscriber is freed.
58  // Calling reset fixes the following error on shutdown of the nodelet:
59  // terminate called after throwing an instance of
60  // 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::lock_error> >'
61  // what(): boost: mutex lock failed in pthread_mutex_lock: Invalid argument
62  // Also see https://github.com/ros/ros_comm/issues/720 .
63  sync_.reset();
64  async_.reset();
65  }
66 
68  {
69  pnh_->param("approximate_sync", approximate_sync_, true);
70  pnh_->param("queue_size", queue_size_, 10);
71  pnh_->param("averaging", averaging_, true); // TODO: -> dynparam
72 
73  XmlRpc::XmlRpcValue input_topics;
74  if (!pnh_->getParam("input_topics", input_topics))
75  {
76  NODELET_ERROR("Rosparam '~input_topics' is required.");
77  return;
78  }
79  if (input_topics.getType() != XmlRpc::XmlRpcValue::TypeArray)
80  {
81  NODELET_ERROR("Invalid type of '~input_topics' is specified. String array is expected.");
82  return;
83  }
84 
85  // Subscribe to the filters
86  filters_.resize(input_topics.size());
87 
88  // 8 topics
90  {
92  sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image,
93  sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image> >(queue_size_));
94  }
95  else
96  {
98  sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image,
99  sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image> > (queue_size_));
100  }
101 
102  // First input_topics.size() filters are valid
103  ROS_INFO("Subscribing to %d topics user given topics as inputs:", input_topics.size());
104  for (size_t i = 0; i < input_topics.size (); i++)
105  {
106  ROS_INFO_STREAM(" - " << (std::string)(input_topics[i]));
108  filters_[i]->subscribe(*pnh_, (std::string)(input_topics[i]), queue_size_);
109  }
110 
111  // Bogus null filter
112  filters_[0]->registerCallback(bind(&FuseDepthImages::input_callback, this, _1));
113 
114  if (input_topics.size() == 2)
115  {
116  if (approximate_sync_)
117  {
118  async_->connectInput(*filters_[0], *filters_[1], nf_, nf_, nf_, nf_, nf_, nf_);
119  }
120  else
121  {
122  sync_->connectInput(*filters_[0], *filters_[1], nf_, nf_, nf_, nf_, nf_, nf_);
123  }
124  }
125  else if (input_topics.size() == 3)
126  {
127  if (approximate_sync_)
128  {
129  async_->connectInput(*filters_[0], *filters_[1], *filters_[2], nf_, nf_, nf_, nf_, nf_);
130  }
131  else
132  {
133  sync_->connectInput(*filters_[0], *filters_[1], *filters_[2], nf_, nf_, nf_, nf_, nf_);
134  }
135  }
136  else if (input_topics.size() == 4)
137  {
138  if (approximate_sync_)
139  {
140  async_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], nf_, nf_, nf_, nf_);
141  }
142  else
143  {
144  sync_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], nf_, nf_, nf_, nf_);
145  }
146  }
147  else if (input_topics.size() == 5)
148  {
149  if (approximate_sync_)
150  {
151  async_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], *filters_[4], nf_, nf_, nf_);
152  }
153  else
154  {
155  sync_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], *filters_[4], nf_, nf_, nf_);
156  }
157  }
158  else if (input_topics.size() == 6)
159  {
160  if (approximate_sync_)
161  {
162  async_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], *filters_[4],
163  *filters_[5], nf_, nf_);
164  }
165  else
166  {
167  sync_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], *filters_[4],
168  *filters_[5], nf_, nf_);
169  }
170  }
171  else if (input_topics.size() == 7)
172  {
173  if (approximate_sync_)
174  {
175  async_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], *filters_[4],
176  *filters_[5], *filters_[6], nf_);
177  }
178  else
179  {
180  sync_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], *filters_[4],
181  *filters_[5], *filters_[6], nf_);
182  }
183  }
184  else if (input_topics.size() == 8)
185  {
186  if (approximate_sync_)
187  {
188  async_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], *filters_[4],
189  *filters_[5], *filters_[6], *filters_[7]);
190  }
191  else
192  {
193  sync_->connectInput(*filters_[0], *filters_[1], *filters_[2], *filters_[3], *filters_[4],
194  *filters_[5], *filters_[6], *filters_[7]);
195  }
196  }
197  else
198  {
199  NODELET_ERROR("Invalid number of topics is specified: %d. It must be > 1 and <= 8.", input_topics.size());
200  return;
201  }
202 
203  if (approximate_sync_)
204  {
205  async_->registerCallback(boost::bind(&FuseDepthImages::inputCb, this, _1, _2, _3, _4, _5, _6, _7, _8));
206  }
207  else
208  {
209  sync_->registerCallback(boost::bind(&FuseDepthImages::inputCb, this, _1, _2, _3, _4, _5, _6, _7, _8));
210  }
211  }
212 
214  {
215  for (size_t i = 0; i < filters_.size(); i++) {
216  filters_[i]->unsubscribe();
217  }
218  }
219 
221  const sensor_msgs::Image::ConstPtr& in,
222  const int height_expected,
223  const int width_expected,
224  std::vector<cv::Mat>& inputs)
225  {
226  if (in->height == 0 && in->width == 0)
227  {
228  // No subscription.
229  return false;
230  }
231  if (in->height != height_expected || in->width != width_expected)
232  {
233  NODELET_ERROR_THROTTLE(10, "Input images must have same size: height=%d, width=%d.",
234  height_expected, width_expected);
235  return false;
236  }
237  if (in->encoding != encoding_)
238  {
239  NODELET_ERROR_THROTTLE(10, "Input images must have same encoding. Expected: %s, Actual: %s",
240  encoding_.c_str(), in->encoding.c_str());
241  return false;
242  }
243  inputs.push_back(cv_bridge::toCvShare(in, encoding_)->image);
244  return true;
245  }
246 
247  void FuseImages::inputCb(
248  const sensor_msgs::Image::ConstPtr& in1, const sensor_msgs::Image::ConstPtr& in2,
249  const sensor_msgs::Image::ConstPtr& in3, const sensor_msgs::Image::ConstPtr& in4,
250  const sensor_msgs::Image::ConstPtr& in5, const sensor_msgs::Image::ConstPtr& in6,
251  const sensor_msgs::Image::ConstPtr& in7, const sensor_msgs::Image::ConstPtr& in8)
252  {
253  int height = in1->height;
254  int width = in1->width;
255  std::vector<cv::Mat> inputs;
256  validateInput(in1, height, width, inputs);
257  validateInput(in2, height, width, inputs);
258  validateInput(in3, height, width, inputs);
259  validateInput(in4, height, width, inputs);
260  validateInput(in5, height, width, inputs);
261  validateInput(in6, height, width, inputs);
262  validateInput(in7, height, width, inputs);
263  validateInput(in8, height, width, inputs);
264 
265  cv::Mat out = fuseInputs(inputs);
267  }
268 
269  cv::Mat FuseDepthImages::fuseInputs(const std::vector<cv::Mat> inputs)
270  {
271  cv::Mat out(inputs[0].rows, inputs[0].cols, cv_bridge::getCvType(encoding_));
272  out.setTo(std::numeric_limits<float>::quiet_NaN());
273  for (size_t j = 0; j < inputs[0].rows; j++)
274  {
275  for (size_t i = 0; i < inputs[0].cols; i++)
276  {
277  int n_fused = 0;
278  float value_fused = 0;
279  for (size_t k = 0; k < inputs.size(); k++)
280  {
281  float value = inputs[k].at<float>(j, i);
282  if (!std::isnan(value))
283  {
284  value_fused += value;
285  n_fused += 1;
286  }
287  }
288  if (n_fused > 0)
289  {
290  out.at<float>(j, i) = value_fused / n_fused;
291  }
292  }
293  }
294  return out;
295  }
296 
297  cv::Mat FuseRGBImages::fuseInputs(const std::vector<cv::Mat> inputs)
298  {
299  cv::Mat out(inputs[0].rows, inputs[0].cols, cv_bridge::getCvType(encoding_));
300  out.setTo(cv::Scalar(0, 0, 0));
301  for (size_t j = 0; j < inputs[0].rows; j++)
302  {
303  for (size_t i = 0; i < inputs[0].cols; i++)
304  {
305  int n_fused = 0;
306  cv::Vec3b value_fused(0, 0, 0);
307  for (size_t k = 0; k < inputs.size(); k++)
308  {
309  cv::Vec3b value = inputs[k].at<cv::Vec3b>(j, i);
310  if (value[0] != 0 || value[1] != 0 || value[2] != 0)
311  {
312  if (!averaging_ && n_fused > 0)
313  {
314  continue;
315  }
316  value_fused += value;
317  n_fused += 1;
318  }
319  }
320  if (n_fused > 0)
321  {
322  out.at<cv::Vec3b>(j, i) = value_fused / n_fused;
323  }
324  }
325  }
326  return out;
327  }
328 
329 } // namespace jsk_pcl_ros
330 
jsk_pcl_ros::FuseImages::filters_
std::vector< boost::shared_ptr< message_filters::Subscriber< sensor_msgs::Image > > > filters_
A vector of message filters.
Definition: fuse_images.h:140
XmlRpc::XmlRpcValue::size
int size() const
NODELET_ERROR
#define NODELET_ERROR(...)
jsk_pcl_ros::FuseImages::onInit
virtual void onInit()
Definition: fuse_images.cpp:80
message_filters::Synchronizer
i
int i
cv_bridge::CvImage::toImageMsg
sensor_msgs::ImagePtr toImageMsg() const
value
value
NODELET_ERROR_THROTTLE
#define NODELET_ERROR_THROTTLE(rate,...)
jsk_pcl_ros::FuseImages::nf_
message_filters::PassThrough< sensor_msgs::Image > nf_
Null passthrough filter, used for pushing empty elements in the synchronizer.
Definition: fuse_images.h:137
jsk_pcl_ros::FuseImages::approximate_sync_
bool approximate_sync_
Definition: fuse_images.h:130
jsk_pcl_ros::FuseImages::inputCb
virtual void inputCb(const sensor_msgs::Image::ConstPtr &in1, const sensor_msgs::Image::ConstPtr &in2, const sensor_msgs::Image::ConstPtr &in3, const sensor_msgs::Image::ConstPtr &in4, const sensor_msgs::Image::ConstPtr &in5, const sensor_msgs::Image::ConstPtr &in6, const sensor_msgs::Image::ConstPtr &in7, const sensor_msgs::Image::ConstPtr &in8)
Definition: fuse_images.cpp:279
jsk_pcl_ros::FuseImages::unsubscribe
virtual void unsubscribe()
Definition: fuse_images.cpp:245
jsk_pcl_ros::FuseImages::pub_out_
ros::Publisher pub_out_
Definition: fuse_images.h:128
jsk_pcl_ros::FuseImages::~FuseImages
virtual ~FuseImages()
Definition: fuse_images.cpp:87
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
message_filters::Subscriber< sensor_msgs::Image >
class_list_macros.h
fuse_images.h
jsk_pcl_ros::FuseImages::async_
boost::shared_ptr< message_filters::Synchronizer< message_filters::sync_policies::ApproximateTime< sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image > > > async_
Synchronizer.
Definition: fuse_images.h:147
message_filters::sync_policies::ApproximateTime
jsk_pcl_ros::FuseDepthImages::fuseInputs
virtual cv::Mat fuseInputs(std::vector< cv::Mat > inputs)
Definition: fuse_images.cpp:301
jsk_pcl_ros::FuseImages::sync_
boost::shared_ptr< message_filters::Synchronizer< message_filters::sync_policies::ExactTime< sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::Image > > > sync_
Definition: fuse_images.h:150
jsk_pcl_ros
Definition: add_color_from_image.h:51
k
int k
jsk_pcl_ros::FuseImages::validateInput
virtual bool validateInput(const sensor_msgs::Image::ConstPtr &in, const int height_expected, const int width_expected, std::vector< cv::Mat > &inputs)
Definition: fuse_images.cpp:252
jsk_pcl_ros::FuseImages::encoding_
std::string encoding_
Definition: fuse_images.h:133
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
jsk_pcl_ros::FuseImages::fuseInputs
virtual cv::Mat fuseInputs(std::vector< cv::Mat > inputs)
Definition: fuse_images.h:169
XmlRpc::XmlRpcValue::getType
const Type & getType() const
XmlRpc::XmlRpcValue::TypeArray
TypeArray
PLUGINLIB_EXPORT_CLASS
PLUGINLIB_EXPORT_CLASS(jsk_pcl_ros::FuseDepthImages, nodelet::Nodelet)
nodelet::Nodelet
width
width
jsk_pcl_ros::FuseImages::subscribe
virtual void subscribe()
Definition: fuse_images.cpp:99
cv_bridge.h
jsk_pcl_ros::FuseImages::input_callback
void input_callback(const sensor_msgs::Image::ConstPtr &input)
Input point cloud callback. Because we want to use the same synchronizer object, we push back empty e...
Definition: fuse_images.h:157
cv_bridge::CvImage
jsk_pcl_ros::FuseImages::averaging_
bool averaging_
Definition: fuse_images.h:132
jsk_pcl_ros::FuseRGBImages
Definition: fuse_images.h:149
cv_bridge::toCvShare
CvImageConstPtr toCvShare(const sensor_msgs::Image &source, const boost::shared_ptr< void const > &tracked_object, const std::string &encoding=std::string())
cv_bridge::getCvType
int getCvType(const std::string &encoding)
message_filters::sync_policies::ExactTime
height
height
ROS_INFO
#define ROS_INFO(...)
jsk_pcl_ros::FuseImages::queue_size_
int queue_size_
Definition: fuse_images.h:131
jsk_pcl_ros::FuseRGBImages::fuseInputs
virtual cv::Mat fuseInputs(std::vector< cv::Mat > inputs)
Definition: fuse_images.cpp:329
jsk_pcl_ros::FuseDepthImages
Definition: fuse_images.h:141
XmlRpc::XmlRpcValue
pcl_conversions.h


jsk_pcl_ros
Author(s): Yohei Kakiuchi
autogenerated on Tue Jan 7 2025 04:05:44