feature_tracker.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2011, Georgia Institute of Technology
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the Georgia Institute of Technology nor the names of
00018  *     its contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 #include <cpl_visual_features/motion/feature_tracker.h>
00036 #include <cpl_visual_features/motion/flow_types.h>
00037 #include <opencv2/imgproc/imgproc.hpp>
00038 #include <opencv2/video/video.hpp>
00039 #include <ros/ros.h>
00040 
00041 #define DISPLAY_TRACKER_OUTPUT 1
00042 #ifdef DISPLAY_TRACKER_OUTPUT
00043 #include <opencv2/highgui/highgui.hpp>
00044 #endif // DISPLAY_TRACKER_OUTPUT
00045 namespace cpl_visual_features
00046 {
00047 
00048 FeatureTracker::FeatureTracker(std::string name, double hessian_thresh,
00049                                int num_octaves, int num_layers, bool extended,
00050                                bool upright, bool use_fast) :
00051     surf_(hessian_thresh, num_octaves, num_layers, extended, upright),
00052     initialized_(false), ratio_threshold_(0.5), window_name_(name),
00053     min_flow_thresh_(0), max_corners_(500), klt_corner_thresh_(0.3),
00054     klt_corner_min_dist_(2), use_fast_(false)
00055 {
00056   prev_keypoints_.clear();
00057   cur_keypoints_.clear();
00058   prev_descriptors_.clear();
00059   cur_descriptors_.clear();
00060 }
00061 
00062 //
00063 // Main tracking logic functions
00064 //
00065 
00066 void FeatureTracker::initTracks(cv::Mat& frame)
00067 {
00068   updateCurrentDescriptors(frame, cv::Mat());
00069   prev_keypoints_ = cur_keypoints_;
00070   prev_descriptors_ = cur_descriptors_;
00071   initialized_ = true;
00072 }
00073 
00074 AffineFlowMeasures FeatureTracker::updateTracksLK(cv::Mat& cur_frame,
00075                                                   cv::Mat& prev_frame)
00076 {
00077   AffineFlowMeasures sparse_flow;
00078   std::vector<cv::Point2f> prev_points;
00079   std::vector<cv::Point2f> new_points;
00080   ROS_INFO_STREAM("max_corners: " << max_corners_);
00081   ROS_INFO_STREAM("quality_level: " << klt_corner_thresh_);
00082   ROS_INFO_STREAM("min_distance: " << klt_corner_min_dist_);
00083   cv::goodFeaturesToTrack(prev_frame, prev_points, max_corners_,
00084                           klt_corner_thresh_, klt_corner_min_dist_);
00085   ROS_INFO_STREAM("Found " << prev_points.size() << " corners.");
00086   std::vector<uchar> status;
00087   std::vector<float> err;
00088   cv::calcOpticalFlowPyrLK(prev_frame, cur_frame, prev_points, new_points,
00089                            status, err);
00090   int moving_points = 0;
00091   for (unsigned int i = 0; i < prev_points.size(); i++)
00092   {
00093     if (! status[i]) continue;
00094     int dx = prev_points[i].x - new_points[i].x;
00095     int dy = prev_points[i].y - new_points[i].y;
00096     sparse_flow.push_back(AffineFlowMeasure(new_points[i].x, new_points[i].y,
00097                                             dx, dy));
00098     if (abs(sparse_flow[i].u) + abs(sparse_flow[i].v) > min_flow_thresh_)
00099       moving_points++;
00100   }
00101   ROS_INFO_STREAM(window_name_ << ": num moving points: " << moving_points);
00102 
00103 #ifdef DISPLAY_TRACKER_OUTPUT
00104   cv::Mat display_cur_frame(cur_frame.rows, cur_frame.cols, CV_8UC3);;
00105   cv::cvtColor(cur_frame, display_cur_frame, CV_GRAY2BGR);
00106   for (unsigned int i = 0; i < sparse_flow.size(); i++)
00107   {
00108     if (abs(sparse_flow[i].u) + abs(sparse_flow[i].v) > min_flow_thresh_)
00109     {
00110       ROS_DEBUG_STREAM("Point is moving (" << sparse_flow[i].u << ", "
00111                        << sparse_flow[i].v << ")");
00112       cv::line(display_cur_frame,
00113                cv::Point(sparse_flow[i].x, sparse_flow[i].y),
00114                cv::Point(sparse_flow[i].x + sparse_flow[i].u,
00115                          sparse_flow[i].y + sparse_flow[i].v),
00116                cv::Scalar(0,0,255), 1);
00117     }
00118   }
00119 
00120   cv::imshow(window_name_, display_cur_frame);
00121 #endif // DISPLAY_TRACKER_OUTPUT
00122   return sparse_flow;
00123 }
00124 
00125 AffineFlowMeasures FeatureTracker::updateTracks(const cv::Mat& frame)
00126 {
00127   return updateTracks(frame, cv::Mat());
00128 }
00129 
00130 AffineFlowMeasures FeatureTracker::updateTracks(const cv::Mat& frame,
00131                                                 const cv::Mat& mask)
00132 {
00133   cur_keypoints_.clear();
00134   cur_descriptors_.clear();
00135   updateCurrentDescriptors(frame, mask);
00136 
00137   std::vector<int> matches_cur;
00138   std::vector<int> matches_prev;
00139   AffineFlowMeasures sparse_flow;
00140   matches_cur.clear();
00141   matches_prev.clear();
00142 
00143   // Find nearest neighbors with previous descriptors
00144   findMatches(cur_descriptors_, prev_descriptors_, matches_cur, matches_prev,
00145               cur_scores_);
00146   ROS_DEBUG_STREAM(window_name_ << ": num feature matches: "
00147                    << matches_cur.size());
00148   int moving_points = 0;
00149   for (unsigned int i = 0; i < matches_cur.size(); i++)
00150   {
00151     int dx = prev_keypoints_[matches_prev[i]].pt.x -
00152         cur_keypoints_[matches_cur[i]].pt.x;
00153     int dy = prev_keypoints_[matches_prev[i]].pt.y -
00154         cur_keypoints_[matches_cur[i]].pt.y;
00155     sparse_flow.push_back(AffineFlowMeasure(
00156         cur_keypoints_[matches_cur[i]].pt.x,
00157         cur_keypoints_[matches_cur[i]].pt.y, dx, dy));
00158     if (abs(sparse_flow[i].u) + abs(sparse_flow[i].v) >= min_flow_thresh_)
00159       moving_points++;
00160   }
00161   ROS_DEBUG_STREAM(window_name_ << ": num moving points: " << moving_points);
00162 
00163 #ifdef DISPLAY_TRACKER_OUTPUT
00164   cv::Mat display_frame(frame.rows, frame.cols, CV_8UC3);;
00165   cv::cvtColor(frame, display_frame, CV_GRAY2BGR);
00166   for (unsigned int i = 0; i < matches_cur.size(); i++)
00167   {
00168     if (abs(sparse_flow[i].u) + abs(sparse_flow[i].v) >= min_flow_thresh_)
00169     {
00170       ROS_DEBUG_STREAM("Point is moving (" << sparse_flow[i].u << ", "
00171                        << sparse_flow[i].v << ")");
00172       cv::line(display_frame,
00173                prev_keypoints_[matches_prev[i]].pt,
00174                cur_keypoints_[matches_cur[i]].pt,
00175                cv::Scalar(0,0,255), 1);
00176     }
00177   }
00178 
00179   cv::imshow(window_name_, display_frame);
00180 #endif // DISPLAY_TRACKER_OUTPUT
00181 
00182   prev_keypoints_ = cur_keypoints_;
00183   prev_descriptors_ = cur_descriptors_;
00184   cur_flow_ = sparse_flow;
00185   return sparse_flow;
00186 }
00187 
00188 //
00189 // Feature Matching Functions
00190 //
00191 
00192 /*
00193  * SSD
00194  *
00195  * @short Computes the squareroot of squared differences
00196  * @param a First descriptor
00197  * @param b second descriptor
00198  * @return value of squareroot of squared differences
00199  */
00200 double FeatureTracker::SSD(Descriptor& a, Descriptor& b)
00201 {
00202   double diff = 0;
00203 
00204   for (unsigned int i = 0; i < a.size(); ++i) {
00205     float delta = a[i] - b[i];
00206     diff += delta*delta;
00207   }
00208 
00209   return diff;
00210 }
00211 
00212 /*
00213  * ratioTest
00214  *
00215  * @short Computes the  ratio test described in Lowe 2004
00216  * @param a Descriptor from the first image to compare
00217  * @param bList List of descriptors from the second image
00218  * @param threshold Threshold value for ratioTest comparison
00219  *
00220  * @return index of the best match, -1 if no match ratio is less than threshold
00221  */
00222 std::pair<int, float> FeatureTracker::ratioTest(Descriptor& a,
00223                                                 Descriptors& bList,
00224                                                 double threshold)
00225 {
00226   double best_score = 1000000;
00227   double second_best = 1000000;
00228   int best_index = -1;
00229 
00230   for (unsigned int b = 0; b < bList.size(); ++b) {
00231     double score = 0;
00232     score = SSD(a, bList[b]);
00233 
00234     if (score < best_score) {
00235       second_best = best_score;
00236       best_score = score;
00237       best_index = b;
00238     } else if (score < second_best) {
00239       second_best = score;
00240     }
00241     if ( best_score / second_best > threshold) {
00242       best_index = -1;
00243     }
00244 
00245   }
00246 
00247   return std::pair<int, float>(best_index, best_score);
00248 }
00249 
00258 void FeatureTracker::findMatches(Descriptors& descriptors1,
00259                                  Descriptors& descriptors2,
00260                                  std::vector<int>& matches1,
00261                                  std::vector<int>& matches2,
00262                                  std::vector<float>& scores)
00263 {
00264   // Determine matches using the Ratio Test method from Lowe 2004
00265   for (unsigned int a = 0; a < descriptors1.size(); ++a) {
00266     const std::pair<int,float> best_match = ratioTest(descriptors1[a],
00267                                                       descriptors2,
00268                                                       ratio_threshold_);
00269     const int best_index = best_match.first;
00270     const int best_score = best_match.second;
00271     if (best_index != -1) {
00272       matches1.push_back(a);
00273       matches2.push_back(best_index);
00274       scores.push_back(best_score);
00275     }
00276   }
00277 
00278   // Check that the matches are unique going the other direction
00279   for (unsigned int x = 0; x < matches2.size();) {
00280     const std::pair<int,float> best_match = ratioTest(descriptors2[matches2[x]],
00281                                                       descriptors1,
00282                                                       ratio_threshold_);
00283     const int best_index = best_match.first;
00284     const int best_score = best_match.second;
00285     if (best_index != matches1[x]) {
00286       matches1.erase(matches1.begin()+x);
00287       matches2.erase(matches2.begin()+x);
00288       scores.erase(scores.begin()+x);
00289     } else {
00290       x++;
00291     }
00292   }
00293 }
00294 
00295 //
00296 // Helper Functions
00297 //
00298 
00299 void FeatureTracker::updateCurrentDescriptors(const cv::Mat& frame,
00300                                               const cv::Mat& mask)
00301 {
00302   std::vector<float> raw_descriptors;
00303   try
00304   {
00305     if (use_fast_)
00306     {
00307       cv::Mat masked_frame(frame.size(), frame.type(), cv::Scalar(0));
00308       frame.copyTo(masked_frame, mask);
00309       cv::FAST(masked_frame, cur_keypoints_, 9, true);
00310       // TODO: Remove keypoints outside the mask
00311       surf_(frame, mask, cur_keypoints_, raw_descriptors, true);
00312     }
00313     else
00314     {
00315       surf_(frame, mask, cur_keypoints_, raw_descriptors, false);
00316     }
00317     for (unsigned int i = 0; i < raw_descriptors.size(); i += 128)
00318     {
00319       Descriptor d(raw_descriptors.begin() + i,
00320                    raw_descriptors.begin() + i + 128);
00321       cur_descriptors_.push_back(d);
00322     }
00323   }
00324   catch(cv::Exception e)
00325   {
00326     ROS_ERROR_STREAM(e.err);
00327     // std::cerr << e.err << std::endl;
00328   }
00329 }
00330 }


cpl_visual_features
Author(s): Tucker Hermans
autogenerated on Wed Nov 27 2013 11:52:36