stereo_depth.cpp
Go to the documentation of this file.
00001 #include "stereo_depth.hpp"
00002 
00003 #include <cstdio>
00004 #include <iostream>
00005 
00006 #include <emmintrin.h>
00007 
00008 #include "visual_odometry.hpp"
00009 #include "tictoc.hpp"
00010 #include "fast.hpp"
00011 #include "sad.hpp"
00012 #include "rectification.hpp"
00013 #include "feature_match.hpp"
00014 #include "refine_feature_match.hpp"
00015 #include "refine_motion_estimate.hpp"
00016 #include "internal_utils.hpp"
00017 #include "stereo_rectify.hpp"
00018 
00019 #define MIN_DISPARITY 0
00020 
00021 namespace fovis
00022 {
00023 
00024 StereoDepth::StereoDepth(const StereoCalibration* calib,
00025                          const VisualOdometryOptions& options) :
00026     _calib(calib),
00027     _width(calib->getWidth()),
00028     _height(calib->getHeight()),
00029     _fast_threshold_min(5),
00030     _fast_threshold_max(70),
00031     _options(options)
00032 {
00033 
00034   const VisualOdometryOptions& defaults(VisualOdometry::getDefaultOptions());
00035 
00036   _feature_window_size = optionsGetIntOrFromDefault(_options, "feature-window-size", defaults);
00037   _num_pyramid_levels = optionsGetIntOrFromDefault(_options, "max-pyramid-level", defaults);
00038   _fast_threshold = optionsGetIntOrFromDefault(_options, "fast-threshold", defaults);
00039   _use_adaptive_threshold = optionsGetBoolOrFromDefault(_options, "use-adaptive-threshold", defaults);
00040   _max_refinement_displacement = optionsGetDoubleOrFromDefault(_options, "stereo-max-refinement-displacement", defaults);
00041   _require_mutual_match = optionsGetBoolOrFromDefault(_options, "stereo-require-mutual-match", defaults);
00042   _max_disparity = optionsGetIntOrFromDefault(_options, "stereo-max-disparity", defaults);
00043   _max_dist_epipolar_line = optionsGetDoubleOrFromDefault(_options, "stereo-max-dist-epipolar-line", defaults);
00044   _target_pixels_per_feature = optionsGetIntOrFromDefault(_options, "target-pixels-per-feature", defaults);
00045   _fast_threshold_adaptive_gain = optionsGetDoubleOrFromDefault(_options, "fast-threshold-adaptive-gain", defaults);
00046 
00047   _matched_right_keypoints_per_level.resize(_num_pyramid_levels);
00048 
00049   _right_frame = new StereoFrame(_width, _height, calib->getRightRectification(), _options);
00050 
00051   _uvd1_to_xyz = new Eigen::Matrix4d(calib->getUvdToXyz());
00052 
00053   _matches_capacity = 200;
00054   _matches = new FeatureMatch[_matches_capacity];
00055   _num_matches = 0;
00056 }
00057 
00058 StereoDepth::~StereoDepth()
00059 {
00060   delete _uvd1_to_xyz;
00061   delete _right_frame;
00062   delete[] _matches;
00063 }
00064 
00065 void
00066 StereoDepth::setRightImage(const uint8_t * img)
00067 {
00068   _right_frame->prepareFrame(img, _fast_threshold);
00069   if (_use_adaptive_threshold) {
00070     // adaptively adjust feature detector threshold based on how many features
00071     // were detected.  Use proportional control
00072     int target_num_features = (_width * _height) / _target_pixels_per_feature;
00073     int err = _right_frame->getNumDetectedKeypoints() - target_num_features;
00074     int thresh_adjustment = (int)((err) * _fast_threshold_adaptive_gain);
00075     _fast_threshold += thresh_adjustment;
00076     _fast_threshold = (_fast_threshold < _fast_threshold_min)? _fast_threshold_min :
00077         (_fast_threshold > _fast_threshold_max)? _fast_threshold_max : _fast_threshold;
00078   }
00079 }
00080 
00081 void
00082 StereoDepth::leftRightMatch(PyramidLevel *left_level,
00083                             PyramidLevel *right_level,
00084                             Points2d* matched_right_keypoints)
00085 {
00086   tictoc("leftRightMatch");
00087 
00088   matched_right_keypoints->clear();
00089 
00090   int num_kp_left = left_level->getNumKeypoints();
00091   int num_kp_right = right_level->getNumKeypoints();
00092   int level_num = left_level->getLevelNum();
00093 
00094   float adj_max_dist_epipolar_line = _max_dist_epipolar_line*(1 << level_num);
00095 
00096   //assert (left_level->getNumLevel() == right_level->getNumLevel());
00097   _legal_matches.resize(num_kp_left);
00098   for (int left_kp_ind = 0; left_kp_ind < num_kp_left; ++left_kp_ind) {
00099     Eigen::Vector2d ref_rect_base_uv = left_level->getKeypointRectBaseUV(left_kp_ind);
00100     std::vector<int>& left_candidates(_legal_matches[left_kp_ind]);
00101     left_candidates.clear();
00102     for (int right_kp_ind=0; right_kp_ind < num_kp_right; ++right_kp_ind) {
00103       Eigen::Vector2d diff = ref_rect_base_uv - right_level->getKeypointRectBaseUV(right_kp_ind);
00104       // TODO some sort of binary search
00105       if (diff(1) < -adj_max_dist_epipolar_line) { break; }
00106       // epipolar and disparity constraints
00107       if ((fabs(diff(1)) < adj_max_dist_epipolar_line) &&
00108           (diff(0) > MIN_DISPARITY) &&
00109           (diff(0) < _max_disparity)) {
00110         left_candidates.push_back(right_kp_ind);
00111       }
00112     }
00113   }
00114 
00115   int max_num_matches = std::min(num_kp_left, num_kp_right);
00116   if (_matches_capacity < max_num_matches) {
00117     _matches_capacity = static_cast<int>(1.2*max_num_matches);
00118     delete[] _matches;
00119     _matches = new FeatureMatch[_matches_capacity];
00120   }
00121   _num_matches = 0;
00122   _matcher.matchFeatures(left_level, right_level, _legal_matches, &_matches[0], &_num_matches);
00123 
00124   // subpixel refinement on correspondences
00125   for (int n=0; n < _num_matches; ++n) {
00126     FeatureMatch& match(_matches[n]);
00127 
00128     KeypointData* left_kpdata(match.ref_keypoint);
00129     KeypointData* right_kpdata(match.target_keypoint);
00130 
00131     Eigen::Vector2d left_uv(left_kpdata->kp.u, left_kpdata->kp.v);
00132     Eigen::Vector2d init_right_uv(right_kpdata->kp.u, right_kpdata->kp.v);
00133 
00134     Eigen::Vector2d refined_right_uv;
00135     float delta_sse = -1;
00136     refineFeatureMatch(left_level, right_level, left_uv, init_right_uv,
00137                        &refined_right_uv, &delta_sse);
00138     double ds = (init_right_uv - refined_right_uv).norm();
00139     Eigen::Vector2d refined_right_base_uv = refined_right_uv * (1 << level_num);
00140 
00141     Eigen::Vector2d rect_refined_right_base_uv;
00142     _right_frame->rectify(refined_right_base_uv, &rect_refined_right_base_uv);
00143 
00144     Eigen::Vector2d diff = left_kpdata->rect_base_uv - rect_refined_right_base_uv;
00145     double disparity = diff(0);
00146 
00147     // re-enforce disparity constraints, epipolar constraints, and excessive
00148     // refinement displacement
00149     if ((disparity < MIN_DISPARITY) ||
00150         (disparity > _max_disparity) ||
00151         (ds > _max_refinement_displacement) ||
00152         (fabs(diff(1)) > adj_max_dist_epipolar_line)) {
00153       left_kpdata->has_depth = false;
00154       left_kpdata->xyzw = Eigen::Vector4d(NAN, NAN, NAN, NAN);
00155       left_kpdata->xyz = Eigen::Vector3d(NAN, NAN, NAN);
00156       left_kpdata->disparity = NAN;
00157       continue;
00158     }
00159 
00160     Eigen::Vector4d uvd1(left_kpdata->rect_base_uv(0),
00161                          left_kpdata->rect_base_uv(1),
00162                          disparity,
00163                          1);
00164 
00165     left_kpdata->xyzw = (*_uvd1_to_xyz) * uvd1;
00166     left_kpdata->xyz = left_kpdata->xyzw.head<3>() / left_kpdata->xyzw.w();
00167     left_kpdata->has_depth = true;
00168     left_kpdata->disparity = disparity;
00169 
00170     // TODO we are relying on matches being ordered by increasing left keypoint index.
00171     matched_right_keypoints->push_back(std::make_pair(refined_right_uv(0), refined_right_uv(1)));
00172   }
00173 
00174   tictoc("leftRightMatch");
00175 }
00176 
00177 bool
00178 StereoDepth::haveXyz(int u, int v)
00179 {
00180   return true;
00181 }
00182 
00183 void
00184 StereoDepth::getXyz(OdometryFrame * odom_frame)
00185 {
00186   int num_levels = odom_frame->getNumLevels();
00187   for(int level_num=0; level_num < num_levels; ++level_num) {
00188     PyramidLevel* left = odom_frame->getLevel(level_num);
00189     PyramidLevel* right = _right_frame->getLevel(level_num);
00190     Points2d* matched_right_keypoints(&_matched_right_keypoints_per_level.at(level_num));
00191     leftRightMatch(left, right, matched_right_keypoints);
00192   }
00193 }
00194 
00195 void
00196 StereoDepth::refineXyz(FeatureMatch * matches,
00197                        int num_matches,
00198                        OdometryFrame * odom_frame)
00199 {
00200   for (int m_ind = 0; m_ind < num_matches; ++m_ind) {
00201     FeatureMatch& match = matches[m_ind];
00202     if (match.status != MATCH_NEEDS_DEPTH_REFINEMENT) {
00203       continue;
00204     }
00205 
00206     const KeypointData * left_kpdata = match.target_keypoint;
00207     int level_num = match.ref_keypoint->pyramid_level;
00208     int kp_ind = left_kpdata->keypoint_index;
00209 
00210     // TODO this is brittle. We are relying too much on what
00211     // external code does with the keypoints in the odom_frame
00212     // (ie that keypoints without depth are deleted).
00213     Eigen::Vector2d left_uv(match.refined_target_keypoint.kp.u,
00214                             match.refined_target_keypoint.kp.v);
00215 
00216     Eigen::Vector2d right_uv(_matched_right_keypoints_per_level[level_num][kp_ind].first,
00217                              _matched_right_keypoints_per_level[level_num][kp_ind].second);
00218 
00219     PyramidLevel* left_level = odom_frame->getLevel(level_num);
00220     PyramidLevel* right_level = _right_frame->getLevel(level_num);
00221 
00222     Eigen::Vector2d refined_right_uv;
00223     float delta_sse;
00224     refineFeatureMatch(left_level, right_level, left_uv, right_uv,
00225                        &refined_right_uv, &delta_sse);
00226 
00227     double ds = (right_uv - refined_right_uv).norm();
00228 
00229     // scale refined and unrefined up to base level dimensions
00230     Eigen::Vector2d base_refined_right_uv = refined_right_uv * (1 << level_num);
00231 
00232     // get rectified and undistorted position
00233     Eigen::Vector2d rect_base_refined_right_uv;
00234     _right_frame->rectify(base_refined_right_uv, &rect_base_refined_right_uv);
00235 
00236     Eigen::Vector2d diff = match.refined_target_keypoint.rect_base_uv - rect_base_refined_right_uv;
00237     double disparity = diff(0);
00238     if (disparity < MIN_DISPARITY || disparity > _max_disparity ||
00239         ds > _max_refinement_displacement ||
00240         fabs(diff(1)) > _max_dist_epipolar_line*(1 << level_num)) {
00241       match.status = MATCH_REFINEMENT_FAILED;
00242       match.inlier = false;
00243       match.refined_target_keypoint.xyzw = Eigen::Vector4d(NAN, NAN, NAN, NAN);
00244       match.refined_target_keypoint.xyz = Eigen::Vector3d(NAN, NAN, NAN);
00245       match.refined_target_keypoint.disparity = NAN;
00246       continue;
00247     }
00248 
00249     KeypointData& kpdata(match.refined_target_keypoint);
00250     Eigen::Vector4d uvd1(kpdata.rect_base_uv(0), kpdata.rect_base_uv(1), disparity, 1);
00251     kpdata.xyzw = (*_uvd1_to_xyz) * uvd1;
00252     kpdata.xyz = kpdata.xyzw.head<3>() / kpdata.xyzw.w();
00253     kpdata.disparity = disparity;
00254     match.status = MATCH_OK;
00255   }
00256 
00257 }
00258 
00259 } /*  */


libfovis
Author(s): Albert Huang, Maurice Fallon
autogenerated on Thu Jun 6 2019 20:16:12