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/time_synchronizer.h>
00006 #include <dynamic_reconfigure/server.h>
00007
00008 #include <cv_bridge/CvBridge.h>
00009 #include <image_geometry/stereo_camera_model.h>
00010
00011 #include <vslam_system/vslam.h>
00012 #include <sba/visualization.h>
00013 #include <vslam_system/any_detector.h>
00014 #include <vslam_system/StereoVslamNodeConfig.h>
00015
00016
00017 #include <sba/Frame.h>
00018 #include <geometry_msgs/PointStamped.h>
00019 #include <opencv2/legacy/legacy.hpp>
00020
00021
00022
00023
00024 static const double MAX_KEYFRAME_DISTANCE = 0.2;
00025 static const double MAX_KEYFRAME_ANGLE = 0.1;
00026 static const int MIN_KEYFRAME_INLIERS = 0;
00027
00028 using namespace vslam;
00029
00030 class VONode
00031 {
00032 private:
00033 ros::NodeHandle nh_;
00034 image_transport::ImageTransport it_;
00035
00036
00037 image_transport::SubscriberFilter l_image_sub_, r_image_sub_;
00038 message_filters::Subscriber<sensor_msgs::CameraInfo> l_info_sub_, r_info_sub_;
00039 message_filters::TimeSynchronizer<sensor_msgs::Image, sensor_msgs::CameraInfo,
00040 sensor_msgs::Image, sensor_msgs::CameraInfo> sync_;
00041
00042
00043 ros::Publisher sba_frames_pub_;
00044
00045
00046 image_transport::CameraPublisher vo_tracks_pub_;
00047 cv::Mat vo_display_;
00048
00049
00050 sensor_msgs::CvBridge l_bridge_, r_bridge_;
00051 image_geometry::StereoCameraModel cam_model_;
00052 cv::Ptr<cv::FeatureDetector> detector_;
00053
00054 frame_common::FrameProc frame_processor_;
00056 std::vector<frame_common::Frame, Eigen::aligned_allocator<frame_common::Frame> > frames_;
00057 vslam::voSt vo_;
00058
00059 int numframes;
00060 int numpoints;
00061 int numcameras;
00062
00063 vector<sba::Projection> proj_msgs;
00064 vector<sba::WorldPoint> point_msgs;
00065 vector<sba::CameraNode> node_msgs;
00066
00067
00068 typedef dynamic_reconfigure::Server<vslam_system::StereoVslamNodeConfig> ReconfigureServer;
00069 ReconfigureServer reconfigure_server_;
00070
00071 bool init;
00072
00073 public:
00074
00075 VONode(const std::string& vocab_tree_file, const std::string& vocab_weights_file,
00076 const std::string& calonder_trees_file)
00077 : it_(nh_), sync_(3),
00078 detector_(new vslam_system::AnyDetector),
00079 vo_(boost::shared_ptr<pe::PoseEstimator>(new pe::PoseEstimator3d(1000,true,6.0,8.0,8.0)),
00080 40, 10, MIN_KEYFRAME_INLIERS, MAX_KEYFRAME_DISTANCE, MAX_KEYFRAME_ANGLE),
00081 numframes(0), numpoints(0), numcameras(0), init(true)
00082 {
00083
00084 typedef cv::CalonderDescriptorExtractor<float> Calonder;
00085 frame_processor_.setFrameDescriptor(new Calonder(calonder_trees_file));
00086
00087
00088 l_image_sub_.subscribe(it_, "left/image_rect", 1);
00089 l_info_sub_ .subscribe(nh_, "left/camera_info", 1);
00090 r_image_sub_.subscribe(it_, "right/image_rect", 1);
00091 r_info_sub_ .subscribe(nh_, "right/camera_info", 1);
00092 vo_tracks_pub_ = it_.advertiseCamera("/vo/vo_tracks/image", 1);
00093 sync_.connectInput(l_image_sub_, l_info_sub_, r_image_sub_, r_info_sub_);
00094 sync_.registerCallback( boost::bind(&VONode::imageCb, this, _1, _2, _3, _4) );
00095
00096
00097
00098
00099 sba_frames_pub_ = nh_.advertise<sba::Frame>("/sba/frames", 5000);
00100
00101
00102 ReconfigureServer::CallbackType f = boost::bind(&VONode::configCb, this, _1, _2);
00103 reconfigure_server_.setCallback(f);
00104 }
00105
00106 void imageCb(const sensor_msgs::ImageConstPtr& l_image,
00107 const sensor_msgs::CameraInfoConstPtr& l_cam_info,
00108 const sensor_msgs::ImageConstPtr& r_image,
00109 const sensor_msgs::CameraInfoConstPtr& r_cam_info)
00110 {
00111 ROS_INFO("In callback, seq = %u", l_cam_info->header.seq);
00112
00113
00114 cv::Mat left, right;
00115 try
00116 {
00117 left = l_bridge_.imgMsgToCv(l_image, "mono8");
00118 right = r_bridge_.imgMsgToCv(r_image, "mono8");
00119 }
00120 catch (sensor_msgs::CvBridgeException& e)
00121 {
00122 ROS_ERROR("Conversion error: %s", e.what());
00123 return;
00124 }
00125 cam_model_.fromCameraInfo(l_cam_info, r_cam_info);
00126
00127 frame_common::CamParams cam_params;
00128 cam_params.fx = cam_model_.left().fx();
00129 cam_params.fy = cam_model_.left().fy();
00130 cam_params.cx = cam_model_.left().cx();
00131 cam_params.cy = cam_model_.left().cy();
00132 cam_params.tx = cam_model_.baseline();
00133
00134 if (addFrame(cam_params, left, right))
00135 {
00136 if (vo_tracks_pub_.getNumSubscribers() > 0)
00137 {
00138 frame_common::drawVOtracks(left, vo_.frames, vo_display_);
00139 IplImage ipl = vo_display_;
00140 sensor_msgs::ImagePtr msg = sensor_msgs::CvBridge::cvToImgMsg(&ipl);
00141 msg->header = l_cam_info->header;
00142 vo_tracks_pub_.publish(msg, l_cam_info);
00143 }
00144 }
00145 }
00146
00147 void configCb(vslam_system::StereoVslamNodeConfig& config, uint32_t level)
00148 {
00149 dynamic_cast<vslam_system::AnyDetector*>((cv::FeatureDetector*)detector_)->update(config);
00150 frame_processor_.detector = detector_;
00151
00152
00153
00154
00155
00156 }
00157
00158 bool addFrame(const frame_common::CamParams& camera_parameters,
00159 const cv::Mat& left, const cv::Mat& right)
00160 {
00161
00162 frame_common::Frame next_frame;
00163 next_frame.setCamParams(camera_parameters);
00164 frame_processor_.setStereoFrame(next_frame, left, right);
00165 next_frame.frameId = numframes++;
00166 next_frame.img = cv::Mat();
00167 next_frame.imgRight = cv::Mat();
00168
00169
00170 bool is_keyframe = vo_.addFrame(next_frame);
00171
00172
00173 if (is_keyframe)
00174 {
00175 frames_.push_back(next_frame);
00176 publishLatestFrame();
00177 }
00178
00179 return is_keyframe;
00180 }
00181
00182 void publishLatestFrame()
00183 {
00184 if (sba_frames_pub_.getNumSubscribers() > 0)
00185 {
00186
00187 proj_msgs.clear();
00188 node_msgs.clear();
00189 point_msgs.clear();
00190
00191
00192
00193
00194
00195
00196 frame_common::Frame &f1 = frames_.back();
00197
00198
00199 f1.ipts.assign(f1.kpts.size(), -1);
00200
00201
00202 Quaterniond fq;
00203 Vector4d trans;
00204 Matrix<double,3,4> f2w;
00205
00206 if (init)
00207 {
00208 trans = Vector4d(0,0,0,1);
00209 fq.coeffs() = Vector4d(0,0,0,1);
00210 fq.normalize();
00211 }
00212 else
00213 {
00214
00215 sba::Node &nd0 = *(vo_.sba.nodes.end()-1);
00216 sba::Node &nd1 = vo_.sba.nodes.back();
00217 fq = nd1.qrot;
00218 trans = nd1.trans;
00219
00220 sba::transformF2W(f2w,nd0.trans,nd0.qrot);
00221
00222 }
00223
00224
00225
00226 int cameraindex = publishNode(trans, fq, f1.cam, init);
00227
00228 if (init)
00229 {
00230 init = false;
00231 return;
00232 }
00233
00236
00238 frame_common::Frame &f0 = *(frames_.end()-2);
00239 addProjections(f0, f1, vo_.pose_estimator_->inliers, f2w, cameraindex-1, cameraindex);
00240
00241 publishFrame();
00242 }
00243 }
00244
00245 void publishFrame()
00246 {
00247 sba::Frame msg;
00248 msg.nodes = node_msgs;
00249 msg.points = point_msgs;
00250 msg.projections = proj_msgs;
00251
00252 sba_frames_pub_.publish(msg);
00253 }
00254
00255 int publishNode(Eigen::Matrix<double,4,1> trans,
00256 Eigen::Quaternion<double> fq,
00257 const frame_common::CamParams &cp,
00258 bool isFixed)
00259 {
00260 sba::CameraNode msg;
00261
00262 msg.index = numcameras;
00263
00264 msg.transform.translation.x = trans.x();
00265 msg.transform.translation.y = trans.y();
00266 msg.transform.translation.z = trans.z();
00267 msg.transform.rotation.x = fq.x();
00268 msg.transform.rotation.y = fq.y();
00269 msg.transform.rotation.z = fq.z();
00270 msg.transform.rotation.w = fq.w();
00271 msg.fx = cp.fx;
00272 msg.fy = cp.fy;
00273 msg.cx = cp.cx;
00274 msg.cy = cp.cy;
00275 msg.baseline = cp.tx;
00276 msg.fixed = isFixed;
00277
00278 node_msgs.push_back(msg);
00279
00280 return numcameras++;
00281
00282 }
00283
00284 int publishPoint(sba::Point pt)
00285 {
00286 sba::WorldPoint msg;
00287
00288 msg.index = numpoints;
00289
00290 msg.x = pt.x();
00291 msg.y = pt.y();
00292 msg.z = pt.z();
00293 msg.w = pt.w();
00294
00295 point_msgs.push_back(msg);
00296
00297 return numpoints++;
00298
00299
00300 }
00301
00302 void publishProjection(int ci, int pi, Eigen::Vector3d &q, bool stereo)
00303 {
00304 sba::Projection msg;
00305 msg.camindex = ci;
00306 msg.pointindex = pi;
00307 msg.u = q.x();
00308 msg.v = q.y();
00309 msg.d = q.z();
00310 msg.stereo = stereo;
00311 msg.usecovariance = false;
00312
00313 proj_msgs.push_back(msg);
00314 }
00315
00316
00317 void addProjections(fc::Frame &f0, fc::Frame &f1,
00318 const std::vector<cv::DMatch> &inliers,
00319 const Matrix<double,3,4>& f2w, int ndi0, int ndi1)
00320 {
00321
00322 vector<bool> matched0(f0.ipts.size(),0);
00323 vector<bool> matched1(f1.ipts.size(),0);
00324
00325
00326 for (int i=0; i<(int)inliers.size(); i++)
00327 {
00328 int i0 = inliers[i].queryIdx;
00329 int i1 = inliers[i].trainIdx;
00330
00331 if (matched0[i0]) continue;
00332 if (matched1[i1]) continue;
00333 matched0[i0] = true;
00334 matched1[i1] = true;
00335
00336 int pti;
00337
00338 if (f0.ipts[i0] < 0 && f1.ipts[i1] < 0)
00339 {
00340 Vector4d pt;
00341 pt.head(3) = f2w*f0.pts[i0];
00342 pt(3) = 1.0;
00343
00344 pti = publishPoint(pt);
00345 f0.ipts[i0] = pti;
00346 f1.ipts[i1] = pti;
00347
00348 vo_.ipts.push_back(-1);
00349
00350 Vector3d ipt = getProjection(f0, i0);
00351 publishProjection(ndi0, pti, ipt, true);
00352
00353
00354 ipt = getProjection(f1, i1);
00355 publishProjection(ndi1, pti, ipt, true);
00356 }
00357
00358
00359 else if (f0.ipts[i0] >= 0 && f1.ipts[i1] >= 0)
00360 {
00361 if (f0.ipts[i0] != f1.ipts[i1])
00362 {
00363
00364
00365 printf("We are trying to merge tracks %d and %d with ipts %d and %d\n", i0, i1, f0.ipts[i0], f1.ipts[i1]);
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387 }
00388 }
00389
00390 else if (f1.ipts[i1] < 0)
00391 {
00392 pti = f0.ipts[i0];
00393 f1.ipts[i1] = pti;
00394
00395
00396 Vector3d ipt = getProjection(f1, i1);
00397 publishProjection(ndi1, pti, ipt, true);
00398 }
00399 else if (f0.ipts[i0] < 0)
00400 {
00401 pti = f1.ipts[i1];
00402 f0.ipts[i0] = pti;
00403
00404
00405 Vector3d ipt = getProjection(f0, i0);
00406 publishProjection(ndi0, pti, ipt, true);
00407 }
00408 }
00409 }
00410
00411 };
00412
00413 int main(int argc, char** argv)
00414 {
00415 ros::init(argc, argv, "stereo_vo");
00416 if (argc < 4) {
00417 printf("Usage: %s <vocab tree file> <vocab weights file> <calonder trees file>\n", argv[0]);
00418 return 1;
00419 }
00420
00421 VONode vonode(argv[1], argv[2], argv[3]);
00422 ros::spin();
00423 }
00424