28 #ifdef USE_NUMERIC_JACOBIANS
50 ASSERT_(
p.check_squares_length_X_meters > 0);
51 ASSERT_(
p.check_squares_length_Y_meters > 0);
52 const bool user_wants_use_robust =
p.use_robust_kernel;
54 if (images.size() < 1)
56 std::cout <<
"ERROR: No input images." << std::endl;
60 const unsigned CORNERS_COUNT =
p.check_size_x *
p.check_size_y;
70 vector<size_t> valid_image_pair_indices;
74 for (
size_t i = 0; i < images.size(); i++)
78 bool corners_found[2] = {
false,
false};
80 for (
int lr = 0; lr < 2; lr++)
91 imgSize[lr] = img_gray.
getSize();
105 if (imgSize[lr].
y != (
int)img_gray.
getHeight() ||
106 imgSize[lr].
x != (
int)img_gray.
getWidth())
108 std::cout <<
"ERROR: All the images in each left/right "
109 "channel must have the same size."
123 if (corners_found[lr] &&
125 corners_found[lr] =
false;
129 "%s img #%u: %s\n", lr == 0 ?
"LEFT" :
"RIGHT",
130 static_cast<unsigned int>(i),
131 corners_found[lr] ?
"DETECTED" :
"NOT DETECTED");
140 (*
p.callback)(cbPars,
p.callback_user_param);
143 if (corners_found[lr])
160 if (corners_found[0] && corners_found[1])
162 valid_image_pair_indices.push_back(i);
174 bool has_to_redraw_corners =
false;
176 const TPixelCoordf pt_l0 = images[i].left.detected_corners[0],
177 pt_l1 = images[i].left.detected_corners[1],
178 pt_r0 = images[i].right.detected_corners[0],
179 pt_r1 = images[i].right.detected_corners[1];
187 if (Al.x * Ar.x + Al.y * Ar.y < 0)
189 has_to_redraw_corners =
true;
192 images[i].right.detected_corners.begin(),
193 images[i].right.detected_corners.end());
196 if (has_to_redraw_corners)
199 images[i].right.img_original.colorImage(
200 images[i].right.img_checkboard);
201 images[i].right.img_checkboard.drawChessboardCorners(
202 images[i].right.detected_corners, check_size.
x,
210 std::cout << valid_image_pair_indices.size()
211 <<
" valid image pairs." << std::endl;
212 if (valid_image_pair_indices.empty())
214 std::cout <<
"ERROR: No valid images. Perhaps the checkerboard "
223 vector<TPoint3D> obj_points;
224 obj_points.reserve(CORNERS_COUNT);
225 for (
unsigned int y = 0;
y <
p.check_size_y;
y++)
226 for (
unsigned int x = 0;
x <
p.check_size_x;
x++)
227 obj_points.push_back(
229 p.check_squares_length_X_meters *
x,
230 p.check_squares_length_Y_meters *
y, 0));
241 const size_t N = valid_image_pair_indices.size();
242 const size_t nObs = 2 * N * CORNERS_COUNT;
247 const double tau = 0.3;
248 const double t1 = 1e-8;
249 const double t2 = 1e-8;
250 const double MAX_LAMBDA = 1e20;
255 lm_stat_t lm_stat(images, valid_image_pair_indices, obj_points);
279 size_t nUnknownsCamParams = 0;
282 std::vector<size_t> vars_to_optimize;
286 for (
int calibRound = 0; calibRound < 2; calibRound++)
291 << ((calibRound == 0) ?
"LM calibration round #0: WITHOUT "
292 "distortion ----------\n"
293 :
"LM calibration round #1: ALL "
294 "parameters --------------\n");
300 vars_to_optimize.clear();
301 vars_to_optimize.push_back(0);
302 vars_to_optimize.push_back(1);
303 vars_to_optimize.push_back(2);
304 vars_to_optimize.push_back(3);
307 if (
p.optimize_k1) vars_to_optimize.push_back(4);
308 if (
p.optimize_k2) vars_to_optimize.push_back(5);
309 if (
p.optimize_t1) vars_to_optimize.push_back(6);
310 if (
p.optimize_t2) vars_to_optimize.push_back(7);
311 if (
p.optimize_k3) vars_to_optimize.push_back(8);
314 nUnknownsCamParams = vars_to_optimize.size();
320 const size_t nUnknowns = N * 6 + 6 + 2 * nUnknownsCamParams;
325 lm_stat, res_jacob,
false ,
326 p.robust_kernel_param);
329 Eigen::VectorXd minus_g;
335 double lambda = tau * H.diagonal().array().maxCoeff();
336 bool done = (minus_g.array().abs().maxCoeff() < t1);
337 int numItersImproving = 0;
338 bool use_robust =
false;
342 while (iter <
p.maxIters && !done)
349 (*
p.callback)(cbPars,
p.callback_user_param);
353 Eigen::MatrixXd HH = H;
354 for (
size_t i = 0; i < nUnknowns; i++) HH(i, i) += lambda;
357 Eigen::LLT<Eigen::MatrixXd> llt(
358 HH.selfadjointView<Eigen::Lower>());
359 if (llt.info() != Eigen::Success)
363 done = (lambda > MAX_LAMBDA);
365 if (
p.verbose && !done)
366 cout <<
"LM iter#" << iter
367 <<
": Couldn't solve LLt, retrying with larger "
372 const Eigen::VectorXd
eps = llt.solve(minus_g);
374 const double eps_norm =
eps.norm();
375 if (eps_norm < t2 * (eps_norm + t2))
378 cout <<
"Termination criterion: eps_norm < "
380 << eps_norm <<
" < " << t2 * (eps_norm + t2)
395 new_lm_stat, new_res_jacob, use_robust,
396 p.robust_kernel_param);
403 if (numItersImproving++ > 5)
404 use_robust = user_wants_use_robust;
408 cout <<
"LM iter#" << iter
409 <<
": Avr.err(px):" << std::sqrt(err / nObs)
410 <<
"->" << std::sqrt(err_new / nObs)
411 <<
" lambda:" << lambda << endl;
414 lm_stat.
swap(new_lm_stat);
415 res_jacob.swap(new_res_jacob);
419 res_jacob, vars_to_optimize, minus_g, H);
422 done = (minus_g.array().abs().maxCoeff() < t1);
425 lambda = std::max(lambda, 1e-100);
434 cout <<
"LM iter#" << iter <<
": No update: err=" << err
435 <<
" -> err_new=" << err_new
436 <<
" retrying with larger lambda=" << lambda
439 done = (lambda > MAX_LAMBDA);
483 for (
size_t i = 0; i < valid_image_pair_indices.size(); i++)
494 const size_t base_idx_H_CPs = H.cols() - 2 * nUnknownsCamParams;
495 for (
size_t i = 0; i < nUnknownsCamParams; i++)
498 H(base_idx_H_CPs + i, base_idx_H_CPs + i);
500 H(base_idx_H_CPs + nUnknownsCamParams + i,
501 base_idx_H_CPs + nUnknownsCamParams + i);
505 for (
size_t i = 0; i < valid_image_pair_indices.size(); i++)
519 const size_t idx = valid_image_pair_indices[i];
552 catch (std::exception& e)
554 std::cout << e.what() << std::endl;
574 Eigen::Matrix<double, 2, 3>&
G)
576 const double pz_ = 1 /
p.z;
577 const double pz_2 = pz_ * pz_;
581 G(0, 2) = -
p.x * pz_2;
585 G(1, 2) = -
p.y * pz_2;
633 const Eigen::Matrix<double, 9, 1>&
c,
634 Eigen::Matrix<double, 2, 2>& Hb, Eigen::Matrix<double, 2, 9>& Hc)
636 const double x = nP.
x / nP.
z;
637 const double y = nP.
y / nP.
z;
639 const double r2 =
x *
x +
y *
y;
640 const double r = std::sqrt(r2);
641 const double r6 = r2 * r2 * r2;
644 const double fx =
c[0], fy =
c[1];
646 const double k1 =
c[4], k2 =
c[5], k3 =
c[6];
647 const double t1 =
c[7], t2 =
c[8];
651 fx * (k2 * r2 + k3 * r6 + 6 * t2 *
x + 2 * t1 *
y +
652 x * (2 * k1 *
x + 4 * k2 *
x *
r + 6 * k3 *
x * r2) + k1 *
r + 1);
653 Hb(0, 1) = fx * (2 * t1 *
x + 2 * t2 *
y +
654 x * (2 * k1 *
y + 4 * k2 *
y *
r + 6 * k3 *
y * r2));
656 Hb(1, 0) = fy * (2 * t1 *
x + 2 * t2 *
y +
657 y * (2 * k1 *
x + 4 * k2 *
x *
r + 6 * k3 *
x * r2));
659 fy * (k2 * r2 + k3 * r6 + 2 * t2 *
x + 6 * t1 *
y +
660 y * (2 * k1 *
y + 4 * k2 *
y *
r + 6 * k3 *
y * r2) + k1 *
r + 1);
663 Hc(0, 0) = t2 * (3 *
x *
x +
y *
y) +
x * (k2 * r2 + k3 * r6 + k1 *
r + 1) +
668 Hc(0, 4) = fx *
x *
r;
669 Hc(0, 5) = fx *
x * r2;
670 Hc(0, 6) = fx *
x * r6;
671 Hc(0, 7) = 2 * fx *
x *
y;
672 Hc(0, 8) = fx * (3 *
x *
x +
y *
y);
675 Hc(1, 1) = t1 * (
x *
x + 3 *
y *
y) +
y * (k2 * r2 + k3 * r6 + k1 *
r + 1) +
679 Hc(1, 4) = fy *
y *
r;
680 Hc(1, 5) = fy *
y * r2;
681 Hc(1, 6) = fy *
y * r6;
682 Hc(1, 7) = fy * (
x *
x + 3 *
y *
y);
683 Hc(1, 8) = 2 * fy *
x *
y;
688 Eigen::Matrix<double, 3, 6>& dpl_del)
692 dpl_del.block<3, 3>(0, 0).setIdentity();
698 Eigen::Matrix<double, 3, 6>& dp_deps)
702 const Eigen::Matrix<double, 1, 3> P(
p.x,
p.y,
p.z);
703 const Eigen::Matrix<double, 1, 3> dr1 =
705 const Eigen::Matrix<double, 1, 3> dr2 =
707 const Eigen::Matrix<double, 1, 3> dr3 =
710 const Eigen::Matrix<double, 1, 3>
v(
711 P.dot(dr1) + D.
x(), P.dot(dr2) + D.
y(), P.dot(dr3) + D.z());
713 Eigen::Matrix<double, 3, 6> H;
714 H.block<3, 3>(0, 0).setIdentity();
729 const double x = nP.
x / nP.
z;
730 const double y = nP.
y / nP.
z;
734 const double r4 =
square(r2);
735 const double r6 = r2 * r4;
738 const double B = 2 *
x *
y;
756 Eigen::VectorXd& minus_g, Eigen::MatrixXd& H)
758 const size_t N = res_jac.size();
759 const size_t nMaxUnknowns = N * 6 + 6 + 9 + 9;
762 Eigen::VectorXd minus_g_tot = Eigen::VectorXd::Zero(nMaxUnknowns);
763 Eigen::MatrixXd H_tot = Eigen::MatrixXd::Zero(nMaxUnknowns, nMaxUnknowns);
766 for (
size_t i = 0; i < N; i++)
768 const size_t nObs = res_jac[i].size();
769 for (
size_t j = 0; j < nObs; j++)
775 const Eigen::Matrix<double, 30, 30> Hij = rje.
J.transpose() * rje.
J;
776 const Eigen::Matrix<double, 30, 1> gij =
780 minus_g_tot.block<6, 1>(i * 6, 0) -= gij.block<6, 1>(0, 0);
781 minus_g_tot.block<6 + 9 + 9, 1>(N * 6, 0) -=
782 gij.block<6 + 9 + 9, 1>(6, 0);
784 H_tot.block<6, 6>(i * 6, i * 6) += Hij.block<6, 6>(0, 0);
786 H_tot.block<6 + 9 + 9, 6 + 9 + 9>(N * 6, N * 6) +=
787 Hij.block<6 + 9 + 9, 6 + 9 + 9>(6, 6);
788 H_tot.block<6, 6 + 9 + 9>(i * 6, N * 6) +=
789 Hij.block<6, 6 + 9 + 9>(0, 6);
790 H_tot.block<6 + 9 + 9, 6>(N * 6, i * 6) +=
791 Hij.block<6 + 9 + 9, 6>(6, 0);
799 const double cost_lr_angular = 1e10;
800 H_tot.block<3,3>(N*6+3,N*6+3) += Eigen::Matrix<double,3,3>::Identity() * cost_lr_angular;
806 const size_t N_Cs = var_indxs.size();
807 const size_t nUnknowns = N * 6 + 6 + 2 * N_Cs;
808 const size_t nUnkPoses = N * 6 + 6;
810 minus_g.setZero(nUnknowns);
811 H.setZero(nUnknowns, nUnknowns);
813 minus_g.block(0, 0, nUnkPoses, 1) = minus_g_tot.block(0, 0, nUnkPoses, 1);
814 H.block(0, 0, nUnkPoses, nUnkPoses) =
815 H_tot.block(0, 0, nUnkPoses, nUnkPoses);
818 for (
size_t i = 0; i < N_Cs; i++)
820 minus_g[nUnkPoses + i] = minus_g_tot[nUnkPoses + var_indxs[i]];
821 minus_g[nUnkPoses + N_Cs + i] =
822 minus_g_tot[nUnkPoses + 9 + var_indxs[i]];
825 for (
size_t k = 0; k < nUnkPoses; k++)
827 for (
size_t i = 0; i < N_Cs; i++)
829 H(nUnkPoses + i, k) = H(k, nUnkPoses + i) =
830 H_tot(k, nUnkPoses + var_indxs[i]);
831 H(nUnkPoses + i + N_Cs, k) = H(k, nUnkPoses + i + N_Cs) =
832 H_tot(k, nUnkPoses + 9 + var_indxs[i]);
836 for (
size_t i = 0; i < N_Cs; i++)
838 for (
size_t j = 0; j < N_Cs; j++)
840 H(nUnkPoses + i, nUnkPoses + j) =
841 H_tot(nUnkPoses + var_indxs[i], nUnkPoses + var_indxs[j]);
842 H(nUnkPoses + N_Cs + i, nUnkPoses + N_Cs + j) = H_tot(
843 nUnkPoses + 9 + var_indxs[i], nUnkPoses + 9 + var_indxs[j]);
851 M1.saveToTextFile(
"H1.txt");
852 g1.saveToTextFile(
"g1.txt");
856 M2.saveToTextFile(
"H2.txt");
857 g2.saveToTextFile(
"g2.txt");
869 const Eigen::VectorXd&
eps,
const std::vector<size_t>& var_indxs,
874 for (
size_t i = 0; i < N; i++)
881 const CPose3D incrPose = CPose3D::exp(incr);
892 const CPose3D incrPose = CPose3D::exp(incr);
900 const size_t idx = 6 * N + 6;
903 for (
size_t i = 0; i < nPC; i++)
911 #ifdef USE_NUMERIC_JACOBIANS
921 const Eigen::Matrix<double, 4, 1>& real_obs;
926 const Eigen::Matrix<double, 4, 1>& _real_obs)
928 obj_point(_obj_point),
930 right2left(_right2left),
936 void numeric_jacob_eval_function(
941 const CPose3D incrPose_l = CPose3D::exp(incr_l);
943 const CPose3D incrPose_rl = CPose3D::exp(incr_rl);
972 dat.obj_point, cam_params.
leftCamera, incrPose_l + dat.left_cam, px_l);
976 incrPose_rl + dat.right2left + incrPose_l + dat.left_cam, px_r);
978 const Eigen::Matrix<double, 4, 1> predicted_obs(
979 px_l.
x, px_l.
y, px_r.
x, px_r.
y);
989 const double x = X[0],
y = X[1];
991 const double r4 =
square(r2);
992 const double r6 = r2 * r4;
995 const double B = 2 *
x *
y;
1016 const CPose3D incrPose = CPose3D::exp(incr);
1019 for (
int i = 0; i < 3; i++) out[i] = D_p_out[i];
1022 struct TEvalData_A_eps_D_p
1028 void eval_dA_eps_D_p(
1033 const CPose3D incrPose = CPose3D::exp(incr);
1034 const CPose3D A_eps_D = dat.A + (incrPose + dat.D);
1037 for (
int i = 0; i < 3; i++) out[i] = pt[i];
1048 bool use_robust_kernel,
double kernel_param)
1050 double total_err = 0;
1078 for (
size_t k = 0; k < N; k++)
1081 const size_t nPts = lm_stat.
obj_points.size();
1082 res_jac[k].resize(nPts);
1084 for (
size_t i = 0; i < nPts; i++)
1088 lm_stat.
images[k_idx].left.detected_corners[i];
1090 lm_stat.
images[k_idx].right.detected_corners[i];
1091 const Eigen::Matrix<double, 4, 1> obs(
1092 px_obs_l.
x, px_obs_l.
y, px_obs_r.
x, px_obs_r.
y);
1106 Eigen::Matrix<double, 4, 1>(px_l.
x, px_l.
y, px_r.
x, px_r.
y);
1112 const double err_sqr_norm = rje.
residual.squaredNorm();
1113 if (use_robust_kernel)
1116 rk.param_sq = kernel_param;
1118 double kernel_1st_deriv, kernel_2nd_deriv;
1119 const double scaled_err =
1120 rk.eval(err_sqr_norm, kernel_1st_deriv, kernel_2nd_deriv);
1123 total_err += scaled_err;
1126 total_err += err_sqr_norm;
1133 #if !defined(USE_NUMERIC_JACOBIANS) || defined(COMPARE_NUMERIC_JACOBIANS)
1135 Eigen::Matrix<double, 2, 6> dhl_del, dhr_del, dhr_der;
1136 Eigen::Matrix<double, 2, 9> dhl_dcl, dhr_dcr;
1139 TPoint3D pt_wrt_left, pt_wrt_right;
1143 .composePoint(lm_stat.
obj_points[i], pt_wrt_right);
1146 Eigen::Matrix<double, 2, 2> dhl_dbl, dhr_dbr;
1161 x_incrs.setConstant(1e-6);
1163 Eigen::Matrix<double,2,2> num_dhl_dbl, num_dhr_dbr;
1171 cout <<
"num_dhl_dbl:\n" << num_dhl_dbl <<
"\ndiff dhl_dbl:\n" << dhl_dbl-num_dhl_dbl << endl << endl;
1172 cout <<
"num_dhr_dbr:\n" << num_dhr_dbr <<
"\ndiff dhr_dbr:\n" << dhr_dbr-num_dhr_dbr << endl << endl;
1177 Eigen::Matrix<double, 2, 3> dbl_dpl, dbr_dpr;
1185 x0[0]=pt_wrt_left.
x;
1186 x0[1]=pt_wrt_left.
y;
1187 x0[2]=pt_wrt_left.
z;
1190 x_incrs.setConstant(1e-8);
1192 Eigen::Matrix<double,2,3> num_dbl_dpl, num_dbr_dpr;
1196 x0[0]=pt_wrt_right.
x;
1197 x0[1]=pt_wrt_right.
y;
1198 x0[2]=pt_wrt_right.
z;
1201 cout <<
"num_dbl_dpl:\n" << num_dbl_dpl <<
"\ndbl_dpl:\n" << dbl_dpl << endl << endl;
1202 cout <<
"num_dbr_dpr:\n" << num_dbr_dpr <<
"\ndbr_dpr:\n" << dbr_dpr << endl << endl;
1210 Eigen::Matrix<double, 3, 6> dpl_del, dpr_del, dpr_der;
1225 x_incrs.setConstant(1e-8);
1227 Eigen::Matrix<double,3,6> num_dpl_del, num_dpr_der;
1231 cout <<
"num_dpl_del:\n" << num_dpl_del <<
"\ndiff dpl_del:\n" << dpl_del-num_dpl_del << endl << endl;
1232 cout <<
"num_dpr_der:\n" << num_dpr_der <<
"\ndiff dpr_der:\n" << dpr_der-num_dpr_der << endl << endl;
1244 x_incrs.setConstant(1e-8);
1246 TEvalData_A_eps_D_p dat;
1251 Eigen::Matrix<double,3,6> num_dpr_del;
1254 cout <<
"num_dpr_del:\n" << num_dpr_del <<
"\ndiff dpr_del:\n" << num_dpr_del-dpr_del << endl << endl;
1259 dhl_del = dhl_dbl * dbl_dpl * dpl_del;
1260 dhr_del = dhr_dbr * dbr_dpr * dpr_del;
1261 dhr_der = dhr_dbr * dbr_dpr * dpr_der;
1263 rje.
J.setZero(4, 30);
1264 rje.
J.block<2, 6>(0, 0) = dhl_del;
1265 rje.
J.block<2, 6>(2, 0) = dhr_del;
1266 rje.
J.block<2, 6>(2, 6) = dhr_der;
1267 rje.
J.block<2, 9>(0, 12) = dhl_dcl;
1268 rje.
J.block<2, 9>(2, 21) = dhr_dcr;
1270 #if defined(COMPARE_NUMERIC_JACOBIANS)
1271 const Eigen::Matrix<double, 4, 30> J_theor = rje.
J;
1276 #if defined(USE_NUMERIC_JACOBIANS) || defined(COMPARE_NUMERIC_JACOBIANS)
1285 const double x_incrs_val[30] = {
1286 1e-6, 1e-6, 1e-6, 1e-7, 1e-7, 1e-7,
1287 1e-6, 1e-6, 1e-6, 1e-7, 1e-7, 1e-7,
1288 1e-3, 1e-3, 1e-3, 1e-3, 1e-8, 1e-8, 1e-8, 1e-8, 1e-4,
1289 1e-3, 1e-3, 1e-3, 1e-3, 1e-8, 1e-8, 1e-8, 1e-8, 1e-4
1297 x0, &numeric_jacob_eval_function, x_incrs, dat, rje.
J);
1299 #if defined(COMPARE_NUMERIC_JACOBIANS)
1300 const Eigen::Matrix<double, 4, 30> J_num = rje.
J;
1302 #endif // ---- end of numeric Jacobians ----
1305 #if defined(COMPARE_NUMERIC_JACOBIANS)
1309 f.open(
"dbg.txt", ios_base::out | ios_base::app);
1315 << J_num - J_theor << endl
1316 <<
"diff (ratio):\n"
1317 << (J_num - J_theor).cwiseQuotient(J_num) << endl
1331 check_squares_length_X_meters(0.02),
1332 check_squares_length_Y_meters(0.02),
1333 normalize_image(true),
1334 skipDrawDetectedImgs(false),
1342 use_robust_kernel(false),
1343 robust_kernel_param(10),
1345 callback_user_param(nullptr)
1350 : final_rmse(0), final_iters(0), final_number_good_image_pairs(0)