我一直在使用 Apples Vision 框架中的特征/关键点,通过 OpenCV 的 cv::solvePNP 进行姿势估计(校正 3D 模型上的关键点和图像上的 2D 点以匹配姿势)。
TL-DR:
我的场景套件模型正在被平移,并且在从solvePnP自省平移和旋转向量时,单位看起来是正确的(即它们是正确的数量级),但是平移的坐标系出现了:
我正在尝试使用solvePnP wrt对Metal / OpenGL坐标系和我的相机投影矩阵来了解坐标系要求。
我的 SCNCamera 需要什么“projectionMatrix”来匹配传递给solvePnP 的基于图像的坐标系?
我读过/相信我正在考虑的一些事情。
- OpenCV vs OpenGL(因此金属)具有行主要与列主要差异。
- OpenCV 的 3D 坐标系不同于 OpenGL(因此是 Metal)。
更长的代码:
我的工作流程是这样的:
第 1 步 - 使用 3D 模型工具检查我的 3D 模型上的点,并获取 2D 检测到的特征中主要关键点的对象顶点位置。我正在使用左瞳孔,右瞳孔,鼻尖,下巴尖,左外唇角,右外唇角。
第 2 步 - 运行视觉请求并提取图像空间中的点列表(转换为 OpenCV 的左上角坐标系)并提取相同的 2D 点有序列表。
第 3 步 - 使用输入图像的大小构造相机矩阵。
第 4 步 - 运行 cv::solvePnP,然后使用 cv::Rodrigues 将旋转向量转换为矩阵
第 5 步 - 将生成的变换的坐标系转换为适合 GPU 的坐标系 - 反转 y 轴和 z 轴并将平移和旋转组合为单个 4x4 矩阵,然后将其转置为 OpenGL/Metal 的适当主干
第 6 步 - 通过以下方式将生成的变换应用到 Scenekit:
let faceNodeTransform = openCVWrapper.transform(for: landmarks, imageSize: size)
self.destinationView.pointOfView?.transform = SCNMatrix4Invert(faceNodeTransform)
下面是我的 Obj-C++ OpenCV 包装器,它包含视觉地标的子集和正在查看的图像的真实像素大小:
/ https://answers.opencv.org/question/23089/opencv-opengl-proper-camera-pose-using-solvepnp/
- (SCNMatrix4) transformFor:(VNFaceLandmarks2D*)landmarks imageSize:(CGSize)imageSize
{
// 1 convert landmarks to image points in image space (pixels) to vector of cv::Point2f's :
// Note that this translates the point coordinate system to be top left oriented for OpenCV's image coordinates:
std::vector<cv::Point2f > imagePoints = [self imagePointsForLandmarks:landmarks imageSize:imageSize];
// 2 Load Model Points
std::vector<cv::Point3f > modelPoints = [self modelPoints];
// 3 create our camera extrinsic matrix
// TODO - see if this is sane?
double max_d = fmax(imageSize.width, imageSize.height);
cv::Mat cameraMatrix = (cv::Mat_<double>(3,3) << max_d, 0, imageSize.width/2.0,
0, max_d, imageSize.height/2.0,
0, 0, 1.0);
// 4 Run solvePnP
double distanceCoef[] = {0,0,0,0};
cv::Mat distanceCoefMat = cv::Mat(1 ,4 ,CV_64FC1,distanceCoef);
// Output Matrixes
std::vector<double> rv(3);
cv::Mat rotationOut = cv::Mat(rv);
std::vector<double> tv(3);
cv::Mat translationOut = cv::Mat(tv);
cv::solvePnP(modelPoints, imagePoints, cameraMatrix, distanceCoefMat, rotationOut, translationOut, false, cv::SOLVEPNP_EPNP);
// 5 Convert rotation matrix (actually a vector)
// To a real 4x4 rotation matrix:
cv::Mat viewMatrix = cv::Mat::zeros(4, 4, CV_64FC1);
cv::Mat rotation;
cv::Rodrigues(rotationOut, rotation);
// Append our transforms to our matrix and set final to identity:
for(unsigned int row=0; row<3; ++row)
{
for(unsigned int col=0; col<3; ++col)
{
viewMatrix.at<double>(row, col) = rotation.at<double>(row, col);
}
viewMatrix.at<double>(row, 3) = translationOut.at<double>(row, 0);
}
viewMatrix.at<double>(3, 3) = 1.0f;
// Transpose OpenCV to OpenGL coords
cv::Mat cvToGl = cv::Mat::zeros(4, 4, CV_64FC1);
cvToGl.at<double>(0, 0) = 1.0f;
cvToGl.at<double>(1, 1) = -1.0f; // Invert the y axis
cvToGl.at<double>(2, 2) = -1.0f; // invert the z axis
cvToGl.at<double>(3, 3) = 1.0f;
viewMatrix = cvToGl * viewMatrix;
// Finally transpose to get correct SCN / OpenGL Matrix :
cv::Mat glViewMatrix = cv::Mat::zeros(4, 4, CV_64FC1);
cv::transpose(viewMatrix , glViewMatrix);
return [self convertCVMatToMatrix4:glViewMatrix];
}
- (SCNMatrix4) convertCVMatToMatrix4:(cv::Mat)matrix
{
SCNMatrix4 scnMatrix = SCNMatrix4Identity;
scnMatrix.m11 = matrix.at<double>(0, 0);
scnMatrix.m12 = matrix.at<double>(0, 1);
scnMatrix.m13 = matrix.at<double>(0, 2);
scnMatrix.m14 = matrix.at<double>(0, 3);
scnMatrix.m21 = matrix.at<double>(1, 0);
scnMatrix.m22 = matrix.at<double>(1, 1);
scnMatrix.m23 = matrix.at<double>(1, 2);
scnMatrix.m24 = matrix.at<double>(1, 3);
scnMatrix.m31 = matrix.at<double>(2, 0);
scnMatrix.m32 = matrix.at<double>(2, 1);
scnMatrix.m33 = matrix.at<double>(2, 2);
scnMatrix.m34 = matrix.at<double>(2, 3);
scnMatrix.m41 = matrix.at<double>(3, 0);
scnMatrix.m42 = matrix.at<double>(3, 1);
scnMatrix.m43 = matrix.at<double>(3, 2);
scnMatrix.m44 = matrix.at<double>(3, 3);
return (scnMatrix);
}
一些问题:
SCNNode 没有 modelViewMatrix (正如我所理解的那样,一个变换,即 modelMatrix )只是抛出一个矩阵 - 所以我已经从 SolvePNP 过程中读取了变换的逆可以用来代替相机,哪个似乎让我得到了关闭结果。我想确保这种方法是正确的。
如果我有modelViewMatrix和projectionMatrix,我应该能够计算出合适的modelMatrix吗?这是我应该采取的方法吗?
我不清楚我应该为我的 SceneKit 场景使用什么投影矩阵,以及这是否对我的结果有任何影响。我是否需要一个像素以使我的视口与图像大小完全匹配,以及如何正确配置我的 SCNCamera 以确保 SolvePnP 的坐标系一致性?
非常感谢!