使用 OpenCV 和 Matlab 的校准图像和三角网格的深度图
Depth map from calibrated image and triangular mesh using OpenCV and Matlab
我想使用从 Matlab 2014b 调用的 OpenCV(使用 OpenCV 绑定)从校准图像和三角网格中提取深度图。我是 Matlab 的普通用户,但我是 OpenCV 的新手。我有以下输入:
im - 场景的未失真 RGB 图像
或 - 相机位置向量
R - 描述相机姿势的旋转矩阵
points - nx3 三角形网格顶点列表表示场景
faces - mx3 三角形网格面列表
EFL - 以像素为单位的图像有效焦距
我已经编写了一个原生的 Matlab 光线追踪引擎来从这些输入中提取深度图,但这很慢并且存在高重投影错误(我想将 OpenCV 函数的结果与我自己的结果进行比较以确定是否这些错误与我的实施有关或只是相机校准不准确)。
如何使用从 Matlab 调用的 OpenCV 从这些输入中获取深度图?
如有任何帮助,我们将不胜感激
谢谢
托马斯
建议策略
您可以将网格中的顶点投影到 2D 像素坐标中(使用校准的相机模型)。然后对于每个面,您可以找到包含在由其投影顶点形成的 2D 三角形中的所有像素中心(格点)。在重叠的情况下,您可能必须跟踪哪个三角形最近。现在您知道每个像素对应于哪张脸了。这应该非常快,除非你的网格比你的图像分辨率高得多。
然后您可以使用相机模型找到对应于每个像素的 3D 射线,并将该射线与该像素的已知面相交以计算深度(听起来您已经完成了这部分)。既然你知道飞机,这也不应该花太长时间。
有关相机投影的更多信息
OpenCV a good resource 使用相机模型(下图)。
基本上,您可以将 3D 点 M'
投影到像素坐标 m'
;这就是将顶点投影到像素位置的方式。换个方向,比例是不可恢复的——你得到的是射线 M'/s
而不是点 M'
。您要查找的深度是 s
,它是 3D 点在相机框架中的 Z
坐标。如果您的网格位于以相机为中心的框架中(X 向右,Y 向下,Z 向外),R = Identity
和 t = 0
。如果不是,[R|t]
将其转换为。
展开每个因子让我们看到矩阵的构成。
您在下面建议的代码使用了 OpenCV 的 projectPoints
函数,该函数实现了上述等式以及一些失真校准(参见主要的 OpenCV 参考资料)。您必须填充矩阵并将它们相乘。 projectPoints
的替代示例可用 on GitHub, and I believe this same example is discussed in this SO question.
Code suggested by asker
Apparently the following code does the job. I may need some time to
pick through it given that my C++ knowledge is practically zero (I
realise that it is commented out BTW):
//CString str;
//cv::Mat CamMatrix(3, 3, CV_64F);
//cv::Mat distCoeffs(5, 1, CV_64F);
//m_CamCalib.GetOpenCVInfo(&CamMatrix, &distCoeffs);
//vector<Point3d> GCP_Points;
//vector<Point2d> Image_Points;
//cv::Mat RVecs(3, 3, CV_64F); // rotation matrix
//cv::Mat TranRVecs(3, 3, CV_64F); // rotation matrix
//cv::Mat TVecs(3, 1, CV_64F); // translation vector
//RVecs.at<double>(0, 0) = m_CamPosMtrx.m_pMtrx[0];
//RVecs.at<double>(1, 0) = m_CamPosMtrx.m_pMtrx[1];
//RVecs.at<double>(2, 0) = m_CamPosMtrx.m_pMtrx[2];
//RVecs.at<double>(0, 1) = m_CamPosMtrx.m_pMtrx[4];
//RVecs.at<double>(1, 1) = m_CamPosMtrx.m_pMtrx[5];
//RVecs.at<double>(2, 1) = m_CamPosMtrx.m_pMtrx[6];
//RVecs.at<double>(0, 2) = m_CamPosMtrx.m_pMtrx[8];
//RVecs.at<double>(1, 2) = m_CamPosMtrx.m_pMtrx[9];
//RVecs.at<double>(2, 2) = m_CamPosMtrx.m_pMtrx[10];
//transpose(RVecs, TranRVecs);
//TVecs.at<double>(0, 0) = 0;
//TVecs.at<double>(1, 0) = 0;
//TVecs.at<double>(2, 0) = 0;
//GCP_Points.push_back(Point3d((x - m_CamPosMtrx.m_pMtrx[12]), (y - m_CamPosMtrx.m_pMtrx[13]), (z - m_CamPosMtrx.m_pMtrx[14])));
//Image_Points.push_back(Point2d(0, 0));
//projectPoints(GCP_Points, TranRVecs, TVecs, CamMatrix, distCoeffs, Image_Points);
/bool CCameraCalibration::GetOpenCVInfo(Mat * cameraMatrix, Mat * distCoeffs)
//{
// int i,j;
// Mat projMatrix;
// CMatrix4x4 m1;
// if(cameraMatrix->rows==0) cameraMatrix->create(3,3, CV_64F);
// if(distCoeffs->rows==0) distCoeffs->create(5, 1, CV_64F);
// for(i=0;i<3;i++)
// for(j=0;j<3;j++){
// cameraMatrix->at<double>(i,j)=m_pCameraMatrix[i][j];
// }
// for(i=0;i<5;i++)
// distCoeffs->at<double>(i,0)=m_pCoefficients[i];
// return false;
//}
我想使用从 Matlab 2014b 调用的 OpenCV(使用 OpenCV 绑定)从校准图像和三角网格中提取深度图。我是 Matlab 的普通用户,但我是 OpenCV 的新手。我有以下输入:
im - 场景的未失真 RGB 图像
或 - 相机位置向量
R - 描述相机姿势的旋转矩阵
points - nx3 三角形网格顶点列表表示场景
faces - mx3 三角形网格面列表
EFL - 以像素为单位的图像有效焦距
我已经编写了一个原生的 Matlab 光线追踪引擎来从这些输入中提取深度图,但这很慢并且存在高重投影错误(我想将 OpenCV 函数的结果与我自己的结果进行比较以确定是否这些错误与我的实施有关或只是相机校准不准确)。
如何使用从 Matlab 调用的 OpenCV 从这些输入中获取深度图?
如有任何帮助,我们将不胜感激
谢谢
托马斯
建议策略
您可以将网格中的顶点投影到 2D 像素坐标中(使用校准的相机模型)。然后对于每个面,您可以找到包含在由其投影顶点形成的 2D 三角形中的所有像素中心(格点)。在重叠的情况下,您可能必须跟踪哪个三角形最近。现在您知道每个像素对应于哪张脸了。这应该非常快,除非你的网格比你的图像分辨率高得多。
然后您可以使用相机模型找到对应于每个像素的 3D 射线,并将该射线与该像素的已知面相交以计算深度(听起来您已经完成了这部分)。既然你知道飞机,这也不应该花太长时间。
有关相机投影的更多信息
OpenCV a good resource 使用相机模型(下图)。
基本上,您可以将 3D 点 M'
投影到像素坐标 m'
;这就是将顶点投影到像素位置的方式。换个方向,比例是不可恢复的——你得到的是射线 M'/s
而不是点 M'
。您要查找的深度是 s
,它是 3D 点在相机框架中的 Z
坐标。如果您的网格位于以相机为中心的框架中(X 向右,Y 向下,Z 向外),R = Identity
和 t = 0
。如果不是,[R|t]
将其转换为。
展开每个因子让我们看到矩阵的构成。
您在下面建议的代码使用了 OpenCV 的 projectPoints
函数,该函数实现了上述等式以及一些失真校准(参见主要的 OpenCV 参考资料)。您必须填充矩阵并将它们相乘。 projectPoints
的替代示例可用 on GitHub, and I believe this same example is discussed in this SO question.
Code suggested by asker
Apparently the following code does the job. I may need some time to pick through it given that my C++ knowledge is practically zero (I realise that it is commented out BTW):
//CString str;
//cv::Mat CamMatrix(3, 3, CV_64F);
//cv::Mat distCoeffs(5, 1, CV_64F);
//m_CamCalib.GetOpenCVInfo(&CamMatrix, &distCoeffs);
//vector<Point3d> GCP_Points;
//vector<Point2d> Image_Points;
//cv::Mat RVecs(3, 3, CV_64F); // rotation matrix
//cv::Mat TranRVecs(3, 3, CV_64F); // rotation matrix
//cv::Mat TVecs(3, 1, CV_64F); // translation vector
//RVecs.at<double>(0, 0) = m_CamPosMtrx.m_pMtrx[0];
//RVecs.at<double>(1, 0) = m_CamPosMtrx.m_pMtrx[1];
//RVecs.at<double>(2, 0) = m_CamPosMtrx.m_pMtrx[2];
//RVecs.at<double>(0, 1) = m_CamPosMtrx.m_pMtrx[4];
//RVecs.at<double>(1, 1) = m_CamPosMtrx.m_pMtrx[5];
//RVecs.at<double>(2, 1) = m_CamPosMtrx.m_pMtrx[6];
//RVecs.at<double>(0, 2) = m_CamPosMtrx.m_pMtrx[8];
//RVecs.at<double>(1, 2) = m_CamPosMtrx.m_pMtrx[9];
//RVecs.at<double>(2, 2) = m_CamPosMtrx.m_pMtrx[10];
//transpose(RVecs, TranRVecs);
//TVecs.at<double>(0, 0) = 0;
//TVecs.at<double>(1, 0) = 0;
//TVecs.at<double>(2, 0) = 0;
//GCP_Points.push_back(Point3d((x - m_CamPosMtrx.m_pMtrx[12]), (y - m_CamPosMtrx.m_pMtrx[13]), (z - m_CamPosMtrx.m_pMtrx[14])));
//Image_Points.push_back(Point2d(0, 0));
//projectPoints(GCP_Points, TranRVecs, TVecs, CamMatrix, distCoeffs, Image_Points);
/bool CCameraCalibration::GetOpenCVInfo(Mat * cameraMatrix, Mat * distCoeffs)
//{
// int i,j;
// Mat projMatrix;
// CMatrix4x4 m1;
// if(cameraMatrix->rows==0) cameraMatrix->create(3,3, CV_64F);
// if(distCoeffs->rows==0) distCoeffs->create(5, 1, CV_64F);
// for(i=0;i<3;i++)
// for(j=0;j<3;j++){
// cameraMatrix->at<double>(i,j)=m_pCameraMatrix[i][j];
// }
// for(i=0;i<5;i++)
// distCoeffs->at<double>(i,0)=m_pCoefficients[i];
// return false;
//}