00001 #include <ros/ros.h>
00002 #include <sensor_msgs/Image.h>
00003 #include <image_transport/image_transport.h>
00004 #include <opencv/cv.h>
00005 #include <opencv/highgui.h>
00006 #include <cv_bridge/CvBridge.h>
00007 #include <boost/thread/mutex.hpp>
00008
00009
00010 #include <pcl/point_cloud.h>
00011 #include <pcl/point_types.h>
00012 #include <pcl_ros/impl/transforms.hpp>
00013 #include <sensor_msgs/PointCloud2.h>
00014 #include <tf/tfMessage.h>
00015 #include <tf/transform_broadcaster.h>
00016
00017 #include <image_transport/image_transport.h>
00018 #include <message_filters/subscriber.h>
00019 #include <message_filters/synchronizer.h>
00020 #include <message_filters/sync_policies/approximate_time.h>
00021 #include <image_transport/subscriber_filter.h>
00022
00023
00024 #include <motld/BoundingBox.h>
00025 #include <motld/Save.h>
00026 #include <motld/Config.h>
00027 #include <motld/TrackedObjects.h>
00028
00029
00030 #include <geometry_msgs/PoseArray.h>
00031
00032
00033 #include <motld/MultiObjectTLD.h>
00034 #ifndef SQR
00035 #define SQR(X) ((X)*(X))
00036 #endif
00037
00038 using namespace motld;
00039
00040
00041 typedef pcl::PointXYZ Point_t;
00042 typedef pcl::PointCloud<Point_t> Cloud_t;
00043 typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::Image, sensor_msgs::PointCloud2> MySyncPolicy;
00044
00045 struct CloudProperties {
00046 pcl::PointXYZ mean;
00047 float width;
00048 float height;
00049 };
00050
00051 bool debug = false;
00052
00053
00054 const int CHANNELS = 3;
00055 int width_ = 640;
00056 int height_ = 480;
00057 bool force_resize_ = true;
00058 float resize_factor_ = 1.f;
00059
00060
00061 bool use_cloud_ = true;
00062 Cloud_t cloud_;
00063 std::string frame_id_;
00064
00065
00066 unsigned char *img_;
00067 IplImage *ipl_img_;
00068 std::string image_frame_;
00069 sensor_msgs::CvBridge bridge_;
00070
00071
00072 motld::TrackedObjects tracked_objects_;
00073 geometry_msgs::PoseArray debug_objects_;
00074 ros::Publisher poses_pub_;
00075 ros::Publisher debug_poses_pub_;
00076
00077
00078
00079 Matrix maRed;
00080 Matrix maGreen;
00081 Matrix maBlue;
00082
00083 IplImage *out_img_;
00084 sensor_msgs::Image image_msg_;
00085 image_transport::Publisher image_pub_;
00086
00087
00088 bool learningEnabled_ = true;
00089 MOTLDSettings settings = MOTLDSettings(COLOR_MODE_RGB);
00090 MultiObjectTLD tld_ = MultiObjectTLD(width_, height_, settings);
00091 boost::mutex tld_mutex_;
00092
00093
00094 bool initialized_ = false;
00095 bool have_initial_box_ = false;
00096 bool started_ = false;
00097 bool read_model_from_file_ = false;
00098 std::string model_file_("");
00099 std::string camera_topic_("/camera/rgb/image_color");
00100 std::string depth_topic_("/camera/depth_registered/points");
00101
00102 CloudProperties calculateCloudProperties(ObjectBox box) {
00103 CloudProperties cp;
00104
00105 int startX, startY;
00106 int endX, endY;
00107
00108 if (!initialized_ || !started_) {
00109 ROS_WARN("calculateCloudProperties called but tracker is not yet started");
00110 return cp;
00111 }
00112
00113 if (force_resize_) {
00114 box.x *= resize_factor_;
00115 box.y *= resize_factor_;
00116 box.width *= resize_factor_;
00117 box.height *= resize_factor_;
00118 }
00119
00120
00121 if (int(cloud_.width) != ipl_img_->width * resize_factor_) {
00122 startX = box.x/2;
00123 startY = box.y/2;
00124 endX = startX + box.width / 2;
00125 endY = startY + box.height / 2;
00126 } else {
00127 startX = box.x;
00128 startY = box.y;
00129 endX = startX + box.width;
00130 endY = startY + box.height;
00131 }
00132
00133
00134 int count = 0;
00135 double mean_z = 0.;
00136 for (int x = startX; x < endX; ++x) {
00137 for (int y = startY; y < endY; ++y) {
00138 const pcl::PointXYZ &p = cloud_(x, y);
00139 if (std::isnan(p.x) || std::isnan(p.y) || std::isnan(p.z))
00140 continue;
00141 mean_z += p.z;
00142 ++count;
00143 }
00144 }
00145 mean_z /=count;
00146
00147 double var_z = 0.;
00148 for (int x = startX; x < endX; ++x) {
00149 for (int y = startY; y < endY; ++y) {
00150 const pcl::PointXYZ &p = cloud_(x, y);
00151 if (std::isnan(p.x) || std::isnan(p.y) || std::isnan(p.z))
00152 continue;
00153 var_z += SQR(p.z - mean_z);
00154 }
00155 }
00156 var_z /= count;
00157 double std_dev_z = std::sqrt(var_z);
00158
00159
00160 float min_x = 1e7, max_x = -1e7;
00161 float min_y = 1e7, max_y = -1e7;
00162 count = 0;
00163 for (int x = startX; x < endX; ++x) {
00164 for (int y = startY; y < endY; ++y) {
00165 const pcl::PointXYZ &p = cloud_(x, y);
00166 if (std::isnan(p.x) || std::isnan(p.y) || std::isnan(p.z))
00167 continue;
00168 if (std::fabs(p.z - mean_z) > 2. * std_dev_z) {
00169
00170
00171 continue;
00172 }
00173 cp.mean.x += p.x;
00174 cp.mean.y += p.y;
00175 cp.mean.z += p.z;
00176 if (p.x < min_x)
00177 min_x = p.x;
00178 if (p.x > max_x)
00179 max_x = p.x;
00180 if (p.y < min_y)
00181 min_y = p.y;
00182 if (p.y > max_y)
00183 max_y = p.y;
00184 ++count;
00185 }
00186 }
00187 if (count != 0) {
00188 cp.mean.x /= count;
00189 cp.mean.y /= count;
00190 cp.mean.z /= count;
00191 cp.width = max_x - min_x;
00192 cp.height = max_y - min_y;
00193 }
00194
00195 if (cp.height < 0)
00196 cp.height = 0. ;
00197 if (cp.width < 0)
00198 cp.width = 0. ;
00199
00200 return cp;
00201 }
00202
00203 void init() {
00204
00205 if (read_model_from_file_)
00206 {
00207 tld_ = MultiObjectTLD::loadClassifier(model_file_.c_str());
00208 have_initial_box_ = true;
00209 }
00210
00211
00212 img_ = new unsigned char[width_ * height_ * CHANNELS];
00213
00214 CvSize wsize = {width_, height_};
00215 ipl_img_ = cvCreateImage(wsize, IPL_DEPTH_8U, CHANNELS);
00216 out_img_ = cvCreateImage(wsize, IPL_DEPTH_8U, CHANNELS);
00217 initialized_ = true;
00218 ROS_INFO("done initializing");
00219 }
00220
00221 void prepareForProcessing(IplImage *image) {
00222 if (!initialized_)
00223 {
00224 ROS_WARN("motld not initialized when calling prepareForProcessing()");
00225 return;
00226 }
00227 IplImage *copy_src = image;
00228 int size = width_ * height_;
00229 if(force_resize_)
00230 {
00231 cvResize(image, ipl_img_);
00232 copy_src = ipl_img_;
00233 }
00234
00235 for(int i = 0; i < size; i++){
00236 img_[i] = copy_src->imageData[i*3+2];
00237 img_[i+size] = copy_src->imageData[i*3+1];
00238 img_[i+2*size] = copy_src->imageData[i*3];
00239 }
00240 }
00241
00242 void processAndPublish() {
00243 if (!initialized_)
00244 {
00245 ROS_WARN("motld not initialized when calling processAndPublish()");
00246 return;
00247 }
00248 double start = ros::Time::now().toSec();
00249 tld_.processFrame(img_);
00250 double elapsed_time = ros::Time::now().toSec() - start;
00251 double fps = 1. / elapsed_time;
00252 ROS_DEBUG("MOTLD fps: %.2f", fps);
00253
00254
00255 if (image_pub_.getNumSubscribers() > 0)
00256 {
00257 tld_.getDebugImage(img_, maRed, maGreen, maBlue, 0);
00258 for(int i = 0; i < width_*height_; ++i){
00259 out_img_->imageData[3*i+2] = maRed.data()[i];
00260 out_img_->imageData[3*i+1] = maGreen.data()[i];
00261 out_img_->imageData[3*i+0] = maBlue.data()[i];
00262 }
00263 bridge_.fromIpltoRosImage(out_img_, image_msg_);
00264 image_msg_.header.stamp = ros::Time::now();
00265 image_msg_.header.frame_id = image_frame_;
00266 image_pub_.publish(image_msg_);
00267 }
00268 }
00269
00270 void processCloud() {
00271
00272 bool poses_pub_subscribed = poses_pub_.getNumSubscribers() > 0;
00273 bool debug_pub_subscribed = debug_poses_pub_.getNumSubscribers() > 0;
00274 if (poses_pub_subscribed || debug_pub_subscribed)
00275 {
00276 std::vector<ObjectBox> boxes = tld_.getObjectBoxes();
00277
00278
00279 tracked_objects_.pose.header.stamp = ros::Time::now();
00280 tracked_objects_.pose.header.frame_id = frame_id_;
00281 tracked_objects_.height.resize(boxes.size());
00282 tracked_objects_.width.resize(boxes.size());
00283 tracked_objects_.status.resize(boxes.size());
00284 tracked_objects_.name.resize(boxes.size());
00285 tracked_objects_.pose.poses.resize(boxes.size());
00286
00287
00288 debug_objects_.header.stamp = ros::Time::now();
00289 debug_objects_.header.frame_id = frame_id_;
00290 debug_objects_.poses.resize(boxes.size());
00291
00292 for (size_t o = 0; o < boxes.size(); ++o)
00293 {
00294 int status = tld_.getStatus(boxes[o].objectId);
00295
00296 tracked_objects_.name[o] = boxes[o].objectId;
00297
00298
00299 CloudProperties cp = calculateCloudProperties(boxes[o]);
00300 tracked_objects_.height[o] = cp.height;
00301 tracked_objects_.width[o] = cp.width;
00302 tracked_objects_.status[o] = status;
00303
00304 tracked_objects_.pose.poses[o].position.x = cp.mean.x;
00305 tracked_objects_.pose.poses[o].position.y = cp.mean.y;
00306 tracked_objects_.pose.poses[o].position.z = cp.mean.z;
00307
00308
00309 tracked_objects_.pose.poses[o].orientation.x = 0.f;
00310 tracked_objects_.pose.poses[o].orientation.y = 0.f;
00311 tracked_objects_.pose.poses[o].orientation.z = 0.f;
00312 tracked_objects_.pose.poses[o].orientation.w = 1.f;
00313
00314 if (debug_pub_subscribed)
00315 {
00316 debug_objects_.poses[o].position.x = cp.mean.x;
00317 debug_objects_.poses[o].position.y = cp.mean.y;
00318 debug_objects_.poses[o].position.z = cp.mean.z;
00319
00320
00321 debug_objects_.poses[o].orientation.x = 0.f;
00322 debug_objects_.poses[o].orientation.y = 0.f;
00323 debug_objects_.poses[o].orientation.z = 0.f;
00324 debug_objects_.poses[o].orientation.w = 1.f;
00325 }
00326 }
00327
00328 if (poses_pub_subscribed)
00329 poses_pub_.publish(tracked_objects_);
00330
00331 if (debug_pub_subscribed)
00332 debug_poses_pub_.publish(debug_objects_);
00333 }
00334 }
00335
00340 void trackerCallback(const sensor_msgs::ImageConstPtr& msg) {
00341
00342 IplImage *color_image = bridge_.imgMsgToCv(msg, "bgr8");
00343 image_frame_ = msg->header.frame_id;
00344
00345
00346 if (!started_)
00347 {
00348 if (!force_resize_) {
00349 width_ = color_image->width;
00350 height_ = color_image->height;
00351 }
00352 else {
00353 resize_factor_ = color_image->width / width_;
00354 }
00355 started_ = true;
00356 }
00357
00358
00359 prepareForProcessing(color_image);
00360
00361 {
00362 boost::mutex::scoped_lock(tld_mutex_);
00363 processAndPublish();
00364 }
00365 }
00366
00367 void synchronizedCallback(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::PointCloud2ConstPtr& msg_cloud) {
00368
00369 trackerCallback(msg);
00370
00371 pcl::fromROSMsg(*msg_cloud, cloud_);
00372 frame_id_ = cloud_.header.frame_id;
00373
00374 processCloud();
00375 }
00376
00377 void boundingBoxCallback(const motld::BoundingBoxConstPtr& msg) {
00378 if (!initialized_ || !started_)
00379 {
00380 ROS_WARN("boundingBoxCallback called but tracker is not yet started");
00381 return;
00382 }
00383 ROS_INFO("boundingBoxCallback called");
00384 ObjectBox box;
00385 box.x = msg->x / resize_factor_;
00386 box.y = msg->y / resize_factor_;
00387 box.width = msg->width / resize_factor_;
00388 box.height = msg->height / resize_factor_;
00389 {
00390 boost::mutex::scoped_lock(tld_mutex_);
00391 tld_.addObject(box);
00392 have_initial_box_ = true;
00393 }
00394 }
00395
00396 void saveModelCallback(const motld::SaveConstPtr& msg) {
00397 boost::unique_lock<boost::mutex> state_lock_(tld_mutex_);
00398 ROS_INFO("Saving model to %s", msg->filename.c_str());
00399 tld_.saveClassifier(msg->filename.c_str());
00400 }
00401
00402 void configCallback(const motld::ConfigConstPtr& msg) {
00403 ROS_INFO("Got new config parameters");
00404 if (msg->learning != -1) {
00405 learningEnabled_ = msg->learning > 0;
00406 }
00407
00408
00409 {
00410 boost::mutex::scoped_lock tld_lock(tld_mutex_);
00411 tld_.enableLearning(learningEnabled_);
00412 }
00413 }
00414
00415
00419 int main(int argc, char **argv) {
00420 ros::init(argc, argv, "motld_node");
00421 ros::NodeHandle nh;
00422 ros::NodeHandle private_nh("~");
00423 image_transport::ImageTransport it(nh);
00424
00425 private_nh.param("width", width_, width_);
00426 private_nh.param("height", width_, width_);
00427 private_nh.param("force_resize", force_resize_, force_resize_);
00428 private_nh.param("learning_enabled", learningEnabled_, learningEnabled_);
00429 private_nh.param("model_file", model_file_, model_file_);
00430 private_nh.param("use_cloud", use_cloud_, use_cloud_);
00431 private_nh.param("camera_topic", camera_topic_, camera_topic_);
00432 private_nh.param("depth_topic", depth_topic_, depth_topic_);
00433 if (model_file_.compare("") != 0)
00434 {
00435 read_model_from_file_ = true;
00436 }
00437
00438 image_transport::SubscriberFilter sub(it, camera_topic_, 1);
00439 image_transport::Subscriber sub_img;
00440 message_filters::Subscriber< sensor_msgs::PointCloud2 > sub_cloud(nh, depth_topic_, 1);
00441 message_filters::Synchronizer< MySyncPolicy > sync(MySyncPolicy(30), sub, sub_cloud);
00442 if (!use_cloud_ || depth_topic_.compare("") == 0)
00443 {
00444 ROS_INFO("not registering with cloud");
00445 ROS_INFO_STREAM("topic: " << camera_topic_);
00446 sub_img = it.subscribe(camera_topic_, 1, trackerCallback);
00447 }
00448 else
00449 {
00450 ROS_INFO("using kinect registered version");
00451 ROS_INFO_STREAM("topics: " << camera_topic_ << " " << depth_topic_);
00452 sync.registerCallback(synchronizedCallback);
00453 }
00454
00455 ros::Subscriber sub_box = private_nh.subscribe<motld::BoundingBox>("tracker_start_box", 1, boundingBoxCallback);
00456 ros::Subscriber sub_config = private_nh.subscribe<motld::Config>("config_opentld", 1, configCallback);
00457 ros::Subscriber sub_save = private_nh.subscribe<motld::Save>("save_tld_model", 1, saveModelCallback);
00458
00459
00460 image_pub_ = it.advertise("tracking_image", 1);
00461 poses_pub_ = private_nh.advertise<motld::TrackedObjects>("tracked_objects", 1);
00462 debug_poses_pub_ = private_nh.advertise<geometry_msgs::PoseArray>("debug_poses", 1);
00463
00464
00465 init();
00466
00467 while (ros::ok())
00468 {
00469 ros::spinOnce();
00470 }
00471 }