【多视几何】对极几何(Epipolar Geometry)基础及OpenCV实现:对极约束、基础矩阵、本质矩阵和单应矩阵


对极几何(Epipolar Geometry)是计算机视觉理论的基础,它描述了同一场景中两幅图像(2D-2D)之间的视觉几何关系,在图像匹配、三维重建等领域应用广泛。本文中将涉及到以下知识点:

  • 对极约束(Epipolar constraint)
  • 基础矩阵(Fundamental Matrix)
  • 本质矩阵(Essential Matrix)
  • 单应矩阵(Homography Matrix)

1 对极约束(Epipolar constraint)

1.1 基本术语

在这里插入图片描述
首先看一个理想情况(无噪声)。如图所示为两帧图像 I 1 , I 2 I_1,I_2 I1,I2在三维空间中的几何关系,相机光心为 O 1 , O 2 O_1,O_2 O1,O2,相机由 O 1 O_1 O1运动到 O 2 O_2 O2位置的变换为 R R R(旋转)和 t t t(平移)。 P P P为三维空间中的一个点,它在两像平面上的投影为 p 1 , p 2 p_1,p_2 p1,p2,则有以下几个术语来描述它们之间的几何关系:

  • 极平面(Epipolar plane): O 1 , O 2 , P O_1,O_2,P O1,O2,P三点确定的平面;
  • 极点(Epipoles): O 1 O 2 O_1O_2 O1O2连线与像平面 I 1 , I 2 I_1,I_2 I1,I2的交点 e 1 , e 2 e_1,e_2 e1,e2
  • 基线(Baseline): O 1 O 2 O_1O_2 O1O2连线;
  • 极线(Epipolar line): 极平面与两像平面的交线 l 1 , l 2 l_1,l_2 l1,l2

1.2 数学推导

接下来从数学角度推导它们之间的几何关系。令 P = [ X , Y , Z ] T P=[X,Y,Z]^T P=[X,Y,Z]T,则根据针孔相机模型可以得到 P P P在两像平面上的投影点为:

{ s 1 p 1 = K P s 2 p 2 = K ( R P + t ) ⇒ { s 1 K − 1 p 1 = P s 2 K − 1 p 2 = R P + t (1) \begin{cases} s_1p_1=KP \\ s_2p_2=K(RP+t) \end{cases} \Rightarrow \begin{cases} s_1K^{-1}p_1=P \\ s_2K^{-1}p_2=RP+t \end{cases} \tag{1} {s1p1=KPs2p2=K(RP+t){s1K1p1=Ps2K1p2=RP+t(1)

其中 s i = 1 Z c i s_i=\frac{1}{Z_c^i} si=Zci1 1 Z c i \frac{1}{Z_c^i} Zci1 P P P在相机坐标空间下 Z Z Z轴坐标, K K K为相机内参矩阵, R , t R,t R,t为相机运动矩阵。

令:

{ x 1 = K − 1 p 1 x 2 = K − 1 p 2 (2) \begin{cases} x_1=K^{-1}p_1 \\ x_2=K^{-1}p_2 \tag{2} \end{cases} {x1=K1p1x2=K1p2(2)

则上式可变为:

{ s 1 x 1 = P s 2 x 2 = R P + t (3) \begin{cases} s_1x_1=P \\ s_2x_2=RP+t \end{cases} \tag{3} {s1x1=Ps2x2=RP+t(3)

合并这两个公式有:

s 2 x 2 = s 1 R x 1 + t (4) s_2x_2=s_1Rx_1+t \tag{4} s2x2=s1Rx1+t(4)

为了消去 t t t,对上式两侧同时与 t t t做外积(也称叉积、向量积)有:

s 2 t ∧ x 2 = s 1 t ∧ R x 1 (5) s_2t^{\wedge}x_2=s_1t^{\wedge}Rx_1 \tag{5} s2tx2=s1tRx1(5)

考虑将上式的两项变为一项,两侧同时左乘 x 2 T x_2^T x2T

s 2 x 2 T t ∧ x 2 = s 1 x 2 T t ∧ R x 1 (6) s_2x_2^Tt^{\wedge}x_2=s_1x_2^Tt^{\wedge}Rx_1 \tag{6} s2x2Ttx2=s1x2TtRx1(6)

观察等式左侧, t ∧ x 2 t^{\wedge}x_2 tx2是一个与 t t t x 2 x_2 x2都垂直的向量,若将它与 x 2 x_2 x2做内积,将得到0,于是上式可简化为:

x 2 T t ∧ R x 1 = 0 (7) x_2^Tt^{\wedge}Rx_1=0 \tag{7} x2TtRx1=0(7)

重新带入 p 1 , p 2 p_1,p_2 p1,p2(公式 ( 2 ) (2) (2))有:

p 2 T K − T t ∧ R K − 1 p 1 = 0 (8) p_2^TK^{-T}t^{\wedge}RK^{-1}p_1=0 \tag{8} p2TKTtRK1p1=0(8)

公式 ( 7 ) (7) 7 ( 8 ) (8) 8都称为对极约束,它的几何意义是 O 1 , P , O 2 O_1, P, O_2 O1,P,O2三点共面。

进一步,我们把中间部分记为两个矩阵,基础矩阵 F F F(Fundamental Matrix): F = K − T E K − 1 F=K^{-T}EK^{-1} F=KTEK1,本质矩阵 E E E(Essential Matrix): E = t ∧ R E=t^{\wedge}R E=tR,即:

x 2 T E x 1 = p 2 T F p 1 = 0 (9) x_2^TEx_1=p_2^TFp_1=0 \tag{9} x2TEx1=p2TFp1=0(9)

在SLAM中,这一步被用来估计相机位姿,即根据匹配点的像素坐标求出基础矩阵F或本质矩阵E,可通过“8点法”求解,进而求出 R , t R,t R,t,具体求解方法可参考

2 基础矩阵(Fundamental Matrix)

基础矩阵 F F F描述了三维空间点 P P P在摄像机不同方位下成像得到的投影像素点之间的关系,即:
p 2 T F p 1 = 0 ⇒ [ u 2 v 2 1 ] T F [ u 1 v 1 1 ] = 0 (10) p_2^T F p_1=0 \\ \Rightarrow \begin{bmatrix} u_2 \\ v_2 \\ 1 \end{bmatrix}^T F \begin{bmatrix} u_1 \\ v_1 \\ 1 \end{bmatrix} =0 \tag{10} p2TFp1=0u2v21TFu1v11=0(10)

3 本质矩阵(Essential Matrix)

本质矩阵 E E E实际上可以被看做是在相机归一化平面上的基本矩阵,它具有基本矩阵的所有性质。对于三维空间点 P P P,定义其在相机归一化平面上的投影坐标为:
P c = [ X c Z c Y c Z c 1 ] (11) P_c= \begin{bmatrix} \frac{X_c}{Z_c} \\ \frac{Y_c}{Z_c} \\ 1 \end{bmatrix} \tag{11} Pc=ZcXcZcYc1(11)

P c P_c Pc经过相机的内参矩阵后,即可投影为像素坐标,即与基础矩阵 F F F相联系(投影方程参考这篇博文中的公式(4))。则本质矩阵 E E E表示为:
[ X c 1 Z c 1 Y c 1 Z c 1 1 ] T E [ X c 2 Z c 2 Y c 2 Z c 2 1 ] = 0 (12) \begin{bmatrix} \frac{X_c^1}{Z_c^1} \\ \frac{Y_c^1}{Z_c^1} \\ 1 \end{bmatrix}^T E \begin{bmatrix} \frac{X_c^2}{Z_c^2} \\ \frac{Y_c^2}{Z_c^2} \\ 1 \end{bmatrix} =0 \tag{12} Zc1Xc1Zc1Yc11TEZc2Xc2Zc2Yc21=0(12)

本质矩阵 E E E可用于估计相机在两个位置的相对运动。

4 OpenCV中的相关函数

4.1 特征点检测与匹配

OpenCV里有关特征点的检测与匹配方法非常多,如SIFT、SURF、FAST、ORB、Harris等特征描述子,以及FlannBasedMatcher等特征点匹配算法,这里不再详细描述。由于初步提取的特征点匹配对会存在很多的误匹配,因此我们通常使用RANSAC等算法对得到的匹配对进行筛选,这一步骤对于后续的三维重建非常重要,错误的匹配将会严重影响重建精度。

4.2 求解基础矩阵F

在上一步得到匹配的特征点后,可以使用cv::findFundamentalMat函数来求解基础矩阵 F F F

\\ points1, points2:std::vector<cv::Point2f>格式的匹配点对
\\ method:求解方法,包括
\\         cv::FM_7POINT - 7点法,点数N=7
\\         cv::FM_8POINT - 8点法,点数N≥8
\\         cv::FM_RANSAC - RANSAC方法,点数N≥8
\\         cv::FM_LMEDS - LMedS方法,点数N≥8
\\ ransacReprojThreshold:只对RANSAC方法有效。表示从点到极线的最大像素距离,超过该距离则点将被视为离群值,并且不被用于计算最终的基本矩阵。可将其设置为1-3
\\ confidence:用于RANSAC和LMedS方法的参数,指定了了所估计矩阵的期望置信度(概率)

CV_EXPORTS_W Mat findFundamentalMat( InputArray points1, InputArray points2,
                                     int method = FM_RANSAC,
                                     double ransacReprojThreshold = 3., double confidence = 0.99,
                                     OutputArray mask = noArray() );

\\ 重载函数,用的比较少,可自行翻阅API文档
/** @overload */
CV_EXPORTS Mat findFundamentalMat( InputArray points1, InputArray points2,
                                   OutputArray mask, int method = FM_RANSAC,
                                   double ransacReprojThreshold = 3., double confidence = 0.99 );

4.3 求解本质矩阵E

同样的,可以利用cv::findEssentialMat来求解相机运动的本质矩阵 E E E

\\ points1, points2:std::vector<cv::Point2f>格式的匹配点对,且点数N≥5
\\ cameraMatrix:相机内参矩阵
\\ method:提供了RANSAC和LMEDS两种算法
\\ prob:用于RANSAC和LMedS方法的参数,指定了了所估计矩阵的期望置信度(概率)。与findFundamentalMat中的参数confidence一致
\\ threshold:只对RANSAC方法有效。表示从点到极线的最大像素距离,超过该距离则点将被视为离群值,并且不被用于计算最终的基本矩阵。可将其设置为1-3。与findFundamentalMat函数中的参数ransacReprojThreshold一致
\\ mask:输出参数,维度与points1和points2一致。0表示外点outliers,1表示内点inliers
CV_EXPORTS_W Mat findEssentialMat( InputArray points1, InputArray points2,
                                 InputArray cameraMatrix, int method = RANSAC,
                                 double prob = 0.999, double threshold = 1.0,
                                 OutputArray mask = noArray() );

此外,findEssentialMat也有一个重载函数,该重载函数利用给定的相机焦距和主点来计算相机的内参矩阵,实际上是一样的。

CV_EXPORTS_W Mat findEssentialMat( InputArray points1, InputArray points2,
                                 double focal = 1.0, Point2d pp = Point2d(0, 0),
                                 int method = RANSAC, double prob = 0.999,
                                 double threshold = 1.0, OutputArray mask = noArray() );

4.4 由本质矩阵E恢复相机运动

利用前面得到的本质矩阵 E E E,可以使用函数cv::recoverPose来估计相机的相对运动。所使用的算法是由Nister于2003年提出的《An efficient solution to the five-point relative pose problem》,函数的参数如下:

\\ E:本质矩阵
\\ points1,points2:std::vector<cv::Point2f>格式的匹配点对。与findEssentialMat中的输入一致
\\ cameraMatrix:相机的内参矩阵
\\ R,t:算法估计的相对变换矩阵
\\ mask:标识points1和points2中的内点和外点
CV_EXPORTS_W int recoverPose( InputArray E, InputArray points1, InputArray points2,
                            InputArray cameraMatrix, OutputArray R, OutputArray t,
                            InputOutputArray mask = noArray() );

\\ 重载函数,利用焦距和主点计算内参矩阵,与上面的函数一样
CV_EXPORTS_W int recoverPose( InputArray E, InputArray points1, InputArray points2,
                            OutputArray R, OutputArray t,
                            double focal = 1.0, Point2d pp = Point2d(0, 0),
                            InputOutputArray mask = noArray() );

\\ E、points1、points2、cameraMatrix、R、t、mask与前面的一致
\\ distanceThresh:区分内点、外点的界限
\\ triangulatedPoints:输出,三角测量得到的特征点的三维坐标
CV_EXPORTS_W int recoverPose( InputArray E, InputArray points1, InputArray points2,
                            InputArray cameraMatrix, OutputArray R, OutputArray t, double distanceThresh, InputOutputArray mask = noArray(),
                            OutputArray triangulatedPoints = noArray());

参考

[1] 《计算机视觉中的多视图几何》
[2] 《视觉SLAM十四讲:从理论到实践》
[3] http://www.mathsword.com/slamessentialmatrixgetrt/
[4] https://blog.csdn.net/cfan927/article/details/104333527
[5] https://www.zhihu.com/question/27581884

  • 19
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 好的,下面是一个简单的 Python 例子,演示如何将极线约束可视化。假设我们已经有了两张图像 `img1` 和 `img2`,以及它们中的对应点 `pts1` 和 `pts2`。我们可以使用以下代码来计算极线约束并可视化: ```python import cv2 import numpy as np import matplotlib.pyplot as plt # 假设我们已经得到了两张图像中的对应点 pts1 = ... # 第一张图像中的对应点坐标 pts2 = ... # 第二张图像中的对应点坐标 # 计算极线约束 F, mask = cv2.findFundamentalMat(pts1, pts2, cv2.FM_RANSAC) # 根据极线约束绘制对应点和极线 def draw_epipolar_lines(img1, img2, pts1, pts2, F): # 在两张图像中绘制对应点 plt.subplot(121) plt.imshow(img1) plt.scatter(pts1[:, 0], pts1[:, 1], marker='o', c='r') plt.subplot(122) plt.imshow(img2) plt.scatter(pts2[:, 0], pts2[:, 1], marker='o', c='r') # 计算两张图像中的极线 lines1 = cv2.computeCorrespondEpilines(pts2.reshape(-1, 1, 2), 2, F) lines1 = lines1.reshape(-1, 3) lines2 = cv2.computeCorrespondEpilines(pts1.reshape(-1, 1, 2), 1, F) lines2 = lines2.reshape(-1, 3) # 绘制极线 for pt1, pt2, line1, line2 in zip(pts1, pts2, lines1, lines2): color = tuple(np.random.randint(0, 255, 3).tolist()) x0, y0 = map(int, [0, -line1[2] / line1[1]]) x1, y1 = map(int, [img1.shape[1], -(line1[2] + line1[0] * img1.shape[1]) / line1[1]]) plt.subplot(121) plt.plot([pt1[0], x0], [pt1[1], y0], color=color) plt.subplot(122) plt.plot([pt2[0], x1], [pt2[1], y1], color=color) plt.show() # 调用函数绘制对应点和极线 draw_epipolar_lines(img1, img2, pts1, pts2, F) ``` 在上面的代码中,`draw_epipolar_lines()` 函数接收两张图像 `img1` 和 `img2`,以及它们中的对应点 `pts1` 和 `pts2`,以及计算得到的基础矩阵 `F`。函数首先在两张图像中绘制对应点,然后计算每个点在另一张图像中的极线,并绘制这些极线。最后,函数将绘制出的两张图像和极线显示在一个窗口中。 ### 回答2: Python实现极线约束的可视化可以通过以下步骤实现: 1. 首先,导入所需的Python库,比如OpenCV和Matplotlib。安装这些库可以使用pip命令:`pip install opencv-python matplotlib` 2. 加载双目图像。使用OpenCV的`cv2.imread()`函数加载左右两个摄像头捕获的图像。 3. 使用立体视觉算法,比如SIFT(尺度不变特征转换)或ORB(Oriented Fast and Rotated Brief)找到左右图像中的关键点。这些关键点可以用来计算匹配点对。 4. 使用OpenCV的`cv2.findFundamentalMat()`函数计算基础矩阵基础矩阵是一个3x3的矩阵,它描述了左右相机之间的投影关系。 5. 使用OpenCV的`cv2.computeCorrespondEpilines()`函数计算极线。极线是通过基础矩阵与关键点投影得出的。 6. 可以使用Matplotlib的绘图函数,比如`plt.plot()`和`plt.imshow()`,将左右图像和对应的极线可视化。可以使用不同的颜色表示不同的极线。 7. 最后,显示可视化的图像。可以使用Matplotlib的`plt.show()`函数将图像显示在屏幕上。 通过上述步骤,可以使用Python实现极线约束的可视化,从而更好地理解双目视觉中的极线匹配。 ### 回答3: Python中可以使用OpenCV实现极线约束的可视化。极线约束是用于对立体视觉中的特征点进行匹配的一种约束条件。以下是实现极线约束可视化的步骤: 1. 导入必要的库:import cv2, numpy as np 2. 读取图像:img1 = cv2.imread('image1.jpg'),img2 = cv2.imread('image2.jpg') 3. 提取图像的特征点:detector = cv2.ORB_create(),kp1, des1 = detector.detectAndCompute(img1, None),kp2, des2 = detector.detectAndCompute(img2, None) 4. 创建一个Flann Matcher对象:matcher = cv2.FlannBasedMatcher_create() 5. 使用k近邻算法进行匹配:matches = matcher.knnMatch(des1, des2, 2) 6. 对匹配结果进行极线约束过滤:good_matches = [] for m, n in matches: if m.distance < 0.75 * n.distance: good_matches.append(m) 7. 绘制极线约束:img_out = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) 8. 显示结果:cv2.imshow("Matches", img_out) cv2.waitKey(0) cv2.destroyAllWindows() 在上述代码中,首先通过ORB算法提取图像的特征点,然后使用Flann Matcher进行特征点的匹配。接着,应用极线约束过滤掉不满足条件的匹配点,最后通过drawMatches函数可视化绘制极线约束。 通过这样的可视化,我们可以清晰地观察到图像中的匹配点是否满足极线约束条件,从而帮助我们更好地理解图像匹配的结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值