单应矩阵/基础矩阵/本质矩阵总结(一)----单应矩阵的基本概念

本文章是综合书籍以及相关资料的一点个人总结。

1、定义

单应矩阵的模式图:(引用自opencv docs)

 观测物平面π上的一个点X(齐次坐标\begin{vmatrix} x\\ y\\ 1 \end{vmatrix})映射到相机平面π'的点X’(齐次坐标\begin{vmatrix} x'\\ y'\\ 1 \end{vmatrix}),存在如下的一种转换关系:

s\begin{vmatrix} x'\\ y'\\ 1 \end{vmatrix}=H\begin{vmatrix} x\\ y\\ 1 \end{vmatrix}=\begin{vmatrix} h1 & h2 & h3\\ h4 & h5 & h6\\ h7 & h8 & h9 \end{vmatrix}\begin{vmatrix} x\\ y\\ 1 \end{vmatrix}

其中H就是单应矩阵,Opencv Docs还定义一般的应用场景:

a)同一平面被两个处于不同位置的相机观测,或者同一个相机观测到的两个相同的,但位置不同的物体;

b)旋转相机拍摄的任意图片,考虑的是因为只有旋转,则所有点可以近似于处在距离相机原点无穷远的平面上

 2、模型条件

  •   基于的是相机的针孔模型,所以想得到精确解,必须对相机进行标定,获得相机内外参数;
  •   单应矩阵是两个平面间点的转换矩阵,如果观测物的点不处于同一平面则单应矩阵不成立,这也是实际使用中的误差来源;

3、公式推导及对应src范例

        a)对于相机坐标系下的H:

设平面\pi在第一个相机坐标系下的单位法向量为\underset{n}{\rightarrow},其到第一个相机中心(坐标原点)的距离为d,则平面\pi可表示为:

        n^{T}X_{1}=d                                                            (1)

根据一般的两个平面间的旋转变换R和平移变换t,有第一个相机坐标系下的平面至第二相机坐标系下变换公式:

X_{2}=RX_{1}+t                                                               (2)

结合上面两式可得:

将(1)式变形为:\frac{1}{d}n^{T}X_{1}=1,带入(2):

X_{2}=RX_{1}+t=RX_{1}+t\frac{1}{d}n^{T}X_{1}=(R+t\frac{1}{d}n^{T})X_{1}=HX_{1}

H=R+t\frac{1}{d}n^{T},                                                                                                               (3)

       opencv有src详细论证使用此公式求解以及直接用函数求解的结果对比:

     int main()
    {
        Mat img1 = imread(img1Path);
		Mat img2 = imread(img2Path);

		//寻找棋盘点
		vector<Point2f> corners1, corners2;
		bool found1 = findChessboardCorners(img1, patternSize, corners1);
		bool found2 = findChessboardCorners(img2, patternSize, corners2);

		if (!found1 || !found2)
		{
			cout << "Error, cannot find the chessboard corners in both images." << endl;
			return;
		}
        
		vector<Point3f> objectPoints;
		calcChessboardCorners(patternSize, squareSize, objectPoints);

        //加载相机内参以及畸变参数
		FileStorage fs(intrinsicsPath, FileStorage::READ);
		Mat cameraMatrix, distCoeffs;
		fs["camera_matrix"] >> cameraMatrix;
		fs["distortion_coefficients"] >> distCoeffs;

        //利用函数solvePnp可以计算出棋盘对应图片1和图片2的旋转矩阵和平移矩阵
		Mat rvec1, tvec1;
		solvePnP(objectPoints, corners1, cameraMatrix, distCoeffs, rvec1, tvec1);
		Mat rvec2, tvec2;
		solvePnP(objectPoints, corners2, cameraMatrix, distCoeffs, rvec2, tvec2);
		//! [compute-poses]

        //将结果显示在图片上
		Mat img1_copy_pose = img1.clone(), img2_copy_pose = img2.clone();
		Mat img_draw_poses;
		drawFrameAxes(img1_copy_pose, cameraMatrix, distCoeffs, rvec1, tvec1, 2 * squareSize);
		drawFrameAxes(img2_copy_pose, cameraMatrix, distCoeffs, rvec2, tvec2, 2 * squareSize);
		hconcat(img1_copy_pose, img2_copy_pose, img_draw_poses);
		imshow("Chessboard poses", img_draw_poses);

		//! [compute-camera-displacement]
		Mat R1, R2;
		Rodrigues(rvec1, R1);
		Rodrigues(rvec2, R2);

        //根据R1,R2计算图片1至图片2的旋转,平移矩阵
		Mat R_1to2, t_1to2;
		computeC2MC1(R1, tvec1, R2, tvec2, R_1to2, t_1to2);
		Mat rvec_1to2;
		Rodrigues(R_1to2, rvec_1to2);
		//! [compute-camera-displacement]

		//! [compute-plane-normal-at-camera-pose-1]
		Mat normal = (Mat_<double>(3, 1) << 0, 0, 1);
		Mat normal1 = R1 * normal;
		//! [compute-plane-normal-at-camera-pose-1]

		//! [compute-plane-distance-to-the-camera-frame-1]
		Mat origin(3, 1, CV_64F, Scalar(0));
		Mat origin1 = R1 * origin + tvec1;
		double d_inv1 = 1.0 / normal1.dot(origin1);
		//! [compute-plane-distance-to-the-camera-frame-1]

		//! [compute-homography-from-camera-displacement]
		Mat homography_euclidean = computeHomography(R_1to2, t_1to2, d_inv1, normal1);
		Mat homography = cameraMatrix * homography_euclidean * cameraMatrix.inv();

		homography /= homography.at<double>(2, 2);
        // homography_euclidean  就是根据公式(3)计算得到H
		homography_euclidean /= homography_euclidean.at<double>(2, 2);
		//! [compute-homography-from-camera-displacement]

		//Same but using absolute camera poses instead of camera displacement, just for check
		Mat homography_euclidean2 = computeHomography(R1, tvec1, R2, tvec2, d_inv1, normal1);
		Mat homography2 = cameraMatrix * homography_euclidean2 * cameraMatrix.inv();

		homography_euclidean2 /= homography_euclidean2.at<double>(2, 2);
		homography2 /= homography2.at<double>(2, 2);

		cout << "\nEuclidean Homography:\n" << homography_euclidean << endl;
		cout << "Euclidean Homography 2:\n" << homography_euclidean2 << endl << endl;

		//直接采用函数findHomography的计算结果
		undistortPoints(corners1, corners1_undist, cameraMatrix, distCoeffs);
		undistortPoints(corners2, corners2_undist, cameraMatrix, distCoeffs);
		Mat H = findHomography(corners1_undist, corners2_undist);
		cout << "\nfindHomography H_undist:\n" << H << endl;
        return 1;
    }
//! [compute-homography]
	Mat computeHomography(const Mat &R_1to2, const Mat &tvec_1to2, const double d_inv, const Mat &normal)
	{
		Mat homography = R_1to2 + d_inv * tvec_1to2*normal.t();
		return homography;
	}
	//! [compute-homography]

	Mat computeHomography(const Mat &R1, const Mat &tvec1, const Mat &R2, const Mat &tvec2,
		const double d_inv, const Mat &normal)
	{
		Mat homography = R2 * R1.t() + d_inv * (-R2 * R1.t() * tvec1 + tvec2) * normal.t();
		return homography;
	}

	//! [compute-c2Mc1]
	void computeC2MC1(const Mat &R1, const Mat &tvec1, const Mat &R2, const Mat &tvec2,
		Mat &R_1to2, Mat &tvec_1to2)
	{
		//c2Mc1 = c2Mo * oMc1 = c2Mo * c1Mo.inv()
		R_1to2 = R2 * R1.t();
		tvec_1to2 = R2 * (-R1.t()*tvec1) + tvec2;
	}

 计算结果:

 以下是3D坐标轴绘图:

         b)图像平面间的转换矩阵H:

        基于针孔模型: 对于相机坐标平面上的一个点X_{i}对应的图像像素平面x_{i}有:

x_{i}=KX_{i}                                                            (4)

其中K是相机内参矩阵,R是旋转矩阵,t是平移矩阵;

 结合H的定义:X_{2}=HX_{1},左乘K^{-1},即:

x_{2}=KX_{2}=KHX_{1}=KHK^{-1}x_{1}=K(R+t\frac{1}{d}n^T)K^{-1}x_{1}

 则对于图像像素坐标系下的H:

 H=KH^{'}K^{-1}=K(R+t\frac{1}{d}n^T)K^{-1}

其中H'就是相机坐标系下的单应矩阵。

实际运用中,如果只需要求解图像坐标系下的相关内容,则直接利用H即可,而如果需要求解实际世界坐标系下的相关内容就需要根据H,以及相机内参K,求解H',进而求解相关的R,t等。

Opencv中分解H获取R t的计算过程就是如此,这个下次分析。

对应的程式资源:

homography_baseconception.zip-C++文档类资源-CSDN文库

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陶醉鱼

感觉写的不错就给个打赏哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值