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