9static auto zhang_bmtx(
const Eigen::VectorXd& b) -> Eigen::Matrix3d {
11 bmtx << b(0), b(1), b(3), b(1), b(2), b(4), b(3), b(4), b(5);
12 return 0.5 * (bmtx + bmtx.transpose());
17 const Eigen::Matrix3d bhat = kmtx.inverse().transpose() * kmtx.inverse();
18 if (bhat.allFinite()) {
21 if (std::abs(bhat(2, 2)) > 0) {
22 s = bmtx(2, 2) / bhat(2, 2);
24 const double rel_err = (bmtx - s * bhat).norm() / std::max(1e-12, bmtx.norm());
25 if (!std::isfinite(rel_err) || rel_err > 1e-6) {
26 std::cerr <<
"Zhang: B consistency warning, rel_err=" << rel_err <<
"\n";
35 std::cerr <<
"Zhang: dual-conic vector must have size 6, got " << bv.size() <<
"\n";
39 const auto try_factor = [](
const Eigen::Matrix3d& bmtx) -> std::optional<Eigen::Matrix3d> {
41 if (!bmtx.allFinite()) {
46 Eigen::LLT<Eigen::Matrix3d> llt(bmtx);
47 if (llt.info() != Eigen::Success) {
52 const Eigen::Matrix3d umtx = llt.matrixU();
55 Eigen::Matrix3d kmtx = umtx.inverse();
56 if (!kmtx.allFinite()) {
61 const double k22 = kmtx(2, 2);
62 if (std::abs(k22) < 1e-15) {
68 if (kmtx(0, 0) <= 0.0 || kmtx(1, 1) <= 0.0) {
81 if (
auto kmtx = try_factor(bmtx)) {
84 if (
auto kmtx = try_factor(-bmtx)) {
88 std::cerr <<
"Zhang: failed to recover K from dual conic (both signs).\n";
93static auto v_ij(
const Eigen::Matrix3d& hmtx,
int i,
int j) -> Eigen::Matrix<double, 1, 6> {
94 assert(0 <= i && i < 3 && 0 <= j && j < 3);
95 const double h0i = hmtx(0, i);
96 const double h1i = hmtx(1, i);
97 const double h2i = hmtx(2, i);
98 const double h0j = hmtx(0, j);
99 const double h1j = hmtx(1, j);
100 const double h2j = hmtx(2, j);
102 Eigen::Matrix<double, 1, 6> v;
103 v << h0i * h0j, h0i * h1j + h1i * h0j, h1i * h1j, h0i * h2j + h2i * h0j, h1i * h2j + h2i * h1j,
113 Eigen::Matrix3d hmtx_norm = hmtx;
114 if (!hmtx_norm.allFinite()) {
119 if (hmtx_norm(2, 2) < 0.0) {
120 hmtx_norm = -hmtx_norm;
124 const double h33 = hmtx_norm(2, 2);
125 if (std::abs(h33) > 1e-12) {
131 const double nf = hmtx_norm.norm();
140 -> std::optional<Eigen::MatrixXd> {
141 const int m =
static_cast<int>(hs.size());
143 std::cerr <<
"Zhang method requires at least 4 views\n";
147 Eigen::MatrixXd vmtx(2 * m, 6);
148 for (Eigen::Index k = 0; k < m; ++k) {
149 Eigen::Matrix3d hmtx =
normalize_hmtx(hs[
static_cast<size_t>(k)].hmtx);
150 Eigen::Matrix<double, 1, 6> v12 =
v_ij(hmtx, 0, 1);
151 Eigen::Matrix<double, 1, 6> v11 =
v_ij(hmtx, 0, 0);
152 Eigen::Matrix<double, 1, 6> v22 =
v_ij(hmtx, 1, 1);
154 auto normalize_row = [](Eigen::Matrix<double, 1, 6>& r) {
162 Eigen::Matrix<double, 1, 6> vr = v11 - v22;
165 vmtx.row(2 * k) = v12;
166 vmtx.row(2 * k + 1) = vr;
172 -> std::optional<CameraMatrix> {
174 if (!vmtx_opt.has_value()) {
175 std::cout <<
"Zhang design matrix creation failed.\n";
179 Eigen::JacobiSVD<Eigen::MatrixXd> svd(vmtx_opt.value(), Eigen::ComputeFullV);
182 Eigen::VectorXd bvec = svd.matrixV().col(5);
183 Eigen::VectorXd resid = vmtx_opt.value() * bvec;
184 double rms = std::sqrt(resid.squaredNorm() /
static_cast<double>(resid.size()));
186 std::cout <<
"Zhang warning: large residual in solving for b: " << rms <<
'\n';
188 std::cout <<
"Zhang: solved for b with residual " << rms <<
'\n';
193 if (!kmtx_opt.has_value()) {
194 std::cout <<
"Zhang kmtx_from_dual_conic failed for one sign, trying the other.\n";
197 if (!kmtx_opt.has_value()) {
198 std::cout <<
"Zhang kmtx_from_dual_conic failed for both signs.\n";
204 .fy = kmtx_opt.value()(1, 1),
205 .cx = kmtx_opt.value()(0, 2),
206 .cy = kmtx_opt.value()(1, 2),
207 .
skew = kmtx_opt.value()(0, 1)};