hector_barrel_detection_nodelet.cpp
Go to the documentation of this file.
3 
5 
7  BarrelDetection::BarrelDetection()
8  {}
9 
10  BarrelDetection::~BarrelDetection()
11  {}
12 
13  void BarrelDetection::onInit(){
14  NODELET_DEBUG("Initializing hector_barrel_detection_nodelet");
15  ROS_INFO("Barreldetection started");
16  ros::NodeHandle pnh_("~");
17  ros::NodeHandle nh_("");
19 
20  pnh_.param("h_min", h_min, 100);
21  pnh_.param("h_max", h_max, 140);
22  pnh_.param("s_min", s_min, 100);
23  pnh_.param("s_max", s_max, 255);
24  pnh_.param("v_min", v_min, 50);
25  pnh_.param("v_max", v_max, 200);
26  pnh_.param("bluePart", bluePart, 4.0);
27  pnh_.param("minRadius", minRadius, 0.15);
28  pnh_.param("maxRadius", maxRadius, 0.4);
29 
30  pcl_sub = nh_.subscribe("/openni/depth/points", 1, &BarrelDetection::PclCallback, this);
31  image_sub = it_.subscribeCamera("/openni/rgb/image_color", 10, &BarrelDetection::imageCallback, this);
32  cloud_filtered_publisher_ = pnh_.advertise<sensor_msgs::PointCloud2> ("cloud_filtered_barrel", 0);
33  pose_publisher_ = pnh_.advertise<geometry_msgs::PoseStamped> ("pose_filtered_barrel", 0);
34  barrel_marker_publisher_ = pnh_.advertise<visualization_msgs::MarkerArray> ("marker_filtered_barrel", 0);
35  imagePercept_pub_ = pnh_.advertise<hector_worldmodel_msgs::ImagePercept> ("/worldmodel/image_percept", 0);
36  posePercept_pub_= pnh_.advertise<hector_worldmodel_msgs::PosePercept> ("/worldmodel/pose_percept", 0);
37  pcl_debug_pub_= pnh_.advertise<sensor_msgs::PointCloud2> ("barrel_pcl_debug", 0);
38  debug_imagePoint_pub_= pnh_.advertise<geometry_msgs::PointStamped>("blaDebugPoseEstimate",0);
39  black_white_image_pub_= it_.advertiseCamera("black_white_image",1);
40 
41  worldmodel_srv_client_=nh_.serviceClient<hector_nav_msgs::GetDistanceToObstacle>("/hector_octomap_server/get_distance_to_obstacle");
42 
43  pub_imageDetection = it_.advertiseCamera("barrelDetectionImage", 1);
44  dynamic_recf_type = boost::bind(&BarrelDetection::dynamic_recf_cb, this, _1, _2);
45  dynamic_recf_server.setCallback(dynamic_recf_type);
46 
47  }
48 
49  void BarrelDetection::imageCallback(const sensor_msgs::ImageConstPtr& img, const sensor_msgs::CameraInfoConstPtr& info){
50  ROS_DEBUG("image callback startet");
51 
52  hector_nav_msgs::GetDistanceToObstacle dist_msgs;
53  dist_msgs.request.point.header= img->header;
54 // dist_msgs.request.point.point.z= 1;
55 
56 // worldmodel_srv_client_.call(dist_msgs);
57 // float distance = dist_msgs.response.distance;
58 // distance=1;
59 
60  float distance;
61 
62  //Read image with cvbridge
65  cv::Mat img_filtered(cv_ptr->image);
66 
67  cv::cvtColor(img_filtered, img_filtered, CV_BGR2HSV);
68 
69  //cut image
70  float cutPercentage= 0.2;
71  cv::Size size= img_filtered.size();
72  img_filtered = img_filtered(cv::Rect(size.width*cutPercentage,size.height*cutPercentage,size.width*(1-2*cutPercentage),size.height*(1-2*cutPercentage)));
73 
74  // cv::imshow("image",img_filtered);
75 
76 
77  cv::Mat blueOnly;
78  cv::inRange(img_filtered, cv::Scalar(h_min, s_min, v_min), cv::Scalar(h_max, s_max, v_max), blueOnly);
79 
80  if(black_white_image_pub_.getNumSubscribers()>0){
81  cv_bridge::CvImage cvImg;
82  cvImg.image = blueOnly;
83 
84  cvImg.header = img->header;
86  black_white_image_pub_.publish(cvImg.toImageMsg() ,info);
87  }
88 
89  // cv::imshow("blau",blueOnly);
90  // cv::waitKey(1000);
91 
92  //Perform blob detection
93  cv::SimpleBlobDetector::Params params;
94  params.filterByColor = true;
95  params.blobColor = 255;
96  params.minDistBetweenBlobs = 0.5;
97  params.filterByArea = true;
98  //TODO: tune parameter
99  params.minArea = (blueOnly.rows * blueOnly.cols) /bluePart;
100  // params.minArea = (blueOnly.rows * blueOnly.cols) / (0.5+distance);
101  params.maxArea = blueOnly.rows * blueOnly.cols;
102  params.filterByCircularity = false;
103  params.filterByColor = false;
104  params.filterByConvexity = false;
105  params.filterByInertia = false;
106 
107  cv::SimpleBlobDetector blob_detector(params);
108  std::vector<cv::KeyPoint> keypoints;
109  keypoints.clear();
110 
111  blob_detector.detect(blueOnly,keypoints);
112  // for(unsigned int i=0; i<keypoints.size();i++)
113  // {
114  // std::cout << keypoints.at(i).pt.x << std::endl;
115  // }
116  //Publish results
117  hector_worldmodel_msgs::ImagePercept ip;
118 
119  ip.header= img->header;
120  ip.info.class_id = "barrel";
121  ip.info.class_support = 1;
122  ip.camera_info = *info;
123 
124  if(pub_imageDetection.getNumSubscribers() > 0){
125  publish_rectangle_for_recf(keypoints, img, info, img_filtered);
126  }
127 
128  for(unsigned int i=0; i<keypoints.size();i++)
129  {
130  ip.x = keypoints.at(i).pt.x;
131  ip.y = keypoints.at(i).pt.y;
132  // imagePercept_pub_.publish(ip);
133 
134  ROS_DEBUG("Barrel blob found at image coord: (%f, %f)", ip.x, ip.y);
135 
136  tf::Pose pose;
137 
138  // retrieve camera model from either the cache or from CameraInfo given in the percept
139  CameraModelPtr cameraModel;
140  cameraModel.reset(new image_geometry::PinholeCameraModel());
141  cameraModel->fromCameraInfo(info);
142 
143  // transform Point using the camera model
144  cv::Point2d rectified = cameraModel->rectifyPoint(cv::Point2d(ip.x+size.width*cutPercentage, ip.y+size.height*cutPercentage));
145  cv::Point3d direction_cv = cameraModel->projectPixelTo3dRay(rectified);
146  tf::Point direction(direction_cv.x, direction_cv.y, direction_cv.z);
147  direction.normalize();
148 
149  // project image percept to the next obstacle
150  dist_msgs.request.point.header = ip.header;
151  tf::pointTFToMsg(direction, dist_msgs.request.point.point);
152 
153  worldmodel_srv_client_.call(dist_msgs);
154 
155  distance = std::max(dist_msgs.response.distance, 0.0f);
156 
157  tf::pointTFToMsg(direction.normalized() * distance, dist_msgs.request.point.point);
158 
159  //transformation point to /map
160  //TODO:: change base_link to /map
161  const geometry_msgs::PointStamped const_point=dist_msgs.request.point;
162  geometry_msgs::PointStamped point_in_map;
163  try{
164  //TODO::change Duration back to 3.0
165  ros::Time time = img->header.stamp;
166  listener_.waitForTransform("/map", img->header.frame_id,
167  time, ros::Duration(3.0));
168  listener_.transformPoint("/map", const_point, point_in_map);
169  }
170  catch (tf::TransformException ex){
171  ROS_ERROR("Lookup Transform failed: %s",ex.what());
172  return;
173  }
174 
175  if(debug_imagePoint_pub_.getNumSubscribers()>0){
176  debug_imagePoint_pub_.publish(point_in_map);
177  }
178 
179  if(current_pc_msg_!=0 && distance>0){
180  findCylinder(current_pc_msg_, point_in_map.point.x, point_in_map.point.y, const_point);
181  }
182 
183  }
184 
185 
186  }
187  void BarrelDetection::PclCallback(const sensor_msgs::PointCloud2::ConstPtr& pc_msg){
188  ROS_DEBUG("pointcloud callback enterd");
189  current_pc_msg_= pc_msg;
190  }
191 
192  void BarrelDetection::findCylinder(const sensor_msgs::PointCloud2::ConstPtr &pc_msg, float xKey, float yKey, const geometry_msgs::PointStamped cut_around_keypoint){
193  ROS_DEBUG("started cylinder search");
194  pcl::PCLPointCloud2 pcl_pc2;
195  pcl_conversions::toPCL(*pc_msg,pcl_pc2);
196  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
197  pcl::fromPCLPointCloud2(pcl_pc2,*cloud);
198 
199  // Filtrar area in openni_depth_optical_frame
200  //x (width)
201  float xmin=cut_around_keypoint.point.x-0.3;
202  float xmax=cut_around_keypoint.point.x+0.3;
203  pass_.setInputCloud (cloud);
204  pass_.setFilterFieldName ("x");
205  pass_.setFilterLimits (xmin, xmax);
206  pass_.filter (*cloud);
207  //y (height; z in /map)
208  float ymin=cut_around_keypoint.point.y-0.4;
209  float ymax=cut_around_keypoint.point.y+0.4;
210  pass_.setInputCloud (cloud);
211  pass_.setFilterFieldName ("y");
212  pass_.setFilterLimits (ymin, ymax);
213  pass_.filter (*cloud);
214 
215  //transformation cloud to /map
216  //TODO:: change base_link to /map
217  tf::StampedTransform transform_cloud_to_map;
218  try{
219  //TODO::change Duration back to 3.0
220  ros::Time time = pc_msg->header.stamp;
221  listener_.waitForTransform("/map", pc_msg->header.frame_id,
222  time, ros::Duration(3.0));
223  listener_.lookupTransform("/map", pc_msg->header.frame_id,
224  time, transform_cloud_to_map);
225  }
226  catch (tf::TransformException ex){
227  ROS_ERROR("Lookup Transform failed: %s",ex.what());
228  return;
229  }
230 
231  tf::transformTFToEigen(transform_cloud_to_map, to_map_);
232 
233  // Transform to /map
234  boost::shared_ptr<pcl::PointCloud<pcl::PointXYZ> > cloud_tmp(new pcl::PointCloud<pcl::PointXYZ>);
235  pcl::transformPointCloud(*cloud, *cloud_tmp, to_map_);
236  cloud = cloud_tmp;
237  cloud->header.frame_id= transform_cloud_to_map.frame_id_;
238 
239 
240  // Publish filtered cloud to ROS for debugging
241  if (pcl_debug_pub_.getNumSubscribers() > 0){
242  sensor_msgs::PointCloud2 filtered_msg;
243  pcl::toROSMsg(*cloud, filtered_msg);
244  filtered_msg.header.frame_id = cloud->header.frame_id;
245  pcl_debug_pub_.publish(filtered_msg);
246  }
247 
248  // trobar cilindre(s)
249  ROS_DEBUG("Normal Estimation");
250  //Estimate point normals
251  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
252  ROS_DEBUG("building Tree");
253  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
254  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal> ());
255  ne.setSearchMethod (tree);
256  ne.setInputCloud (cloud);
257  ne.setKSearch (50);
258  ROS_DEBUG("estimate Normals");
259  ne.compute (*cloud_normals);
260 
261  // Create the segmentation object for cylinder segmentation and set all the parameters
262  ROS_DEBUG("Set Cylinder coefficients");
263  pcl::SACSegmentationFromNormals<pcl::PointXYZ, pcl::Normal> seg;
264  pcl::ModelCoefficients::Ptr coefficients_cylinder (new pcl::ModelCoefficients);
265  pcl::PointIndices::Ptr inliers_cylinder (new pcl::PointIndices);
266  seg.setOptimizeCoefficients (true);
267  seg.setModelType (pcl::SACMODEL_CYLINDER);
268  seg.setMethodType (pcl::SAC_RANSAC);
269  seg.setNormalDistanceWeight (0.1);
270  seg.setMaxIterations (50);
271  seg.setDistanceThreshold (0.01);
272  seg.setRadiusLimits (minRadius, maxRadius);
273  seg.setInputCloud (cloud);
274  seg.setInputNormals (cloud_normals);
275  ROS_DEBUG("search cylinders");
276  Eigen::Vector3f v = Eigen::Vector3f(0.0, 0.0, 1.0);
277  seg.setAxis(v);
278  seg.segment (*inliers_cylinder, *coefficients_cylinder);
279  ROS_DEBUG_STREAM("Cylinder coefficients: " << *coefficients_cylinder);
280 
281  ROS_DEBUG("extract cylinder potins");
282  pcl::ExtractIndices<pcl::PointXYZ> extract;
283  extract.setInputCloud (cloud);
284  extract.setIndices (inliers_cylinder);
285  extract.setNegative (false);
286  extract.filter (*cloud);
287  ROS_DEBUG_STREAM("Extracted: " << cloud->points.size ());
288 
289  // Cylinder Cloud Publisher
290  if (cloud_filtered_publisher_.getNumSubscribers() > 0){
291  sensor_msgs::PointCloud2 cyl_msg;
292  pcl::toROSMsg(*cloud, cyl_msg);
293  cyl_msg.header.frame_id = cloud->header.frame_id;
294  cloud_filtered_publisher_.publish(cyl_msg);
295  }
296 
297  geometry_msgs::Point possibleCylinderPoint;
298  bool inRange= false;
299  float epsilon= 0.25;
300  if( cloud->points.size()>0){
301  possibleCylinderPoint.x= coefficients_cylinder->values[0];
302  possibleCylinderPoint.y= coefficients_cylinder->values[1];
303  float square_distance= std::abs(possibleCylinderPoint.x - xKey)*std::abs(possibleCylinderPoint.x - xKey) +
304  std::abs(possibleCylinderPoint.y - yKey)*std::abs(possibleCylinderPoint.y - yKey);
305  if(square_distance < epsilon){
306  inRange=true;
307  }
308 
309  }
310 
311  //publish debug clysinderPose
312  if (pose_publisher_.getNumSubscribers() > 0){
313  geometry_msgs::PoseStamped pose_msg;
314  pose_msg.header.frame_id=cloud->header.frame_id;
315  pose_msg.header.stamp=pc_msg->header.stamp;
316  pose_msg.pose.position.x=possibleCylinderPoint.x;
317  pose_msg.pose.position.y=possibleCylinderPoint.y;
318  pose_publisher_.publish(pose_msg);
319  }
320 
321  if( cloud->points.size()>0 && inRange)
322  { ROS_DEBUG("publish cylinder ");
323  //Transformation to /map
324  geometry_msgs::PointStamped point_in_map;
325  try{
326  //TODO::change Duration back to 3.0
327  ros::Time time = cut_around_keypoint.header.stamp;
328  listener_.waitForTransform("/map", cut_around_keypoint.header.frame_id,
329  time, ros::Duration(3.0));
330  listener_.transformPoint("/map", cut_around_keypoint, point_in_map);
331  }
332  catch (tf::TransformException ex){
333  ROS_ERROR("Lookup Transform failed: %s",ex.what());
334  return;
335  }
336  //Publish results
337  hector_worldmodel_msgs::PosePercept pp;
338 
339  pp.header.frame_id= cloud->header.frame_id;
340  pp.header.stamp= pc_msg->header.stamp;
341  pp.info.class_id= "barrel";
342  pp.info.class_support=1;
343  pp.info.object_support=1;
344  pp.pose.pose.position.x= coefficients_cylinder->values[0];
345  pp.pose.pose.position.y= coefficients_cylinder->values[1];
346  pp.pose.pose.position.z= point_in_map.point.z;
347  pp.pose.pose.orientation.x= pp.pose.pose.orientation.y = pp.pose.pose.orientation.z= 0;
348  pp.pose.pose.orientation.w= 1;
349 
350  //publish cylinder z<1.1 or z>1.7 only (in simulation z>1.4)
351  if(pp.pose.pose.position.z < 1.1 && pp.pose.pose.position.z >0.2){
352  posePercept_pub_.publish(pp);
353  ROS_INFO("PosePercept published");
354  }
355 
356  // MARKERS ADD
357  ROS_DEBUG("initialize markerArray");
358  visualization_msgs::MarkerArray markerArray_msg_;
359  markerArray_msg_.markers.resize(1);
360  ROS_DEBUG("markerarry created");
361  markerArray_msg_.markers[0].action = visualization_msgs::Marker::ADD;
362  ROS_DEBUG("marker added");
363  // CYLINDER AND TEXT
364  markerArray_msg_.markers[0].header.frame_id = cloud->header.frame_id;
365  markerArray_msg_.markers[0].header.stamp = pc_msg->header.stamp;
366  markerArray_msg_.markers[0].id = 0;
367  markerArray_msg_.markers[0].pose.position.x= pp.pose.pose.position.x;
368  markerArray_msg_.markers[0].pose.position.y = pp.pose.pose.position.y;
369  markerArray_msg_.markers[0].pose.position.z = pp.pose.pose.position.z;
370  markerArray_msg_.markers[0].pose.orientation.x=markerArray_msg_.markers[0].pose.orientation.y= markerArray_msg_.markers[0].pose.orientation.z= pp.pose.pose.orientation.x;
371  markerArray_msg_.markers[0].pose.orientation.w=1;
372  ROS_DEBUG("cylinder and text added");
373  //red
374  markerArray_msg_.markers[0].color.r = 1.0;
375  markerArray_msg_.markers[0].color.g = 0.0;
376  markerArray_msg_.markers[0].color.b = 0.0;
377  ROS_DEBUG("color added");
378  // ONLY CYLINDER
379  markerArray_msg_.markers[0].ns = "cylinder";
380  markerArray_msg_.markers[0].type = visualization_msgs::Marker::CYLINDER;
381  markerArray_msg_.markers[0].pose.position.z = 0.6;
382  markerArray_msg_.markers[0].scale.x = 0.6;
383  markerArray_msg_.markers[0].scale.y = 0.6;
384  markerArray_msg_.markers[0].scale.z = 1;
385  markerArray_msg_.markers[0].color.a = 0.4;
386  ROS_DEBUG("cylinder only added");
387  ROS_DEBUG("makerArray complete");
388  barrel_marker_publisher_.publish(markerArray_msg_);
389  ROS_DEBUG("markerArray published");
390 
391 
392  }
393 
394 
395  }
396 
397  void BarrelDetection::publish_rectangle_for_recf(std::vector<cv::KeyPoint> keypoints, const sensor_msgs::ImageConstPtr& img, const sensor_msgs::CameraInfoConstPtr& info, cv::Mat& img_filtered){
398  cv::cvtColor(img_filtered, img_filtered, CV_HSV2BGR);
399  //Create image with detection frames
400  int width = 3;
401  int height = 3;
402 
403  IplImage ipl_img = img_filtered;
404 
405  //Display Keypoints
406  for(unsigned int i = 0; i < keypoints.size(); i++){
407  //Write rectangle into image
408  width = (int)(keypoints.at(i).size );
409  height = (int)(keypoints.at(i).size );
410  for(int j = -width; j <= width;j++){
411  if ((keypoints.at(i).pt.x + j) >= 0 && (keypoints.at(i).pt.x + j) < ipl_img.width){
412  //Draw upper line
413  if ((keypoints.at(i).pt.y - height) >= 0){
414  cvSet2D(&ipl_img,(int)(keypoints.at(i).pt.y - height), (int)(keypoints.at(i).pt.x + j),cv::Scalar(0));
415  }
416  //Draw lower line
417  if ((keypoints.at(i).pt.y + height) < ipl_img.height){
418  cvSet2D(&ipl_img,(int)(keypoints.at(i).pt.y + height), (int)(keypoints.at(i).pt.x + j),cv::Scalar(0));
419  }
420  }
421  }
422 
423  for(int k = -height; k <= height;k++){
424  if ((keypoints.at(i).pt.y + k) >= 0 && (keypoints.at(i).pt.y + k) < ipl_img.height){
425  //Draw left line
426  if ((keypoints.at(i).pt.x - width) >= 0){
427  cvSet2D(&ipl_img,(int)(keypoints.at(i).pt.y +k), (int)(keypoints.at(i).pt.x - width),cv::Scalar(0));
428  }
429  //Draw right line
430  if ((keypoints.at(i).pt.x + width) < ipl_img.width){
431  cvSet2D(&ipl_img,(int)(keypoints.at(i).pt.y +k), (int)(keypoints.at(i).pt.x + width),cv::Scalar(0));
432  }
433  }
434  }
435  }
436 
437 
438  cv_bridge::CvImage cvImg;
439  cvImg.image = img_filtered;
440 
441 
442  cvImg.header = img->header;
443  cvImg.encoding = img->encoding;
444  pub_imageDetection.publish(cvImg.toImageMsg(),info);
445  // pub_imageDetection.publish(img, info);
446  }
447 
448  void BarrelDetection::dynamic_recf_cb(hector_barrel_detection_nodelet::BarrelDetectionConfig &config, uint32_t level) {
449  ROS_INFO("Reconfigure Callback enterd");
450 
451  h_min= config.min_H_value;
452  h_max= config.max_H_value;
453  s_min= config.min_S_value;
454  s_max= config.max_S_value;
455  v_min= config.min_V_value;
456  v_max= config.max_V_value;
457  bluePart= config.bluePart;
458  minRadius= config.minRadius;
459  maxRadius= config.maxRadius;
460 
461  }
462 
463 }
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
void transformTFToEigen(const tf::Transform &t, Eigen::Affine3d &e)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
CameraSubscriber subscribeCamera(const std::string &base_topic, uint32_t queue_size, const CameraSubscriber::Callback &callback, const ros::VoidPtr &tracked_object=ros::VoidPtr(), const TransportHints &transport_hints=TransportHints())
PLUGINLIB_EXPORT_CLASS(BAGReader, nodelet::Nodelet)
std::string encoding
TFSIMD_FORCE_INLINE tfScalar distance(const Vector3 &v) const
CameraPublisher advertiseCamera(const std::string &base_topic, uint32_t queue_size, bool latch=false)
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
TFSIMD_FORCE_INLINE Vector3 normalized() const
double epsilon
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
#define ROS_DEBUG_STREAM(args)
TFSIMD_FORCE_INLINE Vector3 & normalize()
void toROSMsg(const sensor_msgs::PointCloud2 &cloud, sensor_msgs::Image &image)
#define ROS_ERROR(...)
#define NODELET_DEBUG(...)
sensor_msgs::ImagePtr toImageMsg() const
std::string frame_id_
std_msgs::Header header
void toPCL(const ros::Time &stamp, pcl::uint64_t &pcl_stamp)
#define ROS_DEBUG(...)
static void pointTFToMsg(const Point &bt_v, geometry_msgs::Point &msg_v)


hector_barrel_detection_nodelet
Author(s):
autogenerated on Mon Jun 10 2019 13:36:28