20

我在使用 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 个图像)。

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

照片 1

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

照片 2 照片 3

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

如果有问题我如何检测标记:我正在使用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是处理图像中标记角的坐标(我正在使用自定义算法来做到这一点)

4

3 回答 3

5

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

我假设您正在遵循@kobejohn在他的评论中引用的教程(校准姿势)中建议的方法,因此您的代码遵循以下步骤:

  1. 收集棋盘目标的各种图像
  2. 在第 1 点的图像中找到棋盘角)
  3. 校准相机(用cv::calibrateCamera),从而获得相机的内在参数(我们称之为intrinsic)和镜头畸变参数(我们称之为distortion
  4. 收集您自己的自定义目标的图像(目标在您的视频中 0:57看到),如下图所示Axadiw 自己的自定义目标并在其中找到一些相关点(我们称您在图像中找到的点image_custom_target_verticesworld_custom_target_vertices相应的 3D 点)。
  5. 根据您在第 4 点获得的自定义目标的图像估计相机的旋转矩阵(我们称之为R)和平移向量(我们称之为t),并调用cv::solvePnPlike thiscv::solvePnP(world_custom_target_vertices,image_custom_target_vertices,intrinsic,distortion,R,t)
  6. 在 3D 中给出 8 个角立方体(我们称之为它们),您可以通过调用like this来world_cube_vertices获得 8 个 2D 图像点(我们称之为它们image_cube_verticescv2::projectPointscv::projectPoints(world_cube_vertices,R,t,intrinsic,distortion,image_cube_vertices)
  7. draw用你自己的函数绘制立方体。

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

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

重投影误差

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

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

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

相机的姿势:我认为在5)中您使用intrinsic3)中的发现,您确定同时相机中没有任何变化吗?参考Callari的相机校准第二规则

相机校准第二条规则:“校准后请勿触摸镜头”。特别是,您可能不会重新对焦或更改 f-stop,因为对焦和光圈都会影响非线性镜头失真和(尽管影响较小,取决于镜头)视野。当然,您可以完全自由地更改曝光时间,因为它根本不会影响镜头几何形状。

然后功能可能会出现一些问题draw

于 2013-11-24T10:57:07.760 回答
2

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

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

我尝试了各种校准模式:

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

照片 1 照片 2

我已经尝试过CALIB_CB_CLUSTERING,但并没有太大帮助 - 在某些情况下(不同的光环境)它变得更好,但没有太大帮助。

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

照片 3

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

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

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

我也尝试过useExtrinsicGuessset to true,并且我已经使用了rvecandtvec来自以前的检测,但是这导致检测到的立方体完全消失。

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

于 2013-11-20T00:36:43.980 回答
1

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

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

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

于 2015-07-24T15:51:44.400 回答