[Bf-blender-cvs] [1de23f6] master: Tweaks and improvements to fundamental/homography estimation

Sergey Sharybin noreply at git.blender.org
Wed Nov 20 10:58:56 CET 2013


Commit: 1de23f6c0d52a09227a0dc8d5a01f3acd9973d63
Author: Sergey Sharybin
Date:   Wed Nov 20 15:19:43 2013 +0600
http://developer.blender.org/rB1de23f6c0d52a09227a0dc8d5a01f3acd9973d63

Tweaks and improvements to fundamental/homography estimation

- A bit more reasonable name for the estimation option
  structure and estimation functions.

- Get rid of unclear function and parameter tolerance,
  it wasn't clear at all in which units they are.

  Now we've got expected_average_symmetric_distance as
  an early output check and as soon as average symmetric
  error goes below this threshold refining finishes.

  This distance is measured in the same units as input
  points are.

  It is arguable whether we need callback for this or not,
  but seems Ceres doesn't have some kind of absolute
  threshold for function value and function_tolerance
  behaves different from logic behind expected symmetric
  error.

- Added option to normalize correspondences before
  estimating the homography in order to increase estimation
  stability. See

    R. Hartley and A. Zisserman. Multiple View Geometry in Computer
    Vision. Cambridge University Press, second edition, 2003.

    https://www.cs.ubc.ca/grads/resources/thesis/May09/Dubrofsky_Elan.pdf

===================================================================

M	extern/libmv/libmv-capi.cc
M	extern/libmv/libmv/multiview/fundamental.cc
M	extern/libmv/libmv/multiview/fundamental.h
M	extern/libmv/libmv/multiview/homography.cc
M	extern/libmv/libmv/multiview/homography.h
M	extern/libmv/libmv/simple_pipeline/keyframe_selection.cc

===================================================================

diff --git a/extern/libmv/libmv-capi.cc b/extern/libmv/libmv-capi.cc
index f02ca0e..95ac2ff 100644
--- a/extern/libmv/libmv-capi.cc
+++ b/extern/libmv/libmv-capi.cc
@@ -1089,8 +1089,8 @@ void libmv_homography2DFromCorrespondencesEuc(double (*x1)[2], double (*x2)[2],
 	LG << "x1: " << x1_mat;
 	LG << "x2: " << x2_mat;
 
-	libmv::HomographyEstimationOptions options;
-	libmv::Homography2DFromCorrespondencesEuc(x1_mat, x2_mat, options, &H_mat);
+	libmv::EstimateHomographyOptions options;
+	libmv::EstimateHomography2DFromCorrespondences(x1_mat, x2_mat, options, &H_mat);
 
 	LG << "H: " << H_mat;
 
diff --git a/extern/libmv/libmv/multiview/fundamental.cc b/extern/libmv/libmv/multiview/fundamental.cc
index ec6c70d..7a1433c 100644
--- a/extern/libmv/libmv/multiview/fundamental.cc
+++ b/extern/libmv/libmv/multiview/fundamental.cc
@@ -408,13 +408,16 @@ void FundamentalToEssential(const Mat3 &F, Mat3 *E) {
   *E = svd.matrixU() * diag.asDiagonal() * svd.matrixV().transpose();
 }
 
-FundamentalEstimationOptions::FundamentalEstimationOptions(void) :
-    use_refine_if_algebraic_fails(true),
+// Default settings for fundamental estimation which should be suitable
+// for a wide range of use cases.
+EstimateFundamentalOptions::EstimateFundamentalOptions(void) :
     max_num_iterations(50),
-    parameter_tolerance(1e-16),
-    function_tolerance(1e-16) {
+    expected_average_symmetric_distance(1e-16) {
 }
 
+namespace {
+// Cost functor which computes symmetric epipolar distance
+// used for fundamental matrix refinement.
 class FundamentalSymmetricEpipolarCostFunctor {
  public:
   FundamentalSymmetricEpipolarCostFunctor(const Vec2 &x,
@@ -445,21 +448,60 @@ class FundamentalSymmetricEpipolarCostFunctor {
   const Mat y_;
 };
 
+// Termination checking callback used for fundamental estimation.
+// It finished the minimization as soon as actual average of
+// symmetric epipolar distance is less or equal to the expected
+// average value.
+class TerminationCheckingCallback : public ceres::IterationCallback {
+ public:
+  TerminationCheckingCallback(const Mat &x1, const Mat &x2,
+                              const EstimateFundamentalOptions &options,
+                              Mat3 *F)
+      : options_(options), x1_(x1), x2_(x2), F_(F) {}
+
+  virtual ceres::CallbackReturnType operator()(
+      const ceres::IterationSummary& summary) {
+    // If the step wasn't successful, there's nothing to do.
+    if (!summary.step_is_successful) {
+      return ceres::SOLVER_CONTINUE;
+    }
+
+    // Calculate average of symmetric epipolar distance.
+    double average_distance = 0.0;
+    for (int i = 0; i < x1_.cols(); i++) {
+      average_distance = SymmetricEpipolarDistance(*F_,
+                                                   x1_.col(i),
+                                                   x2_.col(i));
+    }
+    average_distance /= x1_.cols();
+
+    if (average_distance <= options_.expected_average_symmetric_distance) {
+      return ceres::SOLVER_TERMINATE_SUCCESSFULLY;
+    }
+
+    return ceres::SOLVER_CONTINUE;
+  }
+
+ private:
+  const EstimateFundamentalOptions &options_;
+  const Mat &x1_;
+  const Mat &x2_;
+  Mat3 *F_;
+};
+}  // namespace
+
 /* Fundamental transformation estimation. */
-bool FundamentalFromCorrespondencesEuc(
+bool EstimateFundamentalFromCorrespondences(
     const Mat &x1,
     const Mat &x2,
-    const FundamentalEstimationOptions &options,
+    const EstimateFundamentalOptions &options,
     Mat3 *F) {
   // Step 1: Algebraic fundamental estimation.
-  bool algebraic_success = NormalizedEightPointSolver(x1, x2, F);
 
-  LG << "Algebraic result " << algebraic_success
-     << ", estimated matrix:\n" << *F;
+  // Assume algebraic estiation always succeeds,
+  NormalizedEightPointSolver(x1, x2, F);
 
-  if (!algebraic_success && !options.use_refine_if_algebraic_fails) {
-    return false;
-  }
+  LG << "Estimated matrix after algebraic estimation:\n" << *F;
 
   // Step 2: Refine matrix using Ceres minimizer.
   ceres::Problem problem;
@@ -483,8 +525,10 @@ bool FundamentalFromCorrespondencesEuc(
   solver_options.linear_solver_type = ceres::DENSE_QR;
   solver_options.max_num_iterations = options.max_num_iterations;
   solver_options.update_state_every_iteration = true;
-  solver_options.parameter_tolerance = options.parameter_tolerance;
-  solver_options.function_tolerance = options.function_tolerance;
+
+  // Terminate if the average symmetric distance is good enough.
+  TerminationCheckingCallback callback(x1, x2, options, F);
+  solver_options.callbacks.push_back(&callback);
 
   // Run the solve.
   ceres::Solver::Summary summary;
@@ -495,7 +539,8 @@ bool FundamentalFromCorrespondencesEuc(
   LG << "Final refined matrix:\n" << *F;
 
   return !(summary.termination_type == ceres::DID_NOT_RUN ||
-           summary.termination_type == ceres::NUMERICAL_FAILURE);
+           summary.termination_type == ceres::NUMERICAL_FAILURE ||
+           summary.termination_type == ceres::USER_ABORT);
 }
 
 }  // namespace libmv
diff --git a/extern/libmv/libmv/multiview/fundamental.h b/extern/libmv/libmv/multiview/fundamental.h
index 2961a46..51067ae 100644
--- a/extern/libmv/libmv/multiview/fundamental.h
+++ b/extern/libmv/libmv/multiview/fundamental.h
@@ -151,21 +151,22 @@ void FundamentalToEssential(const Mat3 &F, Mat3 *E);
  * Defaults should be suitable for a wide range of use cases, but
  * better performance and accuracy might require tweaking/
  */
-struct FundamentalEstimationOptions {
+struct EstimateFundamentalOptions {
   // Default constructor which sets up a options for generic usage.
-  FundamentalEstimationOptions(void);
-
-  // Refine fundamental matrix even if algebraic estimation reported failure.
-  bool use_refine_if_algebraic_fails;
+  EstimateFundamentalOptions(void);
 
   // Maximal number of iterations for refinement step.
   int max_num_iterations;
 
-  // Paramaneter tolerance used by minimizer termination criteria.
-  float parameter_tolerance;
-
-  // Function tolerance used  by minimizer termination criteria.
-  float function_tolerance;
+  // Expected average of symmetric epipolar distance between
+  // actual destination points and original ones transformed by
+  // estimated fundamental matrix.
+  //
+  // Refinement will finish as soon as average of symmetric
+  // epipolar distance is less or equal to this value.
+  //
+  // This distance is measured in the same units as input points are.
+  double expected_average_symmetric_distance;
 };
 
 /**
@@ -175,10 +176,10 @@ struct FundamentalEstimationOptions {
  * correspondences by doing algebraic estimation first followed with result
  * refinement.
  */
-bool FundamentalFromCorrespondencesEuc(
+bool EstimateFundamentalFromCorrespondences(
     const Mat &x1,
     const Mat &x2,
-    const FundamentalEstimationOptions &options,
+    const EstimateFundamentalOptions &options,
     Mat3 *F);
 
 }  // namespace libmv
diff --git a/extern/libmv/libmv/multiview/homography.cc b/extern/libmv/libmv/multiview/homography.cc
index 6041849..cbd3ec7 100644
--- a/extern/libmv/libmv/multiview/homography.cc
+++ b/extern/libmv/libmv/multiview/homography.cc
@@ -22,6 +22,7 @@
 
 #include "ceres/ceres.h"
 #include "libmv/logging/logging.h"
+#include "libmv/multiview/conditioning.h"
 #include "libmv/multiview/homography_parameterization.h"
 
 namespace libmv {
@@ -155,14 +156,26 @@ bool Homography2DFromCorrespondencesLinear(const Mat &x1,
   }
 }
 
-HomographyEstimationOptions::HomographyEstimationOptions(void) :
-    expected_algebraic_precision(EigenDouble::dummy_precision()),
-    use_refine_if_algebraic_fails(true),
+// Default settings for homography estimation which should be suitable
+// for a wide range of use cases.
+EstimateHomographyOptions::EstimateHomographyOptions(void) :
+    use_normalization(true),
     max_num_iterations(50),
-    parameter_tolerance(1e-16),
-    function_tolerance(1e-16) {
+    expected_average_symmetric_distance(1e-16) {
 }
 
+namespace {
+void GetNormalizedPoints(const Mat &original_points,
+                         Mat *normalized_points,
+                         Mat3 *normalization_matrix) {
+  IsotropicPreconditionerFromPoints(original_points, normalization_matrix);
+  ApplyTransformationToPoints(original_points,
+                              *normalization_matrix,
+                              normalized_points);
+}
+
+// Cost functor which computes symmetric geometric distance
+// used for homography matrix refinement.
 class HomographySymmetricGeometricCostFunctor {
  public:
   HomographySymmetricGeometricCostFunctor(const Vec2 &x,
@@ -200,13 +213,55 @@ class HomographySymmetricGeometricCostFunctor {
   const Vec2 y_;
 };
 
+// Termination checking callback used for homography estimation.
+// It finished the minimization as soon as actual average of
+// symmetric geometric distance is less or equal to the expected
+// average value.
+class TerminationCheckingCallback : public ceres::IterationCallback {
+ public:
+  TerminationCheckingCallback(const Mat &x1, const Mat &x2,
+                              const EstimateHomographyOptions &options,
+                              Mat3 *H)
+      : options_(options), x1_(x1), x2_(x2), H_(H) {}
+
+  virtual ceres::CallbackReturnType operator()(
+      const ceres::IterationSummary& summary) {
+    // If the step wasn't successful, there's nothing to do.
+    if (!summary.step_is_successful) {
+      return ceres::SOLVER_CONTINUE;
+    }
+
+    // Calculate average of symmetric geometric distance.
+    double average_distance = 0.0;
+    for (int i = 0; i < x1_.cols(); i++) {
+      average_distance = SymmetricGeometricDistance(*H_,
+                                                    x1_.col(i),
+                                                    x2_.col(i));
+    }
+    average_distance /= x1_.cols();
+
+    if (average_distance <= options_.expected_average_symmetric_distance) {
+      return ceres::SOLVER_TERMINATE_SUCCESSFULLY;
+    }
+
+    return ceres::SOLVER_CONTINUE;
+  }
+
+ private:
+  const EstimateHomographyOptions &options_;
+  const Mat &x1_;
+  const Mat &x2_;
+  Mat3 *H_;
+};
+}  // namespace
+
 /** 2D Homography transformation estimation in the case that points are in
  * 

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list