OpenCV相机标定与3D重修(54)解决透视 n 点问题(Perspective-n-Point, PnP ...

打印 上一主题 下一主题

主题 1047|帖子 1047|积分 3141

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x

  • 操作系统:ubuntu22.04
  • OpenCV版本:OpenCV4.9
  • IDE:Visual Studio Code
  • 编程语言:C++11
算法描述

根据3D-2D点对应关系找到物体的姿态。
cv::solvePnP 是 OpenCV 库中的一个函数,用于解决透视 n 点问题(Perspective-n-Point, PnP),即通过已知的 3D 点及其对应的 2D 图像点来估计物体的姿态(旋转宁静移)。这个函数可以处理任意数量的点对,并且提供了多种算法来求解姿态。
此函数返盘旋转宁静移向量,这些向量将用物体坐标系表现的3D点变换到相机坐标系中,利用不同的方法:
P3P 方法(SOLVEPNP_P3P, SOLVEPNP_AP3P):需要4个输入点来返回一个唯一解。
SOLVEPNP_IPPE:输入点必须 >= 4 且物体点必须共面。
SOLVEPNP_IPPE_SQUARE:实用于标记姿态估计的特殊环境。输入点的数量必须是4。物体点必须按以下顺序界说:


  • 点 0: [-squareLength / 2, squareLength / 2, 0]
  • 点 1: [ squareLength / 2, squareLength / 2, 0]
  • 点 2: [ squareLength / 2, -squareLength / 2, 0]
  • 点 3: [-squareLength / 2, -squareLength / 2, 0]
    对于所有其他标记,输入点的数量必须 >= 4,且物体点可以是任意设置。
函数原型

  1. bool cv::solvePnP
  2. (
  3.         InputArray         objectPoints,
  4.         InputArray         imagePoints,
  5.         InputArray         cameraMatrix,
  6.         InputArray         distCoeffs,
  7.         OutputArray         rvec,
  8.         OutputArray         tvec,
  9.         bool         useExtrinsicGuess = false,
  10.         int         flags = SOLVEPNP_ITERATIVE
  11. )               
复制代码
参数



  • 参数objectPoints:物体坐标空间中的物体点数组,格式为 Nx3 的单通道或 1xN/Nx1 的三通道,其中 N 是点的数量。也可以通报 vector。
  • 参数imagePoints:对应的图像点数组,格式为 Nx2 的单通道或 1xN/Nx1 的双通道,其中 N 是点的数量。也可以通报 vector。
  • 参数cameraMatrix:输入的相机内参矩阵                                              A                               =                                           [                                                                                                             f                                                 x                                                                                                                    0                                                                                                                     c                                                 x                                                                                                                                            0                                                                                                                     f                                                 y                                                                                                                                     c                                                 y                                                                                                                                            0                                                                                                    0                                                                                                    1                                                                                        ]                                                 A = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix}                        A=                ​fx​00​0fy​0​cx​cy​1​                ​

  • 参数distCoeffs:输入的畸变系数向量 (k1, k2, p1, p2[, k3[, k4, k5, k6[, s1, s2, s3, s4[, τx, τy]]]]),包含 4、5、8、12 或 14 个元素。假如该向量为空,则假设畸变为零。
  • 参数rvec:输出的旋转向量(见 Rodrigues),与 tvec 一起利用,将模型坐标系中的点变换到相机坐标系中。
  • 参数tvec:输出的平移向量。
  • 参数useExtrinsicGuess:仅用于 SOLVEPNP_ITERATIVE 方法。假如为 true(1),函数会利用提供的 rvec 和 tvec 值作为旋转宁静移向量的初始近似值,并进一步优化它们。
  • 参数flags:解决 PnP 问题的方法,详见 calib3d_solvePnP_flags。
注意


  • 关于怎样利用 solvePnP 举行平面增强现实的一个示例可以在 opencv_source_code/samples/python/plane_ar.py 找到。
  • 假如你利用的是 Python:

    • Numpy 数组切片不能作为输入,因为 solvePnP 需要一连的数组(在版本 2.4.9 的 modules/calib3d/src/solvepnp.cpp 文件大约第 55 行通过 cv::Mat::checkVector() 断言强制要求)。
    • P3P 算法要求图像点位于外形为 (N,1,2) 的数组中,因为它调用了 undistortPoints(在版本 2.4.9 的 modules/calib3d/src/solvepnp.cpp 文件大约第 75 行),这需要双通道信息。
    • 因此,给定一些数据 D = np.array(…),其中 D.shape = (N,M),为了利用其子集作为比方 imagePoints,必须有用地将其复制到一个新数组中:imagePoints = np.ascontiguousarray(D[:,:2]).reshape((N,1,2))。

  • 方法 SOLVEPNP_DLS 和 SOLVEPNP_UPNP 不能利用,因为当前实现不稳固,偶然会给出完全错误的结果。假如你通报了这两个标记中的一个,则会利用 SOLVEPNP_EPNP 方法代替。
  • 在一般环境下,最少需要 4 个点。
  • 对于 SOLVEPNP_P3P 和 SOLVEPNP_AP3P 方法,必须利用恰好 4 个点(前 3 个点用于估计 P3P 问题的所有解,最后一个点用于保留最小化重投影误差的最佳解)。
  • 利用 SOLVEPNP_ITERATIVE 方法且 useExtrinsicGuess=true 时,最少需要 3 个点(3 个点足以计算姿态,但最多有 4 个解)。初始解应接近全局解以收敛。
  • 利用 SOLVEPNP_IPPE 时,输入点必须 >= 4 且物体点必须共面。
  • 利用 SOLVEPNP_IPPE_SQUARE 时,这是一个实用于标记姿态估计的特殊环境。输入点的数量必须是 4。物体点必须按以下顺序界说:

    • 点 0: [-squareLength / 2, squareLength / 2, 0]
    • 点 1: [ squareLength / 2, squareLength / 2, 0]
    • 点 2: [ squareLength / 2, -squareLength / 2, 0]
    • 点 3: [-squareLength / 2, -squareLength / 2, 0]


利用 SOLVEPNP_SQPNP 时,输入点必须 >= 3。
代码示例

  1. #include <iostream>
  2. #include <opencv2/opencv.hpp>
  3. #include <vector>
  4. using namespace cv;
  5. using namespace std;
  6. int main()
  7. {
  8.     // 假设我们有一个已知的 3D 点集 (例如一个正方形的四个角)
  9.     std::vector< Point3f > objectPoints = { Point3f( -1.0f, -1.0f, 0.0f ), Point3f( 1.0f, -1.0f, 0.0f ), Point3f( 1.0f, 1.0f, 0.0f ), Point3f( -1.0f, 1.0f, 0.0f ) };
  10.     // 对应的 2D 图像点 (这些点是从图像中检测到的特征点)
  11.     std::vector< Point2f > imagePoints = { Point2f( 594.0f, 487.0f ), Point2f( 673.0f, 487.0f ), Point2f( 673.0f, 552.0f ), Point2f( 594.0f, 552.0f ) };
  12.     // 相机内参矩阵 (假设已知)
  13.     Mat cameraMatrix = ( Mat_< double >( 3, 3 ) << 718.856, 0, 607.1928, 0, 718.856, 185.2157, 0, 0, 1 );
  14.     // 畸变系数 (假设已知)
  15.     Mat distCoeffs = Mat::zeros( 5, 1, CV_64F );  // 如果没有畸变或忽略畸变,则可以是零矩阵
  16.     // 初始化输出变量
  17.     Mat rvec;  // 旋转向量
  18.     Mat tvec;  // 平移向量
  19.     // 调用 solvePnP 函数
  20.     bool success = solvePnP( objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec, false, SOLVEPNP_ITERATIVE );
  21.     if ( success )
  22.     {
  23.         cout << "Rotation Vector:\n" << rvec << "\nTranslation Vector:\n" << tvec << endl;
  24.         // 可选:将旋转向量转换为旋转矩阵以更好地理解结果
  25.         Mat rotationMatrix;
  26.         Rodrigues( rvec, rotationMatrix );
  27.         cout << "Rotation Matrix:\n" << rotationMatrix << endl;
  28.     }
  29.     else
  30.     {
  31.         cout << "solvePnP failed." << endl;
  32.     }
  33.     return 0;
  34. }
复制代码
运行结果

  1. Rotation Vector:
  2. [0.2895361443049176;
  3. 0.01328548677652798;
  4. -0.008684530349597173]
  5. Translation Vector:
  6. [0.6665924885943908;
  7. 8.493287223698232;
  8. 18.23641869746051]
  9. Rotation Matrix:
  10. [0.999874917527441, 0.01047321277960457, 0.01185162915241468;
  11. -0.006653461772789516, 0.9583398410008748, -0.2855529383439369;
  12. -0.01434854508064377, 0.2854383663148514, 0.9582896526048779]
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

麻花痒

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表