重投影误差

#

重投影误差
    偶尔在一篇文章中看到有关于重投影误差的介绍,简洁明了,现整理如下,同时也算是对自己图像基础知识的夯实打牢。
    在计算机视觉中,经常会用到重投影误差(Reprojection error)。比如在计算平面单应矩阵和投影矩阵的时候,往往会使用重投影误差来构造代价函数,然后最小化这个代价函数,以优化单应矩阵或者投影矩阵。之所以使用重投影误差,是因为它不光考虑了单应矩阵的计算误差,也考虑了图像点的测量误差,所以其精度比单边或者对称变换误差高。
    以平面单应矩阵的计算为例,假设两幅图像中的对应点满足:
这里写图片描述
    其中,H是平面单应矩阵,x和x’是图像中的对应点,则重投影误差的形式如下:
这里写图片描述
    subject to:
这里写图片描述
    其中,x是x的估计值,H是H的估计。最小化重投影误差就是优化H和x。从重投影误差公式可以看出,模型认为测量点并非绝对精确,而是存在一定的测量误差,因此需要重新估计图像点的坐标,而估计得到的新的图像点之间完美的满足单应关系。下图就是重投影误差的几何表述,d和d’的和即为重投影误差:
这里写图片描述
    上面都是在假设图像点有测量误差的情况下讨论的,如果x和x’都是图像上的点,那么确实是满足上面的模型的。但是有一种特殊情形,那就是摄像机定位时通常都会遇见的,x不是图像点,而是平面标志点在世界坐标系下的坐标,只有x’是图像点。在这种情况下,由于标志点通常都是很标准的,都是严格按尺寸制定的,所以我们可以认为x是绝对准确的,那么此时只有x’存在测量误差。如下图:
这里写图片描述
    此时,x的估计x就是它本身,即x=x,所以有:
这里写图片描述
    那么重投影误差在强约束条件x=x^下就退化为:
这里写图片描述
上式即为单边变换误差(Transfer error)。

   所以在这种情况下,单边几何变换误差才好的代价函数。也就是说,如果给重投影误差加上约束条件x=x^,重投影误差就退化为单边变换误差,并且极大地简化了优化算法。

   同时,从上面的推导也可以得出结论:在使用已知标志点给摄像机定位时,重投影误差并非最好的选择,因为重投影误差模型会认为标志点存在误差,从而重新估计标志点的坐标,引入多余的误差;而此时,事实上,重投影误差已经退化为单边几何变换误差,所以在这种情况下,单边几何变换误差才好的代价函数。


再谈重投影误差
在这里插入图片描述

  


这里写图片描述

### 绘制重投影误差的散点图 要绘制重投影误差的散点图,可以通过计实际点与其重新投影后的点之间的欧几里得距离实现。以下是具体方法: #### 计重投影误差 在 OpenCV 中,`cv.solvePnP()` 函数用于估计对象的姿态(旋转和平移向量)。通过该函数得到相机姿态后,可以使用 `cv.projectPoints()` 将 3D 点重新投影到图像平面上。然后比较这些重新投影点与原始检测点的位置差异。 #### 实现代码 以下是一个完整的 Python 示例代码,展示如何计并绘制重投影误差的散点图: ```python import numpy as np import cv2 import matplotlib.pyplot as plt # 假设已知的世界坐标系中的 3D 点 (object_points) object_points = np.array([[0, 0, 0], [1, 0, 0], [2, 0, 0]], dtype=np.float32) # 图像上的对应特征点 (image_points),单位为像素 image_points = np.array([[50, 50], [150, 50], [250, 50]], dtype=np.float32) # 相机内参矩阵和畸变系数 camera_matrix = np.array([[800, 0, 320], [0, 800, 240], [0, 0, 1]], dtype=np.float32) dist_coeffs = np.zeros((4, 1)) # 使用 solvePnP 获取旋转和平移向量 ret, rvec, tvec = cv2.solvePnP(object_points, image_points, camera_matrix, dist_coeffs) # 使用 projectPoints 对 3D 点进行重新投影 reprojected_points, _ = cv2.projectPoints(object_points, rvec, tvec, camera_matrix, dist_coeffs) # 提取重新投影点的二维坐标 reprojected_points = reprojected_points.reshape(-1, 2).astype(np.float32) # 计重投影误差 errors = np.linalg.norm(image_points - reprojected_points, axis=1) # 打印每一点的重投影误差 for i, error in enumerate(errors): print(f"Point {i}: Error = {error:.4f}") # 绘制重投影误差的散点图 plt.scatter(range(len(errors)), errors, c='blue', label="Reprojection Errors") plt.title("Reprojection Errors Scatter Plot") plt.xlabel("Point Index") plt.ylabel("Error Value (pixels)") plt.legend() plt.grid(True) plt.show() ``` #### 解释 上述代码实现了以下几个功能: - **输入数据准备**:定义世界坐标系下的 3D 点 (`object_points`) 和对应的图像平面点 (`image_points`)。 - **求解外参数**:调用 `cv.solvePnP()` 来获取旋转矢量 (`rvec`) 和平移矢量 (`tvec`)。 - **重新投影**:利用 `cv.projectPoints()` 将 3D 点映射回图像平面。 - **误差计**:通过 Euclidean 距离测量原点与重新投影点的距离差[^1]。 - **绘图显示**:借助 Matplotlib 库生成散点图可视化结果。 #### 注意事项 如果需要更精确的结果,请确保校准相机以减少镜头失真影响,并提供足够的匹配点对提升姿态估精度[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

3D视觉工坊

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值