• cv::solvePnP使用方法及注意点详解(OpenCV/C++)


    cv::solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess, flags);

    1、参数说明:

    • objectPoints:一个 vector,包含了在世界坐标系中的三维点的坐标,至少需要4个点。
    • imagePoints:一个 vector,包含了对应的图像上的二维点的坐标,与 objectPoints 中的点一一对应。
    • cameraMatrix:相机的内参数矩阵,类型为 cv::Mat,一般为 3x3 的浮点数矩阵。
    • distCoeffs:相机的畸变系数,类型为 cv::Mat,一般为 4x1 或 5x1 的浮点数矩阵。
    • rvec:输出的旋转向量,类型为 cv::Mat,是大小为 3x1 的浮点数矩阵。
    • tvec:输出的平移向量,类型为 cv::Mat,是大小为 3x1 的浮点数矩阵。
    • useExtrinsicGuess:一个布尔值,表示是否使用可选的旋转和平移向量的初始猜测。默认为 false。
    • flags:一个用于控制函数行为的选项标志,默认为 0。

            函数返回:

    • 成功返回 true,失败返回 false。

    2、使用说明:

    objectPoints, imagePoints, cameraMatrix, distCoeffs  四个参数作为输入参数

    rvec, tvec 作为输出参数

    objectPoints是世界坐标系的三维坐标

    imagePoints是图像上的二维点坐标

    例如 使用标定好的单目相机拍摄一个矩形物体(已知相机内参&畸变系数),

    objectPoints:使用量尺测量物体的左上角、右上角、左下角、右下角之间的距离,以任意一个点作为0点,建立世界坐标系,z值设为0,得到所有点的坐标则为objectPoints,保存在vector中。

    imagePoints:在图像中找到物体的左上角、右上角、左下角、右下角四个点,其所有点的像素坐标为imagePoints,保存在vector中。

    使用示例:

    1. #include <iostream>
    2. #include <vector>
    3. #include <opencv2/opencv.hpp>
    4. int main() {
    5. std::vector<cv::Point3f> objectPoints; // 世界坐标系中的三维点
    6. std::vector<cv::Point2f> imagePoints; // 图像上的二维点
    7. // 添加 objectPoints 和 imagePoints 的数据
    8. // 创建相机内参数矩阵
    9. cv::Mat cameraMatrix = (cv::Mat_<double>(3,3) << fx, 0, cx, 0, fy, cy, 0, 0, 1);
    10. // 创建相机畸变系数矩阵
    11. cv::Mat distCoeffs = (cv::Mat_<double>(1,5) << k1, k2, p1, p2, k3;
    12. //完善内参参数&畸变系数参数
    13. cv::Mat rvec; // 输出的旋转向量
    14. cv::Mat tvec; // 输出的平移向量
    15. bool success = cv::solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec);
    16. if (success) {
    17. // 获取旋转向量和平移向量的结果
    18. cv::Mat rotationMatrix;
    19. cv::Rodrigues(rvec, rotationMatrix);
    20. std::cout << "Rotation Vector:" << std::endl << rvec << std::endl;
    21. std::cout << "Translation Vector:" << std::endl << tvec << std::endl;
    22. std::cout << "Rotation Matrix:" << std::endl << rotationMatrix << std::endl;
    23. }
    24. return 0;
    25. }

    3、注意事项:

    在使用solvePnP时,需注意objectPoints和imagePoints容器中的点坐标必须一一对应,例如只有四个点时,全部按左上角、右上角、左下角、右下角的顺序存放在容器中;如果顺序不相同,则最终输出值有误。

    在笔者使用solvePnP时,拍摄物是四个定位圆,圆的像素坐标是通过opencv的SimpleBlobDetector识别的,识别以后的圆像素坐标是无序的,无法和objectPoints对应上,因此有了下述算法,

    用于为四个二维坐标做冒泡排序得到左上角、右上角、左下角、右下角分别对应的点

    (如果没有该需求,可以忽略此段)

    1. int main() {
    2. std::vector<cv::Point2f> imagePoints; // 存放四个点的 vector
    3. // 假设已经将四个点的坐标存入 imagePoints 中
    4. // 寻找左上角、右上角、右下角和左下角对应的点
    5. cv::Point2f topLeft, topRight, bottomRight, bottomLeft;
    6. float minX = FLT_MAX, minY = FLT_MAX;
    7. float maxX = FLT_MIN, maxY = FLT_MIN;
    8. for (const auto& point : imagePoints) {
    9. if (point.x <= minX && point.y <= minY) {
    10. topLeft = point;
    11. minX = point.x;
    12. minY = point.y;
    13. }
    14. if (point.x >= maxX && point.y <= minY) {
    15. topRight = point;
    16. maxX = point.x;
    17. minY = point.y;
    18. }
    19. if (point.x >= maxX && point.y >= maxY) {
    20. bottomRight = point;
    21. maxX = point.x;
    22. maxY = point.y;
    23. }
    24. if (point.x <= minX && point.y >= maxY) {
    25. bottomLeft = point;
    26. minX = point.x;
    27. maxY = point.y;
    28. }
    29. }
    30. // 输出左上角、右上角、右下角和左下角对应的点的坐标
    31. std::cout << "左上角坐标: (" << topLeft.x << ", " << topLeft.y << ")" << std::endl;
    32. std::cout << "右上角坐标: (" << topRight.x << ", " << topRight.y << ")" << std::endl;
    33. std::cout << "右下角坐标: (" << bottomRight.x << ", " << bottomRight.y << ")" << std::endl;
    34. std::cout << "左下角坐标: (" << bottomLeft.x << ", " << bottomLeft.y << ")" << std::endl;
    35. // 新建一个vector存放四个点坐标,按照objectPoints的存放顺序进行存放
    36. std::vector<cv::Point2f> imagePoints2;
    37. imagePoints2.push_back(topLeft);
    38. imagePoints2.push_back(topRight);
    39. imagePoints2.push_back(bottomRight);
    40. imagePoints2.push_back(bottomLeft);
    41. return 0;
    42. }

    4、补充

    通过solvePnP得到旋转向量rvec和平移向量tvec后,可以计算相机到被测物中心的实际距离

    1. #include <cmath>
    2. #include <iostream>
    3. #include <vector>
    4. #include <opencv2/opencv.hpp>
    5. int main() {
    6. std::vector<cv::Point3f> objectPoints; // 世界坐标系中的三维点
    7. std::vector<cv::Point2f> imagePoints; // 图像上的二维点
    8. // 添加 objectPoints 和 imagePoints 的数据
    9. // 创建相机内参数矩阵
    10. cv::Mat cameraMatrix = (cv::Mat_<double>(3,3) << fx, 0, cx, 0, fy, cy, 0, 0, 1);
    11. // 创建相机畸变系数矩阵
    12. cv::Mat distCoeffs = (cv::Mat_<double>(1,5) << k1, k2, p1, p2, k3;
    13. //完善内参参数&畸变系数参数
    14. cv::Mat rvec; // 输出的旋转向量
    15. cv::Mat tvec; // 输出的平移向量
    16. bool success = cv::solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec);
    17. if (success) {
    18. //计算相机距离被测物的实际距离
    19. float distance = sqrt(tvec.at<double>(0,0) * tvec.at<double>(0,0) + tvec.at<double>(1,0) * tvec.at<double>(1,0) + tvec.at<double>(2,0) * tvec.at<double>(2,0)) / 10;
    20. std::cout << "distance = "<< distance << std::endl;
    21. }
    22. return 0;
    23. }

  • 相关阅读:
    大神方案|如何重写一个万行代码的类文件
    java项目-第102期基于ssm的校园二手交易平台-java毕业设计
    Java 多线程之 synchronized (互拆锁/排他锁/非观锁)
    体验 ABP 的功能和服务
    Linux 网络协议栈收消息过程-Ring Buffer
    搭建vue3.2+vite+ts+pinia项目
    【最佳实践】瀚高数据库企业版v6.0.2在Centos7.8安装过程
    内核APC&用户APC详解
    虹科产品 | HK-ATTO 光纤通道卡利用FC-NVMe 提升全闪存存储阵列性能
    数据探索与分析的瑞士军刀:深入Python的pandas库
  • 原文地址:https://blog.csdn.net/qq_19319481/article/details/134013308