00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <assert.h>
00026 #include "tictoc.hpp"
00027 #include "visual_odometry.hpp"
00028 #include "initial_homography_estimation.hpp"
00029 #include "internal_utils.hpp"
00030
00031 #include <iostream>
00032 #include <iomanip>
00033
00034
00035 #define dbg(...)
00036 #define dump(var) (std::cerr<<" "#var<<" =[\n"<< std::setprecision (12)<<var<<"]"<<std::endl)
00037
00038 #ifndef MIN
00039 #define MIN(a,b) ((a)<(b) ? (a) : (b))
00040 #endif
00041
00042 namespace fovis
00043 {
00044
00045 static inline int clamp(int val, int minval, int maxval)
00046 {
00047 if(val > maxval) return maxval;
00048 if(val < minval) return minval;
00049 return val;
00050 }
00051
00052 static void validateOptions(const VisualOdometryOptions& options,
00053 const VisualOdometryOptions& defaults);
00054
00055
00056
00057 VisualOdometry::VisualOdometry(const Rectification* rectification,
00058 const VisualOdometryOptions& options) :
00059 _options(options)
00060 {
00061 _ref_frame = NULL;
00062 _prev_frame = NULL;
00063 _cur_frame = NULL;
00064
00065 _change_reference_frames = false;
00066
00067 _ref_frame_change_threshold = 150;
00068
00069 _frame_count = 0;
00070
00071
00072 const VisualOdometryOptions& defaults = getDefaultOptions();
00073 validateOptions(options, defaults);
00074
00075
00076 _feature_window_size = optionsGetIntOrFromDefault(_options, "feature-window-size", defaults);
00077 _num_pyramid_levels = optionsGetIntOrFromDefault(_options, "max-pyramid-level", defaults);
00078 _target_pixels_per_feature = optionsGetIntOrFromDefault(_options, "target-pixels-per-feature", defaults);
00079 _ref_frame_change_threshold = optionsGetIntOrFromDefault(_options, "ref-frame-change-threshold", defaults);
00080 _use_homography_initialization = optionsGetBoolOrFromDefault(_options, "use-homography-initialization", defaults);
00081 _fast_threshold = optionsGetIntOrFromDefault(_options, "fast-threshold", defaults);
00082 _use_adaptive_threshold = optionsGetBoolOrFromDefault(_options, "use-adaptive-threshold", defaults);
00083 _fast_threshold_adaptive_gain = optionsGetDoubleOrFromDefault(_options, "fast-threshold-adaptive-gain", defaults);
00084
00085 _fast_threshold_min = 5;
00086 _fast_threshold_max = 70;
00087
00088 _p = new VisualOdometryPriv();
00089
00090 _p->motion_estimate.setIdentity();
00091 _p->motion_estimate_covariance.setIdentity(6, 6);
00092 _p->pose.setIdentity();
00093
00094 _p->ref_to_prev_frame.setIdentity();
00095
00096 _rectification = rectification;
00097
00098 _ref_frame = new OdometryFrame(_rectification, options);
00099
00100 _prev_frame = new OdometryFrame(_rectification, options);
00101
00102 _cur_frame = new OdometryFrame(_rectification, options);
00103
00104 _estimator = new MotionEstimator(_rectification, _options);
00105 }
00106
00107 VisualOdometry::~VisualOdometry()
00108 {
00109 delete _estimator;
00110 delete _ref_frame;
00111 delete _prev_frame;
00112 delete _cur_frame;
00113 delete _p;
00114 _ref_frame = NULL;
00115 _prev_frame = NULL;
00116 _cur_frame = NULL;
00117 tictoc_print_stats(TICTOC_AVG);
00118 }
00119
00120
00121
00122
00123 Eigen::Quaterniond
00124 VisualOdometry::estimateInitialRotation(const OdometryFrame* prev, const OdometryFrame* cur,
00125 const Eigen::Isometry3d &init_motion_estimate)
00126 {
00127 _initial_rotation_pyramid_level = 4;
00128 int num_pyr_levels = prev->getNumLevels();
00129 int pyrLevel = MIN(num_pyr_levels-1,_initial_rotation_pyramid_level);
00130 const PyramidLevel * ref_level = prev->getLevel(pyrLevel);
00131 const PyramidLevel * target_level = cur->getLevel(pyrLevel);
00132
00133 InitialHomographyEstimator rotation_estimator;
00134 rotation_estimator.setTemplateImage(ref_level->getGrayscaleImage(),
00135 ref_level->getWidth(), ref_level->getHeight(),
00136 ref_level->getGrayscaleImageStride(),
00137 _initial_rotation_pyramid_level - pyrLevel);
00138
00139 rotation_estimator.setTestImage(target_level->getGrayscaleImage(),
00140 target_level->getWidth(), target_level->getHeight(),
00141 target_level->getGrayscaleImageStride(),
00142 _initial_rotation_pyramid_level - pyrLevel);
00143
00144 Eigen::Matrix3f H = Eigen::Matrix3f::Identity();
00145 double finalRMS = 0;
00146 H = rotation_estimator.track(H,8, &finalRMS);
00147 double scale_factor = 1 << _initial_rotation_pyramid_level;
00148 Eigen::Matrix3f S = Eigen::Matrix3f::Identity() * scale_factor;
00149 S(2, 2) = 1;
00150
00151 H = S * H * S.inverse();
00152 _p->initial_homography_est = H.cast<double>();
00153
00154
00155
00156 Eigen::Vector3d rpy = Eigen::Vector3d::Zero();
00157 const CameraIntrinsicsParameters& input_camera = _rectification->getInputCameraParameters();
00158 rpy(0) = asin(H(1, 2) / input_camera.fx);
00159 rpy(1) = -asin(H(0, 2) / input_camera.fx);
00160 rpy(2) = -atan2(H(1, 0), H(0, 0));
00161
00162
00163
00164
00165 Eigen::Quaterniond q = _rpy_to_quat(rpy);
00166 return q;
00167 }
00168
00169 void
00170 VisualOdometry::processFrame(const uint8_t* gray, DepthSource* depth_source)
00171 {
00172 if(_change_reference_frames) {
00173
00174 std::swap(_ref_frame, _cur_frame);
00175 _p->ref_to_prev_frame.setIdentity();
00176 } else {
00177
00178 std::swap(_prev_frame, _cur_frame);
00179 }
00180
00181 bool changed_reference_frames = _change_reference_frames;
00182
00183
00184 _p->motion_estimate.setIdentity();
00185 _change_reference_frames = false;
00186
00187
00188 _cur_frame->prepareFrame(gray, _fast_threshold, depth_source);
00189
00190 const CameraIntrinsicsParameters& input_camera = _rectification->getInputCameraParameters();
00191 int width = input_camera.width;
00192 int height = input_camera.height;
00193
00194 if (_use_adaptive_threshold) {
00195
00196
00197 int target_num_features = width * height / _target_pixels_per_feature;
00198 int err = _cur_frame->getNumDetectedKeypoints() - target_num_features;
00199 int thresh_adjustment = (int)((err) * _fast_threshold_adaptive_gain);
00200 _fast_threshold += thresh_adjustment;
00201 _fast_threshold = clamp(_fast_threshold, _fast_threshold_min, _fast_threshold_max);
00202
00203 }
00204
00205 _frame_count++;
00206
00207
00208
00209 if(_frame_count < 2) {
00210 _change_reference_frames = true;
00211 return;
00212 }
00213
00214
00215 tictoc("estimateInitialRotation");
00216 Eigen::Quaterniond init_rotation_est;
00217 if (_use_homography_initialization) {
00218 if (changed_reference_frames) {
00219
00220 init_rotation_est = estimateInitialRotation(_ref_frame, _cur_frame);
00221 } else {
00222 init_rotation_est = estimateInitialRotation(_prev_frame, _cur_frame);
00223 }
00224 } else {
00225 init_rotation_est = Eigen::Quaterniond(1, 0, 0, 0);
00226 }
00227
00228 tictoc("estimateInitialRotation");
00229 _p->initial_motion_estimate = _p->ref_to_prev_frame.inverse();
00230 _p->initial_motion_estimate.rotate(init_rotation_est);
00231
00232 _p->initial_motion_cov.setIdentity();
00233
00234
00235 _estimator->estimateMotion(_ref_frame,
00236 _cur_frame,
00237 depth_source,
00238 _p->initial_motion_estimate,
00239 _p->initial_motion_cov);
00240
00241 if(_estimator->isMotionEstimateValid()) {
00242 Eigen::Isometry3d to_reference = _estimator->getMotionEstimate();
00243 _p->motion_estimate = _p->ref_to_prev_frame * to_reference;
00244 Eigen::MatrixXd to_reference_cov = _estimator->getMotionEstimateCov();
00245 _p->motion_estimate_covariance = to_reference_cov;
00246 _p->ref_to_prev_frame = to_reference.inverse();
00247 _p->pose = _p->pose * _p->motion_estimate;
00248 } else if(!changed_reference_frames) {
00249
00250
00251 dbg(" Failed against reference frame, trying previous frame...\n");
00252 _p->initial_motion_estimate.setIdentity();
00253 _p->initial_motion_estimate.rotate(init_rotation_est);
00254 _p->initial_motion_cov.setIdentity();
00255
00256 _estimator->estimateMotion(_prev_frame,
00257 _cur_frame,
00258 depth_source,
00259 _p->initial_motion_estimate,
00260 _p->initial_motion_cov);
00261
00262 if(_estimator->isMotionEstimateValid()) {
00263 dbg(" ok, matched against previous frame.\n");
00264 _p->motion_estimate = _estimator->getMotionEstimate();
00265 _p->motion_estimate_covariance = _estimator->getMotionEstimateCov();
00266 _p->pose = _p->pose * _p->motion_estimate;
00267 _change_reference_frames = true;
00268 }
00269
00270 }
00271
00272
00273 if(!_estimator->isMotionEstimateValid() ||
00274 _estimator->getNumInliers() < _ref_frame_change_threshold) {
00275 _change_reference_frames = true;
00276 }
00277 if(_change_reference_frames)
00278 dbg("Changing reference frames\n");
00279 }
00280
00281 void
00282 VisualOdometry::sanityCheck() const
00283 {
00284 _cur_frame->sanityCheck();
00285 _ref_frame->sanityCheck();
00286 _estimator->sanityCheck();
00287 }
00288
00289 static std::string _toString(double v)
00290 {
00291 char buf[80];
00292 snprintf(buf, sizeof(buf), "%f", v);
00293 return std::string(buf);
00294 }
00295
00296 VisualOdometryOptions
00297 VisualOdometry::getDefaultOptions()
00298 {
00299 VisualOdometryOptions r;
00300
00301
00302
00303 r["feature-window-size"] = "9";
00304 r["max-pyramid-level"] = "3";
00305 r["min-pyramid-level"] = "0";
00306 r["target-pixels-per-feature"] = "250";
00307 r["fast-threshold"] = "20";
00308 r["use-adaptive-threshold"] = "true";
00309 r["fast-threshold-adaptive-gain"] = _toString(0.005);
00310 r["use-homography-initialization"] = "true";
00311 r["ref-frame-change-threshold"] = "150";
00312
00313
00314 r["use-bucketing"] = "true";
00315 r["bucket-width"] = "80";
00316 r["bucket-height"] = "80";
00317 r["max-keypoints-per-bucket"] = "25";
00318 r["use-image-normalization"] = "false";
00319
00320
00321 r["inlier-max-reprojection-error"] = _toString(1.5);
00322 r["clique-inlier-threshold"] = _toString(0.1);
00323 r["min-features-for-estimate"] = "10";
00324 r["max-mean-reprojection-error"] = _toString(10.0);
00325 r["use-subpixel-refinement"] = "true";
00326 r["feature-search-window"] = "25";
00327 r["update-target-features-with-refined"] = "false";
00328
00329
00330 r["stereo-require-mutual-match"] = "true";
00331 r["stereo-max-dist-epipolar-line"] = _toString(1.5);
00332 r["stereo-max-refinement-displacement"] = _toString(1.0);
00333 r["stereo-max-disparity"] = "128";
00334
00335 return r;
00336 }
00337
00338 static void
00339 validateOptions(const VisualOdometryOptions& options,
00340 const VisualOdometryOptions& defaults)
00341 {
00342 VisualOdometryOptions::const_iterator oiter = options.begin();
00343 VisualOdometryOptions::const_iterator oend = options.end();
00344 for(; oiter != oend; ++oiter) {
00345 if(defaults.find(oiter->first) == defaults.end()) {
00346 fprintf(stderr, "VisualOdometry WARNING: unrecognized option [%s]\n", oiter->first.c_str());
00347 }
00348 }
00349
00350
00351 }
00352
00353
00354 }