3

I have a disparity image created with a calibrated stereo camera pair and opencv. It looks good, and my calibration data is good.

I need to calculate the real world distance at a pixel.

From other questions on stackoverflow, i see that the approach is:

depth = baseline * focal / disparity

Using the function:

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;
    }
}

I click on a point that i have measured to be 3 meters away from the stereo camera. What i get is:

val= 31 distance: 0.590693

The depth mat values are between 0 and 255, the depth mat is of type 0, or CV_8UC1. The stereo baseline is 0.0643654 (in meters). The focal length is 284.493

I have also tried: (from 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;

which gives me a (closer to truth, if we assume mm units) distance of val= 31 distance: 2281.27 But is still incorrect.

Which of these approaches is correct? And where am i going wrong?

Left, Right, Depth map. (EDIT: this depth map is from a different pair of images) enter image description here

EDIT: Based on an answer, i am trying this:

`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);

    }
}`

And that gives me a cloud.

anti
  • 3,011
  • 7
  • 36
  • 86
  • A few questions: How did you estimate that the calibration is good? How many images did you use? Did you use StereoSGBM for the disparity estimation? Could you provide the rectified images and the depth map? Why don't use reprojectImageTo3D to remap all points? Why the stereo baseline is in meter and you expect mm? One remark: fMaxDistance is wrong (brackets!), if you divide the disparity by 16 you should get 0.590693 * 16. – 87VN0 Dec 17 '19 at 15:45
  • Hi, thanks for your response. The calibration matrices are created from the on-board calibration data of the stereo camera (factory calibrated). I do use `StereoSGBM` and then `DisparityWLSFilter`. I do not use ReprojectImageTo3d, as because I have created the matrices manually, i do not have a `Q` matrix. The depth map code is heavily based on this: "https://github.com/k22jung/stereo_vision/blob/master/src/stereo_vision.cpp" I will add some images. – anti Dec 17 '19 at 17:15

2 Answers2

2

I would recommend to use reprojectImageTo3D of OpenCV to reconstruct the distance from the disparity. Note that when using this function you indeed have to divide by 16 the output of StereoSGBM. You should already have all the parameters f, cx, cy, Tx. Take care to give f and Tx in the same units. cx, cy are in pixels. Since the problem is that you need the Q matrix, I think that this link or this one should help you to build it. If you don't want to use reprojectImageTo3D I strongly recommend the first link!

I hope this helps!

87VN0
  • 775
  • 3
  • 10
  • Thanks! I am using `reprojectImageTo3D `, (code added to question) and i get a pretty good cloud. Is this approach correct? Do I need to `depth_image.convertTo(depth_image, CV_32F, 1. / 16);` ? – anti Dec 18 '19 at 10:28
  • .. The depth values still look incorrect, however. A 4 meter distance is showing as 1,3 meters, and the cloud looks squashed in depth. – anti Dec 18 '19 at 11:21
  • 1
    I'm not sure about Tx, it should be 0.6 and not 6 right? a variable at the top like fx would be better actually. Then, are cx and cx' the same (for the value Q.at(3, 3))? In my case, and in the example the focal lengths are positive. I think you need to divide the disparity by 16 at some point if you use StereoSGBM. In some examples they do imgDisparity16S.convertTo( imgDisparity32F, CV_32F, 1./16); – 87VN0 Dec 19 '19 at 11:30
  • Actually maybe Tx should be 60 if you want to put everything is mm. I don't know your unit for fx, fy. – 87VN0 Dec 19 '19 at 11:32
  • What does `cx - cx'` mean? what is the `cx'` value? – anti Dec 19 '19 at 14:16
  • From link2: In that image, cx and cy are the coordinates of the principal point in the left camera (if you did stereo matching with the left camera dominant), c'x is the x-coordinate of the principal point in the right camera (cx and c'x will be the same if you specified the CV_CALIB_ZERO_DISPARITY flag for stereoRectify()), f is the focal length and Tx is the baseline length (possibly the negative of the baseline length, it's the translation from one optical centre to the other I think). – 87VN0 Dec 20 '19 at 06:23
0

To find the point-based depth of an object from the camera, use the following formula:

Depth = (Baseline x Focallength)/disparity

I hope you are using it correctly as per your question.

Try the below nerian calculator for the therotical error.

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

Also, use sub-pixel interpolation in your code.

Make sure object you are identifying for depth should have good texture.

The most common problems with depth maps are:

  1. Untextured surfaces (plain object)
  2. Calibration results are bad.

What is the RMS value for your calibration, camera resolution, and lens type(focal length)? This is important to provide much better data for your program.

Zock77
  • 951
  • 8
  • 26
vishnukumar
  • 365
  • 4
  • 17