slam实践:对极约束与三角测量

1.对极约束求解相机运动——R,t

步骤:

  • 根据配对点的像素位置求出本质矩阵E(八点法、SVD奇异值分解)或者基础矩阵F( x 2 T E x 1 = p 2 T F p 1 = 0 x_2^TEx_1 = p_2^TFp_1 = 0 ) 注意: x 1 , x 2 x_1,x_2 为3d点分别在第一帧、第二帧相机归一化坐标系上的坐标; p 1 , p 2 p_1,p_2 为像素坐标。
  • 根据E/F求出R,t( E = t / R , F = K T E K 1 E=t^{/}R,F=K^{-T}EK^{-1} ),K是相机内参矩阵
    第三种方法是求单应矩阵
    注意findHomography() 函数形式和参数,详见参考资料
    参考资料 :视觉slam学习之——ch7 视觉里程计

关键代码:

void pose_estimation_2d2d(std::vector<KeyPoint> keypoints_1,
                          std::vector<KeyPoint> keypoints_2,
                          std::vector<DMatch> matches,
                          Mat &R, Mat &t) {
  // 相机内参,TUM Freiburg2
  Mat K = (Mat_<double>(3, 3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);

  //-- 把匹配点转换为vector<Point2f>的形式
  vector<Point2f> points1;
  vector<Point2f> points2;

  for (int i = 0; i < (int) matches.size(); i++) {
    points1.push_back(keypoints_1[matches[i].queryIdx].pt);
    points2.push_back(keypoints_2[matches[i].trainIdx].pt);
  }

  //-- 计算基础矩阵
  Mat fundamental_matrix;
  fundamental_matrix = findFundamentalMat(points1, points2, CV_FM_8POINT);
  cout << "fundamental_matrix is " << endl << fundamental_matrix << endl;

  //-- 计算本质矩阵
  Point2d principal_point(325.1, 249.7);  //相机光心, TUM dataset标定值
  double focal_length = 521;      //相机焦距, TUM dataset标定值
  Mat essential_matrix;
  essential_matrix = findEssentialMat(points1, points2, focal_length, principal_point);
  cout << "essential_matrix is " << endl << essential_matrix << endl;

  //-- 计算单应矩阵
  //-- 但是本例中场景不是平面,单应矩阵意义不大
  Mat homography_matrix;
  homography_matrix = findHomography(points1, points2, RANSAC, 3);
  cout << "homography_matrix is " << endl << homography_matrix << endl;

  //-- 从本质矩阵中恢复旋转和平移信息.
  // 此函数仅在Opencv3中提供
  recoverPose(essential_matrix, points1, points2, R, t, focal_length, principal_point);
  cout << "R is " << endl << R << endl;
  cout << "t is " << endl << t << endl;

}

注意recoverPose()函数原型和参数,详见参考资料。

实验结果

在这里插入图片描述注意:编译前讲参考图片放到build文件下,终端输入
./pose_estimation_2d2d 1.png 2.png

2.三角测量

  1. 基础知识点补充:
    1.1 Mat::at
    在这里插入图片描述2. triangulatePoints()的参数形式详见参考资料。

相关代码:

void triangulation(
  const vector<KeyPoint> &keypoint_1,
  const vector<KeyPoint> &keypoint_2,
  const std::vector<DMatch> &matches,
  const Mat &R, const Mat &t,
  vector<Point3d> &points) {
  Mat T1 = (Mat_<float>(3, 4) <<
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0);   //这块表示图1的相机作为参考,变换矩阵中没有相机旋转和平移
  Mat T2 = (Mat_<float>(3, 4) <<
    R.at<double>(0, 0), R.at<double>(0, 1), R.at<double>(0, 2), t.at<double>(0, 0),
    R.at<double>(1, 0), R.at<double>(1, 1), R.at<double>(1, 2), t.at<double>(1, 0),
    R.at<double>(2, 0), R.at<double>(2, 1), R.at<double>(2, 2), t.at<double>(2, 0)
  );  //T2中传入R,t,即图2相对图1这个参考位置的变换矩阵
 
  Mat K = (Mat_<double>(3, 3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);  //相机内参
  vector<Point2f> pts_1, pts_2;  //图1和图1上的2D图像像素点转换后的归一化相机坐标点,对应的Zc=1,所以只要计算Xc和Yc
  for (DMatch m:matches) {
    // 将像素坐标转换至相机坐标
    pts_1.push_back(pixel2cam(keypoint_1[m.queryIdx].pt, K));
    pts_2.push_back(pixel2cam(keypoint_2[m.trainIdx].pt, K));
  }
 
  Mat pts_4d;
  cv::triangulatePoints(T1, T2, pts_1, pts_2, pts_4d); //传入两个图像对应相机的变化矩阵,各自相机坐标系下归一化相机坐标,输出的3D坐标是齐次坐标,共四个维度,因此需要将前三个维度除以第四个维度以得到非齐次坐标xyz
 
  // 转换成非齐次坐标
  for (int i = 0; i < pts_4d.cols; i++) {  //遍历所有的点,列数表述点的数量
    Mat x = pts_4d.col(i);  //x为4x1维度
    x /= x.at<float>(3, 0); // 归一化
    Point3d p(
      x.at<float>(0, 0),
      x.at<float>(1, 0),
      x.at<float>(2, 0)
    );
    points.push_back(p);  //将图1测得的目标相对相机实际位置(Xc,Yc,Zc)存入points
  }
}
 
Point2f pixel2cam(const Point2d &p, const Mat &K) {
  return Point2f
    (
      (p.x - K.at<double>(0, 2)) / K.at<double>(0, 0),
      (p.y - K.at<double>(1, 2)) / K.at<double>(1, 1)
    );
}

int main(int argc, char **argv) {
  if (argc != 3) {
    cout << "usage: triangulation img1 img2" << endl;
    return 1;
  }
  //-- 读取图像
  Mat img_1 = imread(argv[1], CV_LOAD_IMAGE_COLOR); //加载彩色图
  Mat img_2 = imread(argv[2], CV_LOAD_IMAGE_COLOR);

  vector<KeyPoint> keypoints_1, keypoints_2;
  vector<DMatch> matches;
  find_feature_matches(img_1, img_2, keypoints_1, keypoints_2, matches);
  cout << "一共找到了" << matches.size() << "组匹配点" << endl;

  //-- 估计两张图像间运动
  Mat R, t;
  pose_estimation_2d2d(keypoints_1, keypoints_2, matches, R, t);

  //-- 三角化
  // 传入图1,2的特征点,刷选后的匹配结果,由本质矩阵恢复得到的R和t,输出图1中目标在其相机姿态中的3D实际相机坐标(Xc,Yc,Zc)
  vector<Point3d> points;
  triangulation(keypoints_1, keypoints_2, matches, R, t, points);

  //-- 验证三角化点与特征点的重投影关系
  Mat K = (Mat_<double>(3, 3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1); //相机内参
  Mat img1_plot = img_1.clone();
  Mat img2_plot = img_2.clone();
  for (int i = 0; i < matches.size(); i++) { //遍历刷选后的匹配点对
    // 第一个图
    float depth1 = points[i].z; //图1中第i个匹配的距离
    cout << "depth: " << depth1 << endl;
    Point2d pt1_cam = pixel2cam(keypoints_1[matches[i].queryIdx].pt, K); // 像素坐标->归一化相机坐标
    cv::circle(img1_plot, keypoints_1[matches[i].queryIdx].pt, 2, get_color(depth1), 2); //在图1中画出刷选后的匹配点(颜色与距离相关)

    // 第二个图
    Mat pt2_trans = R * (Mat_<double>(3, 1) << points[i].x, points[i].y, points[i].z) + t;
    float depth2 = pt2_trans.at<double>(2, 0);
    cv::circle(img2_plot, keypoints_2[matches[i].trainIdx].pt, 2, get_color(depth2), 2);
  }
  cv::imshow("img 1", img1_plot);
  cv::imshow("img 2", img2_plot);
  cv::waitKey();

  return 0;
}

实验结果:
在这里插入图片描述

发布了38 篇原创文章 · 获赞 0 · 访问量 1522

猜你喜欢

转载自blog.csdn.net/weixin_40224537/article/details/105088833