我正在尝试使用OpenCV做一些基本的增强现实。我要使用的方法是使用findChessboardCorners
从摄像机图像中获取一组点。然后,我沿着z = 0平面创建一个3D四边形,并使用solvePnP
获得成像点和平面点之间的单应性。由此,我认为我应该能够建立一个Modelview矩阵,使我可以在图像上方以正确的姿势渲染一个立方体。solvePnP
的documentation说,它输出一个旋转矢量“(与[翻译矢量]一起)将点从模型坐标系带到相机坐标系”。我认为这与我想要的相反。由于我的四边形在z = 0平面上,因此我需要一个Modelview矩阵,它将该四边形转换为适当的3D平面。
我认为通过以相反的顺序执行相反的旋转和平移,我可以计算出正确的Modelview矩阵,但这似乎行不通。尽管渲染的对象(立方体)确实随相机图像移动,并且似乎在翻译上大致正确,但是旋转根本不起作用;当它只能在一个轴上旋转,有时方向错误时,它会在多个轴上旋转。到目前为止,这是我正在做的事情:
std::vector<Point2f> corners;
bool found = findChessboardCorners(*_imageBuffer, cv::Size(5,4), corners,
CV_CALIB_CB_FILTER_QUADS |
CV_CALIB_CB_FAST_CHECK);
if(found)
{
drawChessboardCorners(*_imageBuffer, cv::Size(6, 5), corners, found);
std::vector<double> distortionCoefficients(5); // camera distortion
distortionCoefficients[0] = 0.070969;
distortionCoefficients[1] = 0.777647;
distortionCoefficients[2] = -0.009131;
distortionCoefficients[3] = -0.013867;
distortionCoefficients[4] = -5.141519;
// Since the image was resized, we need to scale the found corner points
float sw = _width / SMALL_WIDTH;
float sh = _height / SMALL_HEIGHT;
std::vector<Point2f> board_verts;
board_verts.push_back(Point2f(corners[0].x * sw, corners[0].y * sh));
board_verts.push_back(Point2f(corners[15].x * sw, corners[15].y * sh));
board_verts.push_back(Point2f(corners[19].x * sw, corners[19].y * sh));
board_verts.push_back(Point2f(corners[4].x * sw, corners[4].y * sh));
Mat boardMat(board_verts);
std::vector<Point3f> square_verts;
square_verts.push_back(Point3f(-1, 1, 0));
square_verts.push_back(Point3f(-1, -1, 0));
square_verts.push_back(Point3f(1, -1, 0));
square_verts.push_back(Point3f(1, 1, 0));
Mat squareMat(square_verts);
// Transform the camera's intrinsic parameters into an OpenGL camera matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Camera parameters
double f_x = 786.42938232; // Focal length in x axis
double f_y = 786.42938232; // Focal length in y axis (usually the same?)
double c_x = 217.01358032; // Camera primary point x
double c_y = 311.25384521; // Camera primary point y
cv::Mat cameraMatrix(3,3,CV_32FC1);
cameraMatrix.at<float>(0,0) = f_x;
cameraMatrix.at<float>(0,1) = 0.0;
cameraMatrix.at<float>(0,2) = c_x;
cameraMatrix.at<float>(1,0) = 0.0;
cameraMatrix.at<float>(1,1) = f_y;
cameraMatrix.at<float>(1,2) = c_y;
cameraMatrix.at<float>(2,0) = 0.0;
cameraMatrix.at<float>(2,1) = 0.0;
cameraMatrix.at<float>(2,2) = 1.0;
Mat rvec(3, 1, CV_32F), tvec(3, 1, CV_32F);
solvePnP(squareMat, boardMat, cameraMatrix, distortionCoefficients,
rvec, tvec);
_rv[0] = rvec.at<double>(0, 0);
_rv[1] = rvec.at<double>(1, 0);
_rv[2] = rvec.at<double>(2, 0);
_tv[0] = tvec.at<double>(0, 0);
_tv[1] = tvec.at<double>(1, 0);
_tv[2] = tvec.at<double>(2, 0);
}
然后在绘图代码中...
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, 0.0f);
modelViewMatrix = GLKMatrix4Translate(modelViewMatrix, -tv[1], -tv[0], -tv[2]);
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -rv[0], 1.0f, 0.0f, 0.0f);
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -rv[1], 0.0f, 1.0f, 0.0f);
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -rv[2], 0.0f, 0.0f, 1.0f);
我渲染的顶点在原点周围创建了一个单位长度的多维数据集(即,沿每个边缘从-0.5到0.5。)我知道使用OpenGL转换函数以“反向顺序”执行转换,因此上面的内容应沿着z,y和x轴,然后平移它。但是,似乎它先被翻译然后又旋转了,所以苹果的
GLKMatrix4
可能有不同的工作方式吗?This question似乎与我的非常相似,特别是coder9的答案似乎或多或少是我在寻找的东西。但是,我尝试了一下,并将结果与我的方法进行了比较,两种情况下得出的矩阵都是相同的。我觉得这个答案是正确的,但是我缺少一些关键的细节。
最佳答案
您必须确保轴朝向正确的方向。特别是,在OpenGL和OpenCV中,y和z轴面向不同的方向,以确保x-y-z基础是直接的。您可以在blog post中找到一些信息和代码(使用iPad相机)。
-编辑-
喔好吧。不幸的是,我以相反的方式使用这些资源(opengl-> opencv)来测试某些算法。我的主要问题是图像的行顺序在OpenGL和OpenCV之间反转(也许有帮助)。
模拟相机时,我遇到了可以在here和generalized projection matrix paper中找到的相同投影矩阵。博客文章的评论中引用的This paper还显示了计算机视觉与OpenGL投影之间的某些联系。
关于ios - OpenCV:旋转/平移向量到OpenGL模型 View 矩阵,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10313040/