【OpenCV 】对极几何标定质量验证

这篇具有很好参考价值的文章主要介绍了【OpenCV 】对极几何标定质量验证。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. computeCorrespondEpilines

评论区有大佬提到 computeCorrespondEpilines 这个接口的作用,在这个基础上又实现了一个新的版本如下,感谢-Focus-大佬@
computeCorrespondEpilines 是OpenCV中的一个函数,它用于计算对极几何中的极线。对极几何是指在两幅图像之间的几何关系,它描述了两个摄像机之间的空间关系,以及图像中的对应点之间的关系。computeCorrespondEpilines 的作用是计算给定图像中的点在另一幅图像中的极线。

其实现原理主要基于极线的几何性质,具体来说,对于图像中的一个点,它在另一幅图像中的极线可以通过对极约束来计算得到。
对极约束认为一对对应点在极线上的投影点满足极线方程,即 u’ * F * u = 0 或者 l’ * F = 0,其中 F 是基础矩阵,u 和 u’ 分别是两幅图像中的对应点的坐标,l’ 是点在另一幅图像中的极线的方程。

具体的数学公式推导略显复杂,在此不便详细展开,但其基本原理是基于对极约束的相关性质。

在OpenCV中,computeCorrespondEpilines 接口接受一组点和它们对应的图像,然后根据给定的基础矩阵计算这些点在另一幅图像中的极线。这样就可以用于对极几何的相关应用,比如立体视觉中的匹配和三维重建等。

#include <opencv2/opencv.hpp>

int main() {
    cv::Mat pts1 = (cv::Mat_<float>(3,2) << 30, 80, 100, 200, 150, 300);
    cv::Mat pts2 = (cv::Mat_<float>(3,2) << 50, 120, 120, 250, 180, 350);

    cv::Mat F = cv::findFundamentalMat(pts1, pts2, cv::FM_RANSAC, 3, 0.99);

    std::vector<cv::Vec<float, 3>> lines1, lines2;

    cv::computeCorrespondEpilines(pts2, 2, F, lines1);
    cv::computeCorrespondEpilines(pts1, 1, F, lines2);

    std::cout << "Epipolar lines in the first image corresponding to points in the second image:" << std::endl;
    for (int i = 0; i < lines1.size(); i++) {
        std::cout << lines1[i] << std::endl;
    }

    std::cout << "Epipolar lines in the second image corresponding to points in the first image:" << std::endl;
    for (int i = 0; i < lines2.size(); i++) {
        std::cout << lines2[i] << std::endl;
    }

    return 0;
}

2. 标定质量验证:

寻找一对对应点,已经知道对应关系及其详细坐标,根据对极几何推导实现文章来源地址https://www.toymoban.com/news/detail-662110.html

  ///get the camera intrinsics and T_Ci_B
  std::vector<Eigen::Matrix3d> M_K;
  std::vector<Eigen::Matrix4d> T_Ci_B;
  for (int i = 0; i < new_frames->size(); ++i) {
    auto frame = new_frames->frames_[i];
    auto project = cams_->getCameraShared(i);
    auto k = project->getIntrinsicParameters();
    Eigen::Matrix3d M_Ki;
    M_Ki << k(0), 0, k(2), 0, k(1), k(3), 0, 0, 1;
    M_K.push_back(M_Ki);

    Eigen::Matrix4d T_Ci_B_temp = cams_->get_T_C_B(i).getTransformationMatrix();
    T_Ci_B.emplace_back(T_Ci_B_temp);
  }
  ///get T_C0_Ci
  std::vector<Eigen::Matrix4d> T_C0_Ci;
  for (int i = 1; i < new_frames->size(); ++i) {
    Eigen::Matrix4d T_C0_Ci_temp = T_Ci_B[0].inverse() * T_Ci_B[i];
    T_C0_Ci.emplace_back(T_C0_Ci_temp);
  }
  std::cout<<"T_C0_Ci : "<<T_C0_Ci.size()<<std::endl;

  ///get t^R
  std::vector<Eigen::Matrix3d> R_C0_Ci;
  std::vector<Eigen::Matrix3d> t_transpose_R;
  for (int i = 0; i < T_C0_Ci.size(); ++i) {
    Eigen::Vector3d t_C0_Ci_temp;
    Eigen::Matrix3d R_C0_Ci_temp,skew_t,skew_t_transpose_R_temp;
    R_C0_Ci_temp = T_C0_Ci[i].block(0,0,3,3);
    t_C0_Ci_temp = T_C0_Ci[i].block(0,3,3,1);
    skew_t << 0, -t_C0_Ci_temp(2), t_C0_Ci_temp(1),
        t_C0_Ci_temp(2), 0, -t_C0_Ci_temp(0),
        -t_C0_Ci_temp(1), t_C0_Ci_temp(0), 0;
    R_C0_Ci.emplace_back(R_C0_Ci_temp);
    skew_t_transpose_R_temp = skew_t.transpose() * R_C0_Ci_temp;
    t_transpose_R.emplace_back(skew_t_transpose_R_temp);
  }

  /// l2 = K(^-1)_2 * t^R * K(^-1)_1 * p1 =F2 * p1
  for (int i = 0; i < new_frames->size(); ++i) {
    auto ld = new_frames->at(i)->landmark_vec_;
    for (int j = 0; j < ld.size(); ++j) {
      if(ld[j] == nullptr)
        continue;
      auto px = new_frames->frames_[i]->px_vec_.col(j);
      Eigen::Vector3d p_norm;
      p_norm << px.x(),px.y(),1;

      Eigen::Vector3d p_norm_corresponding;
      Eigen::Vector3d l_0i = M_K[i+1].inverse() * t_transpose_R[i] *  M_K[i] * p_norm;
      double dist = std::abs(p_norm_corresponding.dot(l_0i.head<3>()) + l_0i(3)) / l_0i.head<3>().norm();
      std::cout<<"dist: "<<dist<<std::endl;

      if(1){
        cv::Mat img_show = new_frames->frames_[i]->image_;
        std::string name = "Calib_Check";
        cv::namedWindow(name, cv::WINDOW_NORMAL);
        cv::resizeWindow(name, img_show.cols, img_show.rows);
        cv::KeyPoint kp_show = cv::KeyPoint(px.x(),px.y(),1);
        std::vector<cv::KeyPoint> kps;
        kps.emplace_back(kp_show);

        cv::drawKeypoints(img_show, kps, img_show, cv::Scalar(0, 0, 255),
            cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
        cv::waitKey(0);
      }

      break;
    }
  }
  NOLOCalibParams<float> calib_params = m_calib_params;
  //  std::vector<float> K0 = m_calib_params.Ks[0];
  //  std::vector<float> D0 = m_calib_params.Ds[0];
  //  std::vector<float> K1 = m_calib_params.Ks[1];
  //  std::vector<float> D1 = m_calib_params.Ds[1];
  //  std::vector<float> K2 = m_calib_params.Ks[2];
  //  std::vector<float> D2 = m_calib_params.Ds[2];
  //  std::vector<float> K3 = m_calib_params.Ks[3];
  //  std::vector<float> D3 = m_calib_params.Ds[3];
  //  cv::Mat cur_K0 = (cv::Mat_<double>(3, 3) << K0[0], 0, K0[2], 0, K0[1],
  //  K0[3], 0, 0, 1); cv::Mat cur_K1 = (cv::Mat_<double>(3, 3) << K1[0], 0,
  //  K1[2], 0, K1[1], K1[3], 0, 0, 1); cv::Mat cur_K2 = (cv::Mat_<double>(3, 3)
  //  << K2[0], 0, K2[2], 0, K2[1], K2[3], 0, 0, 1); cv::Mat cur_K3 =
  //  (cv::Mat_<double>(3, 3) << K3[0], 0, K3[2], 0, K3[1], K3[3], 0, 0, 1);
  //  cv::Mat cur_D_fisheye = (cv::Mat_<double>(4, 1) << D0[0], D0[1], D0[2],
  //  D0[3]);
  //
  //  Eigen::Map<const Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>
  //  eigenMat0(cur_K0.ptr<double>()); Eigen::Matrix3d M_K0 = eigenMat0;
  //  Eigen::Map<const Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>
  //  eigenMat1(cur_K1.ptr<double>()); Eigen::Matrix3d M_K1 = eigenMat1;
  //  Eigen::Map<const Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>
  //  eigenMat2(cur_K2.ptr<double>()); Eigen::Matrix3d M_K2 = eigenMat2;
  //  Eigen::Map<const Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>
  //  eigenMat3(cur_K3.ptr<double>()); Eigen::Matrix3d M_K3 = eigenMat3;

  std::vector<Eigen::Matrix3d> M_K;
  for (int i = 0; i < m_calib_params.T_C_Cs.size(); ++i) {
    std::vector<float> Ki = m_calib_params.Ks[i];
    cv::Mat cur_Ki =
        (cv::Mat_<double>(3, 3) << Ki[0], 0, Ki[2], 0, Ki[1], Ki[3], 0, 0, 1);
    Eigen::Map<const Eigen::Matrix<double, 3, 3, Eigen::RowMajor>> eigenMati(
        cur_Ki.ptr<double>());
    Eigen::Matrix3d M_Ki = eigenMati;
    M_K.emplace_back(M_Ki);
  }

  std::vector<std::vector<float>> T_C_Ci = calib_params.T_C_Cs;
  std::vector<Eigen::Matrix3d> cur_R_C_Ci;
  std::vector<Eigen::Vector3d> cur_t_C_Ci;
  std::vector<Eigen::Matrix3d> skew_t_C_Ci;
  for (auto &data : m_calib_params.T_C_Cs) {
    Eigen::Vector3d t;
    std::vector<float> tq;
    vector2tq(tq, data);
    t[0] = (double)tq[0];
    t[1] = (double)tq[1];
    t[2] = (double)tq[2];
    Eigen::Matrix3d skew_t;
    skew_t << 0, -t(2), t(1), t(2), 0, -t(0), -t(1), t(0), 0;
    skew_t_C_Ci.emplace_back(skew_t);
    Eigen::Quaterniond q(tq[3], tq[4], tq[5], tq[6]);
    Eigen::Matrix3d rot = q.normalized().toRotationMatrix();
    cur_R_C_Ci.emplace_back(rot);
    cur_t_C_Ci.emplace_back(t);
  }

  // l2 = K(^-1)_2 t^R K(^-1)_1 p1 =F2 * p1
  std::cout << " skew_t_C_Ci size " << skew_t_C_Ci.size() << std::endl;
  auto sorted_p2ds = img_info.sorted_p2ds;
  auto p = sorted_p2ds[0][0];
  for (int i = 0; i < skew_t_C_Ci.size(); ++i) {
    Eigen::Vector3d l0i = M_K[i+1].inverse() * skew_t_C_Ci[i] * cur_R_C_Ci[i] * M_K[0] *
               Eigen::Vector3d(sorted_p2ds[i+1][0].x, sorted_p2ds[i+1][0].y, 1);

    Vector2d p_image(sorted_p2ds[i+1][0].x, sorted_p2ds[i+1][0].y);
    double dist = std::abs(p_image.dot(l0i.head<3>()) + l0i(3)) / l0i.head<3>().norm();

    // closed loop proof and adaptor
    if(0){
      cv::Mat img_show = images[i+1].clone();
      std::string name = "Funda";
      cv::namedWindow(name, cv::WINDOW_NORMAL);
      cv::resizeWindow(name, img_show.cols, img_show.rows);
      auto cur_kpts = img_info.sorted_kpts[i];
      cv::drawKeypoints(img_show, cur_kpts, img_show, cv::Scalar(0, 0, 255),
                        cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

    }
  }

到了这里,关于【OpenCV 】对极几何标定质量验证的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • OpenCV标定演示,及如何生成标定板图片

    标定的程序在官方的源码里有, opencv-4.5.5samplescpptutorial_codecalib3dcamera_calibration 很多小白不知道怎么跑起来,这个也怪OpenCV官方,工作没做完善,其实的default.xml是要自己手动改的,输入的图片也要自己去拍摄,还有那个VID5.xml也要改成可以直接找到图片的路径; 我这里拍

    2024年02月06日
    浏览(26)
  • (opencv)图像几何变换——平移

    图像的平移操作是将图像的所有像素坐标进行水平或垂直方向移动,也就是将所有像素点按照给定的偏移量在水平方向沿x轴、垂直方向上沿y轴移动。平移变换分为两种类型:图像大小变化与图像大小不变。第一种类型保证图像平移的完整信息,第二种图像导致原始图像的部

    2024年02月08日
    浏览(33)
  • 【OpenCV】第五章: 几何变换

    第五章: 几何变换 1、什么是图像的几何变换? 图像的几何变换就是将一组图像数据经过某种数学运算,映射成另外一组图像数据的操作。所以, 几何变换的关键就是要确定这种空间映射关系。 几何变换又称空间变换。对于图像数据来说,就是将一幅图像中的坐标位置映射到

    2024年02月03日
    浏览(25)
  • (opencv)图像几何变换——缩放

    图像缩放是指将图像的尺寸变小或变大的过程,也就是减少或增加源图像数据的像素个数。图像缩放一定程度上会造成信息的丢失,因此需要考虑适宜的方法进行操作。 下面介绍两种常用的图像缩放方法的原理及实现 1.基于等间隔提取图像缩放 等间隔提取图像缩放是通过对

    2024年02月16日
    浏览(27)
  • 用OpenCV进行相机标定(张正友标定,有代码)

    理论部分可以参考其他博客或者视觉slam十四讲 相机标定主要是为了获得相机的内参矩阵K和畸变参数 内参矩阵K 畸变系数:径向畸变(k1,k2,k3), 切向畸变(p1,p2) 径向畸变公式 切向畸变公式 张正友标定方法能够提供一个比较好的初始解,用于后序的最优化. 这里用棋盘格进行标定,如

    2024年02月07日
    浏览(30)
  • python 手眼标定OpenCV手眼标定(calibrateHandeye())二

    这一章我们来根据上一章的分析,为手眼标定函数calibrateHandEye 准备他那些麻烦的参数 更详细的参数参考链接 即R_all_end_to_base_1,T_all_end_to_base_1, 我们可用通过输入的机械臂提供的6组参数得到,3个位姿与3个欧拉角 示例代码 这里是关系是 通过 cv2.findChessboardCorners 角点查找函数

    2024年02月01日
    浏览(26)
  • python 手眼标定OpenCV手眼标定(calibrateHandeye())一

    以下代码来源 本篇博客通过该代码,附上记录的公式与查找连接,方面以后调用能弄懂各个参数的意思 本篇看完看第二篇代码踩坑部分python 手眼标定OpenCV手眼标定(calibrateHandeye())二 相机标定原理视频介绍 calibrateHandeye() 参数描述如下:R_gripper2base,t_gripper2base是机械臂抓手

    2024年02月15日
    浏览(29)
  • C++Opencv图像几何绘制

    opencv中提供了绘制矩形的函数rectangle() 参数解释: pt1:矩形的一个顶点 pt2:矩形中与pt1相对的顶点,即两个点在对角线上。 rec:矩形左上角定点和长宽。 Scalar:颜色bgr thickness:线宽 lineType: 线型 shift: 坐标点的小数点位数 该函数利用两点确定一条直线的方式在图像中画出

    2024年02月16日
    浏览(23)
  • OpenCV自学笔记八:几何变换

    缩放是指改变图像的尺寸大小。在OpenCV中,可以使用`cv2.resize()`函数来实现图像的缩放操作。该函数接受源图像、目标图像大小以及插值方法作为参数。 示例代码:i 翻转是指将图像按照水平或垂直方向进行镜像处理。在OpenCV中,可以使用`cv2.flip()`函数来实现图像的翻转操作

    2024年02月07日
    浏览(20)
  • OpenCV中的相机标定

          之前在https://blog.csdn.net/fengbingchun/article/details/130039337 中介绍了相机的内参和外参,这里通过OpenCV中的接口实现对内参和外参的求解。       估计相机参数的过程称为相机标定(camera calibration)。相机标定是使用已知的真实世界模式(例如棋盘)来估计相机镜头和传感器的外

    2023年04月19日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包