darknet_detector.cpp
Go to the documentation of this file.
00001 //
00002 // Created by banerjs on 12/13/16.
00003 //
00004 
00005 #include "rail_object_detector/darknet_detector.h"
00006 
00007 using namespace rail_object_detector;
00008 using namespace rail_object_detection_msgs;
00009 
00010 // Helper functions
00011 //inline double_t timediff_usec(                  // Provide the difference btw
00012 //  timespec start,                               // 2 times in usec
00013 //  timespec end
00014 //);
00015 ObjectPtr createObjectMessage(                  // Create Object message
00016   darknet_object &detected_object
00017 );
00018 
00019 // Functions from darknet
00020 extern "C" bool darknet_detect(network *net, IplImage *image,
00021   float thresh, char **class_names, darknet_object **detected_objects, int
00022   *num_detected_objects);
00023 extern "C" network create_network(char *cfg_filename, char *weight_filename);
00024 extern "C" char **get_class_names(char *classnames_filename);
00025 
00026 // Implementation of start
00027 bool DarknetDetector::start()
00028 {
00029   // Reset the pointers
00030   latest_image_.reset();
00031 
00032   // Initialize the parameters
00033   int num_service_threads;
00034   std::string image_sub_topic_name;
00035   std::string classnames_filename;
00036   std::string cfg_filename;
00037   std::string weight_filename;
00038 
00039   std::stringstream classnames_default;
00040   std::stringstream cfg_default;
00041   std::stringstream weight_default;
00042   classnames_default << ros::package::getPath("rail_object_detector")
00043                      << "/libs/darknet/data/coco.names";
00044   cfg_default << ros::package::getPath("rail_object_detector")
00045               << "/libs/darknet/cfg/yolo.cfg";
00046   weight_default << ros::package::getPath("rail_object_detector")
00047                  << "/libs/darknet/yolo.weights";
00048 
00049   private_nh_.param("num_service_threads", num_service_threads, int(0));
00050 
00051   private_nh_.param("use_scene_service", use_scene_service_, bool(true));
00052   private_nh_.param("use_image_service", use_image_service_, bool(false));
00053   private_nh_.param("publish_detections_topic", publish_detections_topic_,
00054                     bool(false));
00055 
00056   private_nh_.param("max_desired_publish_freq", max_desired_publish_freq_,
00057                     float(5.0));
00058 
00059   private_nh_.param("image_sub_topic_name", image_sub_topic_name, std::string
00060     ("/kinect/hd/image_color_rect"));
00061 
00062   private_nh_.param("probability_threshold", probability_threshold_, float(
00063     .25));
00064 
00065   private_nh_.param("classnames_filename", classnames_filename,
00066                     classnames_default.str());
00067   private_nh_.param("cfg_filename", cfg_filename, cfg_default.str());
00068   private_nh_.param("weight_filename", weight_filename, weight_default.str());
00069 
00070   // Load the network into memory
00071   class_names_ = get_class_names((char *)classnames_filename.c_str());
00072   net_ = create_network(
00073     (char *)cfg_filename.c_str(),
00074     (char *)weight_filename.c_str()
00075   );
00076 
00077   // FIXME: Cannot figure out the size of the class_names_ array :(
00078   // int num_classes = sizeof(class_names_) / sizeof(class_names_[0]);
00079   // ROS_INFO("Created: %d classes", num_classes);
00080 
00081   // Subscribe to the image topic if the scene query or publisher must be used
00082   if (use_scene_service_ || publish_detections_topic_)
00083   {
00084     // NOTE: Might want to add the compressed hint to this subscription
00085     image_sub_ = it_.subscribe(image_sub_topic_name, 1,
00086                                &DarknetDetector::imageSubscriberCallback, this);
00087   }
00088 
00089   // Check to see if the scene service is to be used
00090   if (use_scene_service_)
00091   {
00092     // Initialize the service callback queues
00093     scene_callback_q_ = boost::make_shared<ros::CallbackQueue>();
00094 
00095     // Service for the scene query
00096     ros::AdvertiseServiceOptions scene_opts;
00097     boost::function<bool(SceneQuery::Request &, SceneQuery::Response &)>
00098       scene_callback_ptr = boost::bind(&DarknetDetector::sceneQueryCallback, this,
00099                                        _1, _2);
00100     scene_opts.init("objects_in_scene", scene_callback_ptr);
00101     scene_opts.callback_queue = scene_callback_q_.get();
00102 
00103     // Advertise the service
00104     scene_query_server_ = private_nh_.advertiseService(scene_opts);
00105     if (!scene_query_server_)
00106     {
00107       ROS_FATAL("Error in starting the scene service");
00108       return false;
00109     }
00110 
00111     // Start the spinners
00112     scene_spinner_ = boost::make_shared<ros::AsyncSpinner>(num_service_threads,
00113                                                            scene_callback_q_
00114                                                              .get());
00115     scene_spinner_->start();
00116   }
00117 
00118   // Check to see if the image service is to be used
00119   if (use_image_service_)
00120   {
00121     // Initialize the service callback queues
00122     image_callback_q_ = boost::make_shared<ros::CallbackQueue>();
00123 
00124     // Service for the image query
00125     ros::AdvertiseServiceOptions image_opts;
00126     boost::function<bool(ImageQuery::Request &, ImageQuery::Response &)>
00127       image_callback_ptr = boost::bind(&DarknetDetector::imageQueryCallback, this, _1,
00128                                        _2);
00129     image_opts.init("objects_in_image", image_callback_ptr);
00130     image_opts.callback_queue = image_callback_q_.get();
00131 
00132     // Advertise the service
00133     image_query_server_ = private_nh_.advertiseService(image_opts);
00134     if (!image_query_server_)
00135     {
00136       ROS_FATAL("Error in starting the image scene service");
00137       return false;
00138     }
00139 
00140     // Start the spinners
00141     image_spinner_ = boost::make_shared<ros::AsyncSpinner>(num_service_threads,
00142                                                            image_callback_q_
00143                                                              .get());
00144     image_spinner_->start();
00145   }
00146 
00147   // Check to see if detections should be published asynchronously
00148   if (publish_detections_topic_)
00149   {
00150     perform_detections_ = true;
00151     detections_pub_ = private_nh_.advertise<Detections>("detections", 2);
00152     detections_thread_ = new boost::thread(
00153       &DarknetDetector::runBackgroundDetections,
00154       this
00155     );
00156   }
00157 
00158   return true;
00159 }
00160 
00161 // Implementation of stop
00162 bool DarknetDetector::stop()
00163 {
00164   if (use_scene_service_ || publish_detections_topic_)
00165   {
00166     image_sub_.shutdown();
00167   }
00168 
00169   if (use_scene_service_)
00170   {
00171     scene_spinner_->stop();
00172     scene_spinner_.reset();
00173     scene_query_server_.shutdown();
00174     scene_callback_q_.reset();
00175     latest_image_.reset();
00176   }
00177 
00178   if (use_image_service_)
00179   {
00180     image_spinner_->stop();
00181     image_spinner_.reset();
00182     image_query_server_.shutdown();
00183     image_callback_q_.reset();
00184   }
00185 
00186   if (publish_detections_topic_)
00187   {
00188     perform_detections_ = false;
00189     detections_thread_->join();
00190     delete detections_thread_;
00191     detections_pub_.shutdown();
00192   }
00193 
00194   // FIXME: Double free when trying to free. Leave for now
00195   // free(net_);
00196 
00197   // FIXME: Cannot figure out the size of the class, so can't free :(
00198   // int num_classes = sizeof(class_names_) / sizeof(class_names_[0]);
00199   // ROS_INFO("Freeing: %d classes", num_classes);
00200   // for (int i = 0; i < num_classes; i++)
00201   // {
00202   //   free(class_names_[i]);
00203   // }
00204   // free(class_names_);
00205   return true;
00206 }
00207 
00208 // Implementation of the subscriber
00209 void DarknetDetector::imageSubscriberCallback(
00210   const sensor_msgs::ImageConstPtr &msg)
00211 {
00212   // Update the cache of the latest image if we can acquire the lock to it
00213   boost::mutex::scoped_lock lock(mutex_, boost::try_to_lock);
00214 
00215   if (lock)
00216   {
00217     latest_image_ = msg;
00218   }
00219 }
00220 
00221 // Implementation of detect objects
00222 bool DarknetDetector::detectObjects(cv_bridge::CvImagePtr cv_ptr,
00223   std::vector<Object> &detected_objects)
00224 {
00225   // Create an IplImage and allocate the required variables
00226   IplImage ipl_image = (IplImage)cv_ptr->image;
00227   darknet_object *darknet_detections;
00228   int num_detected_objects = -1;
00229 
00230   // Perform the detection
00231   bool detection_success = darknet_detect(&net_, &ipl_image,
00232                                           probability_threshold_,
00233                                           class_names_, &darknet_detections,
00234                                           &num_detected_objects);
00235 
00236   // Check if detection was successful
00237   if (!detection_success)
00238   {
00239     ROS_ERROR("There was a failure during detection");
00240     return false;
00241   }
00242 
00243   // Insert the detections into the response container
00244   for (int i = 0; i < num_detected_objects; i++)
00245   {
00246     ObjectPtr obj_ptr = createObjectMessage(darknet_detections[i]);
00247     detected_objects.push_back(*obj_ptr);
00248   }
00249 
00250   // Free resources and return
00251   free(darknet_detections);
00252 
00253   return true;
00254 }
00255 
00256 // Implementation of scene query callback
00257 bool DarknetDetector::sceneQueryCallback(SceneQuery::Request &req,
00258   SceneQuery::Response &res)
00259 {
00260   cv_bridge::CvImagePtr cv_ptr;
00261   {
00262     // Try to get a lock to the latest image of the scene
00263     boost::mutex::scoped_lock lock(mutex_);
00264     if (latest_image_.get() == NULL)
00265     {
00266       ROS_INFO_ONCE("No images from camera");
00267       return true;
00268     }
00269     try
00270     {
00271       cv_ptr = cv_bridge::toCvCopy(latest_image_,
00272                                    sensor_msgs::image_encodings::RGB8);
00273     }
00274     catch (const cv_bridge::Exception &ex)
00275     {
00276       ROS_ERROR("Unable to convert image message to mat: %s", ex.what());
00277       return false;
00278     }
00279   }
00280 
00281   // Process the image of the scene
00282   bool detection_success = detectObjects(cv_ptr, res.objects);
00283   if (!detection_success)
00284   {
00285     return false;
00286   }
00287 
00288   // Add metadata to the ROS message
00289   res.image = *(cv_ptr->toImageMsg());
00290   res.header.frame_id = res.image.header.frame_id;
00291   res.header.stamp = ros::Time::now();
00292 
00293   return true;
00294 }
00295 
00296 // Implementation of the image query callback
00297 bool DarknetDetector::imageQueryCallback(ImageQuery::Request &req,
00298   ImageQuery::Response &res)
00299 {
00300   // Create a CV image from the image message
00301   cv_bridge::CvImagePtr cv_ptr;
00302   try
00303   {
00304     cv_ptr = cv_bridge::toCvCopy(req.image, sensor_msgs::image_encodings::RGB8);
00305   }
00306   catch (const cv_bridge::Exception &ex)
00307   {
00308     ROS_ERROR("Unable to convert image message to mat: %s", ex.what());
00309     return false;
00310   }
00311 
00312   // Process the image
00313   bool detection_success = detectObjects(cv_ptr, res.objects);
00314   if (!detection_success)
00315   {
00316     return false;
00317   }
00318 
00319   // Populate the response object
00320   res.image = req.image;
00321   res.header.frame_id = res.image.header.frame_id;
00322   res.header.stamp = ros::Time::now();
00323 
00324   return true;
00325 }
00326 
00327 // Implementation of background detection callback
00328 void DarknetDetector::backgroundDetectionCallback(const ros::TimerEvent &e)
00329 {
00330   cv_bridge::CvImagePtr cv_ptr;
00331   {
00332     boost::mutex::scoped_lock lock(mutex_);
00333     if (latest_image_.get() == NULL)
00334     {
00335       ROS_INFO_ONCE("No images from camera");
00336       return;
00337     }
00338     try
00339     {
00340       cv_ptr = cv_bridge::toCvCopy(latest_image_,
00341                                    sensor_msgs::image_encodings::RGB8);
00342     }
00343     catch (const cv_bridge::Exception &ex)
00344     {
00345       ROS_ERROR("Unable to convert image message to mat: %s", ex.what());
00346       return;
00347     }
00348   }
00349 
00350   // Perform the detection
00351   Detections detections_msg;
00352   bool detection_success = detectObjects(cv_ptr, detections_msg.objects);
00353   if (!detection_success)
00354   {
00355     return;
00356   }
00357 
00358   // Add the metadata to the image
00359   detections_msg.header = cv_ptr->header;
00360 
00361   // Publish the message
00362   detections_pub_.publish(detections_msg);
00363 }
00364 
00365 // Implementation of run object detections
00366 void DarknetDetector::runBackgroundDetections()
00367 {
00368   // Setup the timer and callback for performing the detections
00369   ros::Duration min_desired_sleep = ros::Duration(1/max_desired_publish_freq_);
00370   ros::Timer timer = nh_.createTimer(min_desired_sleep,
00371                                      &DarknetDetector::backgroundDetectionCallback,
00372                                      this);
00373 
00374   while (perform_detections_)
00375   {
00376     // Essentially doing a ros::spin() but we want to exit when the
00377     // perform_detections_ boolean is unset
00378     ros::spinOnce();
00379   }
00380 
00381   timer.stop();
00382 }
00383 
00384 // Implementation of createObjectMessage
00385 ObjectPtr createObjectMessage(darknet_object &detected_object)
00386 {
00387   ObjectPtr msg = boost::make_shared<Object>();
00388   msg->label = std::string(detected_object.label);
00389   msg->probability = detected_object.probability;
00390   msg->centroid_x = detected_object.centroid_x;
00391   msg->centroid_y = detected_object.centroid_y;
00392   msg->left_bot_x = detected_object.left_bot_x;
00393   msg->left_bot_y = detected_object.left_bot_y;
00394   msg->right_top_x = detected_object.right_top_x;
00395   msg->right_top_y = detected_object.right_top_y;
00396   return msg;
00397 }


rail_object_detector
Author(s):
autogenerated on Sat Jun 8 2019 20:26:29