Fix #145185: Crash while solving camera motion

The crash happens in specific cases when doing euclidean resection
in cases when first 3 3D estimates are correlated. While it is a bit
cryptic on user level, what it could mean is:

  - Either there are overlapping markers
  - Or the optical system is highly out of calibration.

Either of these cases is not the primary goal to support with camera
solvers in Libmv for the time being, so while this change avoids the
crash there is possibility to make algorithm more robustly handle the
tracking data and potentially give better result.

Such correlation via some intermediate steps leads to the algorithm
attempting to perform SVD decomposition of a matrix with NaN values
in it.

The exact reason of the crash is not super clear as it happens deep
in the JacobiSVD decomposition code in Eigen and caused by invalid
index somewhere. It might be a bug in Eigen which doesn't do range
checking, hoping that the math does not fail in cases like NaN and
inf.

Pull Request: https://projects.blender.org/blender/blender/pulls/145301
This commit is contained in:
Sergey Sharybin
2025-08-28 11:15:31 +02:00
committed by Sergey Sharybin
parent 20a19c7aa4
commit 1d88e19c32

View File

@@ -377,7 +377,7 @@ static void SelectControlPoints(const Mat3X& X_world,
}
// Computes the barycentric coordinates for all real points
static void ComputeBarycentricCoordinates(const Mat3X& X_world_centered,
static bool ComputeBarycentricCoordinates(const Mat3X& X_world_centered,
const Mat34& X_control_points,
Mat4X* alphas) {
size_t num_points = X_world_centered.cols();
@@ -386,7 +386,21 @@ static void ComputeBarycentricCoordinates(const Mat3X& X_world_centered,
C2.col(c - 1) = X_control_points.col(c) - X_control_points.col(0);
}
Mat3 C2inv = C2.inverse();
// The selected basis might have correlated vectors causing inverse to produce
// undefined result which could lead to issues later on in SVD decomposition.
//
// Evidently, when built with MSVC it could lead to crash in SVD when the
// input contains NaN values:
// https://projects.blender.org/blender/blender/issues/145185
//
// TODO(sergey): Look into choosing a different basis in these cases.
bool is_invertible;
Mat3 C2inv;
C2.computeInverseWithCheck(C2inv, is_invertible);
if (!is_invertible) {
return false;
}
Mat3X a = C2inv * X_world_centered;
alphas->resize(4, num_points);
@@ -395,6 +409,8 @@ static void ComputeBarycentricCoordinates(const Mat3X& X_world_centered,
for (size_t c = 0; c < num_points; c++) {
(*alphas)(0, c) = 1.0 - alphas->col(c).sum();
}
return true;
}
// Estimates the coordinates of all real points in the camera coordinate frame
@@ -450,7 +466,9 @@ bool EuclideanResectionEPnP(const Mat2X& x_camera,
// Compute the barycentric coordinates.
Mat4X alphas(4, num_points);
ComputeBarycentricCoordinates(X_centered, X_control_points, &alphas);
if (!ComputeBarycentricCoordinates(X_centered, X_control_points, &alphas)) {
return false;
}
// Estimates the M matrix with the barycentric coordinates
Mat M(2 * num_points, 12);