【问题标题】:OpenCV: solvePnP detection problemsOpenCV:解决即插即用检测问题
【发布时间】:2013-11-19 21:49:51
【问题描述】:

我在使用 OpenCV 精确检测标记时遇到了问题。

我已经录制了介绍该问题的视频:http://youtu.be/IeSSW4MdyfU

如您所见,我正在检测的标记在某些摄像机角度略有移动。我在网上看到这可能是相机校准问题,所以我会告诉你们我是如何校准相机的,也许你们能告诉我我做错了什么?

在开始时,我从各种图像中收集数据,并将校准角存储在_imagePoints 向量中,如下所示

std::vector<cv::Point2f> corners;
_imageSize = cvSize(image->size().width, image->size().height);

bool found = cv::findChessboardCorners(*image, _patternSize, corners);

if (found) {
    cv::Mat *gray_image = new cv::Mat(image->size().height, image->size().width, CV_8UC1);
    cv::cvtColor(*image, *gray_image, CV_RGB2GRAY);

    cv::cornerSubPix(*gray_image, corners, cvSize(11, 11), cvSize(-1, -1), cvTermCriteria(CV_TERMCRIT_EPS+ CV_TERMCRIT_ITER, 30, 0.1));

    cv::drawChessboardCorners(*image, _patternSize, corners, found);
}

_imagePoints->push_back(_corners);

然后,在收集到足够的数据后,我正在使用以下代码计算相机矩阵和系数:

std::vector< std::vector<cv::Point3f> > *objectPoints = new std::vector< std::vector< cv::Point3f> >();

for (unsigned long i = 0; i < _imagePoints->size(); i++) {
    std::vector<cv::Point2f> currentImagePoints = _imagePoints->at(i);
    std::vector<cv::Point3f> currentObjectPoints;

    for (int j = 0; j < currentImagePoints.size(); j++) {
        cv::Point3f newPoint = cv::Point3f(j % _patternSize.width, j / _patternSize.width, 0);

        currentObjectPoints.push_back(newPoint);
    }

    objectPoints->push_back(currentObjectPoints);
}

std::vector<cv::Mat> rvecs, tvecs;

static CGSize size = CGSizeMake(_imageSize.width, _imageSize.height);
cv::Mat cameraMatrix = [_userDefaultsManager cameraMatrixwithCurrentResolution:size]; // previously detected matrix
cv::Mat coeffs = _userDefaultsManager.distCoeffs; // previously detected coeffs
cv::calibrateCamera(*objectPoints, *_imagePoints, _imageSize, cameraMatrix, coeffs, rvecs, tvecs);

结果与您在视频中看到的一样。

我做错了什么?这是代码中的问题吗?我应该使用多少图像来执行校准(现在我正在尝试在校准结束之前获取 20-30 张图像)。

我是否应该使用包含错误检测到的棋盘角的图像,如下所示:

或者我应该只使用正确检测到的棋盘,如下所示:

我一直在尝试用圆形网格代替棋盘,但结果比现在差得多。

如果对我如何检测标记有疑问:我正在使用solvepnp 函数:

solvePnP(modelPoints, imagePoints, [_arEngine currentCameraMatrix], _userDefaultsManager.distCoeffs, rvec, tvec);

modelPoints 指定如下:

    markerPoints3D.push_back(cv::Point3d(-kMarkerRealSize / 2.0f, -kMarkerRealSize / 2.0f, 0));
    markerPoints3D.push_back(cv::Point3d(kMarkerRealSize / 2.0f, -kMarkerRealSize / 2.0f, 0));
    markerPoints3D.push_back(cv::Point3d(kMarkerRealSize / 2.0f, kMarkerRealSize / 2.0f, 0));
    markerPoints3D.push_back(cv::Point3d(-kMarkerRealSize / 2.0f, kMarkerRealSize / 2.0f, 0));

imagePoints 是处理图像中标记角的坐标(我正在使用自定义算法来做到这一点)

【问题讨论】:

  • 我很快就没有时间尝试这个了,但我环顾四周寻找一个起点(尽管 C++ 和 python 接口非常相似,但我在 Python 中)。我在 python 中找到了this tutorial(请确保检查校准和下一个关于姿势估计的问题),其中提到了您所询问的许多相同问题。也许那里的一些提示/指导对您有用?
  • 感谢本教程的链接,但我不认为它会在这种情况下对我有所帮助。我或多或少地以与教程中所写相同的方式校准相机。校准后,该教程的作者在输入图像上使用undistort 方法,但我认为如果我在solvePnP 中使用该函数的结果图像,它会损坏。我认为在为该函数提供cameraMatrix 和失真系数时就是这种情况,因此solvePnP 会自行使图像不失真。
  • 最后是相反的问题 - projectPointsmetod 用于从 3D 到 2D 对象的转换,而不是相反 :)
  • 我明白了。对于那个很抱歉!将来我一定会尝试让您的系统正常工作,但这需要一段时间。

标签: ios opencv camera markers camera-calibration


【解决方案1】:

所以,我已经对我的代码进行了很多试验,但我仍然没有解决主要问题(移动对象),但我已经设法回答了我提出的一些校准问题。

首先 - 为了获得良好的校准结果您必须使用正确检测到网格元素/圆圈位置的图像!。在校准过程中使用所有捕获的图像(即使是那些未被正确检测到的图像)将导致校准错误。

我尝试过各种校准模式:

  • 非对称圆形图案 (CALIB_CB_ASYMMETRIC_GRID),结果比任何其他图案都差。更糟糕的结果是指它会产生很多错误检测的角点,如下所示:

我已经用CALIB_CB_CLUSTERING 进行了实验,但它并没有太大帮助 - 在某些情况下(不同的光照环境)它变得更好,但效果不大。

  • 对称圆圈图案 (CALIB_CB_SYMMETRIC_GRID) - 比非对称网格更好,但我的结果仍然比标准网格(棋盘)差得多。它通常会产生如下错误:

  • Chessboard(使用findChessboardCorners 函数找到)- 这种方法产生了最好的结果- 它不会经常产生未对齐的角,并且几乎每次校准都会产生与 对称圆形网格

对于每次校准,我都使用了 20-30 张来自不同角度的图像。我什至尝试了 100 多张图像,但与少量图像相比,校准结果并没有产生明显的变化。值得注意的是,大量的测试图像增加了以非线性方式计算相机参数所需的时间(100 个 480x360 分辨率的测试图像在 iPad4 中计算时间为 25 分钟,而在大约 50 个图像中需要 4 分钟)

我还尝试了solvePNP 参数 - 但也没有给我任何可接受的结果:我尝试了所有 3 种检测方法(ITERATIVEEPNPP3P),但我没有看到明显的变化。

我还尝试将useExtrinsicGuess 设置为true,并使用之前检测中的rvectvec,但这导致检测到的立方体完全消失。

我的想法已经用完了 - 还有什么可能会影响这些不断变化的问题?

【讨论】:

  • 不幸的是 - solvePnPRansac 给出了与 solvePnP 相同的结果 - 我尝试了各种参数(迭代次数 / reprojectionError 等)但没有运气。
  • P3P 将仅使用四个对象/图像点,引用自docs.opencv.org/modules/calib3d/doc/… CV_P3P 方法基于 X.S. 的论文。高,X.-R。侯,J.唐,H.-F。 Chang“透视三点问题的完整解分类”。在这种情况下,该函数需要四个对象和图像点。
【解决方案2】:

为了正确调试您的问题,我需要所有代码 :-)

我假设您正在遵循 @kobejohn 在他的 comment 中引用的教程(calibrationpose)中建议的方法,以便您的代码遵循以下步骤:

  1. 收集棋盘目标的各种图像
  2. 在点 1) 的图像中查找棋盘角
  3. 校准相机(使用cv::calibrateCamera)并由此获得相机固有参数(我们称之为intrinsic)和镜头畸变参数(我们称之为distortion
  4. 收集你自己的自定义目标的图像(目标在0:57 in your video看到),如下图所示,并在其中找到一些相关点(我们称你在图像中找到的点@987654335 @ 和 world_custom_target_vertices 对应的 3D 点)。
  5. 通过调用@ 987654326@赞这个cv::solvePnP(world_custom_target_vertices,image_custom_target_vertices,intrinsic,distortion,R,t)
  6. 在 3D 中给出 8 个角的立方体(我们称它们为 world_cube_vertices),您可以通过调用 cv2::projectPoints 得到 8 个 2D 图像点(我们称它们为 image_cube_vertices),就像这个 cv::projectPoints(world_cube_vertices,R,t,intrinsic,distortion,image_cube_vertices)
  7. 使用您自己的draw 函数绘制立方体。

现在,绘制过程的最终结果取决于所有之前的计算数据,我们必须找出问题所在:

校准:正如您在answer 中观察到的那样,在 3) 中,您应该丢弃未正确检测到角的图像。您需要重新投影错误的阈值才能丢弃“坏”棋盘目标图像。引用自calibration tutorial

重投影错误

重投影误差可以很好地估计 找到的参数。这应该尽可能接近于零。给定 固有矩阵、失真矩阵、旋转矩阵和平移矩阵,我们首先 使用 cv2.projectPoints() 将对象点转换为图像点。 然后我们计算我们得到的绝对范数 变换和寻角算法。找到平均值 误差我们计算误差的算术平均值 所有校准图像。

通常您会通过一些实验找到合适的阈值。通过这个额外的步骤,您将获得更好的 intrinsicdistortion 值。

找到您自己的自定义目标:在我看来,您没有解释如何在我标记为第 4 点的步骤中找到自己的自定义目标)。你得到预期的image_custom_target_vertices 了吗?你会丢弃结果“不好”的图像吗?

相机的姿势:我认为在 5) 中你使用了 3) 中的intrinsic,你确定同时相机中没有任何变化吗?参考Callari's Second Rule of Camera Calibration

相机校准的第二条规则:“你不能触摸镜头 校准后”。特别是,您不得重新聚焦或更改 f-stop,因为聚焦和光圈都会影响非线性镜头 失真和(尽管不那么严重,取决于镜头) 看法。当然,你完全可以随意改变曝光时间, 因为它根本不会影响镜头的几何形状。

然后draw函数可能会出现一些问题。

【讨论】:

  • 这里是完整代码:bitbucket.org/axadiw/augmentedmgr 虽然 UI 是用波兰语写的,但所有变量/cmets 都是用英文写的,所以应该是可读的 :) 应用程序的大部分部分都是用 Objective-C 编写的,与 OpenCV 连接的部分是用 C++ 编写的。相机标定在 AMCalibrator 类中处理,相机位姿检测在 AMCameraPoseDetector 中处理。在 AMMarkersDetector 中检测到标记
  • 关于您的问题 - 我正在做与您列出的几乎相同的事情,但有一个区别 - 我在标记检测中使用 4 分而不是 8 分。因为我没有手动绘制每个顶点(我使用的是由 NinevehGL 渲染的模型 - 简单的 iOS 3D 引擎) - 我直接使用相机平移和旋转矢量作为 3D 场景中的相机位置
【解决方案3】:

对于那些仍然感兴趣的人: 这是一个老问题,但我认为你的问题不是校准不好。 我使用 OpenCV 和 SceneKit 为 iOS 开发了一个 AR 应用程序,我遇到了同样的问题。

我认为你的问题是立方体的错误渲染位置: OpenCV 的solvePnP 返回标记中心的X、Y、Z 坐标,但是您想在标记上渲染立方体,沿着标记的Z 轴的特定距离,正好是立方体边尺寸的一半。所以需要改进这个距离的标记平移向量的Z坐标。

事实上,当你从顶部看到你的立方体时,立方体就被正确渲染了。 我做了一张图片来解释这个问题,但我的名声不允许发布它。

【讨论】:

    猜你喜欢
    • 2013-01-08
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多