vo_node.cpp
Go to the documentation of this file.
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 // Messages
00017 #include <sba/Frame.h>
00018 #include <geometry_msgs/PointStamped.h>
00019 #include <opencv2/legacy/legacy.hpp>
00020 
00021 // parameters
00022 //  max distance, angle between keyframes
00023 //  min inliers between keyframes
00024 static const double MAX_KEYFRAME_DISTANCE = 0.2;           // meters
00025 static const double MAX_KEYFRAME_ANGLE    = 0.1;          // radians
00026 static const int    MIN_KEYFRAME_INLIERS  = 0;           // depends on number of points, no?
00027 
00028 using namespace vslam;
00029 
00030 class VONode
00031 {
00032 private:
00033   ros::NodeHandle nh_;
00034   image_transport::ImageTransport it_;
00035 
00036   // Subscriptions
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   // Publications for SBA Node
00043   ros::Publisher sba_frames_pub_;
00044   
00045   // Publications for visualization
00046   image_transport::CameraPublisher vo_tracks_pub_;
00047   cv::Mat vo_display_;
00048  
00049   // Processing state
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   // Reconfigure server (use the same one as Stereo VSLAM for now)
00068   typedef dynamic_reconfigure::Server<vslam_system::StereoVslamNodeConfig> ReconfigureServer;
00069   ReconfigureServer reconfigure_server_;
00070   
00071   bool init;
00072   
00073 public:
00074   // Code copied from stereo_vslam_node.cpp
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     // Use calonder descriptor
00084     typedef cv::CalonderDescriptorExtractor<float> Calonder;
00085     frame_processor_.setFrameDescriptor(new Calonder(calonder_trees_file));
00086     
00087     // Synchronize inputs
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     // Advertise topics for connection to SBA node
00097     // sba_nodes_pub_ = nh_.advertise<sba::CameraNode>("sba_nodes", 5000);
00098     // sba_points_pub_ = nh_.advertise<geometry_msgs::PointStamped>("sba_points", 5000);
00099     sba_frames_pub_ = nh_.advertise<sba::Frame>("/sba/frames", 5000);
00100     
00101     // Dynamic reconfigure for parameters
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     // Convert ROS messages for use with OpenCV
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     /*vslam_system_.setPRRansacIt(config.pr_ransac_iterations);
00153     vslam_system_.setPRPolish(config.pr_polish);
00154     vslam_system_.setVORansacIt(config.vo_ransac_iterations);
00155     vslam_system_.setVOPolish(config.vo_polish);*/
00156   }
00157   
00158   bool addFrame(const frame_common::CamParams& camera_parameters,
00159                            const cv::Mat& left, const cv::Mat& right)
00160   {
00161     // Set up next frame and compute descriptors
00162     frame_common::Frame next_frame;
00163     next_frame.setCamParams(camera_parameters); // this sets the projection and reprojection matrices
00164     frame_processor_.setStereoFrame(next_frame, left, right);
00165     next_frame.frameId = numframes++; // index
00166     next_frame.img = cv::Mat();   // remove the images
00167     next_frame.imgRight = cv::Mat();
00168 
00169     // Add frame to visual odometer
00170     bool is_keyframe = vo_.addFrame(next_frame);
00171 
00172     // grow full SBA
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       // Clear the message vectors.
00187       proj_msgs.clear();
00188       node_msgs.clear();
00189       point_msgs.clear();
00190       
00191       /*sensor_msgs::ImagePtr msg = sensor_msgs::CvBridge::cvToImgMsg(&ipl);
00192       msg->header = l_cam_info->header;
00193       vo_tracks_pub_.publish(msg, l_cam_info);*/
00194       
00195       // latest frame
00196       frame_common::Frame &f1 = frames_.back();
00197 
00198       // set up point indices to NULL
00199       f1.ipts.assign(f1.kpts.size(), -1);
00200 
00201       // get RW position given relative position
00202       Quaterniond fq;
00203       Vector4d trans;
00204       Matrix<double,3,4> f2w;
00205       
00206       if (init)                   // use zero origin
00207         {
00208           trans = Vector4d(0,0,0,1);
00209           fq.coeffs() = Vector4d(0,0,0,1);
00210           fq.normalize();
00211         }
00212       else
00213         {
00214           // Just use the pose of the latest added frame to VO's internal sba.
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           // trans.head(3) = f2w*trans;
00222         }
00223 
00224       // Publish node for the frame, setting it to fixed if we're initializing,
00225       // floating otherwise.
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     //sba_nodes_pub_.publish(msg);
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     //sba_points_pub_.publish(msg);
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     // add connections between frames, based on keypoint matches
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     // set up array to kill duplicate matches
00322     vector<bool> matched0(f0.ipts.size(),0);
00323     vector<bool> matched1(f1.ipts.size(),0);
00324 
00325     // add points and projections
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)    // new point
00339           {
00340             Vector4d pt;
00341             pt.head(3) = f2w*f0.pts[i0]; // transform to RW coords
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);  // external point index
00349 
00350             Vector3d ipt = getProjection(f0, i0);
00351             publishProjection(ndi0, pti, ipt, true);
00352 
00353             // projected point, ul,vl,ur
00354             ipt = getProjection(f1, i1);
00355             publishProjection(ndi1, pti, ipt, true);
00356           }
00357           
00358         // Commented out right now since we have no good way to merge tracks
00359         else if (f0.ipts[i0] >= 0 && f1.ipts[i1] >= 0) // merge two tracks
00360           {
00361             if (f0.ipts[i0] != f1.ipts[i1]) // different tracks
00362               {
00363                 // if (sba_merge_tracks_client_.isValid()) // Do we need to do this instead?
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                 /*sba::MergeTracks::Request req;
00368                 sba::MergeTracks::Response res;
00369                 req.trackid0 = f0.ipts[i0];
00370                 req.trackid1 = f1.ipts[i1];
00371                
00372                 if (sba_merge_tracks_client_.call(req, res))
00373                 {
00374                   int tri = res.trackid;
00375                   if (tri >= 0)   // successful merge
00376                     {
00377                       // update the ipts in frames that connect to this track
00378                       for (int i = 0; i < numframes; i++)
00379                         {
00380                           if (tri == f0.ipts[i0])
00381                             sba::substPointRef(frames_[numframes].ipts, tri, f1.ipts[i1]);
00382                           else
00383                             sba::substPointRef(frames_[numframes].ipts, tri, f0.ipts[i0]);
00384                         }
00385                     }
00386                   } */ 
00387               }
00388           }
00389 
00390         else if (f1.ipts[i1] < 0)                 // add to previous point track
00391           {
00392             pti = f0.ipts[i0];
00393             f1.ipts[i1] = pti;
00394           
00395             // projected point, ul,vl,ur
00396             Vector3d ipt = getProjection(f1, i1);
00397             publishProjection(ndi1, pti, ipt, true);
00398           }
00399         else if (f0.ipts[i0] < 0)                 // add to previous point track
00400           {
00401             pti = f1.ipts[i1];
00402             f0.ipts[i0] = pti;
00403           
00404             // projected point, ul,vl,ur
00405             Vector3d ipt = getProjection(f0, i0);
00406             publishProjection(ndi0, pti, ipt, true);
00407           }
00408       }
00409   }
00410   
00411 }; // class VO Node
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 


vslam_system
Author(s): Kurt Konolige, Patrick Mihelich, Helen Oleynikova
autogenerated on Thu Jan 2 2014 12:12:32