单目+双目标定(模拟单目+投影仪)+基于PNP+RANSAC+BA的双目相对位姿求解
数以及相关文件:
链接: https://pan.baidu.com/s/1BPdMGlLzLJl5HuVpci840w 提取码: p2cc
Note:这里标定“单目+投影仪”的算法并非调用 stereoCalibration,除非二者焦距、成像分辨率一致。
mian.cpp
1 #include"MonoCamCalibration.h" 2 3 4 // 构建PNP world -> right camera -> left camera -> left image plane 5 int computeRT( const vector<vector<Point3f> >& objectPoints, 6 const vector<vector<Point2f> >& imagePoints, 7 const vector<Mat>& rvecsR, 8 const vector<Mat>& tvecsR, 9 const Mat& cameraMatrixL, 10 const Mat& distCoeffsL) 11 { 12 /* if ( !(objectPoints.size() == imagePoints.size() == rvecsR.size() == tvecsR.size()) || 13 objectPoints.empty() || 14 cameraMatrixL.empty() || 15 distCoeffsL.empty()) 16 { 17 return -1; 18 }*/ 19 20 vector<Point3f> objectPointsCam; 21 vector<Point2f> imagePoints_; 22 23 objectPointsCam.reserve(objectPoints.size() * objectPoints[0].size()); // resize is more efficient 24 imagePoints_.reserve(imagePoints.size() * imagePoints[0].size()); 25 26 for (size_t i = 0; i < objectPoints.size(); i++) 27 { 28 Mat R; 29 Rodrigues(rvecsR[i], R); 30 Mat t = tvecsR[i]; 31 32 // T = (R|t) 这里只能用Affine3d 、 double, float会导致点溢出,可能是Mat 仅支持 double 的原因 33 cv::Affine3d T(cv::Affine3d::Mat3(R), 34 cv::Affine3d::Vec3(t.at<double>(0, 0), t.at<double>(1, 0), t.at<double>(2, 0))); 35 36 for (size_t j = 0; j < objectPoints[0].size(); j++) 37 { 38 objectPointsCam.push_back(T * objectPoints[i][j]); // world -> camera 39 imagePoints_.push_back(imagePoints[i][j]); 40 } 41 } 42 43 // optimize the R|t 44 Mat rvec1, t1, inliers; 45 float errThreshold = 0.f; // for getting the outliers and inliers 46 float inliersRatio = 0.f; // num of points in line /num of all the points 47 for (; inliersRatio < 0.5f; errThreshold += 0.02f) 48 { 49 solvePnPRansac(objectPointsCam, imagePoints_, cameraMatrixL, distCoeffsL, rvec1, t1, false, 1000, errThreshold, 0.99, inliers); 50 inliersRatio = (float)inliers.rows / (int)imagePoints_.size(); 51 } 52 cout << "inliersRatio = " << inliersRatio << "; num of samples = " << inliers.rows << endl; 53 54 // copy the inliers 55 vector<Point3f> objectPointsCamInliers; 56 vector<Point2f> imagePointsInliers; 57 objectPointsCamInliers.resize((size_t)inliers.rows); 58 imagePointsInliers.resize((size_t)inliers.rows); 59 60 for (int i = 0; i < inliers.rows; i++) 61 { 62 size_t idx = (size_t)inliers.at<__int32>(i, 0); 63 64 objectPointsCamInliers[i] = objectPointsCam[idx]; 65 imagePointsInliers[i] = imagePoints_[idx]; 66 } 67 68 solvePnPRefineLM(objectPointsCamInliers, imagePointsInliers, cameraMatrixL, distCoeffsL, rvec1, t1, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 1000, FLT_EPSILON)); 69 70 Mat R1; 71 Rodrigues(rvec1, R1); 72 73 vector<Point2f> imagePoints_RePro; 74 double errs = 0.; 75 projectPoints(objectPointsCamInliers, rvec1, t1, cameraMatrixL, distCoeffsL, imagePoints_RePro); 76 for (size_t i = 0; i < imagePointsInliers.size(); i++) 77 { 78 errs += norm(Mat(imagePointsInliers[i]), Mat(imagePoints_RePro[i]), NORM_L2); 79 } 80 81 cout << "the err of PNP:" << errs / (int)imagePointsInliers.size() << " "; 82 83 return 0; 84 } 85 86 void test1() 87 { 88 // 1、棋盘 89 Size boardSize(9, 6); //角点size(w, h) 90 Pattern pattern = CHESSBOARD; // 默认棋盘 91 int nframes = 14; // num of images 92 float squareSize = 0.02f; // 格子实际宽度(m) 93 94 // Left 95 string outputFilenameL = "parameters_imgs1_chessboard_left.yml"; // xml of parameters 96 string inputFilenameL = "imgs1_chessboard_left.xml"; // 图片集路径 97 MonoCamCalibration monoCaliChessBoardL(boardSize, pattern, nframes, squareSize, outputFilenameL, inputFilenameL); 98 if (monoCaliChessBoardL.compute() == 0) 99 { 100 cout << " Left successfully!" << endl; 101 } 102 103 Mat cameraMatrixL = monoCaliChessBoardL.getCameraMatrix(); 104 Mat distCoeffsL = monoCaliChessBoardL.getDistCoeffs(); 105 vector<vector<Point2f> > imagePointsL = monoCaliChessBoardL.getImagePoints(); 106 vector<vector<Point3f> > objectPoints = monoCaliChessBoardL.getObjectPoints(); 107 vector<Mat> rvecsL = monoCaliChessBoardL.getVecs(); 108 vector<Mat> tvecsL = monoCaliChessBoardL.getTvecs(); 109 110 // Right 111 string outputFilenameR = "parameters_imgs1_chessboard_right.yml"; // xml of parameters 112 string inputFilenameR = "imgs1_chessboard_right.xml"; // 图片集路径 113 MonoCamCalibration monoCaliChessBoardR(boardSize, pattern, nframes, squareSize, outputFilenameR, inputFilenameR); 114 if (monoCaliChessBoardR.compute() == 0) 115 { 116 cout << "Right successfully!" << endl; 117 } 118 119 Mat cameraMatrixR = monoCaliChessBoardR.getCameraMatrix(); 120 Mat distCoeffsR = monoCaliChessBoardR.getDistCoeffs(); 121 vector<vector<Point2f> > imagePointsR = monoCaliChessBoardR.getImagePoints(); 122 /*vector<vector<Point3f> > objectPoints = monoCaliChessBoardR.getObjectPoints();*/ 123 vector<Mat> rvecsR = monoCaliChessBoardR.getVecs(); 124 vector<Mat> tvecsR = monoCaliChessBoardR.getTvecs(); 125 126 // 构建PNP world -> right camera -> left camera -> left image plane 127 /* if (computeRT(objectPoints, imagePointsL, rvecsR, tvecsR, cameraMatrixL, distCoeffsL) == 0) 128 { 129 130 }*/ 131 132 if (computeRT(objectPoints, imagePointsR, rvecsL, tvecsL, cameraMatrixR, distCoeffsR) == 0) 133 { 134 135 } 136 cv::Mat M1 = cv::Mat::eye(3, 3, CV_64F); 137 cv::Mat M2 = cv::Mat::eye(3, 3, CV_64F); 138 cv::Mat D1, D2, R, T, E, F; 139 cout << "\nRunning stereo calibration ...\n"; 140 cout << cv::stereoCalibrate( 141 objectPoints, imagePointsL, imagePointsR, cameraMatrixL, distCoeffsL, cameraMatrixR, distCoeffsR, Size(640, 480), R, T, E, F, 142 cv::CALIB_FIX_ASPECT_RATIO, 143 cv::TermCriteria(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 1000, 144 1e-5)) << endl; 145 } 146 147 void test2() 148 { 149 // 2、圆(左) 150 Size boardSize(7, 7); //角点size(w, h) 151 Pattern pattern = CIRCLES_GRID; // 152 int nframes = 15; // num of images 153 float squareSize = 0.00375f; // 格子实际宽度(m) 154 155 string outputFilenameL = "parameters_imgs2_circle_left.yml"; // xml of parameters 156 string inputFilenameL = "imgs2_circle_left.xml"; // 图片集路径 157 MonoCamCalibration monoCaliCircleL(boardSize, pattern, nframes, squareSize, outputFilenameL, inputFilenameL); 158 if (monoCaliCircleL.compute() == 0) 159 { 160 cout << "successfully!" << endl; 161 } 162 163 Mat cameraMatrixL = monoCaliCircleL.getCameraMatrix(); 164 Mat distCoeffsL = monoCaliCircleL.getDistCoeffs(); 165 vector<vector<Point2f> > imagePointsL = monoCaliCircleL.getImagePoints(); 166 vector<vector<Point3f> > objectPoints = monoCaliCircleL.getObjectPoints(); 167 vector<Mat> rvecsL = monoCaliCircleL.getVecs(); 168 vector<Mat> tvecsL = monoCaliCircleL.getTvecs(); 169 170 // 2、圆(右) 171 172 string outputFilenameR = "parameters_imgs2_circle_right.yml"; // xml of parameters 173 string inputFilenameR = "imgs2_circle_right.xml"; // 图片集路径 174 MonoCamCalibration monoCaliCircleR(boardSize, pattern, nframes, squareSize, outputFilenameR, inputFilenameR); 175 if (monoCaliCircleR.compute() == 0) 176 { 177 cout << "successfully!" << endl; 178 } 179 180 Mat cameraMatrixR = monoCaliCircleR.getCameraMatrix(); 181 Mat distCoeffsR = monoCaliCircleR.getDistCoeffs(); 182 vector<vector<Point2f> > imagePointsR = monoCaliCircleR.getImagePoints(); 183 /*vector<vector<Point3f> > objectPoints = monoCaliCircleR.getObjectPoints();*/ 184 vector<Mat> rvecsR = monoCaliCircleR.getVecs(); 185 vector<Mat> tvecsR = monoCaliCircleR.getTvecs(); 186 187 // 构建PNP world -> right camera -> left camera -> left image plane 188 /*if (computeRT(objectPoints, imagePointsL, rvecsR, tvecsR, cameraMatrixL, distCoeffsL) == 0) 189 { 190 191 }*/ 192 if (computeRT(objectPoints, imagePointsR, rvecsL, tvecsL, cameraMatrixR, distCoeffsR) == 0) 193 { 194 195 } 196 197 cv::Mat M1 = cv::Mat::eye(3, 3, CV_64F); 198 cv::Mat M2 = cv::Mat::eye(3, 3, CV_64F); 199 cv::Mat D1, D2, R, T, E, F; 200 cout << "\nRunning stereo calibration ...\n"; 201 cout << cv::stereoCalibrate( 202 objectPoints, imagePointsL, imagePointsR, M1, D1, M2, D2, Size(320, 240), R, T, E, F, 203 cv::CALIB_FIX_ASPECT_RATIO , 204 cv::TermCriteria(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 1000, 205 1e-5)) << endl; 206 } 207 208 void test3() 209 { 210 // 3、圆(左) 211 Size boardSize(7, 7); //角点size(w, h) 212 Pattern pattern = CIRCLES_GRID; // 213 int nframes = 11; // num of images 214 float squareSize = 0.00375f; // 格子实际宽度(m) 215 216 string outputFilenameL = "parameters_imgs3_circle_left.yml"; // xml of parameters 217 string inputFilenameL = "imgs3_circle_left.xml"; // 图片集路径 218 MonoCamCalibration monoCaliCircleL(boardSize, pattern, nframes, squareSize, outputFilenameL, inputFilenameL); 219 if (monoCaliCircleL.compute() == 0) 220 { 221 cout << "successfully!" << endl; 222 } 223 224 Mat cameraMatrixL = monoCaliCircleL.getCameraMatrix(); 225 Mat distCoeffsL = monoCaliCircleL.getDistCoeffs(); 226 vector<vector<Point2f> > imagePointsL = monoCaliCircleL.getImagePoints(); 227 vector<vector<Point3f> > objectPoints = monoCaliCircleL.getObjectPoints(); 228 vector<Mat> rvecsL = monoCaliCircleL.getVecs(); 229 vector<Mat> tvecsL = monoCaliCircleL.getTvecs(); 230 231 // 2、圆(右) 232 233 string outputFilenameR = "parameters_imgs3_circle_right.yml"; // xml of parameters 234 string inputFilenameR = "imgs3_circle_right.xml"; // 图片集路径 235 MonoCamCalibration monoCaliCircleR(boardSize, pattern, nframes, squareSize, outputFilenameR, inputFilenameR); 236 if (monoCaliCircleR.compute() == 0) 237 { 238 cout << "successfully!" << endl; 239 } 240 241 Mat cameraMatrixR = monoCaliCircleR.getCameraMatrix(); 242 Mat distCoeffsR = monoCaliCircleR.getDistCoeffs(); 243 vector<vector<Point2f> > imagePointsR = monoCaliCircleR.getImagePoints(); 244 /*vector<vector<Point3f> > objectPoints = monoCaliCircleR.getObjectPoints();*/ 245 vector<Mat> rvecsR = monoCaliCircleR.getVecs(); 246 vector<Mat> tvecsR = monoCaliCircleR.getTvecs(); 247 248 // 构建PNP world -> right camera -> left camera -> left image plane 249 /* if (computeRT(objectPoints, imagePointsL, rvecsR, tvecsR, cameraMatrixL, distCoeffsL) == 0) 250 { 251 252 }*/ 253 if (computeRT(objectPoints, imagePointsR, rvecsL, tvecsL, cameraMatrixR, distCoeffsR) == 0) 254 { 255 256 } 257 258 cv::Mat M1 = cv::Mat::eye(3, 3, CV_64F); 259 cv::Mat M2 = cv::Mat::eye(3, 3, CV_64F); 260 cv::Mat D1, D2, R, T, E, F; 261 cout << "\nRunning stereo calibration ...\n"; 262 cout << cv::stereoCalibrate( 263 objectPoints, imagePointsL, imagePointsR, cameraMatrixL, distCoeffsL, cameraMatrixR, distCoeffsR, Size(640, 480), R, T, E, F, 264 cv::CALIB_FIX_ASPECT_RATIO, 265 cv::TermCriteria(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 1000, 266 1e-5)) << endl; 267 /* cout << -1 * R.t() * T << endl;*/ 268 } 269 // 单目标定 270 void test4() 271 { 272 Size boardSize(8, 11); //角点size(w, h) 273 Pattern pattern = CHESSBOARD; // 274 int nframes = 6; // num of images 275 float squareSize = 0.03f; // 格子实际宽度(m) 276 277 string outputFilenameL = "parameters_imgs_chessBoard.yml"; // xml of parameters 278 string inputFilenameL = "imgs_chessBoard.xml"; // 图片集路径 279 MonoCamCalibration monoCaliCircleL(boardSize, pattern, nframes, squareSize, outputFilenameL, inputFilenameL); 280 if (monoCaliCircleL.compute() == 0) 281 { 282 cout << "successfully!" << endl; 283 } 284 285 Mat cameraMatrixL = monoCaliCircleL.getCameraMatrix(); 286 Mat distCoeffsL = monoCaliCircleL.getDistCoeffs(); 287 vector<vector<Point2f> > imagePointsL = monoCaliCircleL.getImagePoints(); 288 vector<vector<Point3f> > objectPoints = monoCaliCircleL.getObjectPoints(); 289 vector<Mat> rvecsL = monoCaliCircleL.getVecs(); 290 vector<Mat> tvecsL = monoCaliCircleL.getTvecs(); 291 292 293 } 294 295 int main(int argc, char** argv) 296 { 297 test1(); 298 test2(); 299 test3(); 300 test4(); 301 return 0; 302 }
MonoCamCalibration.h
1 #pragma once 2 #define _CRT_SECURE_NO_WARNINGS 3 4 #ifndef MONOCAMCALIBRATION_H 5 #define MONOCAMCALIBRATION_H 6 7 #include "opencv2/core.hpp" 8 #include <opencv2/core/utility.hpp> 9 #include "opencv2/imgproc.hpp" 10 #include "opencv2/calib3d.hpp" 11 #include "opencv2/imgcodecs.hpp" 12 #include "opencv2/videoio.hpp" 13 #include "opencv2/highgui.hpp" 14 15 #include<iostream> 16 #include <cctype> 17 #include <stdio.h> 18 #include <string.h> 19 #include <time.h> 20 21 using namespace cv; 22 using namespace std; 23 24 enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 }; 25 enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID }; 26 27 class MonoCamCalibration 28 { 29 public: 30 MonoCamCalibration(const Size& boardSize, 31 const Pattern& pattern, 32 const int& nframes, 33 const float& squareSize, 34 const string& outputFilename, 35 const string& inputFilename); 36 37 // 输出重投影误差 38 double computeReprojectionErrors( 39 const vector<vector<Point3f> >& objectPoints, 40 const vector<vector<Point2f> >& imagePoints, 41 const vector<Mat>& rvecs, const vector<Mat>& tvecs, 42 const Mat& cameraMatrix, const Mat& distCoeffs, 43 vector<float>& perViewErrors); 44 // 标定板类型判定、设定3D角点 45 void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType = CHESSBOARD); 46 47 // 执行标定 48 bool runCalibration(vector<vector<Point2f> > imagePoints, 49 Size imageSize, Size boardSize, Pattern patternType, 50 float squareSize, float aspectRatio, 51 int flags, Mat& cameraMatrix, Mat& distCoeffs, 52 vector<Mat>& rvecs, vector<Mat>& tvecs, 53 vector<float>& reprojErrs, 54 double& totalAvgErr); 55 56 // 保存操作 57 void saveCameraParams(const string& filename, 58 Size imageSize, Size boardSize, 59 float squareSize, float aspectRatio, int flags, 60 const Mat& cameraMatrix, const Mat& distCoeffs, 61 const vector<Mat>& rvecs, const vector<Mat>& tvecs, 62 const vector<float>& reprojErrs, 63 const vector<vector<Point2f> >& imagePoints, 64 double totalAvgErr); 65 66 // 从xml/yaml 文件读取文件路径 67 bool readStringList(const string& filename, vector<string>& l); 68 69 // 执行保存操作 70 bool runAndSave(const string& outputFilename, 71 const vector<vector<Point2f> >& imagePoints, 72 Size imageSize, Size boardSize, Pattern patternType, float squareSize, 73 float aspectRatio, int flags, Mat& cameraMatrix, 74 Mat& distCoeffs, bool writeExtrinsics, bool writePoints); 75 76 // 执行整个流程 77 // 以下是输入参数 78 //Size boardSize, //角点size(w, h) 79 //Pattern pattern, // 默认棋盘 80 //int nframes, // num of images 81 //float squareSize,// 格子实际宽度(m) 82 //string outputFilename,// xml of parameters 83 //string inputFilename 84 int compute(); 85 86 // 87 Mat getCameraMatrix() const 88 { 89 return this->cameraMatrix; 90 } 91 Mat getDistCoeffs() const 92 { 93 return this->distCoeffs; 94 } 95 vector<Mat> getVecs() const 96 { 97 return this->rvecs; 98 } 99 100 vector<Mat> getTvecs() const 101 { 102 return this->tvecs; 103 } 104 vector<vector<Point2f> > getImagePoints() const 105 { 106 return this->imagePoints; 107 } 108 vector<vector<Point3f> > getObjectPoints() const 109 { 110 return this->objectPoints; 111 } 112 113 vector<size_t> getGoodPaternIdx() const 114 { 115 return this->goodPaternIdx; 116 } 117 118 private: 119 Size boardSize; // 角点size(w, h) 120 Pattern pattern; // 默认棋盘 121 int nframes; // num of images 122 float squareSize; // 格子实际宽度(m) 123 string outputFilename; // xml of parameters 124 string inputFilename; // 图片集路径 125 126 private: 127 Mat cameraMatrix, distCoeffs; 128 vector<Mat> rvecs, tvecs; 129 vector<vector<Point2f> > imagePoints; 130 vector<vector<Point3f> > objectPoints; 131 vector<size_t> goodPaternIdx; 132 133 }; 134 135 #endif
MonoCamCalibration.cpp
1 #include "MonoCamCalibration.h" 2 3 4 MonoCamCalibration::MonoCamCalibration( const Size& boardSize, 5 const Pattern& pattern, 6 const int& nframes, 7 const float& squareSize, 8 const string& outputFilename, 9 const string& inputFilename ): 10 boardSize(boardSize), 11 pattern(pattern), 12 nframes(nframes), 13 squareSize(squareSize), 14 outputFilename(outputFilename), 15 inputFilename(inputFilename) 16 { 17 objectPoints.resize(1); 18 } 19 20 21 22 // 输出重投影误差 23 double MonoCamCalibration::computeReprojectionErrors( 24 const vector<vector<Point3f> >& objectPoints, 25 const vector<vector<Point2f> >& imagePoints, 26 const vector<Mat>& rvecs, const vector<Mat>& tvecs, 27 const Mat& cameraMatrix, const Mat& distCoeffs, 28 vector<float>& perViewErrors) 29 { 30 vector<Point2f> imagePoints2; 31 int i, totalPoints = 0; 32 double totalErr = 0, err; 33 perViewErrors.resize(objectPoints.size()); 34 35 for (i = 0; i < (int)objectPoints.size(); i++) 36 { 37 projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2); 38 err = norm(Mat(imagePoints[i]), Mat(imagePoints2), NORM_L2); 39 int n = (int)objectPoints[i].size(); 40 perViewErrors[i] = (float)std::sqrt(err * err / n); 41 totalErr += err * err; 42 totalPoints += n; 43 } 44 45 return std::sqrt(totalErr / totalPoints); 46 } 47 48 // 标定板类型判定、设定3D角点 49 void MonoCamCalibration::calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType) 50 { 51 corners.resize(0); 52 53 switch (patternType) 54 { 55 case CHESSBOARD: 56 case CIRCLES_GRID: // 对称圆环 57 // 世界坐标系点 58 for (int i = 0; i < boardSize.height; i++) 59 for (int j = 0; j < boardSize.width; j++) 60 corners.push_back(Point3f(float(j * squareSize), float(i * squareSize), 0)); 61 break; 62 63 case ASYMMETRIC_CIRCLES_GRID: // 不对称圆环 64 // 世界坐标系点 65 for (int i = 0; i < boardSize.height; i++) 66 for (int j = 0; j < boardSize.width; j++) 67 corners.push_back(Point3f(float((2 * j + i % 2) * squareSize), float(i * squareSize), 0)); 68 break; 69 70 default: 71 CV_Error(Error::StsBadArg, "Unknown pattern type\n"); 72 } 73 } 74 75 // 执行标定流程 76 bool MonoCamCalibration::runCalibration(vector<vector<Point2f> > imagePoints, 77 Size imageSize, Size boardSize, Pattern patternType, 78 float squareSize, float aspectRatio, 79 int flags, Mat& cameraMatrix, Mat& distCoeffs, 80 vector<Mat>& rvecs, vector<Mat>& tvecs, 81 vector<float>& reprojErrs, 82 double& totalAvgErr) 83 { 84 cameraMatrix = Mat::eye(3, 3, CV_64F); 85 if (flags & CALIB_FIX_ASPECT_RATIO) 86 cameraMatrix.at<double>(0, 0) = aspectRatio; 87 88 distCoeffs = Mat::zeros(8, 1, CV_64F); 89 90 91 calcChessboardCorners(boardSize, squareSize, objectPoints[0], patternType); 92 93 objectPoints.resize(imagePoints.size(), objectPoints[0]);// 每一张棋盘对应世界坐标系角点3D坐标不变,直接拷贝 94 95 double rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, flags | CALIB_FIX_K4 | CALIB_FIX_K5); 96 ///*|CALIB_FIX_K3*/|CALIB_FIX_K4|CALIB_FIX_K5); 97 printf("RMS error reported by calibrateCamera: %g\n", rms); 98 99 bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs); // 检测是否有无穷大 or 无穷小 100 101 totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs, cameraMatrix, distCoeffs, reprojErrs); 102 103 return ok; 104 } 105 106 // 保存操作 107 void MonoCamCalibration::saveCameraParams(const string& filename, 108 Size imageSize, Size boardSize, 109 float squareSize, float aspectRatio, int flags, 110 const Mat& cameraMatrix, const Mat& distCoeffs, 111 const vector<Mat>& rvecs, const vector<Mat>& tvecs, 112 const vector<float>& reprojErrs, 113 const vector<vector<Point2f> >& imagePoints, 114 double totalAvgErr) 115 { 116 FileStorage fs(filename, FileStorage::WRITE); 117 118 time_t tt; 119 time(&tt); 120 struct tm* t2 = localtime(&tt); 121 char buf[1024]; 122 strftime(buf, sizeof(buf) - 1, "%c", t2); 123 124 fs << "calibration_time" << buf; 125 126 if (!rvecs.empty() || !reprojErrs.empty()) 127 fs << "nframes" << (int)std::max(rvecs.size(), reprojErrs.size()); 128 fs << "image_width" << imageSize.width; 129 fs << "image_height" << imageSize.height; 130 fs << "board_width" << boardSize.width; 131 fs << "board_height" << boardSize.height; 132 fs << "square_size" << squareSize; 133 134 if (flags & CALIB_FIX_ASPECT_RATIO) 135 fs << "aspectRatio" << aspectRatio; 136 137 if (flags != 0) 138 { 139 sprintf(buf, "flags: %s%s%s%s", 140 flags & CALIB_USE_INTRINSIC_GUESS ? "+use_intrinsic_guess" : "", 141 flags & CALIB_FIX_ASPECT_RATIO ? "+fix_aspectRatio" : "", 142 flags & CALIB_FIX_PRINCIPAL_POINT ? "+fix_principal_point" : "", 143 flags & CALIB_ZERO_TANGENT_DIST ? "+zero_tangent_dist" : ""); 144 //cvWriteComment( *fs, buf, 0 ); 145 } 146 147 fs << "flags" << flags; 148 149 fs << "camera_matrix" << cameraMatrix; 150 fs << "distortion_coefficients" << distCoeffs; 151 152 fs << "avg_reprojection_error" << totalAvgErr; 153 if (!reprojErrs.empty()) 154 fs << "per_view_reprojection_errors" << Mat(reprojErrs); 155 156 if (!rvecs.empty() && !tvecs.empty()) 157 { 158 CV_Assert(rvecs[0].type() == tvecs[0].type()); 159 Mat bigmat((int)rvecs.size(), 6, rvecs[0].type()); 160 for (int i = 0; i < (int)rvecs.size(); i++) 161 { 162 Mat r = bigmat(Range(i, i + 1), Range(0, 3)); 163 Mat t = bigmat(Range(i, i + 1), Range(3, 6)); 164 165 CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1); 166 CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1); 167 //*.t() is MatExpr (not Mat) so we can use assignment operator 168 r = rvecs[i].t(); 169 t = tvecs[i].t(); 170 } 171 //cvWriteComment( *fs, "a set of 6-tuples (rotation vector + translation vector) for each view", 0 ); 172 fs << "extrinsic_parameters" << bigmat; 173 } 174 175 if (!imagePoints.empty()) 176 { 177 Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2); 178 for (int i = 0; i < (int)imagePoints.size(); i++) 179 { 180 Mat r = imagePtMat.row(i).reshape(2, imagePtMat.cols); 181 Mat imgpti(imagePoints[i]); 182 imgpti.copyTo(r); 183 } 184 fs << "image_points" << imagePtMat; 185 } 186 } 187 188 // 从xml/yaml 文件读取文件路径 189 bool MonoCamCalibration::readStringList(const string& filename, vector<string>& l) 190 { 191 l.resize(0); 192 FileStorage fs(filename, FileStorage::READ); 193 if (!fs.isOpened()) 194 return false; 195 size_t dir_pos = filename.rfind('/'); 196 if (dir_pos == string::npos) 197 dir_pos = filename.rfind('\\'); 198 FileNode n = fs.getFirstTopLevelNode(); 199 if (n.type() != FileNode::SEQ) 200 return false; 201 FileNodeIterator it = n.begin(), it_end = n.end(); 202 for (; it != it_end; ++it) 203 { 204 string fname = (string)*it; 205 if (dir_pos != string::npos) 206 { 207 string fpath = samples::findFile(filename.substr(0, dir_pos + 1) + fname, false); 208 if (fpath.empty()) 209 { 210 fpath = samples::findFile(fname); 211 } 212 fname = fpath; 213 } 214 else 215 { 216 fname = samples::findFile(fname); 217 } 218 l.push_back(fname); 219 } 220 return true; 221 } 222 223 // 执行保存操作 224 bool MonoCamCalibration::runAndSave(const string& outputFilename, 225 const vector<vector<Point2f> >& imagePoints, 226 Size imageSize, Size boardSize, Pattern patternType, float squareSize, 227 float aspectRatio, int flags, Mat& cameraMatrix, 228 Mat& distCoeffs, bool writeExtrinsics, bool writePoints) 229 { 230 vector<float> reprojErrs; 231 double totalAvgErr = 0; 232 233 bool ok = runCalibration(imagePoints, imageSize, boardSize, patternType, squareSize, aspectRatio, flags, cameraMatrix, distCoeffs, rvecs, tvecs, reprojErrs, totalAvgErr); 234 printf("%s. avg reprojection error = %.2f\n", ok ? "Calibration succeeded" : "Calibration failed", totalAvgErr); 235 return ok; 236 } 237 238 int MonoCamCalibration::compute()// 图片集路径 239 { 240 // 优化参数设置 241 // aspectRatio + flags !!! 242 float aspectRatio = 1.0f; // fx/fy 243 int flags = 2; // 优化参数 244 //flags |= CALIB_FIX_ASPECT_RATIO; // 固定fx/fy 245 246 247 // <1>、参数检查 248 if (boardSize.width <= 0) 249 return fprintf(stderr, "Invalid board width\n"), -1; 250 if (boardSize.height <= 0) 251 return fprintf(stderr, "Invalid board height\n"), -1; 252 if (nframes <= 0) 253 return fprintf(stderr, "Invalid nframes\n"), -1; 254 if (squareSize <= 0) 255 return fprintf(stderr, "Invalid squareSize\n"), -1; 256 if (outputFilename.empty()) 257 return fprintf(stderr, "Invalid outputFilename\n"), -1; 258 if (inputFilename.empty()) 259 return fprintf(stderr, "Invalid inputFilename\n"), -1; 260 261 // <2>、读取数据 262 int mode = DETECTION; 263 vector<string> imageList; 264 265 if (!inputFilename.empty()) 266 { 267 if (readStringList(samples::findFile(inputFilename), imageList)) 268 mode = CAPTURING; 269 } 270 271 if (!imageList.empty()) 272 { 273 nframes = (int)imageList.size(); 274 } 275 else 276 { 277 return -2; 278 } 279 280 namedWindow("Image View", 1); 281 // <3>、开始检测 282 Size imageSize; 283 int i; 284 for (i = 0;; i++) 285 { 286 Mat view, viewGray; 287 288 if (i < (int)imageList.size()) 289 view = imread(imageList[i], 1); 290 291 if (view.empty()) 292 { 293 cout << i << endl; 294 if (imagePoints.size() > 0) 295 runAndSave(outputFilename, imagePoints, imageSize, boardSize, pattern, squareSize, aspectRatio, flags, cameraMatrix, distCoeffs, false, false); 296 break; 297 298 } 299 300 imageSize = view.size(); 301 vector<Point2f> pointbuf; 302 cvtColor(view, viewGray, COLOR_BGR2GRAY); 303 // 检测角点 -> pointbuf 304 bool found; 305 switch (pattern) 306 { 307 case CHESSBOARD: 308 found = findChessboardCorners(view, boardSize, pointbuf, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE); 309 break; 310 case CIRCLES_GRID: 311 found = findCirclesGrid(view, boardSize, pointbuf); 312 break; 313 case ASYMMETRIC_CIRCLES_GRID: 314 found = findCirclesGrid(view, boardSize, pointbuf, CALIB_CB_ASYMMETRIC_GRID); 315 break; 316 default: 317 return fprintf(stderr, "Unknown pattern type\n"), -1; 318 } 319 320 // 提取棋盘亚像素角点 321 if (pattern == CHESSBOARD && found) 322 cornerSubPix(viewGray, pointbuf, Size(11, 11), Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.1)); 323 324 if (mode == CAPTURING && found) 325 { 326 imagePoints.push_back(pointbuf); 327 } 328 329 if (found) 330 drawChessboardCorners(view, boardSize, Mat(pointbuf), found); 331 332 string msg = mode == CAPTURING ? "100/100" : mode == CALIBRATED ? "Calibrated" : "Press 'g' to start"; 333 int baseLine = 0; 334 Size textSize = getTextSize(msg, 1, 1, 1, &baseLine); 335 Point textOrigin(view.cols - 2 * textSize.width - 10, view.rows - 2 * baseLine - 10); 336 337 if (mode == CAPTURING) 338 { 339 msg = format("%d/%d", (int)imagePoints.size(), nframes); 340 } 341 342 putText(view, msg, textOrigin, 1, 1, mode != CALIBRATED ? Scalar(0, 0, 255) : Scalar(0, 255, 0)); 343 imshow("Image View", view); 344 waitKey(10); 345 } 346 return 0; 347 }
输出结果:
另:对于文件中丢了一堆图,如何获取每一张图的“绝对路径”?这里提供一个方法:
1、新建文本,拷贝以下脚本
DIR /S/B > fileList.txt
保存,修改后缀名为:“.bat”
2、双击该文件,会自动生成 包含所有图片路径的文本文件,如下图:
1 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_01.png 2 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_02.png 3 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_03.png 4 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_04.png 5 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_05.png 6 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_06.png 7 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_07.png 8 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_08.png 9 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_09.png 10 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_10.png 11 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_11.png 12 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_12.png 13 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_13.png 14 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_14.png 15 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\calib_l_15.png 16 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\fileList.txt 17 D:\VS2019_Project\外参数求解\MonoProjectorCalib\calibration2_stereo_circle\left\GetFileList.bat
【记得删除最后两行】
3、注意到上述路径为左斜杠,即:\ ,直接保存到 String中去汇报错,我们将文本文件丢到vs中去,再按Ctrl + F 查找并用“\\”替换 \
CV&DL