我在使用OpenCV时,无法正确获取iPad相机的适当相机姿态。
我正在使用自定义制作的2D标记(基于AruCo库) - 我想使用OpenGL在该标记上渲染3D立方体。
为了接收相机姿态,我使用OpenCV的solvePnP函数。
根据此链接,我的操作如下:
cv::solvePnP(markerObjectPoints, imagePoints, [self currentCameraMatrix], _userDefaultsManager.distCoeffs, rvec, tvec);
tvec.at<double>(0, 0) *= -1; // I don't know why I have to do it, but translation in X axis is inverted
cv::Mat R;
cv::Rodrigues(rvec, R); // R is 3x3
R = R.t(); // rotation of inverse
tvec = -R * tvec; // translation of inverse
cv::Mat T(4, 4, R.type()); // T is 4x4
T(cv::Range(0, 3), cv::Range(0, 3)) = R * 1; // copies R into T
T(cv::Range(0, 3), cv::Range(3, 4)) = tvec * 1; // copies tvec into T
double *p = T.ptr<double>(3);
p[0] = p[1] = p[2] = 0;
p[3] = 1;
相机矩阵和畸变系数来自于findChessboardCorners函数,imagePoints是标记的手动检测角点(您可以在下面发布的视频中看到它们是绿色正方形),而markerObjectPoints是手动硬编码的代表标记角落的点:
markerObjectPoints.push_back(cv::Point3d(-6, -6, 0));
markerObjectPoints.push_back(cv::Point3d(6, -6, 0));
markerObjectPoints.push_back(cv::Point3d(6, 6, 0));
markerObjectPoints.push_back(cv::Point3d(-6, 6, 0));
因为实际上标记是12厘米长的,所以我选择了同样大小的标记来更容易地进行调试。
因此,我收到了4x4矩阵T,我将在OpenCV中将其用作ModelView矩阵。 使用GLKit绘图函数看起来差不多像这样:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
// preparations
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
float aspect = fabsf(self.bounds.size.width / self.bounds.size.height);
effect.transform.projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(39), aspect, 0.1f, 1000.0f);
// set modelViewMatrix
float mat[16] = generateOpenGLMatFromFromOpenCVMat(T);
currentModelMatrix = GLKMatrix4MakeWithArrayAndTranspose(mat);
effect.transform.modelviewMatrix = currentModelMatrix;
[effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 36); // draw previously prepared cube
}
我没有将所有东西绕 X 轴旋转180度(正如之前链接的文章中提到的那样),因为我觉得这并不必要。
问题是它不起作用!平移向量看起来没问题,但X和Y旋转错乱了 :(
我录制了一个视频展示了这个问题:
http://www.youtube.com/watch?v=EMNBT5H7-os
我已经尝试了几乎所有的方法(包括逐个反转所有轴),但仍然没有任何效果。
我该怎么办?如何正确地显示那个3D立方体?从solvePnP得到的翻译/旋转向量看起来是合理的,所以我猜测我无法正确地将这些向量映射到OpenGL矩阵中。