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
00071
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
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
00105 if (diff(1) < -adj_max_dist_epipolar_line) { break; }
00106
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
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
00148
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
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
00211
00212
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
00230 Eigen::Vector2d base_refined_right_uv = refined_right_uv * (1 << level_num);
00231
00232
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 }