gazebo_ros_image_sonar.cpp
Go to the documentation of this file.
1 /*
2  * This file was modified from the original version within Gazebo:
3  *
4  * Copyright (C) 2014 Open Source Robotics Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * Modifications:
19  *
20  * Copyright 2018 Nils Bore (nbore@kth.se)
21  *
22  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
23  *
24  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
25  *
26  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
27  *
28  * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33 
34 #include <algorithm>
35 #include <assert.h>
36 #include <boost/thread/thread.hpp>
37 #include <boost/bind.hpp>
38 
39 //#include <smarc_gazebo_ros_plugins/gazebo_ros_image_sonar.h>
41 #include <gazebo/sensors/Sensor.hh>
42 #include <sdf/sdf.hh>
43 #include <gazebo/sensors/SensorTypes.hh>
44 
46 
47 #include <tf/tf.h>
48 
50 #include <cv_bridge/cv_bridge.h>
51 #include <opencv2/core/core.hpp>
52 
53 namespace gazebo
54 {
55 // Register this plugin with the simulator
56 GZ_REGISTER_SENSOR_PLUGIN(GazeboRosImageSonar)
57 
58 // Constructor
60 GazeboRosImageSonar::GazeboRosImageSonar() : SensorPlugin(), width(0), height(0), depth(0)
61 {
62  this->point_cloud_connect_count_ = 0;
63  this->depth_info_connect_count_ = 0;
64  this->last_depth_image_camera_info_update_time_ = common::Time(0);
65 }
66 
68 // Destructor
70 {
71  this->newDepthFrameConnection.reset();
72  this->newRGBPointCloudConnection.reset();
73  this->newImageFrameConnection.reset();
74 
75  this->parentSensor.reset();
76  this->depthCamera.reset();
77 }
78 
80 // Load the controller
81 void GazeboRosImageSonar::Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf)
82 {
83  this->parentSensor =
84  std::dynamic_pointer_cast<sensors::DepthCameraSensor>(_parent);
85  this->depthCamera = this->parentSensor->DepthCamera();
86 
87  if (!this->parentSensor)
88  {
89  gzerr << "DepthCameraPlugin not attached to a depthCamera sensor\n";
90  return;
91  }
92 
93  this->width = this->depthCamera->ImageWidth();
94  this->height = this->depthCamera->ImageHeight();
95  this->depth = this->depthCamera->ImageDepth();
96  this->format = this->depthCamera->ImageFormat();
97 
98  this->newDepthFrameConnection = this->depthCamera->ConnectNewDepthFrame(
100  this, std::placeholders::_1, std::placeholders::_2,
101  std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
102 
103  this->newRGBPointCloudConnection = this->depthCamera->ConnectNewRGBPointCloud(
105  this, std::placeholders::_1, std::placeholders::_2,
106  std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
107 
108  this->newImageFrameConnection = this->depthCamera->ConnectNewImageFrame(
110  this, std::placeholders::_1, std::placeholders::_2,
111  std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
112 
113  this->parentSensor->SetActive(true);
114 
115  // Make sure the ROS node for Gazebo has already been initialized
116  if (!ros::isInitialized())
117  {
118  ROS_FATAL_STREAM_NAMED("depth_camera", "A ROS node for Gazebo has not been initialized, unable to load plugin. "
119  << "Load the Gazebo system plugin 'libgazebo_ros_api_plugin.so' in the gazebo_ros package)");
120  return;
121  }
122 
123  // copying from DepthCameraPlugin into GazeboRosCameraUtils
124  this->parentSensor_ = this->parentSensor;
125  this->width_ = this->width;
126  this->height_ = this->height;
127  this->depth_ = this->depth;
128  this->format_ = this->format;
129  this->camera_ = this->depthCamera;
130 
131  // using a different default
132  if (!_sdf->HasElement("imageTopicName"))
133  this->image_topic_name_ = "ir/image_raw";
134  if (!_sdf->HasElement("cameraInfoTopicName"))
135  this->camera_info_topic_name_ = "ir/camera_info";
136 
137  // point cloud stuff
138  if (!_sdf->HasElement("pointCloudTopicName"))
139  this->point_cloud_topic_name_ = "points";
140  else
141  this->point_cloud_topic_name_ = _sdf->GetElement("pointCloudTopicName")->Get<std::string>();
142 
143  // depth image stuff
144  if (!_sdf->HasElement("depthImageTopicName"))
145  this->depth_image_topic_name_ = "depth/image_raw";
146  else
147  this->depth_image_topic_name_ = _sdf->GetElement("depthImageTopicName")->Get<std::string>();
148 
149  if (!_sdf->HasElement("depthImageCameraInfoTopicName"))
150  this->depth_image_camera_info_topic_name_ = "depth/camera_info";
151  else
152  this->depth_image_camera_info_topic_name_ = _sdf->GetElement("depthImageCameraInfoTopicName")->Get<std::string>();
153 
154  if (!_sdf->HasElement("pointCloudCutoff"))
155  this->point_cloud_cutoff_ = 0.4;
156  else
157  this->point_cloud_cutoff_ = _sdf->GetElement("pointCloudCutoff")->Get<double>();
158 
159  if (!_sdf->HasElement("clip")) {
160  gzerr << "We do not have clip" << std::endl;
161  }
162  else {
163  gzerr << "We do have clip" << std::endl;
164  gzerr << _sdf->GetElement("clip")->GetElement("far")->Get<double>() << std::endl;
165  }
166 
168  GazeboRosCameraUtils::Load(_parent, _sdf);
169 }
170 
172 {
173  ros::AdvertiseOptions point_cloud_ao =
174  ros::AdvertiseOptions::create<sensor_msgs::PointCloud2 >(
175  this->point_cloud_topic_name_,1,
176  boost::bind( &GazeboRosImageSonar::PointCloudConnect,this),
177  boost::bind( &GazeboRosImageSonar::PointCloudDisconnect,this),
178  ros::VoidPtr(), &this->camera_queue_);
179  this->point_cloud_pub_ = this->rosnode_->advertise(point_cloud_ao);
180 
181  ros::AdvertiseOptions depth_image_ao =
182  ros::AdvertiseOptions::create< sensor_msgs::Image >(
183  this->depth_image_topic_name_,1,
184  boost::bind( &GazeboRosImageSonar::DepthImageConnect,this),
185  boost::bind( &GazeboRosImageSonar::DepthImageDisconnect,this),
186  ros::VoidPtr(), &this->camera_queue_);
187  this->depth_image_pub_ = this->rosnode_->advertise(depth_image_ao);
188 
189  ros::AdvertiseOptions normal_image_ao =
190  ros::AdvertiseOptions::create< sensor_msgs::Image >(
191  this->depth_image_topic_name_+"_normals",1,
192  boost::bind( &GazeboRosImageSonar::NormalImageConnect,this),
193  boost::bind( &GazeboRosImageSonar::NormalImageDisconnect,this),
194  ros::VoidPtr(), &this->camera_queue_);
195  this->normal_image_pub_ = this->rosnode_->advertise(normal_image_ao);
196 
197  ros::AdvertiseOptions multibeam_image_ao =
198  ros::AdvertiseOptions::create< sensor_msgs::Image >(
199  this->depth_image_topic_name_+"_multibeam",1,
200  boost::bind( &GazeboRosImageSonar::MultibeamImageConnect,this),
202  ros::VoidPtr(), &this->camera_queue_);
203  this->multibeam_image_pub_ = this->rosnode_->advertise(multibeam_image_ao);
204 
205  ros::AdvertiseOptions sonar_image_ao =
206  ros::AdvertiseOptions::create< sensor_msgs::Image >(
207  this->depth_image_topic_name_+"_sonar",1,
208  boost::bind( &GazeboRosImageSonar::SonarImageConnect,this),
209  boost::bind( &GazeboRosImageSonar::SonarImageDisconnect,this),
210  ros::VoidPtr(), &this->camera_queue_);
211  this->sonar_image_pub_ = this->rosnode_->advertise(sonar_image_ao);
212 
213  ros::AdvertiseOptions raw_sonar_image_ao =
214  ros::AdvertiseOptions::create< sensor_msgs::Image >(
215  this->depth_image_topic_name_+"_raw_sonar",1,
216  boost::bind( &GazeboRosImageSonar::RawSonarImageConnect,this),
218  ros::VoidPtr(), &this->camera_queue_);
219  this->raw_sonar_image_pub_ = this->rosnode_->advertise(raw_sonar_image_ao);
220 
221  ros::AdvertiseOptions depth_image_camera_info_ao =
222  ros::AdvertiseOptions::create<sensor_msgs::CameraInfo>(
224  boost::bind( &GazeboRosImageSonar::DepthInfoConnect,this),
225  boost::bind( &GazeboRosImageSonar::DepthInfoDisconnect,this),
226  ros::VoidPtr(), &this->camera_queue_);
227  this->depth_image_camera_info_pub_ = this->rosnode_->advertise(depth_image_camera_info_ao);
228 }
229 
230 
232 // Increment count
234 {
236  (*this->image_connect_count_)++;
237  this->parentSensor->SetActive(true);
238 }
240 // Decrement count
242 {
244  (*this->image_connect_count_)--;
245  if (this->point_cloud_connect_count_ <= 0)
246  this->parentSensor->SetActive(false);
247 }
248 
250 // Increment count
252 {
254  this->parentSensor->SetActive(true);
255 }
257 // Decrement count
259 {
261 }
262 
264 // Increment count
266 {
268  this->parentSensor->SetActive(true);
269 }
271 // Decrement count
273 {
275 }
276 
278 // Increment count
280 {
282  this->parentSensor->SetActive(true);
283 }
285 // Decrement count
287 {
289 }
290 
292 // Increment count
294 {
296  this->parentSensor->SetActive(true);
297 }
299 // Decrement count
301 {
303 }
304 
306 // Increment count
308 {
310  this->parentSensor->SetActive(true);
311 }
313 // Decrement count
315 {
317 }
318 
320 // Increment count
322 {
324 }
326 // Decrement count
328 {
330 }
331 
333 // Update the controller
334 void GazeboRosImageSonar::OnNewDepthFrame(const float *_image,
335  unsigned int _width, unsigned int _height, unsigned int _depth,
336  const std::string &_format)
337 {
338  if (!this->initialized_ || this->height_ <=0 || this->width_ <=0)
339  return;
340 
341  this->depth_sensor_update_time_ = this->parentSensor->LastMeasurementTime();
342 
343  if (this->parentSensor->IsActive())
344  {
345  if (this->point_cloud_connect_count_ <= 0 &&
346  this->depth_image_connect_count_ <= 0 &&
347  (*this->image_connect_count_) <= 0)
348  {
349  this->parentSensor->SetActive(false);
350  }
351  else
352  {
353  if (this->point_cloud_connect_count_ > 0)
354  this->FillPointdCloud(_image);
355 
356  if (this->depth_image_connect_count_ > 0)
357  //this->FillDepthImage(_image);
358  this->ComputeSonarImage(_image);
359  }
360  }
361  else
362  {
363  if (this->point_cloud_connect_count_ > 0 ||
364  this->depth_image_connect_count_ <= 0)
365  // do this first so there's chance for sensor to run 1 frame after activate
366  this->parentSensor->SetActive(true);
367  }
368 }
369 
371 // Update the controller
373  unsigned int _width, unsigned int _height, unsigned int _depth,
374  const std::string &_format)
375 {
376  if (!this->initialized_ || this->height_ <=0 || this->width_ <=0)
377  return;
378 
379  this->depth_sensor_update_time_ = this->parentSensor->LastMeasurementTime();
380 
381  if (!this->parentSensor->IsActive())
382  {
383  if (this->point_cloud_connect_count_ > 0)
384  // do this first so there's chance for sensor to run 1 frame after activate
385  this->parentSensor->SetActive(true);
386  }
387  else
388  {
389  if (this->point_cloud_connect_count_ > 0)
390  {
391  this->lock_.lock();
392  this->point_cloud_msg_.header.frame_id = this->frame_name_;
393  this->point_cloud_msg_.header.stamp.sec = this->depth_sensor_update_time_.sec;
394  this->point_cloud_msg_.header.stamp.nsec = this->depth_sensor_update_time_.nsec;
395  this->point_cloud_msg_.width = this->width;
396  this->point_cloud_msg_.height = this->height;
397  this->point_cloud_msg_.row_step = this->point_cloud_msg_.point_step * this->width;
398 
400  pcd_modifier.setPointCloud2FieldsByString(2, "xyz", "rgb");
401  pcd_modifier.resize(_width*_height);
402 
403  point_cloud_msg_.is_dense = true;
404 
409 
410  for (unsigned int i = 0; i < _width; i++)
411  {
412  for (unsigned int j = 0; j < _height; j++, ++iter_x, ++iter_y, ++iter_z, ++iter_rgb)
413  {
414  unsigned int index = (j * _width) + i;
415  *iter_x = _pcd[4 * index];
416  *iter_y = _pcd[4 * index + 1];
417  *iter_z = _pcd[4 * index + 2];
418  *iter_rgb = _pcd[4 * index + 3];
419  if (i == _width /2 && j == _height / 2)
420  {
421  uint32_t rgb = *reinterpret_cast<int*>(&(*iter_rgb));
422  uint8_t r = (rgb >> 16) & 0x0000ff;
423  uint8_t g = (rgb >> 8) & 0x0000ff;
424  uint8_t b = (rgb) & 0x0000ff;
425  std::cerr << (int)r << " " << (int)g << " " << (int)b << "\n";
426  }
427  }
428  }
429 
431  this->lock_.unlock();
432  }
433  }
434 }
435 
437 // Update the controller
438 void GazeboRosImageSonar::OnNewImageFrame(const unsigned char *_image,
439  unsigned int _width, unsigned int _height, unsigned int _depth,
440  const std::string &_format)
441 {
442  if (!this->initialized_ || this->height_ <=0 || this->width_ <=0)
443  return;
444 
445  //ROS_ERROR_NAMED("depth_camera", "camera_ new frame %s %s",this->parentSensor_->GetName().c_str(),this->frame_name_.c_str());
446  this->sensor_update_time_ = this->parentSensor->LastMeasurementTime();
447 
448  if (!this->parentSensor->IsActive())
449  {
450  if ((*this->image_connect_count_) > 0)
451  // do this first so there's chance for sensor to run 1 frame after activate
452  this->parentSensor->SetActive(true);
453  }
454  else
455  {
456  if ((*this->image_connect_count_) > 0)
457  {
458  this->PutCameraData(_image);
459  // TODO(lucasw) publish camera info with depth image
460  // this->PublishCameraInfo(sensor_update_time);
461  }
462  }
463 }
464 
466 // Put camera data to the interface
468 {
469  this->lock_.lock();
470 
471  this->point_cloud_msg_.header.frame_id = this->frame_name_;
472  this->point_cloud_msg_.header.stamp.sec = this->depth_sensor_update_time_.sec;
473  this->point_cloud_msg_.header.stamp.nsec = this->depth_sensor_update_time_.nsec;
474  this->point_cloud_msg_.width = this->width;
475  this->point_cloud_msg_.height = this->height;
476  this->point_cloud_msg_.row_step = this->point_cloud_msg_.point_step * this->width;
477 
480  this->height,
481  this->width,
482  this->skip_,
483  (void*)_src );
484 
486 
487  this->lock_.unlock();
488 }
489 
491 // Put depth image data to the interface
492 void GazeboRosImageSonar::FillDepthImage(const float *_src)
493 {
494  this->lock_.lock();
495  // copy data into image
496  this->depth_image_msg_.header.frame_id = this->frame_name_;
497  this->depth_image_msg_.header.stamp.sec = this->depth_sensor_update_time_.sec;
498  this->depth_image_msg_.header.stamp.nsec = this->depth_sensor_update_time_.nsec;
499 
502  this->height,
503  this->width,
504  this->skip_,
505  (void*)_src );
506 
508 
509  this->lock_.unlock();
510 }
511 
512 
513 // Fill depth information
515  sensor_msgs::PointCloud2 &point_cloud_msg,
516  uint32_t rows_arg, uint32_t cols_arg,
517  uint32_t step_arg, void* data_arg)
518 {
519  sensor_msgs::PointCloud2Modifier pcd_modifier(point_cloud_msg);
520  pcd_modifier.setPointCloud2FieldsByString(2, "xyz", "rgb");
521  pcd_modifier.resize(rows_arg*cols_arg);
522 
527 
528  point_cloud_msg.is_dense = true;
529 
530  float* toCopyFrom = (float*)data_arg;
531  int index = 0;
532 
533  double hfov = this->parentSensor->DepthCamera()->HFOV().Radian();
534  double fl = ((double)this->width) / (2.0 *tan(hfov/2.0));
535 
536  // convert depth to point cloud
537  for (uint32_t j=0; j<rows_arg; j++)
538  {
539  double pAngle;
540  if (rows_arg>1) pAngle = atan2( (double)j - 0.5*(double)(rows_arg-1), fl);
541  else pAngle = 0.0;
542 
543  for (uint32_t i=0; i<cols_arg; i++, ++iter_x, ++iter_y, ++iter_z, ++iter_rgb)
544  {
545  double yAngle;
546  if (cols_arg>1) yAngle = atan2( (double)i - 0.5*(double)(cols_arg-1), fl);
547  else yAngle = 0.0;
548 
549  double depth = toCopyFrom[index++];
550 
551  // in optical frame
552  // hardcoded rotation rpy(-M_PI/2, 0, -M_PI/2) is built-in
553  // to urdf, where the *_optical_frame should have above relative
554  // rotation from the physical camera *_frame
555  *iter_x = depth * tan(yAngle);
556  *iter_y = depth * tan(pAngle);
557  if(depth > this->point_cloud_cutoff_)
558  {
559  *iter_z = depth;
560  }
561  else //point in the unseeable range
562  {
563  *iter_x = *iter_y = *iter_z = std::numeric_limits<float>::quiet_NaN ();
564  point_cloud_msg.is_dense = false;
565  }
566 
567  // put image color data for each point
568  uint8_t* image_src = (uint8_t*)(&(this->image_msg_.data[0]));
569  if (this->image_msg_.data.size() == rows_arg*cols_arg*3)
570  {
571  // color
572  iter_rgb[0] = image_src[i*3+j*cols_arg*3+0];
573  iter_rgb[1] = image_src[i*3+j*cols_arg*3+1];
574  iter_rgb[2] = image_src[i*3+j*cols_arg*3+2];
575  }
576  else if (this->image_msg_.data.size() == rows_arg*cols_arg)
577  {
578  // mono (or bayer? @todo; fix for bayer)
579  iter_rgb[0] = image_src[i+j*cols_arg];
580  iter_rgb[1] = image_src[i+j*cols_arg];
581  iter_rgb[2] = image_src[i+j*cols_arg];
582  }
583  else
584  {
585  // no image
586  iter_rgb[0] = 0;
587  iter_rgb[1] = 0;
588  iter_rgb[2] = 0;
589  }
590  }
591  }
592 
593  return true;
594 }
595 
596 // Fill depth information
598  sensor_msgs::Image& image_msg,
599  uint32_t rows_arg, uint32_t cols_arg,
600  uint32_t step_arg, void* data_arg)
601 {
602  image_msg.encoding = sensor_msgs::image_encodings::TYPE_32FC1;
603  image_msg.height = rows_arg;
604  image_msg.width = cols_arg;
605  image_msg.step = sizeof(float) * cols_arg;
606  image_msg.data.resize(rows_arg * cols_arg * sizeof(float));
607  image_msg.is_bigendian = 0;
608 
609  const float bad_point = std::numeric_limits<float>::quiet_NaN();
610 
611  float* dest = (float*)(&(image_msg.data[0]));
612  float* toCopyFrom = (float*)data_arg;
613  int index = 0;
614 
615  // convert depth to point cloud
616  for (uint32_t j = 0; j < rows_arg; j++)
617  {
618  for (uint32_t i = 0; i < cols_arg; i++)
619  {
620  float depth = toCopyFrom[index++];
621 
622  if (depth > this->point_cloud_cutoff_)
623  {
624  dest[i + j * cols_arg] = depth;
625  }
626  else //point in the unseeable range
627  {
628  dest[i + j * cols_arg] = bad_point;
629  }
630  }
631  }
632  return true;
633 }
634 
636 {
637 
638  // copy data into image
639  this->normal_image_msg_.header.frame_id = this->frame_name_;
640  this->normal_image_msg_.header.stamp.sec = this->depth_sensor_update_time_.sec;
641  this->normal_image_msg_.header.stamp.nsec = this->depth_sensor_update_time_.nsec;
642 
643  // filters
644  cv::Mat_<float> f1 = (cv::Mat_<float>(3, 3) << 1, 2, 1,
645  0, 0, 0,
646  -1, -2, -1) / 8;
647 
648  cv::Mat_<float> f2 = (cv::Mat_<float>(3, 3) << 1, 0, -1,
649  2, 0, -2,
650  1, 0, -1) / 8;
651 
652  cv::Mat f1m, f2m;
653  cv::flip(f1, f1m, 0);
654  cv::flip(f2, f2m, 1);
655 
656  cv::Mat n1, n2;
657  cv::filter2D(depth, n1, -1, f1m, cv::Point(-1, -1), 0, cv::BORDER_REPLICATE);
658  cv::filter2D(depth, n2, -1, f2m, cv::Point(-1, -1), 0, cv::BORDER_REPLICATE);
659 
660  cv::Mat no_readings;
661  cv::erode(depth == 0, no_readings, cv::Mat(), cv::Point(-1, -1), 2, 1, 1);
662  //cv::dilate(no_readings, no_readings, cv::Mat(), cv::Point(-1, -1), 2, 1, 1);
663  n1.setTo(0, no_readings);
664  n2.setTo(0, no_readings);
665 
666  std::vector<cv::Mat> images(3);
667  cv::Mat white = cv::Mat::ones(depth.rows, depth.cols, CV_32FC1);
668 
669  // NOTE: with different focal lengths, the expression becomes
670  // (-dzx*fy, -dzy*fx, fx*fy)
671  images.at(0) = n1; //for green channel
672  images.at(1) = n2; //for red channel
673  images.at(2) = 1./this->focal_length_*depth; //for blue channel
674 
675  cv::Mat normal_image; // = cv::Mat::zeros(depth.rows, depth.cols, CV_32FC3);
676  cv::merge(images, normal_image);
677 
678  // TODO: we should do this on the split images instead
679  for (int i = 0; i < normal_image.rows; ++i) {
680  for (int j = 0; j < normal_image.cols; ++j) {
681  cv::Vec3f& n = normal_image.at<cv::Vec3f>(i, j);
682  n = cv::normalize(n);
683  }
684  }
685 
686  cv::split(normal_image.clone(), images);
687  cv::Vec3d minVec, maxVec;
688  for (int i = 0; i < 3; ++i) {
689  cv::minMaxLoc(images[i], &minVec[i], &maxVec[i]);
690  images[i] -= minVec[i];
691  images[i] *= 1./(maxVec[i] - minVec[i]);
692  }
693 
694  cv::merge(images, normal_image);
695  cv::Mat normal_image8;
696  normal_image.convertTo(normal_image8, CV_8UC3, 255.0);
697 
698  cv_bridge::CvImage img_bridge;
699  img_bridge = cv_bridge::CvImage(this->normal_image_msg_.header, sensor_msgs::image_encodings::RGB8, normal_image8);
700  img_bridge.toImageMsg(this->normal_image_msg_); // from cv_bridge to sensor_msgs::Image
701 
703 
704  return normal_image;
705 }
706 
707 cv::Mat GazeboRosImageSonar::ConstructSonarImage(cv::Mat& depth, cv::Mat& normals)
708 {
709  std::vector<cv::Mat> images(3);
710  cv::split(normals, images);
711 
712  float intensity = 100.; // target strength
713  float SL = 200.; // source level
714  float NL = 30; // noise level
715  float DI = 0.0; // directivity index
716 
717  if (dist_matrix_.empty()) {
718  std::vector<float> t_x, t_y;
719  for (int i = 0; i < depth.cols; i++) t_x.push_back((float(i) - this->cx_)/this->focal_length_);
720  for (int i = 0; i < depth.rows; i++) t_y.push_back((float(i) - this->cy_)/this->focal_length_);
721  cv::Mat X, Y;
722  cv::repeat(cv::Mat(t_x).reshape(1,1), t_y.size(), 1, X);
723  cv::repeat(cv::Mat(t_y).reshape(1,1).t(), 1, t_x.size(), Y);
724  dist_matrix_ = cv::Mat::zeros(depth.rows, depth.cols, CV_32FC1);
725  cv::multiply(X, X, X);
726  cv::multiply(Y, Y, Y);
727  cv::sqrt(X + Y + 1, dist_matrix_);
728  }
729 
730  // TODO: make these into proper parameters
731  cv::Mat TS = intensity*images[2]; // target strength, probably dir should be DI
732  cv::Mat TL = 5*depth; // transmission loss
733  cv::multiply(TL, dist_matrix_, TL);
734  cv::Mat SNR = SL - 2.0*TL - (NL-DI) + TS;
735  SNR.setTo(0., SNR < 0.);
736 
737  double minVal, maxVal;
738  cv::minMaxLoc(SNR, &minVal, &maxVal);
739  SNR -= minVal;
740  SNR *= 1./(maxVal - minVal);
741 
742  cv::Mat sonar_image8;
743  SNR.convertTo(sonar_image8, CV_8UC3, 255.0);
744 
745  cv_bridge::CvImage img_bridge;
746  img_bridge = cv_bridge::CvImage(this->multibeam_image_msg_.header, sensor_msgs::image_encodings::MONO8, sonar_image8);
747  img_bridge.toImageMsg(this->multibeam_image_msg_); // from cv_bridge to sensor_msgs::Image
748 
750 
751  return SNR; //sonar_image8;
752 }
753 
754 void GazeboRosImageSonar::ApplySpeckleNoise(cv::Mat& scan, float fov)
755 {
756  std::normal_distribution<double> speckle_dist(1.0, 0.1);
757 
758  for (int i = 0; i < scan.rows; ++i) {
759  for (int j = 0; j < scan.cols; ++j) {
760  float& a = scan.at<float>(i, j);
761  if (a == 0.) {
762  continue;
763  }
764  float speckle = fabs(speckle_dist(generator));
765  a *= speckle;
766  }
767  }
768 }
769 
770 void GazeboRosImageSonar::ApplySmoothing(cv::Mat& scan, float fov)
771 {
772  int nrolls = 300;
773  int window_size = 30;
774 
775  if (angle_range_indices_.empty()) {
776  angle_range_indices_.resize(scan.rows/1);
777  angle_nbr_indices_.resize(scan.rows/2, 0);
778  float threshold = tan(fov);
779  for (int j = 0; j < scan.cols; ++j) {
780  for (int i = 0; i < scan.rows; ++i) {
781  float x = fabs(float(scan.cols)/2. - j);
782  float y = scan.rows - i;
783  int dist = int(sqrt(x*x+y*y))/2;
784  if (dist >= scan.rows/2 || fabs(x)/y > threshold) {
785  continue;
786  }
787  angle_range_indices_[dist].push_back(scan.cols*i+j);
788  angle_nbr_indices_[dist] += 1;
789  }
790  }
791  }
792 
793  std::discrete_distribution<> range_dist(angle_nbr_indices_.begin(), angle_nbr_indices_.end());
794  std::uniform_real_distribution<double> index_dist(0.0, 1.0);
795 
796  std::vector<float> kernel(window_size);
797  for (int i = 0; i < window_size; ++i) {
798  float diff = float(i-window_size/2)/float(window_size/4.);
799  kernel[i] = exp(-0.5*diff);
800  }
801 
802  std::vector<float> conv_results(2*window_size);
803  for (int i = 0; i < nrolls; ++i) {
804  int sampled_range = range_dist(generator);
805  if (angle_nbr_indices_[sampled_range] == 0) {
806  continue;
807  }
808  int sampled_index = int(index_dist(generator)*angle_nbr_indices_[sampled_range]);
809 
810  int window_start = std::max(0, sampled_index-window_size);
811  int window_end = std::min(angle_nbr_indices_[sampled_range], sampled_index+window_size);
812  for (int i = window_start; i < window_end; ++i) {
813  conv_results[i - window_start] = 0.;
814  float conv_mass = 0.;
815  for (int j = 0; j < window_size; ++j) {
816  int index = i + j - window_size/2;
817  if (index >= 0 && index < angle_nbr_indices_[sampled_range]) {
818  conv_results[i - window_start] += kernel[j]*scan.at<float>(angle_range_indices_[sampled_range][index]);
819  conv_mass += kernel[j];
820  }
821  }
822  if (conv_mass == 0.) {
823  conv_results[i - window_start] = scan.at<float>(angle_range_indices_[sampled_range][i]);
824  }
825  else {
826  conv_results[i - window_start] *= 1./conv_mass;
827  }
828  }
829 
830  for (int i = window_start; i < window_end; ++i) {
831  scan.at<float>(angle_range_indices_[sampled_range][i]) = conv_results[i - window_start];
832  }
833 
834  }
835 
836 }
837 
839 {
840  cv::Mat is_zero = scan == 0.;
841  cv::Mat is_bg = scan == 0.2;
842  cv::bitwise_or(is_zero, is_bg, is_zero);
843 
844  cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 9));
845  cv::Mat scan_dilated;
846  cv::dilate(scan, scan_dilated, element, cv::Point(-1, -1), 1, 1, 1);
847  //scan.setTo(scan_dilated, is_zero);
848  scan_dilated.copyTo(scan, is_zero);
849 }
850 
851 cv::Mat GazeboRosImageSonar::ConstructScanImage(cv::Mat& depth, cv::Mat& SNR)
852 {
853  int rows = 400; // TODO: add a parameter for this
854  float range = 17.; // TODO: get this from the sensor config instead
855 
856  float fov = depthCamera->HFOV().Degree();
857  int cols = 2*int(float(rows)*sin(M_PI/180.*fov/2.))+20;
858 
859  cv::Mat scan = cv::Mat::zeros(rows, cols, CV_32FC1);
860  //float fov = 180./M_PI*2.*asin(this->cx_/this->focal_length_);
861  cv::Point center(scan.cols/2, scan.rows);
862  cv::Size full_axes(scan.rows, scan.rows);
863  cv::ellipse(scan, center, full_axes, -90, -fov/2., fov/2., 0.2, -1);
864  cv::Size third_axes(scan.rows/3, scan.rows/3);
865  cv::ellipse(scan, center, third_axes, -90, -fov/2., fov/2., 0, -1);
866 
867  float mapped_range = float(scan.rows);
868 
869  for (int i = 0; i < depth.rows; ++i) {
870  for (int j = 0; j < depth.cols; ++j) {
871  float d = depth.at<float>(i, j);
872  //uchar a = SNR.at<uchar>(i, j);
873  float a = SNR.at<float>(i, j);
874  if (d == 0 || a == 0) {
875  continue;
876  }
877  float x = (float(j) - this->cx_)/this->focal_length_;
878  float y = (float(i) - this->cy_)/this->focal_length_;
879  float z = 1.;
880 
881  if (false) {
882  z = d;
883  }
884  else {
885  z = d*sqrt(y*y + z*z);
886  }
887  x *= z; y *= z;
888 
889  int pi = scan.rows - 1 - int(z/range*mapped_range);
890  int pj = scan.cols/2 + int(x/range*mapped_range);
891  if (pi < scan.rows && pi > 0 && pj < scan.cols && pj > 0 && x*x + z*z < range*range) {
892  scan.at<float>(pi, pj) = a;
893  }
894  }
895  }
896  this->ApplyMedianFilter(scan);
897  this->ApplySpeckleNoise(scan, fov);
898  //this->ApplySmoothing(scan, fov);
899 
900  cv_bridge::CvImage img_bridge;
902  img_bridge.toImageMsg(this->raw_sonar_image_msg_); // from cv_bridge to sensor_msgs::Image
903 
905 
906  return scan;
907 }
908 
910 {
911  float fov = depthCamera->HFOV().Degree();
912  float mapped_range = float(raw_scan.rows);
913 
914  cv::Scalar blue(15, 48, 102);
915  cv::Scalar black(0, 0, 0);
916  cv::Mat scan(raw_scan.rows, raw_scan.cols, CV_8UC3);
917  scan.setTo(blue);
918 
919  cv::Point center(scan.cols/2, scan.rows);
920  cv::Size axes(scan.rows+3, scan.rows+3);
921  cv::ellipse(scan, center, axes, -90, -fov/2., fov/2., black, -1); //, int lineType=LINE_8, 0);
922 
923  for (int i = 0; i < scan.rows; ++i) {
924  for (int j = 0; j < scan.cols; ++j) {
925  float a = raw_scan.at<float>(i, j);
926  if (a == 0.) {
927  continue;
928  }
929 
930  if (a < 0.8) {
931  scan.at<cv::Vec3b>(i, j) = cv::Vec3b(255*1.25*a, 255*0.78*a, 255*0.50*a);
932  }
933  else if(a < 1.) {
934  scan.at<cv::Vec3b>(i, j) = cv::Vec3b(255*a, 255*(1.88*a-0.88), 255*(-1.99*a+1.99));
935  }
936  else {
937  scan.at<cv::Vec3b>(i, j) = cv::Vec3b(255, 255*(1.88-0.88), 255*(-1.99+1.99));
938  }
939  }
940  }
941 
942  cv::Scalar white(255, 255, 255);
943  cv::Size axes1(2./3.*scan.rows, 2./3.*scan.rows);
944  cv::Size axes2(1./3.*scan.rows, 1./3.*scan.rows);
945  cv::ellipse(scan, center, axes, -90, -fov/2.-0.5, fov/2., white, 1, CV_AA); //, int lineType=LINE_8, 0);
946  cv::ellipse(scan, center, axes1, -90, -fov/2., fov/2., white, 1, CV_AA); //, int lineType=LINE_8, 0);
947  cv::ellipse(scan, center, axes2, -90, -fov/2., fov/2., white, 1, CV_AA); //, int lineType=LINE_8, 0);
948 
949  for (int i = 0; i < 5; ++i) {
950  float angle = -fov/2.-0.5 + (fov+0.5)*i/float(5-1);
951  int cornerx = int(mapped_range*sin(M_PI/180.*angle));
952  int cornery = int(mapped_range*cos(M_PI/180.*angle));
953  //cv::Point left_corner(scan.cols/2-cornerx, scan.rows-cornery);
954  //cv::Point right_corner(scan.cols/2+cornerx, scan.rows-cornery);
955  cv::Point corner(scan.cols/2+cornerx, scan.rows-cornery);
956  //cv::line(scan, center, left_corner, white, 2);
957  //cv::line(scan, center, right_corner, white, 2);
958  cv::line(scan, center, corner, white, 1, CV_AA);
959  }
960 
961  cv_bridge::CvImage img_bridge;
963  img_bridge.toImageMsg(this->sonar_image_msg_); // from cv_bridge to sensor_msgs::Image
964 
966 
967  return scan;
968 }
969 
971 // Put depth image data to the interface
973 {
974  this->lock_.lock();
975  // copy data into image
976  this->depth_image_msg_.header.frame_id = this->frame_name_;
977  this->depth_image_msg_.header.stamp.sec = this->depth_sensor_update_time_.sec;
978  this->depth_image_msg_.header.stamp.nsec = this->depth_sensor_update_time_.nsec;
979 
980  // copy from depth to depth image (OpenCV)
981  int rows_arg = this->height;
982  int cols_arg = this->width;
983  int step_arg = this->skip_;
984 
985  sensor_msgs::Image image_msg;
986  image_msg.encoding = sensor_msgs::image_encodings::TYPE_32FC1;
987  image_msg.height = rows_arg;
988  image_msg.width = cols_arg;
989  image_msg.step = sizeof(float) * cols_arg;
990  image_msg.data.resize(rows_arg * cols_arg * sizeof(float));
991  image_msg.is_bigendian = 0;
992 
993  //cv::Mat depth_image = cv::Mat(rows_arg, cols_arg, CV_32FC1, (float*)_src).clone();
994  cv::Mat depth_image(rows_arg, cols_arg, CV_32FC1, (float*)_src);
995 
996  // publish normal image
997  cv::Mat normal_image = this->ComputeNormalImage(depth_image);
998  cv::Mat multibeam_image = this->ConstructSonarImage(depth_image, normal_image);
999  cv::Mat raw_scan = this->ConstructScanImage(depth_image, multibeam_image);
1000  cv::Mat visual_scan = this->ConstructVisualScanImage(raw_scan);
1001 
1002  cv_bridge::CvImage img_bridge;
1003  img_bridge = cv_bridge::CvImage(this->depth_image_msg_.header, sensor_msgs::image_encodings::TYPE_32FC1, depth_image);
1004  img_bridge.toImageMsg(this->depth_image_msg_); // from cv_bridge to sensor_msgs::Image
1005 
1007 
1008 
1009  this->lock_.unlock();
1010 }
1011 
1013 {
1014  ROS_DEBUG_NAMED("depth_camera", "publishing default camera info, then depth camera info");
1016 
1017  if (this->depth_info_connect_count_ > 0)
1018  {
1019  common::Time sensor_update_time = this->parentSensor_->LastMeasurementTime();
1020 
1021  this->sensor_update_time_ = sensor_update_time;
1022  if (sensor_update_time - this->last_depth_image_camera_info_update_time_ >= this->update_period_)
1023  {
1024  this->PublishCameraInfo(this->depth_image_camera_info_pub_); // , sensor_update_time);
1025  this->last_depth_image_camera_info_update_time_ = sensor_update_time;
1026  }
1027  }
1028 }
1029 
1030 //@todo: publish disparity similar to openni_camera_deprecated/src/nodelets/openni_nodelet.cpp.
1031 /*
1032 #include <stereo_msgs/DisparityImage.h>
1033 pub_disparity_ = comm_nh.advertise<stereo_msgs::DisparityImage > ("depth/disparity", 5, subscriberChanged2, subscriberChanged2);
1034 void GazeboRosImageSonar::PublishDisparityImage(const DepthImage& depth, ros::Time time)
1035 {
1036  stereo_msgs::DisparityImagePtr disp_msg = boost::make_shared<stereo_msgs::DisparityImage > ();
1037  disp_msg->header.stamp = time;
1038  disp_msg->header.frame_id = device_->isDepthRegistered () ? rgb_frame_id_ : depth_frame_id_;
1039  disp_msg->image.header = disp_msg->header;
1040  disp_msg->image.encoding = sensor_msgs::image_encodings::TYPE_32FC1;
1041  disp_msg->image.height = depth_height_;
1042  disp_msg->image.width = depth_width_;
1043  disp_msg->image.step = disp_msg->image.width * sizeof (float);
1044  disp_msg->image.data.resize (disp_msg->image.height * disp_msg->image.step);
1045  disp_msg->T = depth.getBaseline ();
1046  disp_msg->f = depth.getFocalLength () * depth_width_ / depth.getWidth ();
1048  disp_msg->min_disparity = 0.0;
1049  disp_msg->max_disparity = disp_msg->T * disp_msg->f / 0.3;
1050  disp_msg->delta_d = 0.125;
1051  depth.fillDisparityImage (depth_width_, depth_height_, reinterpret_cast<float*>(&disp_msg->image.data[0]), disp_msg->image.step);
1052  pub_disparity_.publish (disp_msg);
1053 }
1054 */
1055 
1056 
1057 }
d
cv::Mat ConstructSonarImage(cv::Mat &depth, cv::Mat &normals)
virtual void OnNewImageFrame(const unsigned char *_image, unsigned int _width, unsigned int _height, unsigned int _depth, const std::string &_format)
Update the controller.
std::string depth_image_topic_name_
image where each pixel contains the depth information
event::ConnectionPtr newRGBPointCloudConnection
bool FillDepthImageHelper(sensor_msgs::Image &image_msg, uint32_t rows_arg, uint32_t cols_arg, uint32_t step_arg, void *data_arg)
void ComputeSonarImage(const float *_src)
push depth image data into ros topic
void publish(const boost::shared_ptr< M > &message) const
virtual void OnNewRGBPointCloud(const float *_pcd, unsigned int _width, unsigned int _height, unsigned int _depth, const std::string &_format)
Update the controller.
void ApplySpeckleNoise(cv::Mat &scan, float fov)
void ApplySmoothing(cv::Mat &scan, float fov)
ROSCPP_DECL bool isInitialized()
void Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf, const std::string &_camera_name_suffix="")
common::Time last_depth_image_camera_info_update_time_
TFSIMD_FORCE_INLINE const tfScalar & y() const
cv::Mat ComputeNormalImage(cv::Mat &depth)
ros::CallbackQueue camera_queue_
cv::Mat ConstructVisualScanImage(cv::Mat &raw_scan)
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
boost::shared_ptr< int > image_connect_count_
rendering::DepthCameraPtr depthCamera
sensor_msgs::Image image_msg_
ros::Publisher point_cloud_pub_
A pointer to the ROS node. A node will be instantiated if it does not exist.
#define ROS_DEBUG_NAMED(name,...)
void FillDepthImage(const float *_src)
push depth image data into ros topic
std::string point_cloud_topic_name_
ROS image topic name.
event::ConnectionPtr OnLoad(const boost::function< void()> &)
virtual void OnNewDepthFrame(const float *_image, unsigned int _width, unsigned int _height, unsigned int _depth, const std::string &_format)
Update the controller.
#define ROS_FATAL_STREAM_NAMED(name, args)
const std::string TYPE_32FC1
virtual void Advertise()
Advertise point cloud and depth image.
sensors::DepthCameraSensorPtr parentSensor
virtual void Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf)
Load the plugin.
TFSIMD_FORCE_INLINE const tfScalar & x() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
std::default_random_engine generator
bool FillPointCloudHelper(sensor_msgs::PointCloud2 &point_cloud_msg, uint32_t rows_arg, uint32_t cols_arg, uint32_t step_arg, void *data_arg)
TFSIMD_FORCE_INLINE const tfScalar & z() const
sensor_msgs::PointCloud2 point_cloud_msg_
PointCloud2 point cloud message.
cv::Mat ConstructScanImage(cv::Mat &depth, cv::Mat &SNR)
event::ConnectionPtr newDepthFrameConnection
void PutCameraData(const unsigned char *_src)
rendering::CameraPtr camera_
event::ConnectionPtr newImageFrameConnection
ros::NodeHandle * rosnode_
int depth_image_connect_count_
Keep track of number of connctions for point clouds.
void FillPointdCloud(const float *_src)
Put camera data to the ROS topic.
std::vector< std::vector< int > > angle_range_indices_
sensors::SensorPtr parentSensor_
void setPointCloud2FieldsByString(int n_fields,...)
boost::shared_ptr< void > VoidPtr
int point_cloud_connect_count_
Keep track of number of connctions for point clouds.
sensor_msgs::ImagePtr toImageMsg() const


uuv_sensor_ros_plugins
Author(s): Musa Morena Marcusso Manhaes , Sebastian Scherer , Luiz Ricardo Douat
autogenerated on Thu Jun 18 2020 03:28:33