Skip to content

Commit

Permalink
Add five point algorithm for checking essential matrix outliers
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelchang committed Sep 18, 2019
1 parent a433a21 commit 9fbf770
Show file tree
Hide file tree
Showing 14 changed files with 346 additions and 27 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set(CMAKE_VERBOSE_MAKEFILE FALSE)

add_definitions("-std=c++17")
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif (NOT CMAKE_BUILD_TYPE)

# find catkin dependencies
Expand Down Expand Up @@ -68,6 +68,7 @@ add_library(omni_slam_eval_lib
src/feature/matcher.cc
src/reconstruction/triangulator.cc
src/odometry/pnp.cc
src/odometry/five_point.cc
src/optimization/bundle_adjuster.cc
src/util/hdf_file.cc
)
Expand Down
4 changes: 3 additions & 1 deletion launch/odometry_eval.launch
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
detector_parameters: {maxCorners: 2000, qualityLevel: 0.001, minDistance: 5, blockSize: 5}
tracker_window_size: 128
tracker_num_scales: 4
tracker_delta_pixel_error_threshold: 3.0
tracker_checker_epipolar_threshold: 0.008
tracker_checker_iterations: 1000
tracker_delta_pixel_error_threshold: 0.0
tracker_error_threshold: 20.0
min_features_per_region: 100
pnp_inlier_threshold: 3.0
Expand Down
4 changes: 3 additions & 1 deletion launch/reconstruction_eval.launch
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
detector_parameters: {maxCorners: 5000, qualityLevel: 0.001, minDistance: 5, blockSize: 5}
tracker_window_size: 128
tracker_num_scales: 4
tracker_delta_pixel_error_threshold: 3.0
tracker_checker_epipolar_threshold: 0.008
tracker_checker_iterations: 1000
tracker_delta_pixel_error_threshold: 0.0
tracker_error_threshold: 20.0
min_features_per_region: 10
max_reprojection_error: 5.0
Expand Down
4 changes: 3 additions & 1 deletion launch/tracking_eval.launch
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<arg name="bag_file" default="" />
<arg name="results_file" default="$(arg bag_file).tracking.hdf5" />
<arg name="camera_model" default="double_sphere" />
<arg name="camera_params" default="{fx: 1024, fy: 1024, cx: 512, cy: 512, chi: 0.0, alpha: 0.0}" />
<arg name="camera_params" default="{fx: 295.936, fy: 295.936, cx: 512, cy: 512, chi: 0.3, alpha: 0.6666667}" />
<arg name="rate" default="1" />
<node pkg="omni_slam_eval" type="omni_slam_tracking_eval_node" name="omni_slam_tracking_eval_node" required="true" output="screen">
<param name="bag_file" value="$(arg bag_file)" />
Expand All @@ -19,6 +19,8 @@
detector_parameters: {maxCorners: 100, qualityLevel: 0.01, minDistance: 5, blockSize: 5}
tracker_window_size: 128
tracker_num_scales: 4
tracker_checker_epipolar_threshold: 0.008
tracker_checker_iterations: 1000
tracker_delta_pixel_error_threshold: 3.0
tracker_error_threshold: 20.0
min_features_per_region: 10
Expand Down
33 changes: 18 additions & 15 deletions src/feature/tracker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,29 @@ int Tracker::Track(std::vector<data::Landmark> &landmarks, data::Frame &cur_fram
for (int i = 0; i < results.size(); i++)
{
data::Landmark &landmark = landmarks[orig_inx[i]];
if (landmark.HasGroundTruth() && cur_frame.HasPose())
if (deltaPixErrThresh_ > 0)
{
Vector2d pixel_gnd;
Vector2d pixel_gnd_prev;
if (!cur_frame.GetCameraModel().ProjectToImage(util::TFUtil::WorldFrameToCameraFrame(util::TFUtil::TransformPoint(cur_frame.GetInversePose(), landmark.GetGroundTruth())), pixel_gnd) || !prevFrame_->GetCameraModel().ProjectToImage(util::TFUtil::WorldFrameToCameraFrame(util::TFUtil::TransformPoint(prevFrame_->GetInversePose(), landmark.GetGroundTruth())), pixel_gnd_prev))
if (landmark.HasGroundTruth() && cur_frame.HasPose())
{
continue;
}
else
{
Vector2d pixel_cur;
pixel_cur << results[i].x, results[i].y;
Vector2d pixel_prev;
pixel_prev << points_to_track[i].x, points_to_track[i].y;
double cur_error = (pixel_cur - pixel_gnd).norm();
double prev_error = (pixel_prev - pixel_gnd_prev).norm();
if (cur_error - prev_error > deltaPixErrThresh_)
Vector2d pixel_gnd;
Vector2d pixel_gnd_prev;
if (!cur_frame.GetCameraModel().ProjectToImage(util::TFUtil::WorldFrameToCameraFrame(util::TFUtil::TransformPoint(cur_frame.GetInversePose(), landmark.GetGroundTruth())), pixel_gnd) || !prevFrame_->GetCameraModel().ProjectToImage(util::TFUtil::WorldFrameToCameraFrame(util::TFUtil::TransformPoint(prevFrame_->GetInversePose(), landmark.GetGroundTruth())), pixel_gnd_prev))
{
continue;
}
else
{
Vector2d pixel_cur;
pixel_cur << results[i].x, results[i].y;
Vector2d pixel_prev;
pixel_prev << points_to_track[i].x, points_to_track[i].y;
double cur_error = (pixel_cur - pixel_gnd).norm();
double prev_error = (pixel_prev - pixel_gnd_prev).norm();
if (cur_error - prev_error > deltaPixErrThresh_)
{
continue;
}
}
}
}
if (status[i] == 1 && err[i] <= errThresh_)
Expand Down
1 change: 1 addition & 0 deletions src/feature/tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <opencv2/opencv.hpp>
#include "data/frame.h"
#include "data/landmark.h"
#include "odometry/five_point.h"
#include <vector>

namespace omni_slam
Expand Down
25 changes: 21 additions & 4 deletions src/module/tracking_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ namespace omni_slam
namespace module
{

TrackingModule::TrackingModule(std::unique_ptr<feature::Detector> &detector, std::unique_ptr<feature::Tracker> &tracker, int minFeaturesRegion)
TrackingModule::TrackingModule(std::unique_ptr<feature::Detector> &detector, std::unique_ptr<feature::Tracker> &tracker, std::unique_ptr<odometry::FivePoint> &checker, int minFeaturesRegion)
: detector_(std::move(detector)),
tracker_(std::move(tracker)),
fivePointChecker_(std::move(checker)),
minFeaturesRegion_(minFeaturesRegion)
{
}

TrackingModule::TrackingModule(std::unique_ptr<feature::Detector> &&detector, std::unique_ptr<feature::Tracker> &&tracker, int minFeaturesRegion)
: TrackingModule(detector, tracker, minFeaturesRegion)
TrackingModule::TrackingModule(std::unique_ptr<feature::Detector> &&detector, std::unique_ptr<feature::Tracker> &&tracker, std::unique_ptr<odometry::FivePoint> &&checker, int minFeaturesRegion)
: TrackingModule(detector, tracker, checker, minFeaturesRegion)
{
}

Expand Down Expand Up @@ -47,7 +48,23 @@ void TrackingModule::Update(std::unique_ptr<data::Frame> &frame)
}

vector<double> trackErrors;
tracker_->Track(landmarks_, *frames_.back(), trackErrors);
std::vector<data::Landmark> landmarksTemp(landmarks_);
tracker_->Track(landmarksTemp, *frames_.back(), trackErrors);

if (fivePointChecker_)
{
Matrix3d E;
std::vector<int> inlierIndices;
fivePointChecker_->Compute(landmarksTemp, **next(frames_.rbegin()), *frames_.back(), E, inlierIndices);
for (int inx : inlierIndices)
{
landmarks_[inx].AddObservation(*landmarksTemp[inx].GetObservationByFrameID(frames_.back()->GetID()));
}
}
else
{
landmarks_ = std::move(landmarksTemp);
}

int i = 0;
int numGood = 0;
Expand Down
6 changes: 4 additions & 2 deletions src/module/tracking_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "feature/tracker.h"
#include "feature/detector.h"
#include "odometry/five_point.h"
#include "data/frame.h"
#include "data/landmark.h"

Expand All @@ -27,8 +28,8 @@ class TrackingModule
std::vector<double> failureRadDists;
};

TrackingModule(std::unique_ptr<feature::Detector> &detector, std::unique_ptr<feature::Tracker> &tracker, int minFeaturesRegion = 5);
TrackingModule(std::unique_ptr<feature::Detector> &&detector, std::unique_ptr<feature::Tracker> &&tracker, int minFeaturesRegion = 5);
TrackingModule(std::unique_ptr<feature::Detector> &detector, std::unique_ptr<feature::Tracker> &tracker, std::unique_ptr<odometry::FivePoint> &checker, int minFeaturesRegion = 5);
TrackingModule(std::unique_ptr<feature::Detector> &&detector, std::unique_ptr<feature::Tracker> &&tracker, std::unique_ptr<odometry::FivePoint> &&checker, int minFeaturesRegion = 5);

void Update(std::unique_ptr<data::Frame> &frame);
void Redetect();
Expand Down Expand Up @@ -57,6 +58,7 @@ class TrackingModule

std::shared_ptr<feature::Detector> detector_;
std::shared_ptr<feature::Tracker> tracker_;
std::shared_ptr<odometry::FivePoint> fivePointChecker_;

std::vector<std::unique_ptr<data::Frame>> frames_;
std::vector<data::Landmark> landmarks_;
Expand Down
186 changes: 186 additions & 0 deletions src/odometry/five_point.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#include "five_point.h"

#include "util/math_util.h"
#include <set>
#include <random>

namespace omni_slam
{
namespace odometry
{

FivePoint::FivePoint(int ransac_iterations, double epipolar_threshold)
: ransacIterations_(ransac_iterations),
epipolarThreshold_(epipolar_threshold)
{
}

int FivePoint::Compute(const std::vector<data::Landmark> &landmarks, const data::Frame &frame1, const data::Frame &frame2, Matrix3d &E, std::vector<int> &inlier_indices) const
{
std::vector<Vector3d> x1;
std::vector<Vector3d> x2;
std::map<int, int> indexToLandmarkIndex;
int i = 0;
for (const data::Landmark &landmark : landmarks)
{
if (landmark.IsObservedInFrame(frame2.GetID()) && landmark.IsObservedInFrame(frame1.GetID()))
{
const data::Feature *feat1 = landmark.GetObservationByFrameID(frame1.GetID());
const data::Feature *feat2 = landmark.GetObservationByFrameID(frame2.GetID());
x1.push_back(feat1->GetBearing().normalized());
x2.push_back(feat2->GetBearing().normalized());
indexToLandmarkIndex[x1.size() - 1] = i;
}
i++;
}
int inliers = RANSAC(x1, x2, E);
std::vector<int> indices = GetInlierIndices(x1, x2, E);
inlier_indices.clear();
inlier_indices.reserve(indices.size());
for (int inx : indices)
{
inlier_indices.push_back(indexToLandmarkIndex[inx]);
}
return inliers;
}

int FivePoint::Compute(const std::vector<data::Landmark> &landmarks, const data::Frame &frame1, const data::Frame &frame2, Matrix3d &E) const
{
std::vector<int> temp;
return Compute(landmarks, frame1, frame2, E, temp);
}

int FivePoint::RANSAC(const std::vector<Vector3d> &x1, const std::vector<Vector3d> &x2, Matrix3d &E) const
{
int maxInliers = 0;
#pragma omp parallel for
for (int i = 0; i < ransacIterations_; i++)
{
std::random_device rd;
std::mt19937 eng(rd());
std::uniform_int_distribution<> distr(0, x1.size() - 1);
std::set<int> randset;
while (randset.size() < 5)
{
randset.insert(distr(eng));
}
std::vector<int> indices;
std::copy(randset.begin(), randset.end(), std::back_inserter(indices));

std::vector<Matrix3d> iterE;
std::vector<Vector3d> x1s(5);
std::vector<Vector3d> x2s(5);
for (int j = 0; j < 5; j++)
{
x1s[j] = x1[indices[j]];
x2s[j] = x2[indices[j]];
}
FivePointRelativePose(x1s, x2s, iterE);

for (Matrix3d &e : iterE)
{
int inliers = GetInlierIndices(x1, x2, e).size();
#pragma omp critical
{
if (inliers > maxInliers)
{
maxInliers = inliers;
E = e;
}
}
}
}
return maxInliers;
}

std::vector<int> FivePoint::GetInlierIndices(const std::vector<Vector3d> &x1, const std::vector<Vector3d> &x2, const Matrix3d &E) const
{
std::vector<int> indices;
for (int i = 0; i < x1.size(); i++)
{
RowVector3d epiplane1 = x2[i].transpose() * E;
epiplane1.normalize();
double epiErr1 = std::abs(epiplane1 * x1[i]);
Vector3d epiplane2 = E * x1[i];
epiplane2.normalize();
double epiErr2 = std::abs(x2[i].transpose() * epiplane2);
if (epiErr1 < epipolarThreshold_ && epiErr2 < epipolarThreshold_)
{
indices.push_back(i);
}
}
return indices;
}

void FivePoint::FivePointRelativePose(const std::vector<Vector3d> &x1, const std::vector<Vector3d> &x2, std::vector<Matrix3d> &Es) const
{
MatrixXd epipolarConstraint(x1.size(), 9);
for (int i = 0; i < x1.size(); i++)
{
epipolarConstraint.row(i) << x2[i](0) * x1[i].transpose(), x2[i](1) * x1[i].transpose(), x2[i](2) * x1[i].transpose();
}
Eigen::SelfAdjointEigenSolver<MatrixXd> solver(epipolarConstraint.transpose() * epipolarConstraint);
Matrix<double, 9, 4> basis = solver.eigenvectors().leftCols<4>();
Matrix<double, 1, 4> E[3][3] = {
basis.row(0), basis.row(3), basis.row(6),
basis.row(1), basis.row(4), basis.row(7),
basis.row(2), basis.row(5), basis.row(8)
};

Matrix<double, 1, 10> EET[3][3];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
EET[i][j] = 2 * (util::MathUtil::MultiplyDegOnePoly(E[i][0], E[j][0]) + util::MathUtil::MultiplyDegOnePoly(E[i][1], E[j][1]) + util::MathUtil::MultiplyDegOnePoly(E[i][2], E[j][2]));
}
}
Matrix<double, 1, 10> trace = EET[0][0] + EET[1][1] + EET[2][2];
Matrix<double, 9, 20> traceConstraint;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
traceConstraint.row(3 * i + j) = util::MathUtil::MultiplyDegTwoDegOnePoly(EET[i][0], E[0][j]) + util::MathUtil::MultiplyDegTwoDegOnePoly(EET[i][1], E[1][j]) + util::MathUtil::MultiplyDegTwoDegOnePoly(EET[i][2], E[2][j]) - 0.5 * util::MathUtil::MultiplyDegTwoDegOnePoly(trace, E[i][j]);
}
}
Matrix<double, 1, 20> determinantConstraint = util::MathUtil::MultiplyDegTwoDegOnePoly(Matrix<double, 1, 10>(util::MathUtil::MultiplyDegOnePoly(E[0][1], E[1][2]) - util::MathUtil::MultiplyDegOnePoly(E[0][2], E[1][1])), E[2][0])
+ util::MathUtil::MultiplyDegTwoDegOnePoly(Matrix<double, 1, 10>(util::MathUtil::MultiplyDegOnePoly(E[0][2], E[1][0]) - util::MathUtil::MultiplyDegOnePoly(E[0][0], E[1][2])), E[2][1])
+ util::MathUtil::MultiplyDegTwoDegOnePoly(Matrix<double, 1, 10>(util::MathUtil::MultiplyDegOnePoly(E[0][0], E[1][1]) - util::MathUtil::MultiplyDegOnePoly(E[0][1], E[1][0])), E[2][2]);
Matrix<double, 10, 20> constraints;
constraints.block<9, 20>(0, 0) = traceConstraint;
constraints.row(9) = determinantConstraint;

Eigen::FullPivLU<Matrix<double, 10, 10>> LU(constraints.block<10, 10>(0, 0));
Matrix<double, 10, 10> elim = LU.solve(constraints.block<10, 10>(0, 10));

Matrix<double, 10, 10> action = Matrix<double, 10, 10>::Zero();
action.block<3, 10>(0, 0) = elim.block<3, 10>(0, 0);
action.row(3) = elim.row(4);
action.row(4) = elim.row(5);
action.row(5) = elim.row(7);
action(6, 0) = -1.0;
action(7, 1) = -1.0;
action(8, 3) = -1.0;
action(9, 6) = -1.0;

Eigen::EigenSolver<Matrix<double, 10, 10>> eigensolver(action);
const auto &eigenvectors = eigensolver.eigenvectors();
const auto &eigenvalues = eigensolver.eigenvalues();

Es.reserve(10);
for (int i = 0; i < 10; i++)
{
if (eigenvalues(i).imag() != 0)
{
continue;
}
Matrix3d EMat;
Eigen::Map<Matrix<double, 9, 1>>(EMat.data()) = basis * eigenvectors.col(i).tail<4>().real();
Es.emplace_back(EMat.transpose());
}
}


}
}
Loading

0 comments on commit 9fbf770

Please sign in to comment.