# 雙目視覺辨識 https://blog.csdn.net/piaoxuezhong/article/details/79016615 https://zhuanlan.zhihu.com/p/81016834 相機校正 https://zhuanlan.zhihu.com/p/55648494 https://blog.csdn.net/wangxiaokun671903/article/details/37966891 https://blog.csdn.net/wangxiaokun671903/article/details/37935113 http://www.vision.caltech.edu/bouguetj/calib_doc/ 算法時間與誤差 https://www.ixueshu.com/document/4e0dce557d53fd6c0bb3e8cf84599dc1318947a18e7f9386.html https://blog.csdn.net/KYJL888/article/details/79240525 https://blog.csdn.net/wintergeng/article/details/51049596 一些經驗 https://www.cnblogs.com/daihengchen/p/5492729.html https://blog.csdn.net/sunanger_wang/article/details/7744015 https://blog.csdn.net/scyscyao/article/details/5443341 https://blog.csdn.net/rocky69/article/details/7726569 https://www.itread01.com/content/1542248533.html https://blog.csdn.net/KYJL888/article/details/79240525 ## 相機模型與標定(參考Learning OpenCV 3) ### 目的:計算相機的內參以及畸變矩陣 #### 原因: 物體透過鏡頭投影的影像,會根據鏡頭的特性而有不同投影 而我們標定的工作就是要把這些求出來,後續做矯正的時候會使用到 1. Camera Matrix(內參矩陣) & Distortion Coefficients Matrix(畸變矩陣) [fx .0 cx] [.0 fy cy] = Camera Matrix M [.0 .0 1.] 畸變參數(4, 5, 8個係數不等,這裡討論4個,除非變形太多,否則會盡量減少運算量) k1 k2 p1 p2 2. 旋轉矩陣&平移矩陣 3. 使用函數(OpenCV) Note that. 我們使用一般棋盤格(6x9)作為標定的目標 ``` //找到每個黑白方格間的頂點(角點) cv::findChessboardCorners( cv::InputArray image, //讀入的影像 cv::Size patternSize, //棋盤格的角點數量 (row, column) cv::OutputArray corners, //輸出角點位置 int flags = //幫助找到角點的程序 cv::CALIB_CB_APATIVE_THRESH | CV::CALIB_CB_NORMALIZE_IMAGE ); //輔助用來辨識是否成功抓到所有的角點,會將所有角點用顏色標註好 cv::drawChessboardCorners( cv::InputOutputArray image, cv::Size patternSize, cv::InputArray corners, //是findChessboardCorners後的輸出矩陣 bool patternWasFound ); ``` ``` //標定函數(Camera Matrix & distortion Coefficients Matrix) double cv::calibrateCamera( cv::InputArrayOfArrays objectPoints, // 物體3D cv::InputArrayOfArrays imagePoints, // 影像2D cv::Size imageSize, cv::InputOutputArray cameraMatrix, cv::InputOutputArray distCoeffs, cv::OutputArrayOfArrays rvecs, cv::OutputArrayOfArrays tvecs, int flags, cv::TermCriteria criteria = cv::TermCriteria( cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 30, DBL_EPSILON ) ); ``` ### 目前進度 ``` #include <opencv2/opencv.hpp> #include <iostream> using namespace std; enum { STEREO_BM = 0, STEREO_SGBM = 1, STEREO_HH = 2, STEREO_VAR = 3, STEREO_3WAY = 4 }; void cameraCalibrate(cv::Mat& matrix, cv::Mat& coeffs, int number, cv::Size& image_size) { int n_boards = 25; int board_w = 6; int board_h = 9; int board_n = board_w * board_h; cv::Size board_sz = cv::Size(board_w, board_h); cv::VideoCapture camera = cv::VideoCapture(number); if (!camera.isOpened()) return; vector<vector<cv::Point2f>> image_points; vector<vector<cv::Point3f>> object_points; double last_captured_timestamp = 0; while (image_points.size() < (size_t)n_boards) { cv::waitKey(10); cv::Mat image; camera >> image; image_size = image.size(); vector<cv::Point2f> corners; bool found = cv::findChessboardCorners(image, board_sz, corners); drawChessboardCorners(image, board_sz, corners, found); double timestamp = (double)clock() / CLOCKS_PER_SEC; if (found && timestamp - last_captured_timestamp > 1) { last_captured_timestamp = timestamp; image_points.push_back(corners); object_points.push_back(vector<cv::Point3f>()); vector<cv::Point3f>& opts = object_points.back(); opts.resize(board_n); for (int j = 0; j < board_n; j++) { opts[j] = cv::Point3f((float)(j / board_w), (float)(j % board_h), 0.f); } } if (number == 1) cv::imshow( "Left Calibration", image); else cv::imshow("Right Calibration", image); } if (number == 1) cv::destroyWindow("Left Calibration"); else cv::destroyWindow("Right Calibration"); cout << "Done!" << endl; cv::Mat rvecs, tvecs; double err = cv::calibrateCamera( object_points, image_points, image_size, matrix, coeffs, rvecs, tvecs, cv::CALIB_ZERO_TANGENT_DIST | cv::CALIB_FIX_PRINCIPAL_POINT ); } void calDispWithSGBM(cv::Mat imgL, cv::Mat imgR, cv::Mat& imgDisparity8U) { cv::Size imgSize = imgL.size(); int numberOfDisparities = ((imgSize.width / 8) + 15) & -16; cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(0, 16, 3); sgbm->setPreFilterCap(63); int SADWindowSize = 9; int sgbmWinSize = SADWindowSize > 0 ? SADWindowSize : 3; sgbm->setBlockSize(sgbmWinSize); int cn = imgL.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); int alg = STEREO_SGBM; if (alg == STEREO_HH) sgbm->setMode(cv::StereoSGBM::MODE_HH); else if (alg == STEREO_SGBM) sgbm->setMode(cv::StereoSGBM::MODE_SGBM); else if (alg == STEREO_3WAY) sgbm->setMode(cv::StereoSGBM::MODE_SGBM_3WAY); cv::Mat imgDisparity16S = cv::Mat(imgL.rows, imgL.cols, CV_16S); sgbm->compute(imgL, imgR, imgDisparity16S); //--Display it as a CV_8UC1 image:16位有符号转为8位无符号 imgDisparity16S.convertTo(imgDisparity8U, CV_8U, 255 / (numberOfDisparities * 16.)); } int main() { cv::Mat imgL, imgR, R, T, E, F, R1, R2, P1, P2, Q, map11, map12, map21, map22; cv::Size image_size; vector<vector<cv::Point3f>> object_points; cv::Mat cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2; cameraCalibrate(cameraMatrix1, distCoeffs1, 1, image_size); cameraCalibrate(cameraMatrix2, distCoeffs2, 0, image_size); for (int i = 0; i < 25; i++) { object_points.push_back(vector<cv::Point3f>()); vector<cv::Point3f>& opts = object_points.back(); opts.resize(54); for (int j = 0; j < 54; j++) { opts[j] = cv::Point3f((float)(j / 6), (float)(j % 9), 0.f); } } cv::VideoCapture capture1 = cv::VideoCapture(1); cv::VideoCapture capture2 = cv::VideoCapture(0); if (!capture1.isOpened() || !capture2.isOpened()) { return -1; } while (true) { capture1 >> imgL; capture2 >> imgR; cv::stereoCalibrate( object_points, imgL, imgR, cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, image_size, R, T, E, F, cv::CALIB_FIX_INTRINSIC, cv::TermCriteria( cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 30, 1e-6 ) ); cv::stereoRectify( cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, image_size, R, T, R1, R2, P1, P2, cv::noArray(), 0 ); cv::initUndistortRectifyMap( cameraMatrix1, distCoeffs1, R1, cameraMatrix1, image_size, CV_8UC3, map11, map12 ); cv::initUndistortRectifyMap( cameraMatrix2, distCoeffs2, R2, cameraMatrix2, image_size, CV_8UC3, map21, map22 ); //--And create the image in which we will save our disparities cv::Mat imgDisparity8U = cv::Mat(map22.rows, map12.cols, CV_8UC1); calDispWithSGBM(map12, map22, imgDisparity8U); cv::imshow("Disparity", imgDisparity8U); //cv::imshow("Left ", imgL); //cv::imshow("Right", imgR); if ((cv::waitKey(30) & 255) == 27) break; } return 0; } ``` 問題:校正第二隻眼睛的時候,會強制關閉系統,跳出error http://wiki.ros.org/stereo_image_proc http://wiki.ros.org/image_proc http://wiki.ros.org/camera_calibration/Tutorials/StereoCalibration http://wiki.ros.org/usb_cam https://ros-planning.github.io/moveit_tutorials/ http://fugjo16.blogspot.com/2017/05/ros-moveit.html