【问题标题】:Opencv, calculate distance at pixel from depth matOpencv,从深度垫计算像素距离
【发布时间】:2020-04-08 20:51:50
【问题描述】:

我有一个使用校准的立体相机对和 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 距离:0.590693

深度垫值介于 0 和 255 之间,深度垫的类型为 0CV_8UC1。 立体声基线为0.0643654(以米为单位)。 焦距为284.493

我也试过: (来自OpenCV - compute real distance from disparity map

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的距离(如果我们假设毫米单位,更接近事实) 但还是不正确。

以下哪种方法是正确的?我哪里错了?

左、右、深度图。 (编辑:这个深度图来自一对不同的图像)

编辑:根据答案,我正在尝试:

`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。
  • 您好,感谢您的回复。校准矩阵是根据立体相机的板载校准数据创建的(出厂校准)。我确实使用StereoSGBM,然后使用DisparityWLSFilter。我不使用 ReprojectImageTo3d,因为我手动创建了矩阵,所以我没有 Q 矩阵。深度图代码很大程度上基于这个:“github.com/k22jung/stereo_vision/blob/master/src/…”我会添加一些图片。

标签: opencv computer-vision


【解决方案1】:

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

我希望这会有所帮助!

【讨论】:

  • 谢谢!我正在使用reprojectImageTo3D ,(代码添加到问题中)并且我得到了一个非常好的云。这种方法正确吗?我需要depth_image.convertTo(depth_image, CV_32F, 1. / 16); 吗?
  • .. 然而,深度值看起来仍然不正确。 4 米的距离显示为 1.3 米,云看起来很深。
  • 我不确定 Tx,它应该是 0.6 而不是 6 对吧?实际上,像 fx 这样的顶部变量会更好。那么,cx 和 cx' 是否相同(对于值 Q.at(3, 3))?在我的例子中,焦距是正的。如果您使用 StereoSGBM,我认为您需要在某个时候将视差除以 16。在某些示例中,他们执行 imgDisparity16S.convertTo(imgDisparity32F, CV_32F, 1./16);
  • 其实Tx应该是60,如果你想把一切都放mm。我不知道你的 fx 单位,fy。
  • cx - cx' 是什么意思? cx' 的值是什么?
【解决方案2】:

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

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

希望您按照您的问题正确使用它。

试试下面的 nerian 计算器来计算理论误差。

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

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

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

深度图最常见的问题是:

  1. 无纹理表面(普通对象)
  2. 校准结果不好。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-27
    • 1970-01-01
    • 2013-06-10
    • 2017-04-15
    • 2020-07-29
    • 2019-06-22
    • 2021-07-20
    相关资源
    最近更新 更多