face_detection.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2008, Willow Garage, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of the Willow Garage nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
36 /* Author: Caroline Pantofaru */
37 
38 
39 
40 #include <stdio.h>
41 #include <iostream>
42 #include <vector>
43 #include <fstream>
44 
45 #include <ros/ros.h>
46 #include <ros/console.h>
47 #include <boost/filesystem.hpp>
48 #include <boost/thread/mutex.hpp>
49 
50 #include <people_msgs/PositionMeasurement.h>
51 #include <people_msgs/PositionMeasurementArray.h>
57 #include "sensor_msgs/CameraInfo.h"
58 #include "sensor_msgs/Image.h"
59 #include "stereo_msgs/DisparityImage.h"
61 #include "cv_bridge/cv_bridge.h"
62 #include "tf/transform_listener.h"
63 #include "sensor_msgs/PointCloud.h"
64 #include "geometry_msgs/Point32.h"
65 
66 #include "opencv/cxcore.hpp"
67 #include "opencv/cv.hpp"
68 #include "opencv/highgui.h"
69 
70 #include "face_detector/faces.h"
71 
73 
75 #include <face_detector/FaceDetectorAction.h>
76 
77 using namespace std;
78 
79 namespace people
80 {
81 
85 {
86 public:
87  // Constants
88  const double BIGDIST_M;// = 1000000.0;
89 
90  // Node handle
92 
93  boost::mutex connect_mutex_;
94  bool use_rgbd_;
95 
96  // Subscription topics
97  string image_image_;
98  // Stereo
100  string left_topic_;
104  // RGB-D
105  string camera_;
107  string depth_image_;
108  string depth_topic_;
111  string rgb_ns_;
112  string depth_ns_;
113 
114  // Images and conversion for both the stereo camera and rgb-d camera cases.
122  // Disparity:
129 
130  // Depth:
137 
138  // Action
140  face_detector::FaceDetectorFeedback feedback_;
141  face_detector::FaceDetectorResult result_;
142 
143  // If running the face detector as a component in part of a larger person tracker, this subscribes to the tracker's position measurements and whether it was initialized by some other node.
144  // Todo: resurrect the person tracker.
147 
148 
149  // Publishers
150  // A point cloud of the face positions, meant for visualization in rviz.
151  // This could be replaced by visualization markers, but they can't be modified
152  // in rviz at runtime (eg the alpha, display time, etc. can't be changed.)
155 
156  // Display
157  bool do_display_;
158  cv::Mat cv_image_out_;
160  // Depth
161  bool use_depth_;
164  // Face detector params and output
166  string name_;
167  string haar_filename_;
168  double reliability_;
171  {
173  people_msgs::PositionMeasurement pos;
174  double dist;
175  };
176  map<string, RestampedPositionMeasurement> pos_list_;
178  int max_id_;
179 
180  bool quit_;
181 
183 
184  std::string fixed_frame_;
185 
186  boost::mutex cv_mutex_, pos_mutex_, limage_mutex_, dimage_mutex_;
187 
192  FaceDetector(std::string name) :
193  BIGDIST_M(1000000.0),
194  it_(nh_),
195  as_(nh_, name, false),
196  faces_(0),
197  max_id_(-1),
198  quit_(false)
199  {
200  ROS_INFO_STREAM_NAMED("face_detector", "Constructing FaceDetector.");
201 
202  // Action stuff
203  as_.registerGoalCallback(boost::bind(&FaceDetector::goalCB, this));
204  as_.registerPreemptCallback(boost::bind(&FaceDetector::preemptCB, this));
205  as_.start();
206 
207  faces_ = new Faces();
208  double face_size_min_m, face_size_max_m, max_face_z_m, face_sep_dist_m;
209  int queue_size;
210  bool approx;
211 
212  // Parameters
213  ros::NodeHandle local_nh("~");
214  local_nh.param("classifier_name", name_, std::string(""));
215  local_nh.param("classifier_filename", haar_filename_, std::string(""));
216  local_nh.param("classifier_reliability", reliability_, 0.0);
217  local_nh.param("do_display", do_display_, false);
218  local_nh.param("do_continuous", do_continuous_, true);
219  local_nh.param("do_publish_faces_of_unknown_size", do_publish_unknown_, false);
220  local_nh.param("use_depth", use_depth_, true);
221  local_nh.param("use_external_init", external_init_, false);
222  local_nh.param("face_size_min_m", face_size_min_m, FACE_SIZE_MIN_M);
223  local_nh.param("face_size_max_m", face_size_max_m, FACE_SIZE_MAX_M);
224  local_nh.param("max_face_z_m", max_face_z_m, MAX_FACE_Z_M);
225  local_nh.param("face_separation_dist_m", face_sep_dist_m, FACE_SEP_DIST_M);
226  local_nh.param("use_rgbd", use_rgbd_, false);
227  local_nh.param("queue_size", queue_size, 5);
228  local_nh.param("approximate_sync", approx, false);
229 
230  if (do_display_)
231  {
232  // OpenCV: pop up an OpenCV highgui window
233  cv::namedWindow("Face detector: Face Detection", CV_WINDOW_AUTOSIZE);
234  }
235 
236  // Init the detector and subscribe to the images and camera parameters. One case for rgbd, one for stereo.
237  if (use_rgbd_)
238  {
239 
240  faces_->initFaceDetectionDepth(1, haar_filename_, face_size_min_m, face_size_max_m, max_face_z_m, face_sep_dist_m);
241 
242  camera_ = nh_.resolveName("camera");
243  image_image_ = nh_.resolveName("image_topic");
244  depth_image_ = nh_.resolveName("depth_topic");
245  rgb_ns_ = nh_.resolveName("rgb_ns");
246  depth_ns_ = nh_.resolveName("depth_ns");
247  camera_topic_ = ros::names::clean(camera_ + "/" + rgb_ns_ + "/" + image_image_);
248  depth_topic_ = ros::names::clean(camera_ + "/" + depth_ns_ + "/" + depth_image_);
249  camera_info_topic_ = ros::names::clean(camera_ + "/" + rgb_ns_ + "/camera_info");
250  depth_info_topic_ = ros::names::clean(camera_ + "/" + depth_ns_ + "/camera_info");
251 
252  ROS_DEBUG("camera_topic_: %s", camera_topic_.c_str());
253  ROS_DEBUG("depth_topic_: %s", depth_topic_.c_str());
254  ROS_DEBUG("camera_info_topic_: %s", camera_info_topic_.c_str());
255  ROS_DEBUG("depth_info_topic_: %s", depth_info_topic_.c_str());
256 
257  local_nh.param("fixed_frame", fixed_frame_, std::string("camera_rgb_optical_frame"));
258 
259  if (approx)
260  {
261  approximate_depth_sync_.reset(new ApproximateDepthSync(ApproximateDepthPolicy(queue_size),
262  image_sub_, depth_image_sub_, c1_info_sub_, c2_info_sub_));
263  approximate_depth_sync_->registerCallback(boost::bind(&FaceDetector::imageCBAllDepth,
264  this, _1, _2, _3, _4));
265  }
266  else
267  {
268  exact_depth_sync_.reset(new ExactDepthSync(ExactDepthPolicy(queue_size),
269  image_sub_, depth_image_sub_, c1_info_sub_, c2_info_sub_));
270  exact_depth_sync_->registerCallback(boost::bind(&FaceDetector::imageCBAllDepth,
271  this, _1, _2, _3, _4));
272  }
273 
274  }
275  else
276  {
277  faces_->initFaceDetectionDisparity(1, haar_filename_, face_size_min_m, face_size_max_m, max_face_z_m, face_sep_dist_m);
278 
279  stereo_namespace_ = nh_.resolveName("camera");
280  image_image_ = nh_.resolveName("image_topic");
281  left_topic_ = ros::names::clean(stereo_namespace_ + "/left/" + image_image_);
282  disparity_topic_ = ros::names::clean(stereo_namespace_ + "/disparity");
283  left_camera_info_topic_ = ros::names::clean(stereo_namespace_ + "/left/camera_info");
284  right_camera_info_topic_ = ros::names::clean(stereo_namespace_ + "/right/camera_info");
285 
286  local_nh.param("fixed_frame", fixed_frame_, stereo_namespace_.append("_optical_frame"));
287 
288  if (approx)
289  {
290  approximate_disp_sync_.reset(new ApproximateDispSync(ApproximateDispPolicy(queue_size),
291  image_sub_, disp_image_sub_, c1_info_sub_, c2_info_sub_));
292  approximate_disp_sync_->registerCallback(boost::bind(&FaceDetector::imageCBAllDisp,
293  this, _1, _2, _3, _4));
294  }
295  else
296  {
297  exact_disp_sync_.reset(new ExactDispSync(ExactDispPolicy(queue_size),
298  image_sub_, disp_image_sub_, c1_info_sub_, c2_info_sub_));
299  exact_disp_sync_->registerCallback(boost::bind(&FaceDetector::imageCBAllDisp,
300  this, _1, _2, _3, _4));
301  }
302  }
303 
304  // Connection callbacks and advertise
305  ros::SubscriberStatusCallback pos_pub_connect_cb = boost::bind(&FaceDetector::connectCb, this);
306  ros::SubscriberStatusCallback cloud_pub_connect_cb = boost::bind(&FaceDetector::connectCb, this);
307  if (do_continuous_)
308  ROS_INFO_STREAM_NAMED("face_detector", "You must subscribe to one of FaceDetector's outbound topics or else it will not publish anything.");
309 
310  {
311  boost::mutex::scoped_lock lock(connect_mutex_);
312 
313  // Advertise a position measure message.
314  pos_array_pub_ = nh_.advertise<people_msgs::PositionMeasurementArray>("face_detector/people_tracker_measurements_array", 1, pos_pub_connect_cb, pos_pub_connect_cb);
315 
316  cloud_pub_ = nh_.advertise<sensor_msgs::PointCloud>("face_detector/faces_cloud", 0, cloud_pub_connect_cb, cloud_pub_connect_cb);
317  }
318 
319 
320  // If running as an action server, just stay connected so that action calls are fast.
321  if (!do_continuous_) connectCb();
322 
324  ros::spin(s);
325 
326  }
327 
329  {
330 
331  cv_image_out_.release();
332 
333  if (do_display_)
334  {
335  cv::destroyWindow("Face detector: Face Detection");
336  }
337 
338  if (faces_)
339  {
340  delete faces_;
341  faces_ = 0;
342  }
343  }
344 
345  // Handles (un)subscribing when clients (un)subscribe
346  void connectCb()
347  {
348  boost::mutex::scoped_lock lock(connect_mutex_);
349  if (use_rgbd_)
350  {
351  if (do_continuous_ && cloud_pub_.getNumSubscribers() == 0 && pos_array_pub_.getNumSubscribers() == 0)
352  {
353  ROS_INFO_STREAM_NAMED("face_detector", "You have unsubscribed to FaceDetector's outbound topics, so it will no longer publish anything.");
354  image_sub_.unsubscribe();
355  depth_image_sub_.unsubscribe();
356  c1_info_sub_.unsubscribe();
357  c2_info_sub_.unsubscribe();
358  pos_sub_.shutdown();
359  }
360  else if (!do_continuous_ || !image_sub_.getSubscriber())
361  {
362  image_sub_.subscribe(it_, camera_topic_, 3);
363  depth_image_sub_.subscribe(it_, depth_topic_, 3);
364  c1_info_sub_.subscribe(nh_, camera_info_topic_, 3);
365  c2_info_sub_.subscribe(nh_, depth_info_topic_, 3);
366  // // Subscribe to filter measurements.
367  // if (external_init_) {
368  // //pos_sub_ = nh_.subscribe("people_tracker_filter",1,&FaceDetector::posCallback,this);
369  // pos_sub_ = nh_.subscribe("/face_detector/people_tracker_measurements_array",1,&FaceDetector::posCallback,this);
370  // }
371  }
372  }
373  else
374  {
375  if (do_continuous_ && cloud_pub_.getNumSubscribers() == 0 && pos_array_pub_.getNumSubscribers() == 0)
376  {
377  ROS_INFO_STREAM_NAMED("face_detector", "You have unsubscribed to FaceDetector's outbound topics, so it will no longer publish anything.");
378  image_sub_.unsubscribe();
379  disp_image_sub_.unsubscribe();
380  c1_info_sub_.unsubscribe();
381  c2_info_sub_.unsubscribe();
382  pos_sub_.shutdown();
383  }
384  else if (!do_continuous_ || !image_sub_.getSubscriber())
385  {
386  image_sub_.subscribe(it_, left_topic_, 3);
387  disp_image_sub_.subscribe(nh_, disparity_topic_, 3);
388  c1_info_sub_.subscribe(nh_, left_camera_info_topic_, 3);
389  c2_info_sub_.subscribe(nh_, right_camera_info_topic_, 3);
390  // // Subscribe to filter measurements.
391  // if (external_init_) {
392  // //pos_sub_ = nh_.subscribe("people_tracker_filter",1,&FaceDetector::posCallback,this);
393  // pos_sub_ = nh_.subscribe("/face_detector/people_tracker_measurements_array",1,&FaceDetector::posCallback,this);
394  // }
395  }
396  }
397  }
398 
399 
400  void goalCB()
401  {
402  as_.acceptNewGoal();
403  }
404 
405  void preemptCB()
406  {
407  as_.setPreempted();
408  }
409 
410 
417  // void posCallback(const people_msgs::PositionMeasurementArrayConstPtr& pos_array_ptr) {
418 
419  // // Put the incoming position into the position queue. It'll be processed in the next image callback.
420  // boost::mutex::scoped_lock lock(pos_mutex_);
421 
422  // map<string, RestampedPositionMeasurement>::iterator it;
423  // for (uint ipa = 0; ipa < pos_array_ptr->people.size(); ipa++) {
424  // it = pos_list_.find(pos_array_ptr->people[ipa].object_id);
425  // RestampedPositionMeasurement rpm;
426  // rpm.pos = pos_array_ptr->people[ipa];
427  // rpm.restamp = pos_array_ptr->people[ipa].header.stamp;
428  // rpm.dist = BIGDIST_M;
429  // if (it == pos_list_.end()) {
430  // pos_list_.insert(pair<string, RestampedPositionMeasurement>(pos_array_ptr->people[ipa].object_id, rpm));
431  // }
432  // else if ((pos_array_ptr->people[ipa].header.stamp - (*it).second.pos.header.stamp) > ros::Duration().fromSec(-1.0) ){
433  // (*it).second = rpm;
434  // }
435  // }
436  // lock.unlock();
437 
438  // }
439 
440  // Workaround to convert a DisparityImage->Image into a shared pointer for cv_bridge in imageCBAll.
441  struct NullDeleter
442  {
443  void operator()(void const *) const {}
444  };
445 
454  void imageCBAllDepth(const sensor_msgs::Image::ConstPtr &image, const sensor_msgs::Image::ConstPtr& depth_image, const sensor_msgs::CameraInfo::ConstPtr& c1_info, const sensor_msgs::CameraInfo::ConstPtr& c2_info)
455  {
456 
457  // Only run the detector if in continuous mode or the detector was turned on through an action invocation.
458  if (!do_continuous_ && !as_.isActive())
459  return;
460 
461  // Clear out the result vector.
462  result_.face_positions.clear();
463 
464  if (do_display_)
465  {
466  cv_mutex_.lock();
467  }
468 
469  // ROS --> OpenCV
470  cv_bridge::CvImageConstPtr cv_image_ptr = cv_bridge::toCvShare(image, "bgr8");
471  cv_bridge::CvImageConstPtr cv_depth_ptr = cv_bridge::toCvShare(depth_image);
472  cv::Mat depth_32fc1 = cv_depth_ptr->image;
473  if (depth_image->encoding != "32FC1")
474  {
475  cv_depth_ptr->image.convertTo(depth_32fc1, CV_32FC1, 0.001);
476  }
477 
478 
479 
480  cam_model_.fromCameraInfo(c1_info, c2_info);
481 
482  // For display, keep a copy of the image that we can draw on.
483  if (do_display_)
484  {
485  cv_image_out_ = (cv_image_ptr->image).clone();
486  }
487 
488  struct timeval timeofday;
489  gettimeofday(&timeofday, NULL);
490  ros::Time starttdetect = ros::Time().fromNSec(1e9 * timeofday.tv_sec + 1e3 * timeofday.tv_usec);
491 
492  vector<Box2D3D> faces_vector = faces_->detectAllFacesDepth(cv_image_ptr->image, 1.0, depth_32fc1, &cam_model_);
493  gettimeofday(&timeofday, NULL);
494  ros::Time endtdetect = ros::Time().fromNSec(1e9 * timeofday.tv_sec + 1e3 * timeofday.tv_usec);
495  ros::Duration diffdetect = endtdetect - starttdetect;
496  ROS_INFO_STREAM_NAMED("face_detector", "Detection duration = " << diffdetect.toSec() << "sec");
497 
498  matchAndDisplay(faces_vector, image->header);
499  }
500 
509  void imageCBAllDisp(const sensor_msgs::Image::ConstPtr &image, const stereo_msgs::DisparityImage::ConstPtr& disp_image, const sensor_msgs::CameraInfo::ConstPtr& c1_info, const sensor_msgs::CameraInfo::ConstPtr& c2_info)
510  {
511 
512  // Only run the detector if in continuous mode or the detector was turned on through an action invocation.
513  if (!do_continuous_ && !as_.isActive())
514  return;
515 
516  // Clear out the result vector.
517  result_.face_positions.clear();
518 
519  if (do_display_)
520  {
521  cv_mutex_.lock();
522  }
523 
524 
525  // ROS --> OpenCV
527  cv_bridge::CvImageConstPtr cv_disp_ptr = cv_bridge::toCvShare(disp_image->image, disp_image);
528  cam_model_.fromCameraInfo(c1_info, c2_info);
529 
530  // For display, keep a copy of the image that we can draw on.
531  if (do_display_)
532  {
533  cv_image_out_ = (cv_image_ptr->image).clone();
534  }
535 
536  struct timeval timeofday;
537  gettimeofday(&timeofday, NULL);
538  ros::Time starttdetect = ros::Time().fromNSec(1e9 * timeofday.tv_sec + 1e3 * timeofday.tv_usec);
539 
540  vector<Box2D3D> faces_vector = faces_->detectAllFacesDisparity(cv_image_ptr->image, 1.0, cv_disp_ptr->image, &cam_model_);
541  gettimeofday(&timeofday, NULL);
542  ros::Time endtdetect = ros::Time().fromNSec(1e9 * timeofday.tv_sec + 1e3 * timeofday.tv_usec);
543  ros::Duration diffdetect = endtdetect - starttdetect;
544  ROS_INFO_STREAM_NAMED("face_detector", "Detection duration = " << diffdetect.toSec() << "sec");
545 
546  matchAndDisplay(faces_vector, image->header);
547  }
548 
549 
550 private:
551 
552  void matchAndDisplay(vector<Box2D3D> faces_vector, std_msgs::Header header)
553  {
554  bool found_faces = false;
555 
556  int ngood = 0;
557  sensor_msgs::PointCloud cloud;
558  cloud.header.stamp = header.stamp;
559  cloud.header.frame_id = header.frame_id;
560 
561  if (faces_vector.size() > 0)
562  {
563 
564  // Transform the positions of the known faces and remove anyone who hasn't had an update in a long time.
565  // boost::mutex::scoped_lock pos_lock(pos_mutex_);
566  map<string, RestampedPositionMeasurement>::iterator it;
567  it = pos_list_.begin();
568  while (it != pos_list_.end())
569  {
570  if ((header.stamp - (*it).second.restamp) > ros::Duration().fromSec(5.0))
571  {
572  // Position is too old, remove the person.
573  pos_list_.erase(it++);
574  }
575  else
576  {
577  // Transform the person to this time. Note that the pos time is updated but not the restamp.
578  tf::Point pt;
579  tf::pointMsgToTF((*it).second.pos.pos, pt);
580  tf::Stamped<tf::Point> loc(pt, (*it).second.pos.header.stamp, (*it).second.pos.header.frame_id);
581  try
582  {
583  tf_.transformPoint(header.frame_id, header.stamp, loc, fixed_frame_, loc);
584  (*it).second.pos.header.stamp = header.stamp;
585  (*it).second.pos.pos.x = loc[0];
586  (*it).second.pos.pos.y = loc[1];
587  (*it).second.pos.pos.z = loc[2];
588  }
589  catch (tf::TransformException& ex)
590  {
591  }
592  it++;
593  }
594  }
595  // End filter face position update
596 
597  // Associate the found faces with previously seen faces, and publish all good face centers.
598  Box2D3D *one_face;
599  people_msgs::PositionMeasurement pos;
600  people_msgs::PositionMeasurementArray pos_array;
601 
602  for (uint iface = 0; iface < faces_vector.size(); iface++)
603  {
604  one_face = &faces_vector[iface];
605 
606  if (one_face->status == "good" || (one_face->status == "unknown" && do_publish_unknown_))
607  {
608 
609  std::string id = "";
610 
611  // Convert the face format to a PositionMeasurement msg.
612  pos.header.stamp = header.stamp;
613  pos.name = name_;
614  pos.pos.x = one_face->center3d.x;
615  pos.pos.y = one_face->center3d.y;
616  pos.pos.z = one_face->center3d.z;
617  pos.header.frame_id = header.frame_id;//"*_optical_frame";
618  pos.reliability = reliability_;
619  pos.initialization = 1;//0;
620  pos.covariance[0] = 0.04;
621  pos.covariance[1] = 0.0;
622  pos.covariance[2] = 0.0;
623  pos.covariance[3] = 0.0;
624  pos.covariance[4] = 0.04;
625  pos.covariance[5] = 0.0;
626  pos.covariance[6] = 0.0;
627  pos.covariance[7] = 0.0;
628  pos.covariance[8] = 0.04;
629 
630  // Check if this person's face is close enough to one of the previously known faces and associate it with the closest one.
631  // Otherwise publish it with an empty id.
632  // Note that multiple face positions can be published with the same id, but ids in the pos_list_ are unique. The position of a face in the list is updated with the closest found face.
633  double dist, mindist = BIGDIST_M;
634  map<string, RestampedPositionMeasurement>::iterator close_it = pos_list_.end();
635  for (it = pos_list_.begin(); it != pos_list_.end(); it++)
636  {
637  dist = pow((*it).second.pos.pos.x - pos.pos.x, 2.0) + pow((*it).second.pos.pos.y - pos.pos.y, 2.0) + pow((*it).second.pos.pos.z - pos.pos.z, 2.0);
638  if (dist <= faces_->face_sep_dist_m_ && dist < mindist)
639  {
640  mindist = dist;
641  close_it = it;
642  }
643  }
644  if (close_it != pos_list_.end())
645  {
646  pos.object_id = (*close_it).second.pos.object_id;
647  if (mindist < (*close_it).second.dist)
648  {
649  (*close_it).second.restamp = header.stamp;
650  (*close_it).second.dist = mindist;
651  (*close_it).second.pos = pos;
652  }
653  ROS_INFO_STREAM_NAMED("face_detector", "Found face to match with id " << pos.object_id);
654  }
655  else
656  {
657  max_id_++;
658  pos.object_id = static_cast<ostringstream*>(&(ostringstream() << max_id_))->str();
659  ROS_INFO_STREAM_NAMED("face_detector", "Didn't find face to match, starting new ID " << pos.object_id);
660  }
661  result_.face_positions.push_back(pos);
662  found_faces = true;
663 
664  }
665 
666  }
667  pos_array.header.stamp = header.stamp;
668  pos_array.header.frame_id = header.frame_id;
669  pos_array.people = result_.face_positions;
670  if (pos_array.people.size() > 0)
671  {
672  pos_array_pub_.publish(pos_array);
673 
674  // Update the position list greedily. This should be rewritten.
675  map<string, RestampedPositionMeasurement>::iterator it;
676  for (uint ipa = 0; ipa < pos_array.people.size(); ipa++)
677  {
678  it = pos_list_.find(pos_array.people[ipa].object_id);
680  rpm.pos = pos_array.people[ipa];
681  rpm.restamp = pos_array.people[ipa].header.stamp;
682  rpm.dist = BIGDIST_M;
683  if (it == pos_list_.end())
684  {
685  pos_list_.insert(pair<string, RestampedPositionMeasurement>(pos_array.people[ipa].object_id, rpm));
686  }
687  else if ((pos_array.people[ipa].header.stamp - (*it).second.pos.header.stamp) > ros::Duration().fromSec(-1.0))
688  {
689  (*it).second = rpm;
690  }
691  }
692  // pos_lock.unlock();
693 
694  // Clean out all of the distances in the pos_list_
695  for (it = pos_list_.begin(); it != pos_list_.end(); it++)
696  {
697  (*it).second.dist = BIGDIST_M;
698  }
699 
700  }
701 
702 
703  // Done associating faces
704 
705  /******** Display **************************************************************/
706 
707  // Publish a point cloud of face centers.
708 
709  cloud.channels.resize(1);
710  cloud.channels[0].name = "intensity";
711 
712  for (uint iface = 0; iface < faces_vector.size(); iface++)
713  {
714  one_face = &faces_vector[iface];
715 
716  // Visualization of good faces as a point cloud
717  if (one_face->status == "good")
718  {
719 
720  geometry_msgs::Point32 p;
721  p.x = one_face->center3d.x;
722  p.y = one_face->center3d.y;
723  p.z = one_face->center3d.z;
724  cloud.points.push_back(p);
725  cloud.channels[0].values.push_back(1.0f);
726 
727  ngood ++;
728  }
729  }
730 
731  cloud_pub_.publish(cloud);
732  // Done publishing the point cloud.
733 
734  } // Done if faces_vector.size() > 0
735 
736 
737  // Draw an appropriately colored rectangle on the display image and in the visualizer.
738  if (do_display_)
739  {
740  displayFacesOnImage(faces_vector);
741  cv_mutex_.unlock();
742  }
743  // Done drawing.
744 
745  /******** Done display **********************************************************/
746 
747  ROS_INFO_STREAM_NAMED("face_detector", "Number of faces found: " << faces_vector.size() << ", number with good depth and size: " << ngood);
748 
749  // If you don't want continuous processing and you've found at least one face, turn off the detector.
750  if (!do_continuous_ && found_faces)
751  {
752  as_.setSucceeded(result_);
753  }
754  }
755 
756  // Draw bounding boxes around detected faces on the cv_image_out_ and show the image.
757  void displayFacesOnImage(vector<Box2D3D> faces_vector)
758  {
759 
760  Box2D3D *one_face;
761 
762  for (uint iface = 0; iface < faces_vector.size(); iface++)
763  {
764  one_face = &faces_vector[iface];
765  // Visualization by image display.
766  if (do_display_)
767  {
768  cv::Scalar color;
769  if (one_face->status == "good")
770  {
771  color = cv::Scalar(0, 255, 0);
772  }
773  else if (one_face->status == "unknown")
774  {
775  color = cv::Scalar(255, 0, 0);
776  }
777  else
778  {
779  color = cv::Scalar(0, 0, 255);
780  }
781 
782  cv::rectangle(cv_image_out_,
783  cv::Point(one_face->box2d.x, one_face->box2d.y),
784  cv::Point(one_face->box2d.x + one_face->box2d.width, one_face->box2d.y + one_face->box2d.height), color, 4);
785  }
786  }
787 
788  cv::imshow("Face detector: Face Detection", cv_image_out_);
789  cv::waitKey(2);
790 
791  }
792 
793 
794 }; // end class
795 
796 }; // end namespace people
797 
798 // Main
799 int main(int argc, char **argv)
800 {
801  ros::init(argc, argv, "face_detector");
802 
804 
805  return 0;
806 }
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
message_filters::Subscriber< sensor_msgs::CameraInfo > c2_info_sub_
image_transport::SubscriberFilter depth_image_sub_
#define FACE_SIZE_MIN_M
Definition: faces.h:65
boost::shared_ptr< const Goal > acceptNewGoal()
image_geometry::StereoCameraModel cam_model_
A structure for holding information about boxes in 2d and 3d.
Definition: faces.h:78
boost::function< void(const SingleSubscriberPublisher &)> SubscriberStatusCallback
boost::shared_ptr< ApproximateDepthSync > approximate_depth_sync_
static void pointMsgToTF(const geometry_msgs::Point &msg_v, Point &bt_v)
message_filters::Synchronizer< ExactDepthPolicy > ExactDepthSync
void publish(const boost::shared_ptr< M > &message) const
f
bool fromCameraInfo(const sensor_msgs::CameraInfo &left, const sensor_msgs::CameraInfo &right)
Time & fromNSec(uint64_t t)
void displayFacesOnImage(vector< Box2D3D > faces_vector)
boost::shared_ptr< ExactDispSync > exact_disp_sync_
face_detector::FaceDetectorFeedback feedback_
image_transport::ImageTransport it_
string status
Definition: faces.h:85
std::string resolveName(const std::string &name, bool remap=true) const
ros::Subscriber pos_sub_
XmlRpcServer s
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
#define FACE_SEP_DIST_M
Definition: faces.h:69
ROSCPP_DECL const std::string & getName()
message_filters::sync_policies::ApproximateTime< sensor_msgs::Image, stereo_msgs::DisparityImage, sensor_msgs::CameraInfo, sensor_msgs::CameraInfo > ApproximateDispPolicy
Definition: faces.h:71
cv::Point3d center3d
Definition: faces.h:81
void transformPoint(const std::string &target_frame, const geometry_msgs::PointStamped &stamped_in, geometry_msgs::PointStamped &stamped_out) const
#define ROS_INFO_STREAM_NAMED(name, args)
ROSCPP_DECL std::string clean(const std::string &name)
map< string, RestampedPositionMeasurement > pos_list_
face_detector::FaceDetectorResult result_
message_filters::Synchronizer< ApproximateDispPolicy > ApproximateDispSync
message_filters::sync_policies::ApproximateTime< sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::CameraInfo, sensor_msgs::CameraInfo > ApproximateDepthPolicy
Position message callback.
void setSucceeded(const Result &result=Result(), const std::string &text=std::string(""))
ROSCPP_DECL void spin(Spinner &spinner)
#define FACE_SIZE_MAX_M
Definition: faces.h:66
void imageCBAllDepth(const sensor_msgs::Image::ConstPtr &image, const sensor_msgs::Image::ConstPtr &depth_image, const sensor_msgs::CameraInfo::ConstPtr &c1_info, const sensor_msgs::CameraInfo::ConstPtr &c2_info)
Image callback for synced messages.
FaceDetector(std::string name)
cv::Rect box2d
Definition: faces.h:84
message_filters::Subscriber< sensor_msgs::CameraInfo > c1_info_sub_
ros::Publisher pos_array_pub_
message_filters::sync_policies::ExactTime< sensor_msgs::Image, stereo_msgs::DisparityImage, sensor_msgs::CameraInfo, sensor_msgs::CameraInfo > ExactDispPolicy
void setPreempted(const Result &result=Result(), const std::string &text=std::string(""))
message_filters::Synchronizer< ExactDispPolicy > ExactDispSync
tf::TransformListener tf_
Duration & fromSec(double t)
actionlib::SimpleActionServer< face_detector::FaceDetectorAction > as_
bool param(const std::string &param_name, T &param_val, const T &default_val) const
void registerPreemptCallback(boost::function< void()> cb)
const Subscriber & getSubscriber() const
#define MAX_FACE_Z_M
Definition: faces.h:67
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
int main(int argc, char **argv)
message_filters::sync_policies::ExactTime< sensor_msgs::Image, sensor_msgs::Image, sensor_msgs::CameraInfo, sensor_msgs::CameraInfo > ExactDepthPolicy
vector< Box2D3D > detectAllFacesDisparity(const cv::Mat &image, double threshold, const cv::Mat &disparity_image, image_geometry::StereoCameraModel *cam_model)
Detect all faces in an image and disparity image.
Definition: faces.cpp:101
void subscribe(ImageTransport &it, const std::string &base_topic, uint32_t queue_size, const TransportHints &transport_hints=TransportHints())
void initFaceDetectionDepth(uint num_cascades, string haar_classifier_filename, double face_size_min_m, double face_size_max_m, double max_face_z_m, double face_sep_dist_m)
Initialize the face detector with images and depth.
Definition: faces.cpp:244
vector< Box2D3D > detectAllFacesDepth(const cv::Mat &image, double threshold, const cv::Mat &depth_image, image_geometry::StereoCameraModel *cam_model)
Detect all faces in an image and depth image.
Definition: faces.cpp:268
uint32_t getNumSubscribers() const
void subscribe(ros::NodeHandle &nh, const std::string &topic, uint32_t queue_size, const ros::TransportHints &transport_hints=ros::TransportHints(), ros::CallbackQueueInterface *callback_queue=0)
boost::shared_ptr< ExactDepthSync > exact_depth_sync_
Contains a list of faces and functions that can be performed on that list. This includes utility task...
Definition: faces.h:105
ros::NodeHandle nh_
image_transport::SubscriberFilter image_sub_
void initFaceDetectionDisparity(uint num_cascades, string haar_classifier_filename, double face_size_min_m, double face_size_max_m, double max_face_z_m, double face_sep_dist_m)
Initialize the face detector with images and disparities.
Definition: faces.cpp:79
boost::shared_ptr< ApproximateDispSync > approximate_disp_sync_
message_filters::Subscriber< stereo_msgs::DisparityImage > disp_image_sub_
void registerGoalCallback(boost::function< void()> cb)
boost::mutex connect_mutex_
void matchAndDisplay(vector< Box2D3D > faces_vector, std_msgs::Header header)
void imageCBAllDisp(const sensor_msgs::Image::ConstPtr &image, const stereo_msgs::DisparityImage::ConstPtr &disp_image, const sensor_msgs::CameraInfo::ConstPtr &c1_info, const sensor_msgs::CameraInfo::ConstPtr &c2_info)
Image callback for synced messages.
ros::Publisher cloud_pub_
message_filters::Synchronizer< ApproximateDepthPolicy > ApproximateDepthSync
void operator()(void const *) const
#define ROS_DEBUG(...)


face_detector
Author(s): Caroline Pantofaru
autogenerated on Fri Jun 7 2019 22:07:47