我在使用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
是处理图像时标记角点的坐标(我正在使用自定义算法来实现)
发布于 2013-11-24 10:57:08
为了正确调试你的问题,我需要所有的代码:-)
我假设您正在遵循@kobejohn在他的comment中引用的教程(calibration和pose)中建议的方法,因此您的代码遵循以下步骤:
cv::calibrateCamera
),因此获得内部相机参数(让我们称它们为intrinsic
)和镜头失真参数(让我们称它们为您自己自定义目标的图像(目标可在0:57 in your video上看到),如下图<代码>H216<代码>G217所示并在其中找到一些相关点(让我们将您在图像image_custom_target_vertices
中找到的点命名为world_custom_target_vertices
,并将相应的3D点命名为该点)。
R
)和平移向量(我们称其为t
)来自您在点4中获得的自定义目标的图像),通过像这样的cv::solvePnP
调用cv::solvePnP(world_custom_target_vertices,image_custom_target_vertices,intrinsic,distortion,R,t)
world_cube_vertices
),您可以通过像这样的cv2::projectPoints
调用获得8个2D图像点(让我们称它们为draw
)。现在,绘制过程的最终结果取决于所有以前计算的数据,我们必须找出问题所在:
Calibration:正如您在answer中观察到的,在3)中,您应该丢弃未正确检测到角点的图像。你需要一个重新投影误差的阈值,以便丢弃“坏的”棋盘目标图像。引用自calibration tutorial
重新投影错误
重新投影误差很好地估计了找到的参数的精确度。这应该尽可能接近于零。给定固有矩阵、失真矩阵、旋转矩阵和平移矩阵,我们首先使用cv2.projectPoints()将对象点转换为图像点。然后,我们计算我们通过变换得到的结果和角点查找算法之间的绝对范数。为了找到平均误差,我们计算所有校准图像的误差的算术平均值。
通常你会通过一些实验找到一个合适的阈值。有了这个额外的步骤,您将获得更好的intrinsic
和distortion
值。
查找您自己的自定义目标:在我标记为第4点的步骤中,您似乎没有解释如何查找您自己的自定义目标。你得到预期的image_custom_target_vertices
了吗?你会丢弃那些结果“不好”的图像吗?
相机的姿势:我想你在5)中使用了3)中的intrinsic
,你确定在此期间相机没有任何变化吗?参考Callari's Second Rule of Camera Calibration
摄像机校准第二条规则:“校准后不得触摸镜头”。特别是,您不能重新调焦或更改f光圈,因为调焦和光圈都会影响非线性镜头失真和(尽管影响较小,取决于镜头)视野。当然,您可以完全自由地更改曝光时间,因为它根本不会影响镜头的几何形状。
然后在draw
函数中可能会有一些问题。
发布于 2013-11-20 00:36:44
所以,我已经用我的代码做了很多实验,我仍然没有修复主要的问题(移动对象),但我已经设法回答了我提出的一些校准问题。
首先-为了获得良好的校准结果,你必须使用正确检测到网格元素/圆位置的图像!。在校准过程中使用所有捕获的图像(即使是那些没有正确检测的图像)将导致糟糕的校准。
我尝试过各种校准模式:
CALIB_CB_ASYMMETRIC_GRID
),得到的结果比其他任何模式都差得多。说到更糟糕的结果,我的意思是它会产生很多像这样的错误检测到的角落:
我已经用CALIB_CB_CLUSTERING
做了实验,但效果不是很好--在某些情况下(不同的光环境),它变得更好了,但效果不是很好。
CALIB_CB_SYMMETRIC_GRID
) -比非对称网格更好的结果,但我得到的结果仍然比标准网格(棋盘)差得多。它通常会产生这样的错误:
findChessboardCorners
函数找到)-此方法可以产生最佳结果-它不会经常产生未对齐的角点,并且几乎每个校准都会产生与对称圆grid的最佳结果相似的结果
对于每一次校准,我都使用了20-30张来自不同角度的图像。我甚至尝试过100+图像,但与较小数量的图像相比,它在校准结果上并没有产生明显的变化。值得注意的是,更多的测试图像增加了以非线性方式计算相机参数所需的时间( 480x360分辨率的100张测试图像在iPad4中计算25分钟,而约50张图像计算4分钟)
我也尝试过solvePNP
参数,但也没有给我任何可接受的结果:我尝试了所有3种检测方法(ITERATIVE
,EPNP
和P3P
),但我没有看到明显的变化。
此外,我还尝试了将useExtrinsicGuess
设置为true
,并使用了之前检测到的rvec
和tvec
,但这次检测到的立方体完全消失了。
我的想法已经用完了--还有什么会影响这些转移问题呢?
发布于 2015-07-24 15:51:44
对于那些仍然感兴趣的人:这是一个老问题,但我认为你的问题不是糟糕的校准。我使用OpenCV和SceneKit为iOS开发了一个AR应用程序,我也遇到过同样的问题。
我认为你的问题是立方体的渲染位置错误:OpenCV的solvePnP返回标记中心的X,Y,Z坐标,但你想在标记上渲染立方体,沿着标记的Z轴的特定距离,恰好是立方体侧面大小的一半。因此,您需要改进此距离的标记平移向量的Z坐标。
实际上,当你从顶部看到你的立方体时,这个立方体被正确地渲染了。我已经做了一张图片来解释这个问题,但是我的名声阻止了我把它贴出来。
https://stackoverflow.com/questions/19849683
复制相似问题