Opencv,从深度矩阵计算像素距离

3

我使用OpenCV和校准过的立体相机对图像进行了视差处理,效果不错,校准数据也很好。

我需要计算像素点上的真实世界距离。

从stackoverflow上的其他问题中,我看到的方法是:

depth = baseline * focal / disparity

使用以下函数:

setMouseCallback("disparity", onMouse, &disp); 

static void onMouse(int event, int x, int y, int flags, void* param)
{
    cv::Mat &xyz = *((cv::Mat*)param); //cast and deref the param
    if (event == cv::EVENT_LBUTTONDOWN)
    {
        unsigned int val = xyz.at<uchar>(y, x);

        double depth = (camera_matrixL.at<float>(0, 0)*T.at<float>(0, 0)) / val;

        cout << "x= " << x << " y= " << y << " val= " << val << " distance: " << depth<< endl;
    }
}

我点击了距离立体相机3米的点。 我得到的结果是:
val= 31 distance: 0.590693
深度矩阵值介于0和255之间,深度矩阵的类型为0CV_8UC1。 立体基线为0.0643654(以米为单位)。 焦距为284.493 我还尝试过: (来自OpenCV-计算视差图的实际距离
float fMaxDistance = static_cast<float>((1. / T.at<float>(0, 0) * camera_matrixL.at<float>(0, 0)));
//outputDisparityValue is single 16-bit value from disparityMap
float fDisparity = val / (float)cv::StereoMatcher::DISP_SCALE;
float fDistance = fMaxDistance / fDisparity;

如果我们假设以毫米为单位,则给出了距离的更接近真实值,如下所示:val= 31 distance: 2281.27,但仍然不正确。

这些方法哪一个是正确的?我在哪里出错了?

左、右、深度图。(编辑:此深度图来自另一组图像)enter image description here

编辑:根据答案,我正在尝试以下操作:

`std::vector pointcloud;

float fx = 284.492615;
float fy = 285.683197;
float cx = 424;// 425.807709;
float cy = 400;// 395.494293;

cv::Mat Q = cv::Mat(4,4, CV_32F);
Q.at<float>(0, 0) = 1.0;
Q.at<float>(0, 1) = 0.0;
Q.at<float>(0, 2) = 0.0;
Q.at<float>(0, 3) = -cx; //cx
Q.at<float>(1, 0) = 0.0;
Q.at<float>(1, 1) = 1.0;
Q.at<float>(1, 2) = 0.0;
Q.at<float>(1, 3) = -cy;  //cy
Q.at<float>(2, 0) = 0.0;
Q.at<float>(2, 1) = 0.0;
Q.at<float>(2, 2) = 0.0;
Q.at<float>(2, 3) = -fx;  //Focal
Q.at<float>(3, 0) = 0.0;
Q.at<float>(3, 1) = 0.0;
Q.at<float>(3, 2) = -1.0 / 6;    //1.0/BaseLine
Q.at<float>(3, 3) = 0.0;    //cx - cx'

//
cv::Mat XYZcv(depth_image.size(), CV_32FC3);
reprojectImageTo3D(depth_image, XYZcv, Q, false, CV_32F);

for (int y = 0; y < XYZcv.rows; y++)
{
    for (int x = 0; x < XYZcv.cols; x++)
    {
        cv::Point3f pointOcv = XYZcv.at<cv::Point3f>(y, x);

        Eigen::Vector4d pointEigen(0, 0, 0, left.at<uchar>(y, x) / 255.0);
        pointEigen[0] = pointOcv.x;
        pointEigen[1] = pointOcv.y;
        pointEigen[2] = pointOcv.z;

        pointcloud.push_back(pointEigen);

    }
}`

这使我有了一个云端。


几个问题:您是如何判断校准结果良好的?使用了多少张图像?在视差估计中是否采用了StereoSGBM算法?能否提供已矫正的图像和深度地图?为什么不使用reprojectImageTo3D来重新映射所有点?为什么立体基线以米为单位,但您期望得到的是毫米?有一个备注:fMaxDistance是错误的(括号!),如果将视差除以16,您应该得到0.590693 * 16。 - 87VN0
你好,感谢您的回复。校准矩阵是从立体相机的内部校准数据(出厂校准)创建的。我使用StereoSGBMDisparityWLSFilter。因为我手动创建了矩阵,所以我不使用ReprojectImageTo3d,也没有Q矩阵。深度图代码在这个基础上进行了大量修改:"https://github.com/k22jung/stereo_vision/blob/master/src/stereo_vision.cpp"我会添加一些图片。 - anti
2个回答

2
我建议使用OpenCV的reprojectImageTo3D来重建视差距离。请注意,在使用此函数时,确实需要将StereoSGBM的输出除以16。您应该已经拥有所有参数fcxcyTx。注意以相同单位给出f和Tx。cx、cy以像素为单位。 由于问题在于您需要Q矩阵,我认为this linkthis one可以帮助您构建它。如果您不想使用reprojectImageTo3D,我强烈推荐第一个链接! 希望这可以帮到您!

谢谢!我正在使用reprojectImageTo3D(代码已添加到问题中),并且我得到了一个相当不错的点云。这种方法正确吗?我需要执行depth_image.convertTo(depth_image, CV_32F, 1. / 16);吗? - anti
深度值仍然看起来不正确。4米的距离显示为1.3米,云在深度上看起来被压扁了。 - anti
1
我不确定Tx,应该是0.6而不是6对吧?实际上,在顶部放一个像fx这样的变量会更好。那么,cx和cx'是否相同(对于值Q.at<float>(3, 3))?在我的情况下,以及在示例中,焦距为正。如果您使用StereoSGBM,则需要在某个时候将视差除以16。在一些示例中,它们执行imgDisparity16S.convertTo(imgDisparity32F,CV_32F,1./16)。 - 87VN0
实际上,如果您想将所有内容放在毫米中,Tx可能应该为60。我不知道fx,fy的单位是什么。 - 87VN0
cx - cx' 是什么意思?cx' 的值是多少? - anti
在该图像中,cx和cy是左侧相机主点的坐标(如果您使用左侧相机进行立体匹配),c'x是右侧相机主点的x坐标(如果您为stereoRectify()指定了CV_CALIB_ZERO_DISPARITY标志,则cx和c'x将相同),f是焦距,Tx是基线长度(可能是基线长度的负数,我认为它是从一个光学中心到另一个光学中心的平移)。 - 87VN0

0

要从相机找到物体的基于点的深度,请使用以下公式:

深度 =(基线 x 焦距)/视差

我希望您根据问题正确使用它。

尝试下面的Nerian计算器以获取理论误差。

https://nerian.com/support/resources/calculator/

此外,在您的代码中使用亚像素插值。

确保您要识别深度的对象具有良好的纹理。

深度图最常见的问题是:

  1. 无纹理表面(平面物体)
  2. 校准结果不佳。

您的校准、相机分辨率和镜头类型(焦距)的RMS值是多少?这对于为您的程序提供更好的数据非常重要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接