00001 #include <ros/ros.h>
00002 #include <image_transport/image_transport.h>
00003 #include <image_transport/subscriber_filter.h>
00004 #include <message_filters/subscriber.h>
00005 #include <message_filters/cache.h>
00006 #include <tf/transform_listener.h>
00007 #include <kidnapped_robot/SavePlace.h>
00008 #include <kidnapped_robot/MatchRequest.h>
00009 #include <kidnapped_robot/MatchResult.h>
00010
00011 #include <cv_bridge/CvBridge.h>
00012 #include <image_geometry/stereo_camera_model.h>
00013
00014 #include <kidnapped_robot/place_database.h>
00015 #include <posest/pe3d.h>
00016
00017 #include <boost/thread.hpp>
00018 #include <boost/scoped_ptr.hpp>
00019 #include <cmath>
00020
00021 template<class M>
00022 boost::shared_ptr<M const> getCached(const ros::Time& time, const message_filters::Cache<M>& cache)
00023 {
00024 boost::shared_ptr<M const> before = cache.getElemBeforeTime(time);
00025 return before;
00026 }
00027
00028 class PlaceRecognitionNode
00029 {
00030
00031 ros::Subscriber save_sub_, match_sub_;
00032 image_transport::SubscriberFilter l_image_sub_, r_image_sub_;
00033 message_filters::Subscriber<sensor_msgs::CameraInfo> l_info_sub_, r_info_sub_;
00034 message_filters::Cache<sensor_msgs::Image> l_image_cache_, r_image_cache_;
00035 message_filters::Cache<sensor_msgs::CameraInfo> l_info_cache_, r_info_cache_;
00036 tf::TransformListener tf_;
00037
00038
00039 image_transport::Publisher image_sample_pub_, image_match_pub_;
00040 ros::Publisher transform_pub_;
00041
00042
00043 kidnapped_robot::PlaceDatabase place_db_;
00044 frame_common::FrameProc frame_processor_;
00045 pe::PoseEstimator3d pose_estimator_;
00046 int pr_inliers_;
00047
00048
00049 sensor_msgs::CvBridge l_bridge_, r_bridge_;
00050 image_geometry::StereoCameraModel cam_model_;
00051
00052 std::string target_frame_;
00053
00054 public:
00055 PlaceRecognitionNode(const std::string& db_file,
00056 const std::string& vocab_tree_file,
00057 const std::string& vocab_weights_file,
00058 const std::string& calonder_trees_file)
00059 : place_db_(db_file, vocab_tree_file, vocab_weights_file),
00060 frame_processor_(10),
00061 pose_estimator_(50000, false, 10.0, 3.0, 3.0),
00062 pr_inliers_(70)
00063 {
00064 ros::NodeHandle local_nh("~");
00065 int cache_size;
00066 local_nh.param("cache_size", cache_size, 10);
00067 l_image_cache_.setCacheSize(cache_size);
00068 l_info_cache_ .setCacheSize(cache_size);
00069 r_image_cache_.setCacheSize(cache_size);
00070 r_info_cache_ .setCacheSize(cache_size);
00071
00072
00073 local_nh.param<std::string>("target_frame", target_frame_, "/base_footprint");
00074
00075 pose_estimator_.windowed = false;
00076
00077
00078
00079
00080 typedef cv::CalonderDescriptorExtractor<float> Calonder;
00081 frame_processor_.setFrameDescriptor(new Calonder(calonder_trees_file));
00082
00083
00084 ros::NodeHandle nh;
00085 image_transport::ImageTransport it(nh);
00086 ros::NodeHandle stereo_nh("stereo");
00087 image_transport::ImageTransport stereo_it(stereo_nh);
00088 save_sub_ = nh.subscribe<kidnapped_robot::SavePlace>("save_image", 1, &PlaceRecognitionNode::saveCb, this);
00089 match_sub_ = nh.subscribe<kidnapped_robot::MatchRequest>("match", 1, &PlaceRecognitionNode::matchCb, this);
00090
00091 l_image_sub_.subscribe(stereo_it, "left/image_rect", 1);
00092 l_image_cache_.connectInput(l_image_sub_);
00093 l_info_sub_ .subscribe(stereo_nh, "left/camera_info", 1);
00094 l_info_cache_.connectInput(l_info_sub_);
00095 r_image_sub_.subscribe(stereo_it, "right/image_rect", 1);
00096 r_image_cache_.connectInput(r_image_sub_);
00097 r_info_sub_ .subscribe(stereo_nh, "right/camera_info", 1);
00098 r_info_cache_.connectInput(r_info_sub_);
00099
00100
00101 image_sample_pub_ = it.advertise("image_samples", 1);
00102 image_match_pub_ = it.advertise("image_matches", 1);
00103 transform_pub_ = nh.advertise<kidnapped_robot::MatchResult>("transform_matches", 1);
00104 }
00105
00106 void saveCb(const kidnapped_robot::SavePlaceConstPtr& msg)
00107 {
00108
00109 sensor_msgs::ImageConstPtr l_image = getCached(msg->stamp, l_image_cache_);
00110 sensor_msgs::ImageConstPtr r_image = getCached(msg->stamp, r_image_cache_);
00111 sensor_msgs::CameraInfoConstPtr l_info = getCached(msg->stamp, l_info_cache_);
00112 sensor_msgs::CameraInfoConstPtr r_info = getCached(msg->stamp, r_info_cache_);
00113
00114 if (!l_image || !r_image || !l_info || !r_info)
00115 {
00116 ROS_WARN_STREAM("Unable to retrieve all messages at requested time " << msg->stamp << " : " << (bool) l_image
00117 << ", " << (bool) r_image << ", " << (bool) l_info << ", " << (bool) r_info << "; range is "
00118 << l_image_cache_.getOldestTime() << ", " << l_image_cache_.getLatestTime());
00119 return;
00120 }
00121
00122 imageCb(l_image, l_info, r_image, r_info, true, msg->id);
00123 }
00124
00125 void matchCb(const kidnapped_robot::MatchRequestConstPtr& msg)
00126 {
00127
00128 sensor_msgs::ImageConstPtr l_image = getCached(msg->stamp, l_image_cache_);
00129 sensor_msgs::ImageConstPtr r_image = getCached(msg->stamp, r_image_cache_);
00130 sensor_msgs::CameraInfoConstPtr l_info = getCached(msg->stamp, l_info_cache_);
00131 sensor_msgs::CameraInfoConstPtr r_info = getCached(msg->stamp, r_info_cache_);
00132
00133 if (!l_image || !r_image || !l_info || !r_info)
00134 {
00135 ROS_WARN("Unable to retrieve all messages at requested time");
00136 return;
00137 }
00138
00139 imageCb(l_image, l_info, r_image, r_info, false, 0);
00140 }
00141
00142 void imageCb(const sensor_msgs::ImageConstPtr& l_image,
00143 const sensor_msgs::CameraInfoConstPtr& l_cam_info,
00144 const sensor_msgs::ImageConstPtr& r_image,
00145 const sensor_msgs::CameraInfoConstPtr& r_cam_info,
00146 bool save_in_db, uint32_t save_id)
00147 {
00150
00151
00152 cv::Mat left, right;
00153 try {
00154 left = l_bridge_.imgMsgToCv(l_image, "mono8");
00155 right = r_bridge_.imgMsgToCv(r_image, "mono8");
00156 }
00157 catch (sensor_msgs::CvBridgeException& e) {
00158 ROS_ERROR("Conversion error: %s", e.what());
00159 return;
00160 }
00161 cam_model_.fromCameraInfo(l_cam_info, r_cam_info);
00162
00163 frame_common::CamParams cam_params;
00164 cam_params.fx = cam_model_.left().fx();
00165 cam_params.fy = cam_model_.left().fy();
00166 cam_params.cx = cam_model_.left().cx();
00167 cam_params.cy = cam_model_.left().cy();
00168 cam_params.tx = cam_model_.baseline();
00169
00170
00171 frame_common::Frame frame;
00172 frame.setCamParams(cam_params);
00173 frame_processor_.setStereoFrame(frame, left, right);
00174
00175 frame.imgRight = cv::Mat();
00176
00177
00178 if (image_sample_pub_.getNumSubscribers() > 0) {
00179 cv::Mat display;
00180 cv::drawKeypoints(left, frame.kpts, display, CV_RGB(255,0,0));
00181 IplImage display_ipl = display;
00182 image_sample_pub_.publish(l_bridge_.cvToImgMsg(&display_ipl, "bgr8"));
00183 }
00184
00185
00186 try {
00187 tf::StampedTransform camera_in_base;
00188 ros::Time cam_time = cam_model_.left().stamp();
00189 ros::Duration timeout(0.5);
00190 tf_.waitForTransform(target_frame_, cam_model_.tfFrame(), cam_time, timeout);
00191 tf_.lookupTransform(target_frame_, cam_model_.tfFrame(), cam_time, camera_in_base);
00192
00193 if (save_in_db) {
00194
00195
00196 int64_t id = place_db_.add(cam_time, tf::Pose(), camera_in_base, frame, save_id);
00197 ROS_INFO("Added place with id %li", id);
00198 }
00199 else {
00200
00201 const size_t N = 10;
00202 vt::Matches matches;
00203 place_db_.findMatching(frame, N, matches);
00204
00205 printf("Matches (id, score, inliers):\n");
00206 for (int i = 0; i < (int)matches.size(); ++i) {
00207 int64_t id = matches[i].id;
00208 frame_common::Frame db_frame;
00209 place_db_.getFrame(id, db_frame);
00210 int inliers = pose_estimator_.estimate(db_frame, frame);
00211 printf("\t%li\t%f\t%i\n", id, matches[i].score, inliers);
00212
00213 if (inliers > pr_inliers_) {
00214 tf::Pose db_base_in_map;
00215 tf::Transform db_camera_in_base;
00216 place_db_.getTransforms(id, db_base_in_map, db_camera_in_base);
00217
00218
00219 tf::Transform pe_transform;
00220 Eigen::Matrix3d rot = pose_estimator_.rot;
00221 pe_transform.getBasis().setValue(rot(0,0), rot(0,1), rot(0,2),
00222 rot(1,0), rot(1,1), rot(1,2),
00223 rot(2,0), rot(2,1), rot(2,2));
00224 Eigen::Vector3d trans = pose_estimator_.trans;
00225 pe_transform.getOrigin().setValue(trans[0], trans[1], trans[2]);
00227 tf::Transform transform = db_camera_in_base * pe_transform * camera_in_base.inverse();
00228
00229 publishTransform(transform, id);
00230
00231
00232 if (image_match_pub_.getNumSubscribers() > 0) {
00233 cv::Mat display, left_db, right_db;
00234 place_db_.getImages(id, left_db, right_db);
00235 cv::drawMatches(left_db, db_frame.kpts, left, frame.kpts, pose_estimator_.inliers, display);
00236 IplImage display_ipl = display;
00237 image_match_pub_.publish(l_bridge_.cvToImgMsg(&display_ipl, "bgr8"));
00238 }
00239 }
00240 }
00241 }
00242 }
00243 catch (tf::TransformException& ex) {
00244 ROS_WARN("TF exception:\n%s", ex.what());
00245 return;
00246 }
00247 }
00248
00249 void publishTransform(const tf::Transform& transform, int64_t match_id)
00250 {
00251 kidnapped_robot::MatchResult result;
00252 tf::transformTFToMsg(transform, result.transform);
00253 result.match_id = match_id;
00254 transform_pub_.publish(result);
00255 }
00256 };
00257
00258 int main(int argc, char **argv)
00259 {
00260 ros::init(argc, argv, "place_recognition_node");
00261
00262 if (argc < 5) {
00263 printf("Usage: %s places.db vocab.tree vocab.weights calonder.rtc\n", argv[0]);
00264 return 1;
00265 }
00266
00267 PlaceRecognitionNode node(argv[1], argv[2], argv[3], argv[4]);
00268
00269 ros::spin();
00270 }