Как найти реальную глубину (расстояние от камеры до объекта) в метрах по карте диспропорций, зная все параметры калибровки?

Я использую двухточечную моно-камеру Gray Chameleon3, настроенную как Master Salve и синхронизированную, чтобы можно было работать как стереокамера. Затем следовал руководству OpenCV по калибровке камер. Я взял 70 образцов для калибровки и получил результаты калибровки. У меня есть все параметры камеры, так как я имею к ним доступ и получил внутренние и внешние параметры из калибровки.

Итак, я взял левое и правое изображения, не исказил их, используя матрицу камеры и коэффициенты искажения, и скорректировал их в эпиполярную форму, используя матрицы сдвига и вращения. Затем использовал класс StereoSGBM модуля calib3d для создания карты несоответствия. Вот мой код

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <vector>
#include <string>
#include <iostream>
#include <iterator>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d.hpp>

using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
     int i, j, lr;

    String imageName_left( "/home/OpenCVCode/left_7m.pgm" ); // by default
        if( argc > 1)
        {
        imageName_left = argv[1];
        }

        Mat image_left;
        image_left = imread( imageName_left, IMREAD_COLOR ); // Read the file
        if( image_left.empty() )                      // Check for invalid input
        {
        cout <<  "Could not open or find the image" << std::endl ;
        return -1;
        }

    String imageName_right( "/home/OpenCVCode/right_7m.pgm" ); // by default
        if( argc > 1)
        {
        imageName_right = argv[1];
        }

        Mat image_right;
        image_right = imread( imageName_right, IMREAD_COLOR ); // Read the file
        if( image_right.empty() )                      // Check for invalid input
        {
        cout <<  "Could not open or find the image" << std::endl ;
        return -1;
        }
    cv::Size imageSize;
    imageSize = image_left.size();
    //Mat outputImage = image.clone();

    vector<cv::Point3f> lines[2];
    //double err = 0;

    Mat R1=Mat1d(3, 3);
    Mat R2=Mat1d(3, 3);
    Mat P1=Mat1d(3, 4);
    Mat P2=Mat1d(3, 4);
    Mat Q=Mat1d(4, 4);
    Rect validRoi[2];

    double R_data[] = {0.9997286422545486, 0.000835337139108146, -0.023279692174526096,
           0.0008925647184101439, 0.9998880281815514, -0.00244797956076857,
           0.02327756826002481, 0.0024680939144444804, 0.9997259941245548};
    double T_data[] = {-0.13808630092854335, -0.0016795993989732654, -0.005964101318274714};

    cv::Mat R(3, 3, CV_64F, R_data);
    cv::Mat T(3, 1, CV_64F, T_data);

    Mat Camera_Matrix_Left = (Mat1d(3, 3) << 446.441726, 0, 266.768096, 0, 448.805499, 186.652251, 0, 0, 1);
    Mat Distortion_Coefficients_Left = (Mat1d(1, 5) << -0.174502, 0.079787, 0.001010, 0.000899, 0);
    Mat Camera_Matrix_Right = (Mat1d(3, 3) << 440.825465, 0, 277.733688, 0, 442.324307, 198.863384, 0, 0, 1);
    Mat Distortion_Coefficients_Right = (Mat1d(1, 5) << -0.226564, 0.290791, 0.000979, 0.003149, 0);

    Mat Matrix_R = (Mat1d(3, 3) << 0.9997286422545486, 0.000835337139108146, -0.023279692174526096,
           0.0008925647184101439, 0.9998880281815514, -0.00244797956076857,
           0.02327756826002481, 0.0024680939144444804, 0.9997259941245548);
    Mat Matrix_T = (Mat1d(3, 1) << -0.13808630092854335, -0.0016795993989732654, -0.005964101318274714);

            //undistort(image, outputImage, Camera_Matrix, Distortion_Coefficients);
        stereoRectify(Camera_Matrix_Left, Distortion_Coefficients_Left, Camera_Matrix_Right, Distortion_Coefficients_Right, image_left.size(), R, T, R1, R2, P1, P2, Q, 0, 1, imageSize, &validRoi[0]);
                    FileStorage fs1("4m2.yml", FileStorage::WRITE);
                    fs1 << "R1" << R1;
                    fs1 << "R2" << R2;
                    fs1 << "P1" << P1;
                    fs1 << "P2" << P2;
                    fs1 << "Q" << Q;
                    fs1.release();



        // Maps for AP View
        //cv::stereoRectify(camera_matrix_ap, distortion_ap, camera_matrix_lat, distortion_lat, rectimg_ap.size(), R, T, R1, R2, P1, P2, Q, CALIB_ZERO_DISPARITY, 1, rectimg_ap.size(), &validRoi[0], &validRoi[1] );
        Mat map1x(image_left.size(), CV_32FC1, 255.0);
        Mat map2x(image_left.size(), CV_32FC1, 255.0);
        // Maps for LAT View
        Mat map1y(image_right.size(), CV_32FC1, 255.0);
        Mat map2y(image_right.size(), CV_32FC1, 255.0);

        cv::initUndistortRectifyMap(Camera_Matrix_Left, Distortion_Coefficients_Left, R1, P1, image_left.size(), CV_32FC1, map1x, map1y);
        cv::initUndistortRectifyMap(Camera_Matrix_Right, Distortion_Coefficients_Right, R2, P2, image_right.size(), CV_32FC1, map2x, map2y);

        cv::Mat tmp1, tmp2;
        cv::remap(image_left, tmp1, map1x, map1y, INTER_LINEAR);
        cv::remap(image_right, tmp2, map2x, map2y, INTER_LINEAR);
        namedWindow( "Display window1", WINDOW_AUTOSIZE ); // Create a window for display.
            imshow( "Display window1", tmp2);                // Show our image inside it.
        namedWindow( "Display window2", WINDOW_AUTOSIZE ); // Create a window for display.
            imshow( "Display window2", tmp2 );   

        //Dispaity Map
        cv::Mat pair; 
        pair.create(imageSize.height, imageSize.width * 2, CV_8UC3);
        cv::Ptr<cv::StereoSGBM> stereo = cv::StereoSGBM::create(
        -64, 128, 11, 100, 1000, 32, 0, 15, 1000, 16, cv::StereoSGBM::MODE_HH);

        cv::Mat img1 = cv::imread(imageName_left, 0);
        cv::Mat img2 = cv::imread(imageName_right, 0);
        cv::Mat img1r, img2r, disp, vdisp;
        cv::remap(img1, img1r, map1x, map1y, cv::INTER_LINEAR);
        cv::remap(img2, img2r, map2x, map2y, cv::INTER_LINEAR);
        stereo->compute(img1r, img2r, disp);
        cv::normalize(disp, vdisp, 0, 256, cv::NORM_MINMAX, CV_8U);
        cv::imshow("Vertical disparity", vdisp);

        cv::Mat part = pair.colRange(0, imageSize.width);
        cvtColor(img1r, part, cv::COLOR_GRAY2BGR);
        part = pair.colRange(imageSize.width, imageSize.width * 2);
        cvtColor(img2r, part, cv::COLOR_GRAY2BGR);
        for (j = 0; j < imageSize.height; j += 16)
        cv::line(pair, cv::Point(0, j), cv::Point(imageSize.width * 2, j),cv::Scalar(0, 255, 0));       
        cv::imshow("Vertical rectified", pair);


    waitKey(); // Wait for a keystroke in the window
        //return 0;

}

Вот левое и правое необработанные изображения. Изображение слева Изображение справа

Здесь ссылка на левое левое необработанное изображение и на правое изображение правое необработанное изображение

Исправленные изображения и карта несоответствия Карта несоответствия

У меня есть исправленные изображения и карта несоответствия. Я хочу вычислить реальное расстояние от камеры до шахматной доски в метрах. Как это сделать? Я знаю истинное расстояние от камеры до этого объекта на земле, но хотел бы увидеть, насколько точна моя камера. Любая помощь? Спасибо


person Bob9710    schedule 05.09.2018    source источник


Ответы (1)


Поскольку вы проделали всю тяжелую работу и получили карту диспаратности, вычислить глубину отсюда проще простого. Чтобы рассчитать глубину каждого пикселя, вам понадобятся три вещи:
1- Значение несоответствия пикселей
2- Расстояние между вашими камерами
3- Фокусное расстояние (если по какой-то причине ваши камеры имеют другое фокусное расстояние, вы можно использовать среднее)

Depth = (focal length * distance between cameras) / (disparity value)

Примените это уравнение к каждому пикселю вашей карты диспаратности. Если единицами измерения и фокусного расстояния, и значения диспаратности являются пиксели, вы остаетесь с единицей измерения расстояния между камерами (см, мм ...)

//  assuming dmap is the single channel disparity map Mat
//  f:focal length, b:distance between cameras
depthImage = cv::Mat(dmap.size(), CV_32FC1);
for (int row = 0; row < dmap.rows; ++row)
{
    for (int col = 0; col < dmap.cols; ++col)
    {
        int disparityValue = (int) dmap.at<uchar>(row, col);
        depthImage.at<float>(row, col) = (f * b) / disparityValue;
    }
}

Или вы можете просто использовать доступная функция opencv для этого, возможно, лучше сделать это.

person unlut    schedule 05.09.2018
comment
Спасибо. Я ищу по этой теме и обнаруживаю, что формула Глубина = (фокусное расстояние * расстояние между камерами) / (значение диспаратности) может применяться, если обе камеры идеально параллельны. Поэтому я не знаю, как это может быть точным. Если нет, то является ли доступная функция opencv reprojectImageTo3D лучшим решением? Также я хотел бы знать глубину не всего изображения, а только этой шахматной доски (средств определенных точек). Итак, как получить глубину только этого шахматного объекта на изображении? - person Bob9710; 06.09.2018
comment
Что касается вашего первого вопроса, вот почему вы выполняете исправление. Если у вас будут две камеры, идеально выровненные по горизонтали, вам не потребуется исправление. Второй вопрос. Как я уже сказал, если вы знаете, где находится шахматная доска на вашем изображении, вы можете применить это уравнение к пикселям, принадлежащим шахматной доске. - person unlut; 06.09.2018
comment
Ok. понятно. Но тогда математика не работает. Я получил значение диспаратности 72. Из матрицы Q у меня фокусное расстояние 450. Итак, предположим, что оба значения указаны в пикселях. Тогда здесь возникает путаница, потому что от базовой линии Q матрицы - 25,5. На самом деле расстояние между камерами составляет 13 см. Таким образом, реальное расстояние до измеряемого объекта составляет 7,9 м. Математика не работает. Подскажите, пожалуйста, что не так? Спасибо - person Bob9710; 06.09.2018
comment
Расстояние между камерами можно получить из T-матрицы (перевод между кадрами камеры). По вашим оценкам, глубина должна составлять ~ 85 см. Давайте попробуем следующее: значение Disparity: 72 пикселя, фокусное расстояние: 450 пикселей, базовая линия: sqrt (0,13 ^ 2 + 0,0016 ^ 2 + 0,006 ^ 2) ~ = 0,13 м. Z = (450 * 0,13) / 72 ~ = 80 см. Это значение вам подходит? Если нет, можете ли вы привести пример значения измеренной глубины по коду и фактического реального значения глубины? - person unlut; 06.09.2018
comment
Матрица T, полученная в результате калибровки, равна {-0,13808630092854335, -0,0016795993989732654, -0,005964101318274714}. Реальное значение, которое я измерил с помощью лазера, составляет 7,9 м. Не использовал никакого кода для этого простого лазерного измерения. - person Bob9710; 06.09.2018
comment
Значения несоответствия также сильно различались, в зависимости от того, какой метод выпрямления стереозвука я использую как sbm или sgbm. - person Bob9710; 06.09.2018
comment
Могу я задать несколько вопросов о процессе калибровки? 1- Вы калибровали свои камеры по отдельности или вместе? 2- Выполняли ли вы какое-либо масштабирование изображений во время процесса калибровки? 3- Правильно ли базовое значение 13 с реальными измерениями? - person unlut; 06.09.2018
comment
1. Вместе как главный раб. 2. Нет никакого масштабирования. 3. Да, базовая линия 13,2 см соответствует измерению в реальном мире. И расстояние от шахматной доски до камеры было получено в реальном измерении лазером, без кода. 7. м расстояние от шахматной доски до камеры соответствует уровню земли. - person Bob9710; 06.09.2018
comment
извините, была опечатка, реальное расстояние, измеренное от лазера, составляет 7.9м. А в центре шахматной доски стоит точка (260,191). - person Bob9710; 06.09.2018
comment
Вы проверяли перепроецированную погрешность калибровки, сколько она стоит? Также вы можете попробовать оценить глубину очков на более близкой шахматной доске? Было бы полезно, если бы мы поняли, является ли источником проблемы ваши внутренние параметры, алгоритм стереосопоставления или и то, и другое. - person unlut; 06.09.2018
comment
да. его 0,21. Так должно быть хорошо. У меня доводчик вроде 4,6 м. Все еще не бог. Но есть большая разница между SGBM и BM. Когда мерцает, параметры SGBM улучшаются, но все еще слишком далеко от реальных значений глубины - person Bob9710; 07.09.2018
comment
Не могли бы вы поделиться своими левыми и правыми изображениями, я бы хотел проверить себя. - person unlut; 07.09.2018
comment
Да я их редактирую. Вы можете открыть его или я должен поделиться с вами? - person Bob9710; 10.09.2018
comment
Добавляю ссылку на левое и правое изображения. Пожалуйста, посмотрите. Спасибо - person Bob9710; 10.09.2018