Files
vio/svo/test/test_matcher.cpp
T

201 lines
7.6 KiB
C++

// This file is part of SVO - Semi-direct Visual Odometry.
//
// Copyright (C) 2014 Christian Forster <forster at ifi dot uzh dot ch>
// (Robotics and Perception Group, University of Zurich, Switzerland).
//
// SVO is free software: you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or any later version.
//
// SVO is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <algorithm>
#include <vikit/pinhole_camera.h>
#include <vikit/math_utils.h>
#include <vikit/blender_utils.h>
#include <svo/matcher.h>
#include <svo/frame.h>
#include <svo/config.h>
#include <svo/feature.h>
#include "test_utils.h"
namespace {
/// Matcher Test-Fixture
class MatcherTest {
public:
MatcherTest()
{
cam_ = new vk::PinholeCamera(752, 480, 315.5, 315.5, 376.0, 240.0);
// load images
std::string dataset_dir(svo::test_utils::getDatasetDir() + "/sin2_tex2_h1_v8_d");
std::string img_name(dataset_dir+"/img/frame_000002_0.png");
printf("Loading image '%s'\n", img_name.c_str());
cv::Mat img_ref(cv::imread(img_name, 0));
img_name = std::string(dataset_dir+"/img/frame_000006_0.png");
printf("Loading image '%s'\n", img_name.c_str());
cv::Mat img_cur(cv::imread(img_name, 0));
assert(!img_ref.empty() && !img_cur.empty());
// create frame
frame_ref_ = new svo::Frame(cam_, img_ref, 1.0);
frame_cur_ = new svo::Frame(cam_, img_cur, 2.0);
ref_ftr_ = new svo::Feature(frame_ref_, Eigen::Vector2d(300, 260), 0);
// set poses
Eigen::Vector3d t_w_ref(0.1131, 0.1131, 2.0000);
Eigen::Vector3d t_w_cur(0.5673, 0.5641, 2.0000);
Eigen::Quaterniond q_w_ref(0.0, 0.8227, 0.2149, 0.0);
Eigen::Quaterniond q_w_cur(0.0, 0.8235, 0.2130, 0.0);
frame_ref_->T_f_w_ = Sophus::SE3(q_w_ref, t_w_ref).inverse();
frame_cur_->T_f_w_ = Sophus::SE3(q_w_cur, t_w_cur).inverse();
// load ground-truth depth
vk::blender_utils::loadBlenderDepthmap(dataset_dir+"/depth/frame_000002_0.depth", *cam_, depth_ref_);
}
virtual ~MatcherTest()
{
delete cam_;
delete frame_ref_;
delete frame_cur_;
delete ref_ftr_;
}
// Individual tests
void testEpipolarSearchFullImg();
void testWarpAffine();
// Objects declared here can be used by all tests
vk::PinholeCamera* cam_;
svo::Frame* frame_ref_;
svo::Frame* frame_cur_;
svo::Feature* ref_ftr_;
cv::Mat depth_ref_;
};
void MatcherTest::testEpipolarSearchFullImg()
{
svo::Matcher matcher;
cv::Mat depthmap(frame_ref_->cam_->height(), frame_ref_->cam_->width(), CV_32FC1, cv::Scalar(0));
cv::Mat error_map(depthmap.rows, depthmap.cols, CV_32FC1, cv::Scalar(0));
cv::Mat mask(depthmap.rows, depthmap.cols, CV_8UC1, cv::Scalar(1));
double depth_range = 0.8;
size_t n_converged = 0;
double sum_error = 0;
std::vector<double> errors; errors.reserve(depthmap.cols*depthmap.rows);
for(int y=4; y<depthmap.rows-4; ++y)
{
for(int x=4; x<depthmap.cols-4; ++x)
{
svo::Feature ftr(frame_ref_, Eigen::Vector2d(x,y), 0);
double depth_gt = depth_ref_.at<float>(y, x);
double depth_estimate;
int res = matcher.findEpipolarMatchDirect(
*frame_ref_, *frame_cur_, ftr, depth_gt, fmax(depth_gt-depth_range,0.0),
depth_gt+depth_range, depth_estimate);
if(res)
{
depthmap.at<float>(y,x) = depth_estimate;
error_map.at<float>(y,x) = fabsf(depth_estimate-depth_gt);
++n_converged;
sum_error += error_map.at<float>(y,x);
errors.push_back(error_map.at<float>(y,x));
}
else
mask.at<uint8_t>(y,x) = 0;
}
}
// compute mean, median and variance of error in converged area
printf("n converged: \t %zu mm (ref: 216114)\n", n_converged);
printf("mean error: \t %f mm (ref: 0.410084)\n", sum_error*100/n_converged);
std::vector<double>::iterator it = errors.begin()+0.5*errors.size();
std::nth_element(errors.begin(), it, errors.end());
printf("50-percentile: \t %f mm (ref: 0.083203)\n", *it*100);
it = errors.begin()+0.8*errors.size();
std::nth_element(errors.begin(), it, errors.end());
printf("80-percentile: \t %f mm (ref: 0.161824)\n", *it*100);
it = errors.begin()+0.95*errors.size();
std::nth_element(errors.begin(), it, errors.end());
printf("95-percentile: \t %f mm (ref: 0.263539)\n", *it*100);
// save results to file
std::ofstream output_stream;
std::string trace_dir(svo::test_utils::getTraceDir());
std::string output_filename(trace_dir + "/depthmap.bin");
output_stream.open(output_filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
output_stream.write((const char *)depthmap.data, depthmap.rows*depthmap.cols*sizeof(float));
output_stream.close();
output_filename.assign(trace_dir + "/depthmap_error.bin");
output_stream.open(output_filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
output_stream.write((const char *)error_map.data, error_map.rows*error_map.cols*sizeof(float));
output_stream.close();
output_filename.assign(trace_dir + "/depthmap_mask.bin");
output_stream.open(output_filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
output_stream.write((const char *)mask.data, mask.rows*mask.cols*sizeof(uint8_t));
output_stream.close();
}
void MatcherTest::testWarpAffine()
{
const int halfpatch_size = 15;
const int patch_size=halfpatch_size*2;
svo::Matcher matcher;
double depth = 1.0;
Sophus::SE3 T_cur_ref(frame_cur_->T_f_w_*frame_ref_->T_f_w_.inverse());
Eigen::Vector2d px_cur(frame_cur_->cam_->world2cam(T_cur_ref*(ref_ftr_->f*depth)));
// compute reference patch
cv::Mat ref_patch(matcher.halfpatch_size_+1, matcher.halfpatch_size_+1,
CV_8U, matcher.patch_with_border_);
Eigen::Matrix2d A_cur_ref;
svo::warp::getWarpMatrixAffine(
*cam_, *cam_, ref_ftr_->px, ref_ftr_->f, 1.0, T_cur_ref, ref_ftr_->level, A_cur_ref);
int level_cur = svo::warp::getBestSearchLevel(A_cur_ref, svo::Config::nPyrLevels()-1);
svo::warp::warpAffine(
A_cur_ref, ref_ftr_->frame->img_pyr_[ref_ftr_->level], ref_ftr_->px,
ref_ftr_->level, level_cur, matcher.halfpatch_size_+1, matcher.patch_with_border_);
// copy current patch
Eigen::Vector2i pxi(px_cur[0]/(1<<level_cur)+0.5, px_cur[1]/(1<<level_cur)+0.5);
cv::Mat cur_patch(patch_size, patch_size, CV_8U);
frame_cur_->img_pyr_.at(level_cur)(cv::Range(pxi[1]-halfpatch_size,
pxi[1]+halfpatch_size),
cv::Range(pxi[0]-halfpatch_size,
pxi[0]+halfpatch_size)).copyTo(cur_patch);
// display results
cv::circle(frame_cur_->img_pyr_[level_cur],
cv::Point2f(pxi[0], pxi[1]),
patch_size, cv::Scalar(255), 2);
cv::circle(frame_ref_->img_pyr_[ref_ftr_->level],
cv::Point2f(ref_ftr_->px[0]/(1<<ref_ftr_->level), ref_ftr_->px[1]/(1<<ref_ftr_->level)),
patch_size, cv::Scalar(255), 2);
cv::imshow("cur_patch", cur_patch);
cv::imshow("ref_patch", ref_patch);
cv::imshow("ref_img", frame_ref_->img_pyr_[ref_ftr_->level]);
cv::imshow("cur_img", frame_cur_->img_pyr_[level_cur]);
cv::waitKey(0);
}
} // namespace
int main(int argc, char** argv)
{
MatcherTest test;
test.testEpipolarSearchFullImg();
// test.testWarpAffine();
return 0;
}