keyframe_graph_detector.cpp
Go to the documentation of this file.
00001 
00024 #include "ccny_rgbd/mapping/keyframe_graph_detector.h"
00025 
00026 namespace ccny_rgbd {
00027 
00028 KeyframeGraphDetector::KeyframeGraphDetector(
00029   const ros::NodeHandle& nh, 
00030   const ros::NodeHandle& nh_private):
00031   nh_(nh), 
00032   nh_private_(nh_private)
00033 {
00034   srand(time(NULL));
00035 
00036   // params
00037   if (!nh_private_.getParam ("graph/max_ransac_iterations", max_ransac_iterations_))
00038     max_ransac_iterations_ = 2000;
00039   if (!nh_private_.getParam ("graph/save_ransac_results", save_ransac_results_))
00040     save_ransac_results_ = false;
00041   if (!nh_private_.getParam ("graph/ransac_results_path", ransac_results_path_))
00042     ransac_results_path_ = std::getenv("HOME");
00043   if (!nh_private_.getParam ("graph/n_ransac_candidates", n_ransac_candidates_))
00044     n_ransac_candidates_ = 15;
00045   if (!nh_private_.getParam ("graph/k_nearest_neighbors", k_nearest_neighbors_))
00046     k_nearest_neighbors_ = 15;
00047   if (!nh_private_.getParam ("graph/min_ransac_inliers", min_ransac_inliers_))
00048     min_ransac_inliers_ = 30;
00049   if (!nh_private_.getParam ("graph/max_corresp_dist_desc", max_corresp_dist_desc_))
00050     max_corresp_dist_desc_ = 1.0;
00051   if (!nh_private_.getParam ("graph/max_corresp_dist_eucl", max_corresp_dist_eucl_))
00052     max_corresp_dist_eucl_ = 0.03;
00053   if (!nh_private_.getParam ("graph/n_keypoints", n_keypoints_))
00054     n_keypoints_ = 200;
00055     
00056   // derived params
00057   max_corresp_dist_eucl_sq_ = max_corresp_dist_eucl_ * max_corresp_dist_eucl_;
00058 }
00059 
00060 KeyframeGraphDetector::~KeyframeGraphDetector()
00061 {
00062 
00063 }
00064 
00065 void KeyframeGraphDetector::generateKeyframeAssociations(
00066   KeyframeVector& keyframes,
00067   KeyframeAssociationVector& associations)
00068 {
00069   prepareFeaturesForRANSAC(keyframes);
00070 
00071   // inserts consecutive associations from the visual odometry
00072   visualOdometryAssociations(keyframes, associations);
00073   
00074   treeAssociations(keyframes, associations);
00075   //manualBruteForceAssociations(keyframes, associations);
00076 }
00077 
00078 void KeyframeGraphDetector::prepareFeaturesForRANSAC(
00079   KeyframeVector& keyframes)
00080 {
00081   double init_surf_threshold = 400.0;
00082   double min_surf_threshold = 25;
00083 
00084   printf("preparing SURF features for RANSAC associations...\n");  
00085 
00086   cv::SurfDescriptorExtractor extractor;
00087  
00088   for (unsigned int kf_idx = 0; kf_idx < keyframes.size(); kf_idx++)
00089   { 
00090     RGBDKeyframe& keyframe = keyframes[kf_idx];
00091 
00092     double surf_threshold = init_surf_threshold;
00093 
00094     while (surf_threshold >= min_surf_threshold)
00095     {
00096       cv::SurfFeatureDetector detector(surf_threshold);
00097       keyframe.keypoints.clear();
00098       detector.detect(keyframe.rgb_img, keyframe.keypoints);
00099 
00100       printf("[KF %d of %d] %d SURF keypoints detected (threshold: %.1f)\n", 
00101         (int)kf_idx, (int)keyframes.size(), 
00102         (int)keyframe.keypoints.size(), surf_threshold); 
00103 
00104       if ((int)keyframe.keypoints.size() < n_keypoints_)
00105         surf_threshold /= 2.0;
00106       else break;
00107     }
00108 
00109     if (save_ransac_results_)
00110     {
00111       cv::Mat kp_img;
00112       cv::drawKeypoints(keyframe.rgb_img, keyframe.keypoints, kp_img);
00113       std::stringstream ss1;
00114       ss1 << "kp_" << kf_idx;
00115       cv::imwrite(ransac_results_path_ + "/" + ss1.str() + ".png", kp_img);
00116     }
00117 
00118     extractor.compute(keyframe.rgb_img, keyframe.keypoints, keyframe.descriptors);
00119     keyframe.computeDistributions();
00120   }
00121 }
00122 
00123 void KeyframeGraphDetector::visualOdometryAssociations(
00124   KeyframeVector& keyframes,
00125   KeyframeAssociationVector& associations)
00126 {
00127   for (unsigned int kf_idx_a = 0; kf_idx_a < keyframes.size()-1; ++kf_idx_a)
00128   {
00129     // determine second index, NO wrap-around
00130     unsigned int kf_idx_b = kf_idx_a + 1;
00131 
00132     // set up the two keyframe references
00133     RGBDKeyframe& keyframe_a = keyframes[kf_idx_a];
00134     RGBDKeyframe& keyframe_b = keyframes[kf_idx_b];
00135 
00136     // create an association object
00137     KeyframeAssociation association;
00138     
00139     association.type = KeyframeAssociation::VO;    
00140     association.kf_idx_a = kf_idx_a;
00141     association.kf_idx_b = kf_idx_b;
00142     association.a2b = keyframe_a.pose.inverse() * keyframe_b.pose;
00143 
00144     associations.push_back(association);
00145   }
00146 }
00147 
00148 void KeyframeGraphDetector::manualBruteForceAssociations(
00149   KeyframeVector& keyframes,
00150   KeyframeAssociationVector& associations)
00151 {
00152   // params
00153   double max_eucl_dist    = 0.05;
00154   double max_desc_dist    = 10.0;
00155   double min_inlier_ratio = 0.75;
00156   double min_inliers      = 20;
00157 
00158   double max_eucl_dist_sq = max_eucl_dist * max_eucl_dist;
00159 
00160   // generate a list of all keyframe indices, for which the keyframe
00161   // is manually added
00162   std::vector<unsigned int> manual_keyframe_indices;
00163   
00164   for (unsigned int kf_idx = 0; kf_idx < keyframes.size(); ++kf_idx)
00165   {
00166     const RGBDKeyframe& keyframe = keyframes[kf_idx];
00167     if(keyframe.manually_added) 
00168     {
00169       printf("Manual keyframe: %d\n", kf_idx);
00170       manual_keyframe_indices.push_back(kf_idx);
00171     }
00172   }
00173 
00174   for (unsigned int mn_idx_a = 0; mn_idx_a < manual_keyframe_indices.size(); ++mn_idx_a)
00175   for (unsigned int mn_idx_b = mn_idx_a+1; mn_idx_b < manual_keyframe_indices.size(); ++mn_idx_b)
00176   {
00177     // extract the indices of the manual keyframes
00178     unsigned int kf_idx_a = manual_keyframe_indices[mn_idx_a];
00179     unsigned int kf_idx_b = manual_keyframe_indices[mn_idx_b];
00180 
00181     // set up the two keyframe references
00182     RGBDKeyframe& keyframe_a = keyframes[kf_idx_a];
00183     RGBDKeyframe& keyframe_b = keyframes[kf_idx_b];
00184 
00185     // perform ransac matching, b onto a
00186     std::vector<cv::DMatch> all_matches, inlier_matches;
00187     Eigen::Matrix4f transformation;
00188 
00189     pairwiseMatchingRANSAC(keyframe_a, keyframe_b, 
00190       max_eucl_dist_sq, max_desc_dist, min_inlier_ratio,
00191       all_matches, inlier_matches, transformation);
00192 
00193     if (inlier_matches.size() >= min_inliers)
00194     {
00195       printf("[RANSAC %d -> %d] OK   (%d / %d)\n", 
00196         kf_idx_a, kf_idx_b,
00197         (int)inlier_matches.size(), (int)all_matches.size());
00198 
00199       // create an association object
00200       KeyframeAssociation association;
00201       association.type = KeyframeAssociation::RANSAC;
00202       association.kf_idx_a = kf_idx_a;
00203       association.kf_idx_b = kf_idx_b;
00204       association.matches  = inlier_matches;
00205       association.a2b = tfFromEigen(transformation);
00206       associations.push_back(association);
00207     }
00208     else
00209     {
00210       printf("[RANSAC %d -> %d] FAIL (%d / %d)\n", 
00211         kf_idx_a, kf_idx_b,
00212         (int)inlier_matches.size(), (int)all_matches.size());
00213     }
00214   }
00215 }
00216 
00217 void KeyframeGraphDetector::pairwiseMatchingRANSAC(
00218   RGBDFrame& frame_a, RGBDFrame& frame_b,
00219   double max_eucl_dist_sq, 
00220   double max_desc_dist,
00221   double sufficient_inlier_ratio,
00222   std::vector<cv::DMatch>& all_matches,
00223   std::vector<cv::DMatch>& best_inlier_matches,
00224   Eigen::Matrix4f& best_transformation)
00225 {
00226   // constants
00227   int min_sample_size = 3;
00228 
00229   cv::FlannBasedMatcher matcher;          // for SURF
00230   TransformationEstimationSVD svd;
00231 
00232   // **** build candidate matches ***********************************
00233 
00234   // assumes detectors and distributions are computed
00235   // establish all matches from b to a
00236   matcher.match(frame_b.descriptors, frame_a.descriptors, all_matches);
00237 
00238   // remove bad matches - too far away in descriptor space,
00239   //                    - nan, too far, or cov. too big
00240   std::vector<cv::DMatch> candidate_matches;
00241   for (unsigned int m_idx = 0; m_idx < all_matches.size(); ++m_idx)
00242   {
00243     const cv::DMatch& match = all_matches[m_idx];
00244     int idx_b = match.queryIdx;
00245     int idx_a = match.trainIdx; 
00246 
00247     if (match.distance < max_desc_dist && 
00248         frame_a.kp_valid[idx_a] && 
00249         frame_b.kp_valid[idx_b])
00250     {
00251       candidate_matches.push_back(all_matches[m_idx]);
00252     }
00253   }
00254 
00255   int size = candidate_matches.size();
00256 
00257   // **** build 3D features for SVD ********************************
00258 
00259   PointCloudFeature features_a, features_b;
00260 
00261   features_a.resize(size);
00262   features_b.resize(size);
00263 
00264   for (int m_idx = 0; m_idx < size; ++m_idx)
00265   {
00266     const cv::DMatch& match = candidate_matches[m_idx];
00267     int idx_b = match.queryIdx;
00268     int idx_a = match.trainIdx; 
00269 
00270     PointFeature& p_a = features_a[m_idx];
00271     p_a.x = frame_a.kp_means[idx_a](0,0);
00272     p_a.y = frame_a.kp_means[idx_a](1,0);
00273     p_a.z = frame_a.kp_means[idx_a](2,0);
00274 
00275     PointFeature& p_b = features_b[m_idx];
00276     p_b.x = frame_b.kp_means[idx_b](0,0);
00277     p_b.y = frame_b.kp_means[idx_b](1,0);
00278     p_b.z = frame_b.kp_means[idx_b](2,0);
00279   }
00280 
00281   // **** main RANSAC loop ****************************************
00282 
00283   int best_n_inliers = 0;
00284   Eigen::Matrix4f transformation; // transformation used inside loop
00285   
00286   for (int iteration = 0; iteration < max_ransac_iterations_; ++iteration)
00287   {
00288     // generate random indices
00289     IntVector sample_idx;
00290     getRandomIndices(min_sample_size, size, sample_idx);
00291 
00292     // build initial inliers from random indices
00293     IntVector inlier_idx;
00294     std::vector<cv::DMatch> inlier_matches;
00295 
00296     for (unsigned int s_idx = 0; s_idx < sample_idx.size(); ++s_idx)
00297     {
00298       int m_idx = sample_idx[s_idx];
00299       inlier_idx.push_back(m_idx);
00300       inlier_matches.push_back(candidate_matches[m_idx]);
00301     } 
00302 
00303     // estimate transformation from minimum set of random samples
00304     svd.estimateRigidTransformation(
00305       features_b, inlier_idx,
00306       features_a, inlier_idx,
00307       transformation);
00308 
00309     // evaluate transformation fitness by checking distance to all points
00310     PointCloudFeature features_b_tf;
00311     pcl::transformPointCloud(features_b, features_b_tf, transformation);
00312 
00313     for (int m_idx = 0; m_idx < size; ++m_idx)
00314     {
00315       const PointFeature& p_a = features_a[m_idx];
00316       const PointFeature& p_b = features_b_tf[m_idx];
00317 
00318       float dist_sq = distEuclideanSq(p_a, p_b);
00319       
00320       if (dist_sq < max_eucl_dist_sq)
00321       {
00322         inlier_idx.push_back(m_idx);
00323         inlier_matches.push_back(candidate_matches[m_idx]);
00324 
00325         // reestimate transformation from all inliers
00326         svd.estimateRigidTransformation(
00327           features_b, inlier_idx,
00328           features_a, inlier_idx,
00329           transformation);
00330         pcl::transformPointCloud(features_b, features_b_tf, transformation);
00331       }
00332     }
00333 
00334     // check if inliers are better than the best model so far
00335     int n_inliers = inlier_idx.size();
00336 
00337     if (n_inliers > best_n_inliers)
00338     {
00339       svd.estimateRigidTransformation(
00340         features_b, inlier_idx,
00341         features_a, inlier_idx,
00342         transformation);
00343 
00344       best_n_inliers = n_inliers;
00345       best_transformation = transformation;
00346       best_inlier_matches = inlier_matches;
00347     }
00348 
00349     // check if we reached ratio termination criteria
00350     double inlier_ratio = (double) n_inliers / (double) size;
00351 
00352     if (inlier_ratio > sufficient_inlier_ratio)
00353       break;
00354   }
00355 }
00356 
00357 void KeyframeGraphDetector::trainMatcher(
00358   const KeyframeVector& keyframes,
00359   cv::FlannBasedMatcher& matcher)
00360 {
00361   printf("Building aggregate feature vector...\n"); 
00362   std::vector<cv::Mat> descriptors_vector;
00363 
00364   for (unsigned int kf_idx = 0; kf_idx < keyframes.size(); ++kf_idx)
00365   {
00366     const RGBDKeyframe& keyframe = keyframes[kf_idx];
00367     descriptors_vector.push_back(keyframe.descriptors);
00368   }
00369   matcher.add(descriptors_vector);
00370 
00371   printf("Training feature matcher...\n");
00372   matcher.train();
00373 }
00374 
00375 void KeyframeGraphDetector::treeAssociations(
00376   KeyframeVector& keyframes,
00377   KeyframeAssociationVector& associations)
00378 {
00379   // extra params
00380   double sufficient_ransac_inlier_ratio = 1.0;
00381   
00382   // train matcher from all teh features
00383   cv::FlannBasedMatcher matcher;
00384   trainMatcher(keyframes, matcher);
00385 
00386   // lookup per frame
00387   printf("Keyframe lookups...\n");
00388 
00389   for (unsigned int kf_idx = 0; kf_idx < keyframes.size(); ++kf_idx)
00390   {
00391     printf("[KF %d of %d]:\n", (int)kf_idx, (int)keyframes.size());
00392     RGBDFrame& keyframe = keyframes[kf_idx];
00393 
00394     // find k nearest matches for each feature in the keyframe
00395     std::vector<std::vector<cv::DMatch> > matches_vector;
00396     matcher.knnMatch(keyframe.descriptors, matches_vector, k_nearest_neighbors_);
00397 
00398     // create empty bins vector of Pairs <count, image_index>
00399     std::vector<std::pair<int, int> > bins;
00400     bins.resize(keyframes.size());
00401     for (unsigned int b = 0; b < bins.size(); ++b) 
00402       bins[b] = std::pair<int, int>(0, b);
00403 
00404     // fill out bins with match indices
00405     for (unsigned int j = 0; j < matches_vector.size(); ++j)
00406     {
00407       std::vector<cv::DMatch>& matches = matches_vector[j];
00408       for (unsigned int k = 0; k < matches.size(); ++k)
00409       {
00410         bins[matches[k].imgIdx].first++;
00411       }
00412     }
00413   
00414     // sort - highest counts first
00415     std::sort(bins.begin(), bins.end(), std::greater<std::pair<int, int> >());
00416 
00417     // output results
00418     printf(" - best matches: ");
00419     for (int b = 0; b < n_ransac_candidates_; ++b)
00420       printf("[%d(%d)] ", bins[b].second, bins[b].first);
00421     printf("\n");
00422 
00423     // **** find top X candidates
00424     
00425     printf(" - candidate matches: ");
00426     IntVector ransac_candidates;
00427     int n_ransac_candidates_found = 0;
00428     for (unsigned int b = 0; b < bins.size(); ++b)
00429     {
00430       unsigned int index_a = kf_idx;
00431       unsigned int index_b = bins[b].second;
00432       int corresp_count = bins[b].first;
00433 
00434       // test for order consistence
00435       // and for minimum number of keypoints
00436       if (index_b > index_a && 
00437           corresp_count >= min_ransac_inliers_)
00438       {
00439         ransac_candidates.push_back(index_b);
00440         ++n_ransac_candidates_found;
00441         printf("[%d(%d)] ", index_b, corresp_count);
00442       }
00443 
00444       if (n_ransac_candidates_found >= n_ransac_candidates_) break;
00445     }
00446     printf("\n");
00447 
00448     // **** test top X candidates using RANSAC
00449 
00450     for (unsigned int rc = 0; rc < ransac_candidates.size(); ++rc)
00451     {
00452       unsigned int kf_idx_a = kf_idx;
00453       unsigned int kf_idx_b = ransac_candidates[rc];
00454 
00455       RGBDKeyframe& keyframe_a = keyframes[kf_idx_a];
00456       RGBDKeyframe& keyframe_b = keyframes[kf_idx_b];
00457 
00458       std::vector<cv::DMatch> all_matches;
00459       std::vector<cv::DMatch> inlier_matches;
00460 
00461       // perform ransac matching, b onto a
00462       Eigen::Matrix4f transformation;
00463 
00464       pairwiseMatchingRANSAC(keyframe_a, keyframe_b, 
00465         max_corresp_dist_eucl_sq_, max_corresp_dist_desc_, 
00466         sufficient_ransac_inlier_ratio,
00467         all_matches, inlier_matches, transformation);
00468 
00469       if ((int)inlier_matches.size() >= min_ransac_inliers_)
00470       {
00471         if (save_ransac_results_)
00472         {
00473           cv::Mat img_matches;
00474           cv::drawMatches(keyframe_b.rgb_img, keyframe_b.keypoints, 
00475                           keyframe_a.rgb_img, keyframe_a.keypoints, 
00476                           inlier_matches, img_matches);
00477 
00478           std::stringstream ss1;
00479           ss1 << kf_idx_a << "_to_" << kf_idx_b;
00480           cv::imwrite(ransac_results_path_ + "/" + ss1.str() + ".png", img_matches);
00481         }
00482 
00483         printf(" - RANSAC %d -> %d: PASS\n", kf_idx_a, kf_idx_b);
00484 
00485         // create an association object
00486         KeyframeAssociation association;
00487         association.type = KeyframeAssociation::RANSAC;
00488         association.kf_idx_a = kf_idx_a;
00489         association.kf_idx_b = kf_idx_b;
00490         association.matches  = inlier_matches;
00491         association.a2b = tfFromEigen(transformation);
00492         associations.push_back(association);      
00493       }
00494       else  
00495         printf(" - RANSAC %d -> %d: FAIL\n", kf_idx_a, kf_idx_b);
00496     }
00497   }
00498 }
00499 
00500 // produces k random numbers in the range [0, n).
00501 // Monte-Carlo based random sampling
00502 void KeyframeGraphDetector::getRandomIndices(
00503   int k, int n, IntVector& output)
00504 {
00505   while ((int)output.size() < k)
00506   {
00507     int random_number = rand() % n;
00508     bool duplicate = false;    
00509 
00510     for (unsigned int i = 0; i < output.size(); ++i)
00511     {
00512       if (output[i] == random_number)
00513       {
00514         duplicate = true;
00515         break;
00516       }
00517     }
00518 
00519     if (!duplicate)
00520       output.push_back(random_number);
00521   }
00522 }
00523 
00524 } // namespace ccny_rgbd
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends


ccny_rgbd
Author(s): Ivan Dryanovski
autogenerated on Fri Apr 12 2013 20:38:48