Vitalii 1 Жалоба Опубликовано May 24, 2020 Добрый день! У меня возникли вопросы при расчёте дистанции до объекта по двум камерам в OpenCV. Калибровку камер выполнил в Matlab Stereo Camera Calibrator App (в OpenCV возникли проблемы) и затем сохранил параметры в формате OpenCV с помощью: https://github.com/JinyongJeong/StereoCalibration_matlab2opencv Получил матрицы: R1, R2, P1, P2, Q, R, T, M1, M2, D1, D2. При использовании алгоритма: https://github.com/mixr26/stereo_vision cv::Rect RectLeft=cv::Rect(1190,960,550,450); //Объект на левом кадре cv::Rect RectRight=cv::Rect(790,960,550,450); //Объект на правом кадре cv::Point3f P3f(RectLeft.x, RectRight.y, abs(RectLeft.x - RectRight.x)); std::vector<cv::Point3f> input; std::vector<cv::Point3f> output; input.push_back(P3f); //X, Y and Z coordinates of a real-world object cv::perspectiveTransform(input, output, Q); std::cout << output[0] <<std::endl; получаю значение X, Y, Z [-2.04382, 0.548109, 9.06563] – Не совсем понятно, как перевести эти значения в метры. При использовании другого алгоритма, в котором рассчитывается карта глубины, получаю некорректные значения дистанции (в то числе, отрицательные). cv::Mat img1 = cv::imread("left_12.bmp", 1); cv::Mat img2 = cv::imread("right_12.bmp", 1); Size img_size = img1.size(); Ptr<StereoSGBM> sgbm = StereoSGBM::create(0,16,3); int numberOfDisparities = 64; //известно cv::Mat M1, D1, M2 D2 Q R1, R2, P1, P2, R, T; //устраняем дисторсию Mat img1r, img2r; cv::undistort(img1, img1r, M1, D1); cv::undistort(img2, img2r, M2, D2); img1 = img1r; img2 = img2r; numberOfDisparities = numberOfDisparities > 0 ? numberOfDisparities : ((img_size.width/8) + 15) & -16; sgbm->setPreFilterCap(63); int sgbmWinSize = 3; sgbm->setBlockSize(sgbmWinSize); int cn = img1.channels(); sgbm->setP1(8*cn*sgbmWinSize*sgbmWinSize); sgbm->setP2(32*cn*sgbmWinSize*sgbmWinSize); sgbm->setMinDisparity(0); sgbm->setNumDisparities(numberOfDisparities); sgbm->setUniquenessRatio(10); sgbm->setSpeckleWindowSize(100); sgbm->setSpeckleRange(32); sgbm->setDisp12MaxDiff(1); Mat disp, disp8, disp32; sgbm->compute(img1, img2, disp); // compute the real-world distance [mm] //distance = (baseline * focal length) / disparity //Q (output from stereoRectify). Q[2][3] = focal length, 1/Q[3][2] = baseline. float fMaxDistance = static_cast<float>((1. / Q.at<double>(3, 2)) * Q.at<double>(2, 3)); disp.convertTo(disp32, CV_32F); auto outputDisparityValue = disp32.at<float>(1200,1200); // DISP_SCALE = 16 float fDisparity = outputDisparityValue / cv::StereoMatcher::DISP_SCALE; float fDistance = fMaxDistance / fDisparity; std::cout << "Distance = " <<fDistance <<std::endl; В чём может быть проблема? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано May 25, 2020 А можете привести схему эксперимента? Как установлены камеры, расположение и размеры объекта. А то вы привели результат работы алгоритма, а что должно быть мы не знаем. Может быть еще минимальный запускаемый кусок кода с тестовым изображением для пощупать проблему. 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Vitalii 1 Жалоба Опубликовано May 26, 2020 Добрый день! Сейчас пытаюсь определить расстояние до калибровочной доски (расстояние примерно 1.6 метра, размер клетки 28 мм). Левая камера Правая камера Примерное расположение доски при калибровке. Расстояние рассчитано автоматически в Matlab Stereo Camera Calibrator App. Первый алгоритм #include <iostream> #include <vector> #include <stdint.h> #include <string> #include <fstream> #include "opencv2/calib3d/calib3d.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include "opencv2/core/utility.hpp" #include <opencv2/calib3d.hpp> #include "opencv2/ximgproc.hpp" #include <stdio.h> using namespace cv; using namespace cv::ximgproc; using namespace std; int main(int argc, char *argv[]) { cv::Mat Image_Left = cv::imread("left_3.png"); cv::Mat Image_Right = cv::imread("right_3.png"); cv::Mat M1 = (cv::Mat_<double>(3, 3) << 7986.2559620260, 0.0000000000, 524.7794558939, 0.0000000000, 7603.3099586941, 765.6466654278, 0.0000000000, 0.0000000000, 1.0000000000); cv::Mat D1 = (cv::Mat_<double>(1, 5) << -0.0626244338, -23.0384673454, 0.0000000000, 0.0000000000, 0.0000000000); cv::Mat M2 = (cv::Mat_<double>(3, 3) << 7789.0835738047, 0.0000000000, 2085.4988788672, 0.0000000000, 7523.3700278846, 271.1883527428, 0.0000000000, 0.0000000000, 1.0000000000); cv::Mat D2 = (cv::Mat_<double>(1, 5) << 0.2407018952, -7.8745465803, 0.0000000000, 0.0000000000, 0.0000000000); cv::Mat Q = (cv::Mat_<double>(4, 4) << 1, 0, 0, -2.8951307125091553e+03, 0, 1, 0, -5.0271924400329590e+02, 0, 0, 0, 7.5633399932893499e+03, 0, 0, 2.0857193103842788e+00, 0); cv::Rect RectLeft=cv::Rect(1080,890, 300,290); //Доска на левом фото cv::Rect RectRight=cv::Rect(880, 890,300,290); //Доска на правом фото cv::Point3f P3f(RectLeft.x, RectRight.y, abs(RectLeft.x - RectRight.x)); std::vector<cv::Point3f> input; std::vector<cv::Point3f> output; input.push_back(P3f); cv::perspectiveTransform(input, output, Q); std::cout << output[0] <<std::endl; std::cin.get(); return 0; } Результат: XYZ: [-4.35133, 0.928411, 18.1313] Второй алгоритм: Расстояние рассчитываю по формуле: Расстояние = (Расстояние между камерами (мм) × фокусное расстояние) / карта несоответствия (disparity) Фокусное расстояние = Q[2][3] Расстояние между камерами (мм) = 1/Q[3][2] =0,5 м Для расчёта disparity (в том числе отфильтрованной) использую пример: https://docs.opencv.org/master/d3/d14/tutorial_ximgproc_disparity_filtering.html Код: #include <iostream> #include <vector> #include <stdint.h> #include <string> #include <fstream> #include "opencv2/calib3d/calib3d.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include "opencv2/core/utility.hpp" #include <opencv2/calib3d.hpp> #include "opencv2/ximgproc.hpp" #include <stdio.h> using namespace cv; using namespace cv::ximgproc; using namespace std; //https://docs.opencv.org/master/d3/d14/tutorial_ximgproc_disparity_filtering.html int main(int argc, char** argv) { cv::Mat M1 = (cv::Mat_<double>(3, 3) << 7986.2559620260, 0.0000000000, 524.7794558939, 0.0000000000, 7603.3099586941, 765.6466654278, 0.0000000000, 0.0000000000, 1.0000000000); cv::Mat D1 = (cv::Mat_<double>(1, 5) << -0.0626244338, -23.0384673454, 0.0000000000, 0.0000000000, 0.0000000000); cv::Mat M2 = (cv::Mat_<double>(3, 3) << 7789.0835738047, 0.0000000000, 2085.4988788672, 0.0000000000, 7523.3700278846, 271.1883527428, 0.0000000000, 0.0000000000, 1.0000000000); cv::Mat D2 = (cv::Mat_<double>(1, 5) << 0.2407018952, -7.8745465803, 0.0000000000, 0.0000000000, 0.0000000000); cv::Mat Q = (cv::Mat_<double>(4, 4) << 1, 0, 0, -2.8951307125091553e+03, 0, 1, 0, -5.0271924400329590e+02, 0, 0, 0, 7.5633399932893499e+03, 0, 0, 2.0857193103842788e+00, 0); cv::Mat left = cv::imread("left_3.bmp"); cv::Mat right = cv::imread("right_3.bmp"); //Устраняем дисторсию cv::Mat tmp1, tmp2; cv::undistort(left, tmp1, M1, D1); cv::undistort(right, tmp2, M2, D2); left = tmp1; right = tmp2; cv::Mat left_for_matcher, right_for_matcher; //int numberOfDisparities = 160; int max_disp= 160; max_disp/=2; if(max_disp%16!=0) max_disp += 16-(max_disp%16); float scale = 2; resize(left ,left_for_matcher ,Size(),scale,scale, INTER_LINEAR_EXACT); resize(right,right_for_matcher,Size(),scale,scale, INTER_LINEAR_EXACT); Ptr<StereoBM> left_matcher = StereoBM::create(max_disp,5); /* left_matcher->setPreFilterCap(31); left_matcher->setBlockSize(31); left_matcher->setMinDisparity(0); left_matcher->setNumDisparities(numberOfDisparities); left_matcher->setTextureThreshold(10); left_matcher->setUniquenessRatio(15); left_matcher->setSpeckleWindowSize(100); left_matcher->setSpeckleRange(32); left_matcher->setDisp12MaxDiff(1);*/ auto wls_filter = createDisparityWLSFilter(left_matcher); Ptr<StereoMatcher> right_matcher = createRightMatcher(left_matcher); cvtColor(left_for_matcher, left_for_matcher, COLOR_BGR2GRAY); cvtColor(right_for_matcher, right_for_matcher, COLOR_BGR2GRAY); cv::Mat left_disp, right_disp, filtered_disp; left_matcher->compute(left_for_matcher, right_for_matcher,left_disp); right_matcher->compute(right_for_matcher,left_for_matcher, right_disp); wls_filter->setLambda(8000); wls_filter->setSigmaColor(1.5); wls_filter->filter(left_disp,left_for_matcher,filtered_disp,right_disp); Mat raw_disp_vis; double vis_mult = 1; cv::ximgproc::getDisparityVis(left_disp,raw_disp_vis,vis_mult); namedWindow("raw disparity", WINDOW_NORMAL); imshow("raw disparity", raw_disp_vis); Mat filtered_disp_vis; getDisparityVis(filtered_disp,filtered_disp_vis,vis_mult); namedWindow("filtered disparity", WINDOW_NORMAL); imshow("filtered disparity", filtered_disp_vis); std::cout << filtered_disp_vis.size() <<std::endl; resize(filtered_disp_vis ,filtered_disp_vis ,Size(),1/scale,1/scale, INTER_LINEAR_EXACT); std::cout << filtered_disp_vis.size() <<std::endl; cv::Point p(1200,900); //Берём произвольную точку на доске filtered_disp_vis.convertTo(filtered_disp_vis, CV_32F); float fDisp = filtered_disp_vis.at<float>(p.y, p.x); //Расчёт расстояния float distance = Q.at<double>(2, 3)*(1./ Q.at<double>(3, 2))/fDisp; std::cout << "Distance = " << distance << std::endl; cv::imwrite("filtered_disp_vis.png", filtered_disp_vis); waitKey(); return 0; } Disparity: Результат: 57.55 Расположение точки: 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано May 26, 2020 Да, disparity как то не сошлось. Видимо картинка не подходящая, квадратики гладкие и регулярные текстуры. Не то что любят алгоритмы стерео матчинга. Посмотрите примеры здесь: https://answers.opencv.org/question/117141/triangulate-3d-points-from-a-stereo-camera-and-chessboard/ может наведет на мысли. А почему калибруете через матлаб ? OpenCv - шная тулза не подходит ? Можно попробовать откалибровать opencv-шным и сравнить коэффициенты. 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
fotomer 16 Жалоба Опубликовано May 27, 2020 Вы хотите по 2 снимкам с такой малой базой получить премлимую 3-d карту местности? Ничего не получится, возьмите какой-нибудь софт типа Agisoft или 3DF Zephyr Free (он бесплатен) и поэксперементируте там. Кстати 3DF Zephyr Free может делать калибровку по набору произвольных фоток (штук 30) и по опыту лучше чем с шахматной доской. Если просто нужно измерять расстояния можно использовать Aruco с 4 точками, которое выдаст координаты и направление камер. Еще Aruco нельзя в лоб снимать, точно резко падает, нужно чтоб квадрат на снимках был типа трапеции 1 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Vitalii 1 Жалоба Опубликовано May 27, 2020 20 часов назад, Smorodov сказал: Посмотрите примеры здесь: https://answers.opencv.org/question/117141/triangulate-3d-points-from-a-stereo-camera-and-chessboard/ может наведет на мысли. Благодарю! 20 часов назад, Smorodov сказал: А почему калибруете через матлаб ? OpenCv - шная тулза не подходит ? Можно попробовать откалибровать opencv-шным и сравнить коэффициенты. Пробовал. Получаются разные результаты. Значения матрицы M1, полученные в Matlab: M1 = 7986.2559620260, 0.0000000000, 524.7794558939, 0.0000000000, 7603.3099586941, 765.6466654278, 0.0000000000, 0.0000000000, 1.0000000000); Значения матрицы M1, полученные в OpenCV: M1 = 3.4296806178638740e+03, 0., 1.2727552031201496e+03, 0., 3.3866798437426000e+03, 6.9065925391676274e+02, 0., 0., 1. Фокусные расстояния, полученные в OpenCV в 2,3 раза меньше, чем в Matlab. Результат расчёта disparity (для устранения дисторсии использовал матрицы полученные при калибровке с помощью OpenCV): 3 часа назад, fotomer сказал: Вы хотите по 2 снимкам с такой малой базой получить премлимую 3-d карту местности? Ничего не получится, возьмите какой-нибудь софт типа Agisoft или 3DF Zephyr Free (он бесплатен) и поэксперементируте там. Кстати 3DF Zephyr Free может делать калибровку по набору произвольных фоток (штук 30) и по опыту лучше чем с шахматной доской. Если просто нужно измерять расстояния можно использовать Aruco с 4 точками, которое выдаст координаты и направление камер. Еще Aruco нельзя в лоб снимать, точно резко падает, нужно чтоб квадрат на снимках был типа трапеции Благодарю за рекомендации! Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
fotomer 16 Жалоба Опубликовано May 27, 2020 26 минут назад, Vitalii сказал: Благодарю! Пробовал. Получаются разные результаты. Значения матрицы M1, полученные в Matlab: M1 = 7986.2559620260, 0.0000000000, 524.7794558939, 0.0000000000, 7603.3099586941, 765.6466654278, 0.0000000000, 0.0000000000, 1.0000000000); M1 = 3.4296806178638740e+03, 0., 1.2727552031201496e+03, 0., 3.3866798437426000e+03, 6.9065925391676274e+02, 0., 0., 1. Фокусные расстояния, полученные в OpenCV в 2,3 раза меньше, чем в Matlab. Вообще именно фокус никогда точно не угадывается, его я на контрольном отрезке точно выставлял. Если интересно в свое время писал программку http://xn--e1anfamim.xn--p1ai/index.php/novosti/iphotomeasure здесь процесс калибровки http://xn--e1anfamim.xn--p1ai/index.php/novosti/kalibrovka-kamery Только снимки должны быть пестрые и не однообразные, шахматные доски не подходят. Даже дешевые смартфоны потом точно измеряют 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Vitalii 1 Жалоба Опубликовано May 27, 2020 1 час назад, fotomer сказал: Вообще именно фокус никогда точно не угадывается, его я на контрольном отрезке точно выставлял. Если интересно в свое время писал программку http://xn--e1anfamim.xn--p1ai/index.php/novosti/iphotomeasure здесь процесс калибровки http://xn--e1anfamim.xn--p1ai/index.php/novosti/kalibrovka-kamery Только снимки должны быть пестрые и не однообразные, шахматные доски не подходят. Даже дешевые смартфоны потом точно измеряют Интересно, благодарю! Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах