00001
00002
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
00011
00012
00013
00014
00015 ObjectPtr createObjectMessage(
00016 darknet_object &detected_object
00017 );
00018
00019
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
00027 bool DarknetDetector::start()
00028 {
00029
00030 latest_image_.reset();
00031
00032
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
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
00078
00079
00080
00081
00082 if (use_scene_service_ || publish_detections_topic_)
00083 {
00084
00085 image_sub_ = it_.subscribe(image_sub_topic_name, 1,
00086 &DarknetDetector::imageSubscriberCallback, this);
00087 }
00088
00089
00090 if (use_scene_service_)
00091 {
00092
00093 scene_callback_q_ = boost::make_shared<ros::CallbackQueue>();
00094
00095
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
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
00112 scene_spinner_ = boost::make_shared<ros::AsyncSpinner>(num_service_threads,
00113 scene_callback_q_
00114 .get());
00115 scene_spinner_->start();
00116 }
00117
00118
00119 if (use_image_service_)
00120 {
00121
00122 image_callback_q_ = boost::make_shared<ros::CallbackQueue>();
00123
00124
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
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
00141 image_spinner_ = boost::make_shared<ros::AsyncSpinner>(num_service_threads,
00142 image_callback_q_
00143 .get());
00144 image_spinner_->start();
00145 }
00146
00147
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
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
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205 return true;
00206 }
00207
00208
00209 void DarknetDetector::imageSubscriberCallback(
00210 const sensor_msgs::ImageConstPtr &msg)
00211 {
00212
00213 boost::mutex::scoped_lock lock(mutex_, boost::try_to_lock);
00214
00215 if (lock)
00216 {
00217 latest_image_ = msg;
00218 }
00219 }
00220
00221
00222 bool DarknetDetector::detectObjects(cv_bridge::CvImagePtr cv_ptr,
00223 std::vector<Object> &detected_objects)
00224 {
00225
00226 IplImage ipl_image = (IplImage)cv_ptr->image;
00227 darknet_object *darknet_detections;
00228 int num_detected_objects = -1;
00229
00230
00231 bool detection_success = darknet_detect(&net_, &ipl_image,
00232 probability_threshold_,
00233 class_names_, &darknet_detections,
00234 &num_detected_objects);
00235
00236
00237 if (!detection_success)
00238 {
00239 ROS_ERROR("There was a failure during detection");
00240 return false;
00241 }
00242
00243
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
00251 free(darknet_detections);
00252
00253 return true;
00254 }
00255
00256
00257 bool DarknetDetector::sceneQueryCallback(SceneQuery::Request &req,
00258 SceneQuery::Response &res)
00259 {
00260 cv_bridge::CvImagePtr cv_ptr;
00261 {
00262
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
00282 bool detection_success = detectObjects(cv_ptr, res.objects);
00283 if (!detection_success)
00284 {
00285 return false;
00286 }
00287
00288
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
00297 bool DarknetDetector::imageQueryCallback(ImageQuery::Request &req,
00298 ImageQuery::Response &res)
00299 {
00300
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
00313 bool detection_success = detectObjects(cv_ptr, res.objects);
00314 if (!detection_success)
00315 {
00316 return false;
00317 }
00318
00319
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
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
00351 Detections detections_msg;
00352 bool detection_success = detectObjects(cv_ptr, detections_msg.objects);
00353 if (!detection_success)
00354 {
00355 return;
00356 }
00357
00358
00359 detections_msg.header = cv_ptr->header;
00360
00361
00362 detections_pub_.publish(detections_msg);
00363 }
00364
00365
00366 void DarknetDetector::runBackgroundDetections()
00367 {
00368
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
00377
00378 ros::spinOnce();
00379 }
00380
00381 timer.stop();
00382 }
00383
00384
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 }