|
@@ -11,6 +11,7 @@
|
|
|
|
|
|
|
|
#include <glm/ext/quaternion_geometric.hpp>
|
|
#include <glm/ext/quaternion_geometric.hpp>
|
|
|
#include <glm/gtc/matrix_access.hpp>
|
|
#include <glm/gtc/matrix_access.hpp>
|
|
|
|
|
+#include <glm/gtx/string_cast.hpp>
|
|
|
|
|
|
|
|
#define NANOVG_GL3_IMPLEMENTATION
|
|
#define NANOVG_GL3_IMPLEMENTATION
|
|
|
|
|
|
|
@@ -159,13 +160,67 @@ namespace camera_calibrator_impl {
|
|
|
ref_t_vec.push_back(to_cv_mat(t_vec));
|
|
ref_t_vec.push_back(to_cv_mat(t_vec));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- cv::Mat ret_r, ret_t;
|
|
|
|
|
- cv::calibrateRobotWorldHandEye(
|
|
|
|
|
- cam_r_vec, cam_t_vec, // chess board in camera
|
|
|
|
|
- ref_r_vec, ref_t_vec, // tracker in reference
|
|
|
|
|
- aux_r, aux_t, // tracker in chess board
|
|
|
|
|
- ret_r, ret_t); // reference in camera
|
|
|
|
|
- return to_transform_mat(to_vec3(ret_t), to_mat3(ret_r));
|
|
|
|
|
|
|
+ auto inv_op = [](const cv::Mat &r, const cv::Mat &t) {
|
|
|
|
|
+ return glm::inverse(to_transform_mat(to_vec3(t), to_mat3(r)));
|
|
|
|
|
+ };
|
|
|
|
|
+ auto mat_A = std::vector<glm::mat4>(); // reference in tracker
|
|
|
|
|
+ auto mat_B = std::vector<glm::mat4>(); // camera in chess board
|
|
|
|
|
+ std::ranges::transform(ref_r_vec, ref_t_vec, std::back_inserter(mat_A), inv_op);
|
|
|
|
|
+ std::ranges::transform(cam_r_vec, cam_t_vec, std::back_inserter(mat_B), inv_op);
|
|
|
|
|
+
|
|
|
|
|
+ // weighted shah algorithm
|
|
|
|
|
+ using namespace Eigen;
|
|
|
|
|
+ using mat_T_type = Matrix<float, 9, 9>;
|
|
|
|
|
+ auto mat_T = mat_T_type();
|
|
|
|
|
+ mat_T.setZero();
|
|
|
|
|
+ for (auto k = 0; k < sample_num; ++k) {
|
|
|
|
|
+ auto Ra = glm::mat3(mat_A[k]), Rb = glm::mat3(mat_B[k]);
|
|
|
|
|
+ mat_T += sample_w[k] * kron_product(to_eigen(Ra), to_eigen(Rb));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JacobiSVD<mat_T_type> svdT(mat_T, ComputeFullU | ComputeFullV);
|
|
|
|
|
+ auto x = svdT.matrixV().col(0), y = svdT.matrixU().col(0);
|
|
|
|
|
+ auto shah_solve_op = [](Matrix3f x) -> Matrix3f {
|
|
|
|
|
+ auto x_det = x.determinant();
|
|
|
|
|
+ x *= (x_det >= 0 ? 1.f : -1.f)
|
|
|
|
|
+ / std::pow(std::abs(x_det), 1.f / 3.f);
|
|
|
|
|
+ JacobiSVD <Matrix3f> svdX(x, ComputeFullU | ComputeFullV);
|
|
|
|
|
+ x = svdX.matrixU() * svdX.matrixV().transpose();
|
|
|
|
|
+ return x.transpose();
|
|
|
|
|
+ };
|
|
|
|
|
+ auto Rx = shah_solve_op(Map<const Matrix3f>(x.data()));
|
|
|
|
|
+ auto Ry = shah_solve_op(Map<const Matrix3f>(y.data()));
|
|
|
|
|
+
|
|
|
|
|
+ using mat_A_type = Matrix<float, Dynamic, 6>;
|
|
|
|
|
+ mat_A_type A = MatrixXf::Zero(3 * sample_num, 6);
|
|
|
|
|
+ VectorXf b = VectorXf::Zero(3 * sample_num);
|
|
|
|
|
+ for (int k = 0; k < sample_num; ++k) {
|
|
|
|
|
+ auto ta = to_eigen(to_translation(mat_A[k]));
|
|
|
|
|
+ auto tb = to_eigen(to_translation(mat_B[k]));
|
|
|
|
|
+ A.block<3, 3>(3 * k, 0) = sample_w[k] * -to_eigen(glm::mat3(mat_A[k]));
|
|
|
|
|
+ A.block<3, 3>(3 * k, 3) = sample_w[k] * Matrix3f::Identity();
|
|
|
|
|
+ b.segment<3>(3 * k) = sample_w[k] * (ta - Ry * tb);
|
|
|
|
|
+ }
|
|
|
|
|
+ using vec_t_type = Vector<float, 6>;
|
|
|
|
|
+ vec_t_type t = A.colPivHouseholderQr().solve(b);
|
|
|
|
|
+ Matrix4f X, Y;
|
|
|
|
|
+ X << Rx, t.segment<3>(0), 0, 0, 0, 1; // camera in reference
|
|
|
|
|
+ Y << Ry, t.segment<3>(3), 0, 0, 0, 1; // chess board in tracker
|
|
|
|
|
+
|
|
|
|
|
+ auto aux_mat = glm::inverse(to_mat4(Y));
|
|
|
|
|
+ aux_r = to_cv_mat(glm::mat3(aux_mat));
|
|
|
|
|
+ aux_t = to_cv_mat(to_translation(aux_mat));
|
|
|
|
|
+// SPDLOG_DEBUG("Shah: {}", glm::to_string(aux_mat));
|
|
|
|
|
+ return glm::inverse(to_mat4(X));
|
|
|
|
|
+
|
|
|
|
|
+// cv::Mat ret_r, ret_t;
|
|
|
|
|
+// cv::calibrateRobotWorldHandEye(
|
|
|
|
|
+// cam_r_vec, cam_t_vec, // chess board in camera
|
|
|
|
|
+// ref_r_vec, ref_t_vec, // tracker in reference
|
|
|
|
|
+// aux_r, aux_t, // tracker in chess board
|
|
|
|
|
+// ret_r, ret_t); // reference in camera
|
|
|
|
|
+// SPDLOG_DEBUG("OpenCV: {}", glm::to_string(to_transform_mat(to_vec3(aux_t), to_mat3(aux_r))));
|
|
|
|
|
+// return to_transform_mat(to_vec3(ret_t), to_mat3(ret_r));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
cv::Scalar hand_eye_calib::evaluate_hand_eye(const glm::mat4 &ret_mat) {
|
|
cv::Scalar hand_eye_calib::evaluate_hand_eye(const glm::mat4 &ret_mat) {
|
|
@@ -205,7 +260,19 @@ namespace camera_calibrator_impl {
|
|
|
err_mat.at<cv::Vec2f>(j, i)[1] = err_2d;
|
|
err_mat.at<cv::Vec2f>(j, i)[1] = err_2d;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- return cv::mean(err_mat);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // calculate weighted average error
|
|
|
|
|
+ cv::Scalar err_sum = {};
|
|
|
|
|
+ float w_sum = 0.f;
|
|
|
|
|
+ for (auto j = 0; j < sample_num; ++j) {
|
|
|
|
|
+ for (auto i = 0; i < corner_num; ++i) {
|
|
|
|
|
+ auto err = sample_w[j] * err_mat.at<cv::Vec2f>(j, i);
|
|
|
|
|
+ err_sum[0] += err[0];
|
|
|
|
|
+ err_sum[1] += err[1];
|
|
|
|
|
+ }
|
|
|
|
|
+ w_sum += sample_w[j] * corner_num;
|
|
|
|
|
+ }
|
|
|
|
|
+ return err_sum / w_sum;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
hand_eye_calib::camera_calib_result
|
|
hand_eye_calib::camera_calib_result
|
|
@@ -286,6 +353,24 @@ namespace camera_calibrator_impl {
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ void hand_eye_calib::calc_sample_weights() {
|
|
|
|
|
+ auto cam_p = std::vector<glm::vec3>();
|
|
|
|
|
+ auto cam_p_op = [](const cv::Mat &r, const cv::Mat &t) {
|
|
|
|
|
+ auto t_mat = to_transform_mat(to_vec3(t), to_mat3(r));
|
|
|
|
|
+ return to_translation(t_mat);
|
|
|
|
|
+ };
|
|
|
|
|
+ std::ranges::transform(cam_r_vec, cam_t_vec,
|
|
|
|
|
+ std::back_inserter(cam_p), cam_p_op);
|
|
|
|
|
+
|
|
|
|
|
+ sample_w.clear();
|
|
|
|
|
+ auto w_op = [&](glm::vec3 x) {
|
|
|
|
|
+ return 1.f / std::ranges::count_if(cam_p, [=](glm::vec3 y) {
|
|
|
|
|
+ return glm::distance(x, y) <= sample_neighbor_radius;
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+ std::ranges::transform(cam_p, std::back_inserter(sample_w), w_op);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
void hand_eye_calib::calc() {
|
|
void hand_eye_calib::calc() {
|
|
|
sample_num = img_pool.size();
|
|
sample_num = img_pool.size();
|
|
|
corner_num = img_pool.begin()->corner.size();
|
|
corner_num = img_pool.begin()->corner.size();
|
|
@@ -341,6 +426,7 @@ namespace camera_calibrator_impl {
|
|
|
cam_r_vec.push_back(cam_r);
|
|
cam_r_vec.push_back(cam_r);
|
|
|
cam_t_vec.push_back(cam_t);
|
|
cam_t_vec.push_back(cam_t);
|
|
|
}
|
|
}
|
|
|
|
|
+ calc_sample_weights();
|
|
|
|
|
|
|
|
// find minimal error using trichotomy algorithm
|
|
// find minimal error using trichotomy algorithm
|
|
|
auto offset_l = -128000; // -128ms
|
|
auto offset_l = -128000; // -128ms
|
|
@@ -450,11 +536,13 @@ void camera_calibrator::impl::process() {
|
|
|
conf.sophiar_conn->update_transform_variable(
|
|
conf.sophiar_conn->update_transform_variable(
|
|
|
conf.result_transform_var, ret);
|
|
conf.result_transform_var, ret);
|
|
|
}
|
|
}
|
|
|
- conf.cb_func(result_type{
|
|
|
|
|
- .cam_in = calib->intrinsic_v0(),
|
|
|
|
|
- .transform = calib->result_mat,
|
|
|
|
|
- .time_offset = calib->result_ts_offset,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ if (conf.cb_func) {
|
|
|
|
|
+ conf.cb_func(result_type{
|
|
|
|
|
+ .cam_in = calib->intrinsic_v0(),
|
|
|
|
|
+ .transform = calib->result_mat,
|
|
|
|
|
+ .time_offset = calib->result_ts_offset,
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void camera_calibrator::impl::simulate_process(const simulate_info_type &info) {
|
|
void camera_calibrator::impl::simulate_process(const simulate_info_type &info) {
|