C++ 什么';重投影3D(OpenCV)和视差到3D坐标之间的区别是什么?

C++ 什么';重投影3D(OpenCV)和视差到3D坐标之间的区别是什么?,c++,opencv,3d,camera-calibration,stereo-3d,C++,Opencv,3d,Camera Calibration,Stereo 3d,我正试图用立体相机获取三维坐标 第一种方法是直接用这个公式计算。 第二种方法是在opencv中使用ReprojectMageto3D 但我不知道这种方法的原理 结果不是以毫米为单位的,因此很难匹配大小 请告诉我这两种方法的区别。 (这些代码中的第一个代码是在匹配后将点特征转换为三维坐标。) (第二个代码是使用SGBM计算整个立体图像的视差,并使用重投影3d计算点特征的3d坐标。) *第一种方法 cv::Mat depth(m_input.m_leftImg.size(), CV_32FC3,

我正试图用立体相机获取三维坐标

第一种方法是直接用这个公式计算。

第二种方法是在opencv中使用ReprojectMageto3D

但我不知道这种方法的原理

结果不是以毫米为单位的,因此很难匹配大小

请告诉我这两种方法的区别。

(这些代码中的第一个代码是在匹配后将点特征转换为三维坐标。) (第二个代码是使用SGBM计算整个立体图像的视差,并使用重投影3d计算点特征的3d坐标。)

*第一种方法

cv::Mat depth(m_input.m_leftImg.size(), CV_32FC3, cv::Scalar::all(0));
int size = feOutput.m_leftKp.size();
for (int i = 0; i < size; i++)
{
    cv::Point pt = cv::Point((int)(feOutput.m_leftKp.at(i).pt.x + 0.5f), (int)(feOutput.m_leftKp.at(i).pt.y + 0.5f));

    depth.at<cv::Vec3f>(pt)[2] = fX * baseLine / (feOutput.m_leftKp.at(i).pt.x - feOutput.m_rightKp.at(i).pt.x);        // Z
    depth.at<cv::Vec3f>(pt)[0] = (feOutput.m_leftKp.at(i).pt.x - cX) * depth.at<cv::Vec3f>(pt)[2] / fX;                 // X
    depth.at<cv::Vec3f>(pt)[1] = (feOutput.m_leftKp.at(i).pt.y - cY) * depth.at<cv::Vec3f>(pt)[2] / fY;                 // Y
}
depth /= 1000.f; //milli-meter to meter
cv::Mat depth(m_input.m_leftImg.size(),cv_32FC3,cv::Scalar::all(0));
int size=feOutput.m_leftKp.size();
对于(int i=0;i
*第二种方法

cv::Mat disparity16S(m_input.m_leftImg.size(), CV_16S);
sgbm->compute(m_input.m_leftImg, m_input.m_rightImg, disparity16S);
cv::Mat xyz;
cv::Matx44d Q = cv::Matx44d(    
    1.0, 0.0, 0.0, -cX,
    0.0, 1.0, 0.0, -cY,
    0.0, 0.0, 0.0, fX,
    0.0, 0.0, -1.0 / baseLine, 0/*(CX - CX) / baseLine*/
);
cv::reprojectImageTo3D(disparity16S, xyz, Q, true);

cv::Mat pointXYZ(xyz.size(), xyz.type(), cv::Scalar::all(0));
for (int i = 0; i < size; i++)
{
    cv::Point pt = cv::Point((int)(feOutput.m_leftKp.at(i).pt.x + 0.5f), (int)(feOutput.m_leftKp.at(i).pt.y + 0.5f));
    pointXYZ.at<cv::Vec3f>(pt) = xyz.at<cv::Vec3f>(pt) / 1000.f;
}
cv::Mat disparity16S(m_input.m_leftImg.size(),cv_16S);
sgbm->compute(m_input.m_leftImg,m_input.m_rightImg,disparity16S);
cv::Mat xyz;
cv::Matx44d Q=cv::Matx44d(
1.0,0.0,0.0,-cX,
0.0,1.0,0.0,-cY,
0.0,0.0,0.0,外汇,
0.0,0.0,-1.0/基线,0/*(CX-CX)/基线*/
);
cv::重新投影到3D(不均匀16S、xyz、Q、真);
cv::Mat pointXYZ(xyz.size(),xyz.type(),cv::Scalar::all(0));
对于(int i=0;i
加+ 粉红色是重投影3D方法的大小,比例为1/100,黄色是第一种方法中的1/1000(毫米2米)。 如果这两种方法是相同的,为什么规模会有差异


理论上没有什么不同,只是方法不同。 您可以使用sgbm opencv方法(不执行任何匹配,但解决了最小化问题)计算单个匹配点(第一种方法)或图像中每个像素的视差

一旦你有了视差D,你就可以从三角测量中得到第一个公式中的深度Z。这应该是与参考图像平面的“距离”(通常为:左摄影机)

一旦你有了Z,知道投影方程说明了主摄像机的状态(伪代码)

你可以逆转

[X Y Z 1] = inv(K)* [Z*x Z*y Z] /*Z is known from disparity */
在第一张图片的第一列中找到了你的x,y。 这些都在主(左)相机参考系统中,但如果您想在右侧相机中显示您发布的图像,请进行2次假设

b is all along x

the two camera planes are perfectly parallel
一般来说,对于另一个摄影机,假设b是一个已知向量。这两个参考系之间可能发生旋转,因此还必须定义R矩阵)。我认为所有这些情况都是由不同的Q矩阵表示的(从立体摄像机校准中获得,例如
stereorective


cv::reprojectmageto3d
只是“authomatic方法”。他需要相机参数和连续的视差图。它可以处理单个选定点的差异

谢谢你的回答。那么,[xyz1]=inv(K)*[Z*xz*yz]与第一个方程相同吗?低公式和重投影3D之间没有区别吗?我补充了一些细节。为什么这两种方法的规模不同?@user6445248它们应该是等效的(在我的假设下)。你把sgbm运算符的值除以16了吗(你用的是CV_16S)?这似乎是个问题。我在网上看到我必须除以numberOfDistance。为什么要分割它?@user6445248我不知道确切的“为什么”(我想它必须与数值计算相联系),只要试着按照opencv指令操作,看看结果是否正确(并在这里报告)。较低的视差值意味着较高的深度值,因此粉色点应达到深度值的黄色范围。
[X Y Z 1] = inv(K)* [Z*x Z*y Z] /*Z is known from disparity */
b is all along x

the two camera planes are perfectly parallel