1.本文要点说明
本文介绍如何基于OpenCV提供的标定函数搭建一套简易的标定框架,从而掌握OpenCV标定模块的核心API。
此框架的主要目的是通过保存中间结果为YML文件来解耦整个标定流程,使得各模块可以独立运行及任意组合运行,整个标定框架被拆分为如下几个模块:采集图像、提取角点、标定相机(分单双目)、矫正相机(分单双目)、矫正图像(分单双目)。
除采集图像和矫正图像外都存在多种实现方式,
(1)不同成像模型的属性差异不大,所以不专门设计一个基类而通过重载函数或异名函数或条件判断来实现不同的标定算法和矫正算法,
(2)不同标定板子的属性差异较大,所以需专门设计一个基类来提供公用的接口然后在每个子类为相应的板子定义专有属性。
考虑到OpenCV当前在标定方面的实际能力,本文
(1)在标定板子方面只阐述棋盘板子,
(2)在标定成像模型方面只阐述透视模型和等距模型。
对于其它标定板子和成像模型按相同设计方式加入即可。
测试数据来源opencv/samples/data和opencv_extra/testdata/cv/cameracalibration/fisheye/calib-3_stereo_from_JY,复制到../data/calib/cvrtcm/cam1|cam2|all和../data/calib/cvkbcm/cam1|cam2|all。
2.实验测试代码
依赖于OpenCV、Ceres和Spdlog,封装为如下类:
(0)CapCam:用户指定抓取保存目录CapDir,若为双目相机则在此目录内新建all、cam1及cam2目录,分别用于保存双目图、左相机图及右相机图。
(1)CalibAPP:用户指定配置所在文件ConfigYML,包括是否执行DetBD、CalibMono、RectMono、UndMono、CalibBino、RectBino、UndBino及它们的配置,其中输出路径根据输入路径自动生成,不能配置。
(2)DetBD/DetChBD/...:用户指定图像所在目录MonoDir,程序保存角点到MonoDir/rst/det.yml。
(3)CalibMono:用户指定角点所在文件DetYML,程序保存标定结果到同级目录下的calib.yml。
(4)RectMono:用户指定标定结果所在文件CalibYML,程序保存矫正结果到同级目录下的rect.yml。
(5)UndMono:用户指定矫正结果所在文件RectYML及畸变图像目录ImaDir,程序保存去畸变图像到畸变图像同级目录下的rst目录。
(6)CalibBino:CalibBino:用户指定标定目录CalibDir(包含cam1/rst/det.yml|calib.yml和cam2/rst/det.yml|calib.yml),程序保存标定结果到该目录下的calib.yml。
(7)RectBino:RectBino:用户指定标定结果所在文件CalibYML,程序保存矫正结果到同级目录下的rect.yml。
(8)undBino:用户指定矫正结果所在文件RectYML及二合一的双目畸变图像目录ImaDir,程序保存去畸变图像到畸变图像同级目录下的rst目录。
以上类的resetIO、read、load及构造函数都将自动生成输出路径,不能指定。
1 #ifndef __MPPCalibFrame_h__ 2 #define __MPPCalibFrame_h__ 3 4 #include <cscv/Motion2D.h> 5 #include <opencv2/aruco.hpp> 6 7 class MPPCalibFrame 8 { 9 public://CapCam 10 class CapCam 11 { 12 public://1.CapArgs 13 int capId = 0; 14 int capCnt = INT_MAX; 15 int capRows = 480; 16 int capCols = 640; 17 bool capSplitted = false; 18 19 public://2.IOConfig 20 string capIO = "../data/calib"; 21 CapCam(string capIO0 = "") { if (!capIO0.empty()) capIO = capIO0; } 22 23 public://3.IOAction 24 void write(FileStorage& fs, bool rdArgs = true, bool rdData = true) 25 { 26 if (rdArgs) 27 { 28 fs.writeComment("######CapCam######"); 29 fs << "capId" << capId; 30 fs << "capCnt" << capCnt; 31 fs << "capRows" << capRows; 32 fs << "capCols" << capCols; 33 fs << "capSplitted" << capSplitted; 34 fs << "capIO" << capIO; 35 } 36 } 37 38 void read(FileStorage& fs, bool rdArgs = true, bool rdData = true) 39 { 40 if (rdArgs) 41 { 42 fs["capId"] >> capId; 43 fs["capCnt"] >> capCnt; 44 fs["capRows"] >> capRows; 45 fs["capCols"] >> capCols; 46 fs["capSplitted"] >> capSplitted; 47 fs["capIO"] >> capIO; 48 } 49 } 50 51 public://4.CoreTask 52 void capture() 53 { 54 //0.Initialization 55 utils::fs::createDirectories(capIO); 56 if (capSplitted) 57 { 58 utils::fs::createDirectories(capIO + "/all"); 59 utils::fs::createDirectories(capIO + "/cam1"); 60 utils::fs::createDirectories(capIO + "/cam2"); 61 } 62 cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 63 64 //1.SetCamera 65 VideoCapture cap(capId); 66 cap.set(CAP_PROP_FRAME_HEIGHT, capRows); 67 cap.set(CAP_PROP_FRAME_WIDTH, capCols); 68 69 //2.CaptureImages 70 Mat ima; 71 int cnt = 0; 72 while (cap.read(ima)) 73 { 74 cv::imshow(__FUNCTION__, ima); 75 char c = cv::waitKey(30); 76 if (c == ' ') 77 { 78 if (capSplitted) 79 { 80 int64 ts = tsns; 81 cv::imwrite(fmt::format("{}/bino/{}.png", capIO, ts), ima); 82 cv::imwrite(fmt::format("{}/cam1/{}.png", capIO, ts), ima.colRange(0, ima.cols >> 1)); 83 cv::imwrite(fmt::format("{}/cam2/{}.png", capIO, ts), ima.colRange(ima.cols >> 1, ima.cols)); 84 } 85 else cv::imwrite(fmt::format("{}/{}.png", capIO, tsns), ima); 86 cv::displayStatusBar(__FUNCTION__, fmt::format("Captured image count: {} Tips: press space to save and q/Q to exit", ++cnt), 1000); 87 } 88 if (c == 'q' || c == 'Q' || cnt >= capCnt) break; 89 }cv::destroyWindow(__FUNCTION__); 90 } 91 92 public://9.Utils 93 static void split2merge(string allDir, string cam1Dir, string cam2Dir, bool splitted = true, string pattern = "*", bool verbose = true) 94 { 95 if (splitted) 96 { 97 //CreateDir 98 utils::fs::createDirectories(cam1Dir); 99 utils::fs::createDirectories(cam2Dir); 100 101 //SplitFrame 102 vector<string> allPaths = Tool2D::globPaths(allDir, pattern, 0, false); 103 for (uint k = 0; k < allPaths.size(); ++k) 104 { 105 Mat frame = imread(allPaths[k], -1); 106 Mat frame1 = frame.colRange(0, frame.cols >> 1); 107 Mat frame2 = frame.colRange(frame.cols >> 1, frame.cols); 108 string cam1Path = fmt::format("{}/{}", cam1Dir, Tool2D::pathProps(allPaths[k])[4]); 109 string cam2Path = fmt::format("{}/{}", cam2Dir, Tool2D::pathProps(allPaths[k])[4]); 110 imwrite(cam1Path, frame1); 111 imwrite(cam2Path, frame1); 112 if (verbose) spdlog::info("Splitted {} as {} and {}", allPaths[k], cam1Path, cam2Path); 113 } 114 } 115 else 116 { 117 //CreateDir 118 utils::fs::createDirectories(allDir); 119 120 //MergeFrame 121 vector<string> cam1Paths = Tool2D::globPaths(cam1Dir, pattern, 0, false); 122 vector<string> cam2Paths = Tool2D::globPaths(cam2Dir, pattern, 0, false); 123 if (cam1Paths.size() != cam2Paths.size()) { spdlog::critical("cam1Paths.size() != cam2Paths.size()"); return; } 124 for (size_t k = 0; k < cam1Paths.size(); ++k) 125 { 126 Mat frame1 = imread(cam1Paths[k], -1); 127 Mat frame2 = imread(cam2Paths[k], -1); 128 Mat frame; hconcat(frame1, frame2, frame); 129 string allPath = fmt::format("{}/{}", allDir, Tool2D::pathProps(cam1Paths[k])[4]); 130 imwrite(allPath, frame); 131 if (verbose) spdlog::info("Merged {} and {} to {}", cam1Paths[k], cam2Paths[k], allPath); 132 } 133 } 134 } 135 }; 136 137 public://DetBD 138 class DetBD 139 { 140 public://1.DetArgs 141 142 public://2.IOConfig 143 string detIn = "../data/calib/cam1"; 144 string detOut = fmt::format("{}/rst/det.yml", detIn); 145 DetBD(string detIn0 = "") { resetIO(detIn0); } 146 void resetIO(string detIn0 = "") { if (!detIn0.empty()) { detIn = detIn0; detOut = fmt::format("{}/rst/det.yml", detIn); } } 147 int validViews() { int cnt = 0; for (int k = 0; k < oksView.size(); ++k) cnt += oksView[k]; return cnt; } 148 149 public://3.IOAction 150 void load(string inPath) { FileStorage fs(inPath, FileStorage::READ); read(fs); fs.release(); } 151 152 virtual void write(FileStorage& fs, bool rdArgs = true, bool rdData = true) = 0; 153 154 virtual void read(FileStorage& fs, bool rdArgs = true, bool rdData = true) = 0; 155 156 public://4.CoreTask 157 vector<short> oksView; 158 vector<vector<Point2f>> point2D; 159 vector<vector<Point3f>> point3D; 160 virtual void detect() = 0; 161 }; 162 163 class DetCHBD : public DetBD 164 { 165 public://1.DetArgs 166 int chHorCross = 8; 167 int chVerCross = 6; 168 int chSqrSide = 50; 169 int chDetFlag = 3; 170 int chWinSize = 5; 171 int chShowMS = 30; 172 string chPattern = "*"; 173 174 public://2.IOConfig 175 DetCHBD(string detIn0 = "") : DetBD(detIn0) { } 176 177 public://3.IOAction 178 void write(FileStorage& fs, bool rdArgs = true, bool rdData = true) 179 { 180 if (rdArgs) 181 { 182 fs.writeComment("######DetectCHBD######"); 183 fs << "chHorCross" << chHorCross; 184 fs << "chVerCross" << chVerCross; 185 fs << "chSqrSide" << chSqrSide; 186 fs << "chDetFlag" << chDetFlag; fs.writeComment("Refer to opencv docs for details", true); 187 fs << "chWinSize" << chWinSize; 188 fs << "chShowMS" << chShowMS; 189 fs << "chPattern" << chPattern; 190 fs << "detIn" << detIn; 191 } 192 if (rdData) 193 { 194 fs << "oksView" << oksView; 195 fs << "point2D" << point2D; 196 fs << "point3D" << point3D; 197 } 198 } 199 200 void read(FileStorage& fs, bool rdArgs = true, bool rdData = true) 201 { 202 if (rdArgs) 203 { 204 fs["chHorCross"] >> chHorCross; 205 fs["chVerCross"] >> chVerCross; 206 fs["chSqrSide"] >> chSqrSide; 207 fs["chDetFlag"] >> chDetFlag; 208 fs["chWinSize"] >> chWinSize; 209 fs["chShowMS"] >> chShowMS; 210 fs["chPattern"] >> chPattern; 211 fs["detIn"] >> detIn; resetIO(detIn); 212 } 213 if (rdData) 214 { 215 fs["oksView"] >> oksView; 216 fs["point2D"] >> point2D; 217 fs["point3D"] >> point3D; 218 } 219 } 220 221 public://4.CoreTask 222 void detect() 223 { 224 //0.Initialize 225 if (!utils::fs::exists(detIn)) spdlog::critical("{}: {} not exist", __FUNCTION__, detIn); else spdlog::info("Current node: {}", __FUNCTION__); 226 oksView.clear(); 227 point2D.clear(); 228 point3D.clear(); 229 if (chShowMS >= 0) cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 230 231 //1.DetectPoint2D 232 vector<string> filePaths = Tool2D::globPaths(detIn, chPattern, 0, false); 233 for (size_t fileId = 0, cnt = 0; fileId < filePaths.size(); ++fileId) 234 { 235 //1.1 236 Mat_<Vec3b> color = imread(filePaths[fileId]); 237 Mat_<uchar> gray; cvtColor(color, gray, COLOR_BGR2GRAY); 238 239 //1.2 240 vector<Point2f> corner2D; 241 bool found = findChessboardCorners(gray, Size(chHorCross, chVerCross), corner2D, chDetFlag); 242 if (found == false) 243 { 244 Mat_<uchar> tmp; resize(gray, tmp, gray.size() / 2); 245 found = findChessboardCorners(tmp, Size(chHorCross, chVerCross), corner2D, chDetFlag); 246 for (size_t k = 0; k < corner2D.size(); ++k) corner2D[k] *= 2; 247 } 248 249 //1.3 250 if (found) cornerSubPix(gray, corner2D, Size(chWinSize, chWinSize), Size(-1, -1), TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, 0.01)); 251 if (found) drawChessboardCorners(color, Size(chHorCross, chVerCross), corner2D, found); 252 point2D.push_back(found ? corner2D : vector<Point2f>()); 253 oksView.push_back(found); 254 string tips = fmt::format("{} detect {} and current vaild views {}", found ? "Could" : "Cannot", filePaths[fileId], cnt += found); 255 if (!found) spdlog::warn(tips); 256 if (chShowMS >= 0) cv::displayStatusBar(__FUNCTION__, tips, 1000); 257 258 //1.4 259 if (chShowMS < 0) continue; 260 cv::imshow(__FUNCTION__, color); 261 cv::waitKey(chShowMS); 262 } if (chShowMS >= 0) cv::destroyWindow(__FUNCTION__); 263 264 //2.CalcPoint3D 265 vector<Point3f> corner3D; 266 for (int i = 0; i < chVerCross; ++i) 267 for (int j = 0; j < chHorCross; ++j) 268 corner3D.push_back(Point3f(float(j * chSqrSide), float(i * chSqrSide), 0)); 269 for (int i = 0; i < oksView.size(); ++i) point3D.push_back(oksView[i] ? corner3D : vector<Point3f>()); 270 271 //3.SaveResult 272 utils::fs::createDirectories(utils::fs::getParent(detOut)); 273 FileStorage fs(detOut, FileStorage::WRITE); 274 this->write(fs); 275 fs.release(); 276 } 277 278 public://9.Utils 279 static Mat_<uchar> createChessboard(int chSqrSide = 300, int chVerCross = 6, int chHorCross = 9) 280 { 281 Mat_<uchar> chessboard(chVerCross * chSqrSide, chHorCross * chSqrSide, (uchar)255); 282 for (int i = 0; i < chVerCross; ++i) 283 { 284 Mat_<uchar> _roi = chessboard.rowRange(i * chSqrSide, (i + 1) * chSqrSide); 285 for (int j = 0; j < chHorCross; ++j) 286 { 287 Mat_<uchar> roi = _roi.colRange(j * chSqrSide, (j + 1) * chSqrSide); 288 if (i == 0 && j == 0) roi = 0; 289 if (i == 0 && j != 0) roi = ~roi(0, -1); 290 if (i != 0 && j == 0) roi = ~roi(-1, 0); 291 if (i != 0 && j != 0) roi = ~roi(-1, 0); 292 } 293 } 294 return chessboard; 295 } 296 }; 297 298 class DetARBD : public DetBD 299 { 300 public: 301 int arHorMarker = 8; 302 int arVerMarker = 5; 303 int arMarkerSide = 50; 304 int arMarkerGap = 10; 305 int arFirstId = 0; 306 int arDicType = aruco::DICT_ARUCO_ORIGINAL; 307 int arMinMarker = arHorMarker * arVerMarker / 2; 308 int arShowMS = 30; 309 string arPattern = "*"; 310 311 public://2.IOConfig 312 DetARBD(string detIn0 = "") : DetBD(detIn0) { } 313 314 public://3.IOAction 315 void write(FileStorage& fs, bool rdArgs = true, bool rdData = true) 316 { 317 if (rdArgs) 318 { 319 fs.writeComment("######DetectARBD######"); 320 fs << "arHorMarker" << arHorMarker; 321 fs << "arVerMarker" << arVerMarker; 322 fs << "arMarkerSide" << arMarkerSide; 323 fs << "arMarkerGap" << arMarkerGap; 324 fs << "arFirstId" << arFirstId; 325 fs << "arDicType" << arDicType; fs.writeComment("Refer to opencv docs for details", true); 326 fs << "arMinMarker" << arMinMarker; 327 fs << "arShowMS" << arShowMS; 328 fs << "arPattern" << arPattern; 329 fs << "detIn" << detIn; 330 } 331 if (rdData) 332 { 333 fs << "oksView" << oksView; 334 fs << "point2D" << point2D; 335 fs << "point3D" << point3D; 336 } 337 } 338 339 void read(FileStorage& fs, bool rdArgs = true, bool rdData = true) 340 { 341 if (rdArgs) 342 { 343 fs["arHorMarker"] >> arHorMarker; 344 fs["arVerMarker"] >> arVerMarker; 345 fs["arMarkerSide"] >> arMarkerSide; 346 fs["arMarkerGap"] >> arMarkerGap; 347 fs["arFirstId"] >> arFirstId; 348 fs["arDicType"] >> arDicType; 349 fs["arMinMarker"] >> arMinMarker; 350 fs["arShowMS"] >> arShowMS; 351 fs["arPattern"] >> arPattern; 352 fs["detIn"] >> detIn; resetIO(detIn); 353 } 354 if (rdData) 355 { 356 fs["oksView"] >> oksView; 357 fs["point2D"] >> point2D; 358 fs["point3D"] >> point3D; 359 } 360 } 361 362 public://4.CoreTask 363 void detect() 364 { 365 //0.Initialize 366 using namespace cv::aruco; 367 if (!utils::fs::exists(detIn)) spdlog::critical("{}: {} not exist", __FUNCTION__, detIn); else spdlog::info("Current node: {}", __FUNCTION__); 368 oksView.clear(); 369 point2D.clear(); 370 point3D.clear(); 371 if (arShowMS >= 0) cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 372 373 //1.ConfigeBoard 374 Ptr<Dictionary> dic = getPredefinedDictionary(arDicType); 375 Ptr<GridBoard> board = GridBoard::create(arHorMarker, arVerMarker, float(arMarkerSide), float(arMarkerGap), dic, arFirstId); 376 //{ Mat_<uchar> boardIma; board->draw(Size(1280, 1280 * 0.9), boardIma, 1280 * 0.1); imwrite("../data/markerboard.png", boardIma); } 377 Ptr<DetectorParameters> detectParams = DetectorParameters::create(); 378 379 //2.DetectCorner2D 380 vector<string> filePaths = Tool2D::globPaths(detIn, arPattern, 0, false); 381 for (size_t fileId = 0, cnt = 0; fileId < filePaths.size(); ++fileId) 382 { 383 //2.1 384 Mat_<Vec3b> color = imread(filePaths[fileId]); 385 Mat_<uchar> gray; cvtColor(color, gray, COLOR_BGR2GRAY); 386 387 //2.2 388 vector<vector<Point2f>> marker2D, marker2DReject; vector<int> markerId; 389 detectMarkers(gray, dic, marker2D, markerId, detectParams, marker2DReject); 390 bool found = markerId.size() >= arMinMarker; 391 392 //2.3 393 if (found) 394 { 395 refineDetectedMarkers(gray, (Ptr<Board>)board, marker2D, markerId, marker2DReject); 396 vector<Point2f> corner2D; 397 vector<Point3f> corner3D; 398 getBoardObjectAndImagePoints((Ptr<Board>)board, marker2D, markerId, corner3D, corner2D); 399 point2D.push_back(corner2D); 400 point3D.push_back(corner3D); 401 drawDetectedMarkers(color, marker2D, markerId); 402 } 403 else { point2D.push_back(vector<Point2f>()); point3D.push_back(vector<Point3f>()); } 404 oksView.push_back(found); 405 string tips = fmt::format("{} detect {} and current vaild views {}", found ? "Could" : "Cannot", filePaths[fileId], cnt += found); 406 if (!found) spdlog::warn(tips); 407 if (arShowMS >= 0) cv::displayStatusBar(__FUNCTION__, tips, 1000); 408 409 //2.5 410 if (arShowMS < 0) continue; 411 cv::imshow(__FUNCTION__, color); 412 cv::waitKey(arShowMS); 413 } if (arShowMS >= 0) cv::destroyWindow(__FUNCTION__); 414 } 415 416 public: 417 static Mat_<uchar> createArucoboard(int arFirstId = 0, int arVerMarker = 5, int arHorMarker = 8, int arMarkerSide = 300, int arMarkerGap = 60, int arDicType = aruco::DICT_ARUCO_ORIGINAL, vector<Mat_<uchar>>& markers = vector<Mat_<uchar>>()) 418 { 419 //1.Init 420 using namespace cv::aruco; 421 int cnt = arVerMarker * arHorMarker; 422 Ptr<Dictionary> dic = getPredefinedDictionary(arDicType); 423 markers.clear(); 424 425 //2.Create 426 Mat_<uchar> marker, _makerboard, markerboard; 427 for (int i = 0, id = arFirstId, num = 0; i < arVerMarker; ++i) 428 { 429 _makerboard.release(); 430 for (int j = 0; j < arHorMarker; ++j, ++id, ++num) 431 { 432 //2.1 CreateOne 433 if (num < cnt) 434 { 435 drawMarker(dic, id, arMarkerSide, marker); 436 markers.push_back(marker.clone()); 437 } 438 else marker = Mat_<uchar>(arMarkerSide, arMarkerSide, (uchar)0); 439 440 //2.2 ExtendMargin 441 copyMakeBorder(marker, marker, arMarkerGap >> 1, arMarkerGap >> 1, arMarkerGap >> 1, arMarkerGap >> 1, cv::BORDER_CONSTANT, Scalar(255)); 442 443 //2.3 ConcatOneRow 444 if (_makerboard.empty()) _makerboard = marker.clone(); 445 else hconcat(_makerboard, marker, _makerboard); 446 } 447 448 //2.4 ConcatOneColumn 449 if (markerboard.empty()) markerboard = _makerboard; 450 else vconcat(markerboard, _makerboard, markerboard); 451 } 452 return markerboard; 453 } 454 }; 455 456 public://MonoSuite 457 class CalibMono 458 { 459 public://1.CalibArgs 460 int calibRows = 480; 461 int calibCols = 640; 462 int calibBoard = CHBD; 463 int calibModel = RTCM; 464 int calibFlag = calibModel == KBCM ? fisheye::CALIB_RECOMPUTE_EXTRINSIC : 0; 465 enum { CHBD, SBBD, CCBD, ARBD, APBD, RDBD }; 466 enum { RTCM, KBCM, MUCM, EUCM, DSCM };//From ModelCamera 467 const string strCalibBoard = fmt::format("{}=chessboard {}=chessboardSB {}=circleboard {}=arucoboard {}=aprilboard {}=randomboard", CHBD, SBBD, CCBD, ARBD, APBD, RDBD); 468 const string strCalibModel = fmt::format("{}=RTCV {}=KBCM {}=MUCM {}=EUCM {}=DSCM", RTCM, KBCM, MUCM, EUCM, DSCM); 469 470 public://2.IOConfig 471 string calibIn = "../data/calib/cam1/rst/det.yml"; 472 string calibOut = fmt::format("{}/calib.yml", utils::fs::getParent(calibIn)); 473 CalibMono(string calibIn0 = "") { resetIO(calibIn0); } 474 void resetIO(string calibIn0 = "") { if (!calibIn0.empty()) { calibIn = calibIn0; calibOut = fmt::format("{}/calib.yml", utils::fs::getParent(calibIn)); } } 475 476 public://3.IOAction 477 void load(string inPath) { FileStorage fs(inPath, FileStorage::READ); read(fs); fs.release(); } 478 479 void write(FileStorage& fs, bool rdArgs = true, bool rdData = true) 480 { 481 if (rdArgs) 482 { 483 fs.writeComment("######CalibMono######"); 484 fs << "calibRows" << calibRows; 485 fs << "calibCols" << calibCols; 486 fs << "calibBoard" << calibBoard; fs.writeComment(strCalibBoard, true); 487 fs << "calibModel" << calibModel; fs.writeComment(strCalibModel, true); 488 fs << "calibFlag" << calibFlag; fs.writeComment("Refer to opencv docs for details", true); 489 fs << "calibIn" << calibIn; 490 } 491 if (rdData) 492 { 493 fs << "camK" << camK; 494 fs << "camD" << camD; 495 fs << "camAB" << camAB; 496 fs << "camRs" << camRs; 497 fs << "camTs" << camTs; 498 fs << "rmsView" << rmsView; 499 fs << "rmsMean" << rmsMean; 500 fs << "errPoints" << errPoints; 501 } 502 } 503 504 void read(FileStorage& fs, bool rdArgs = true, bool rdData = true) 505 { 506 if (rdArgs) 507 { 508 fs["calibRows"] >> calibRows; 509 fs["calibCols"] >> calibCols; 510 fs["calibBoard"] >> calibBoard; 511 fs["calibModel"] >> calibModel; 512 fs["calibFlag"] >> calibFlag; 513 fs["calibIn"] >> calibIn; resetIO(calibIn); 514 } 515 if (rdData) 516 { 517 fs["camK"] >> camK; 518 fs["camD"] >> camD; 519 fs["camAB"] >> camAB; 520 fs["camRs"] >> camRs; 521 fs["camTs"] >> camTs; 522 fs["rmsView"] >> rmsView; 523 fs["rmsMean"] >> rmsMean; 524 fs["errPoints"] >> errPoints; 525 } 526 } 527 528 public://4.CoreTask 529 Mat_<double> camK; 530 Mat_<double> camD; 531 Mat_<double> camAB; 532 Mat_<Vec3d> camRs; 533 Mat_<Vec3d> camTs; 534 Mat_<double> rmsKD; 535 Mat_<double> rmsRT; 536 Mat_<double> rmsView; 537 double rmsMean = -1; 538 vector<vector<Vec2d>> errPoints; 539 void calibrate() 540 { 541 //0.LoadDetection 542 if (!utils::fs::exists(calibIn)) spdlog::critical("{}: {} not exist", __FUNCTION__, calibIn); else spdlog::info("Current node: {}", __FUNCTION__); 543 shared_ptr<DetBD> detBD; 544 if (calibBoard == CHBD) detBD = make_shared<DetCHBD>(); 545 detBD->load(calibIn); 546 547 //1.ExtractDetection 548 vector<vector<Point2f>> point2D; 549 vector<vector<Point3f>> point3D; 550 for (size_t k = 0; k < detBD->oksView.size(); ++k) 551 { 552 if (detBD->oksView[k]) point2D.push_back(detBD->point2D[k]); 553 if (detBD->oksView[k]) point3D.push_back(detBD->point3D[k]); 554 } 555 556 //2.CalibCamera 557 if (calibModel == RTCM) rmsMean = calibrateCamera(point3D, point2D, Size(calibCols, calibRows), camK, camD, camRs, camTs, rmsKD, rmsRT, rmsView, calibFlag, TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, 1e-5)); 558 if (calibModel == KBCM) rmsMean = fisheye::calibrate(point3D, point2D, Size(calibCols, calibRows), camK, camD, camRs, camTs, calibFlag, TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 200, 1e-8)); 559 560 //3.SaveResults 561 utils::fs::createDirectories(utils::fs::getParent(calibOut)); 562 FileStorage fs(calibOut, FileStorage::WRITE); 563 this->write(fs); 564 fs.release(); 565 } 566 }; 567 568 class RectMono 569 { 570 public://1.RectArgs 571 int rectRows = 480; 572 int rectCols = 640; 573 float rectFocusScale = 1.; 574 Vec3d rectReviewEuler = Vec3d(0, 0, 0); 575 576 public://2.IOConfig 577 string rectIn = "../data/calib/cam1/rst/calib.yml"; 578 string rectOut = fmt::format("{}/rect.yml", utils::fs::getParent(rectIn)); 579 RectMono(string rectIn0 = "") { resetIO(rectIn0); } 580 void resetIO(string rectIn0 = "") { if (!rectIn0.empty()) { rectIn = rectIn0; rectOut = fmt::format("{}/rect.yml", utils::fs::getParent(rectIn)); } } 581 582 public://3.IOAction 583 void load(string inPath) { FileStorage fs(inPath, FileStorage::READ); read(fs); fs.release(); } 584 585 void write(FileStorage& fs, bool rdArgs = true, bool rdData = true) 586 { 587 if (rdArgs) 588 { 589 fs.writeComment("######RectMono######"); 590 fs << "rectRows" << rectRows; 591 fs << "rectCols" << rectCols; 592 fs << "rectFocusScale" << rectFocusScale; 593 fs << "rectReviewEuler" << rectReviewEuler; 594 fs << "rectIn" << rectIn; 595 } 596 if (rdData) 597 { 598 fs << "camP" << camP; 599 fs << "validRect" << validRect; 600 fs << "mapx" << mapx; 601 fs << "mapy" << mapy; 602 } 603 } 604 605 void read(FileStorage& fs, bool rdArgs = true, bool rdData = true) 606 { 607 if (rdArgs) 608 { 609 fs["rectRows"] >> rectRows; 610 fs["rectCols"] >> rectCols; 611 fs["rectFocusScale"] >> rectFocusScale; 612 fs["rectReviewEuler"] >> rectReviewEuler; 613 fs["rectIn"] >> rectIn; resetIO(rectIn); 614 } 615 if (rdData) 616 { 617 fs["camP"] >> camP; 618 fs["validRect"] >> validRect; 619 fs["mapx"] >> mapx; 620 fs["mapy"] >> mapy; 621 } 622 } 623 624 public://4.CoreTask 625 Mat_<double> camP; 626 Rect validRect; 627 Mat_<float> mapx; 628 Mat_<float> mapy; 629 void rectify() 630 { 631 //0.LoadCalibration 632 if (!utils::fs::exists(rectIn)) spdlog::critical("{}: {} not exist", __FUNCTION__, rectIn); else spdlog::info("Current node: {}", __FUNCTION__); 633 CalibMono calibMono; 634 calibMono.load(rectIn); 635 636 //1.ExtractCalibration 637 int calibModel = calibMono.calibModel; 638 Size calibSize(calibMono.calibCols, calibMono.calibRows); 639 Mat_<double> camK = calibMono.camK; 640 Mat_<double> camD = calibMono.camD; 641 Mat_<double> camAB = calibMono.camAB; 642 643 //2.RectCamera 644 Mat_<double> reviewRMat = Mat_<double>::eye(3, 3); 645 ceres::EulerAnglesToRotationMatrix(rectReviewEuler.val, 0, reviewRMat.ptr<double>()); 646 if (calibModel == CalibMono::RTCM) 647 { 648 camP = getOptimalNewCameraMatrix(camK, camD, calibSize, rectFocusScale, Size(rectCols, rectRows), &validRect, false); 649 initUndistortRectifyMap(camK, camD, reviewRMat, camP, Size(rectCols, rectRows), CV_32F, mapx, mapy); 650 } 651 if (calibModel == CalibMono::KBCM) 652 { 653 fisheye::estimateNewCameraMatrixForUndistortRectify(camK, camD, calibSize, Mat(), camP, rectFocusScale, Size(rectCols, rectRows), 1.0); 654 fisheye::initUndistortRectifyMap(camK, camD, reviewRMat, camP, Size(rectCols, rectRows), CV_32F, mapx, mapy); 655 } 656 657 //3.SaveResults 658 utils::fs::createDirectories(utils::fs::getParent(rectOut)); 659 FileStorage fs(rectOut, FileStorage::WRITE); 660 this->write(fs); 661 fs.release(); 662 } 663 }; 664 665 class UndMono 666 { 667 public://1.UndArgs 668 int undShowMS = 30; 669 string undPattern = "*"; 670 string undRectFile = "../data/calib/cam1/rst/rect.yml"; 671 672 public://2.IOConfig 673 string undIn = "../data/calib"; 674 string undOut = fmt::format("{}/rst", undIn); 675 UndMono(string undIn0 = "") { resetIO(undIn0); } 676 void resetIO(string undIn0 = "") { if (!undIn0.empty()) { undIn = undIn0; undOut = fmt::format("{}/rst", undIn); } } 677 678 public://3.IOAction 679 void load(string inPath) { FileStorage fs(inPath, FileStorage::READ); read(fs); fs.release(); } 680 681 void write(FileStorage& fs, bool rdArgs = true, bool rdData = true) 682 { 683 if (rdArgs) 684 { 685 fs.writeComment("######UndMono######"); 686 fs << "undShowMS" << undShowMS; 687 fs << "undPattern" << undPattern; 688 fs << "undRectFile" << undRectFile; 689 fs << "undIn" << undIn; 690 } 691 } 692 693 void read(FileStorage& fs, bool rdArgs = true, bool rdData = true) 694 { 695 if (rdArgs) 696 { 697 fs["undShowMS"] >> undShowMS; 698 fs["undPattern"] >> undPattern; 699 fs["undRectFile"] >> undRectFile; 700 fs["undIn"] >> undIn; resetIO(undIn); 701 } 702 } 703 704 public://4.CoreTask 705 void undistort() 706 { 707 //0.LoadRectification 708 if (!utils::fs::exists(undRectFile)) spdlog::critical("{}: {} not exist", __FUNCTION__, undRectFile); else spdlog::info("Current node: {}", __FUNCTION__); 709 RectMono rectMono; 710 rectMono.load(undRectFile); 711 utils::fs::createDirectories(undOut); 712 713 //1.ExtractRectification 714 Mat_<float> mapx = rectMono.mapx; 715 Mat_<float> mapy = rectMono.mapy; 716 717 //2.UndCamera 718 if (undShowMS >= 0) cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 719 vector<string> filePaths = Tool2D::globPaths(undIn, undPattern, 0, false); 720 for (size_t fileId = 0; fileId < filePaths.size(); ++fileId) 721 { 722 //2.1 723 Mat_<Vec3b> color = imread(filePaths[fileId], 1); 724 Mat_<uchar> gray; cvtColor(color, gray, COLOR_BGR2GRAY); 725 726 //2.2 727 Mat_<uchar> undIma; 728 remap(gray, undIma, mapx, mapy, INTER_LINEAR); 729 730 //2.3 731 Mat_<uchar> catIma; 732 if (gray.size() != undIma.size()) resize(gray, gray, undIma.size()); 733 hconcat(gray, undIma, catIma); 734 cv::imwrite(fmt::format("{}/und_{}", undOut, Tool2D::pathProps(filePaths[fileId])[4]), catIma); 735 if (undShowMS >= 0) cv::displayStatusBar(__FUNCTION__, "Undistorting " + filePaths[fileId], 1000); 736 737 //2.4 738 if (undShowMS < 0) continue; 739 cv::imshow(__FUNCTION__, catIma); 740 cv::waitKey(undShowMS); 741 } if (undShowMS >= 0) cv::destroyWindow(__FUNCTION__); 742 } 743 }; 744 745 public://BinoSuite 746 class CalibBino 747 { 748 public://1.CalibArgs 749 int binoCalibFlag = CALIB_FIX_INTRINSIC;//same as fisheye::CALIB_FIX_INTRINSIC; 750 751 public://2.IOConfig 752 string binoCalibIn = "../data/calib"; 753 string binoCalibOut = fmt::format("{}/calib.yml", binoCalibIn); 754 string detPath1 = fmt::format("{}/cam1/rst/det.yml", binoCalibIn); 755 string detPath2 = fmt::format("{}/cam2/rst/det.yml", binoCalibIn); 756 string calibPath1 = fmt::format("{}/cam1/rst/calib.yml", binoCalibIn); 757 string calibPath2 = fmt::format("{}/cam2/rst/calib.yml", binoCalibIn); 758 CalibBino(string binoCalibIn0 = "") { resetIO(binoCalibIn0); } 759 void resetIO(string binoCalibIn0 = "") 760 { 761 if (!binoCalibIn0.empty()) 762 { 763 binoCalibIn = binoCalibIn0; 764 binoCalibOut = fmt::format("{}/calib.yml", binoCalibIn); 765 detPath1 = fmt::format("{}/cam1/rst/det.yml", binoCalibIn); 766 detPath2 = fmt::format("{}/cam2/rst/det.yml", binoCalibIn); 767 calibPath1 = fmt::format("{}/cam1/rst/calib.yml", binoCalibIn); 768 calibPath2 = fmt::format("{}/cam2/rst/calib.yml", binoCalibIn); 769 } 770 } 771 772 public://3.IOAction 773 void load(string inPath) 774 { 775 FileStorage fs(inPath, FileStorage::READ); 776 if (!detBD1 || !detBD2) 777 { 778 CalibMono ca; ca.read(fs, true, false); 779 if (ca.calibBoard == CalibMono::CHBD) detBD1 = make_shared<DetCHBD>(); 780 if (ca.calibBoard == CalibMono::CHBD) detBD2 = make_shared<DetCHBD>(); 781 } 782 read(fs); 783 fs.release(); 784 } 785 786 void write(FileStorage& fs, bool rdArgs = true, bool rdData = true, bool rdMono = true) 787 { 788 if (rdArgs) 789 { 790 if (rdMono) 791 { 792 detBD1->write(fs, true, false); 793 calibMono1.write(fs, true, false); 794 } 795 fs.writeComment("######CalibBino######"); 796 fs << "binoCalibFlag" << binoCalibFlag; fs.writeComment("Refer to opencv docs for details", true); 797 fs << "binoCalibIn" << binoCalibIn; 798 } 799 if (rdData) 800 { 801 if (rdMono) 802 { 803 fs << "camK1" << calibMono1.camK; 804 fs << "camK2" << calibMono2.camK; 805 fs << "camD1" << calibMono1.camD; 806 fs << "camD2" << calibMono2.camD; 807 fs << "camAB1" << calibMono1.camAB; 808 fs << "camAB2" << calibMono2.camAB; 809 } 810 fs << "cam21R" << cam21R; 811 fs << "cam21T" << cam21T; 812 fs << "cam21E" << cam21E; 813 fs << "cam21F" << cam21F; 814 fs << "rmsView" << rmsView; 815 fs << "rmsMean" << rmsMean; 816 fs << "errPoints" << errPoints; 817 if (rdMono) 818 { 819 fs << "oksView1" << detBD1->oksView; 820 fs << "oksView2" << detBD2->oksView; 821 fs << "point3D" << detBD1->point3D; 822 fs << "point2D1" << detBD1->point2D; 823 fs << "point2D2" << detBD2->point2D; 824 } 825 } 826 } 827 828 void read(FileStorage& fs, bool rdArgs = true, bool rdData = true, bool rdMono = true) 829 { 830 if (rdArgs) 831 { 832 if (rdMono) 833 { 834 detBD1->read(fs, true, false); 835 detBD2->read(fs, true, false); 836 calibMono1.read(fs, true, false); 837 calibMono2.read(fs, true, false); 838 } 839 fs["binoCalibFlag"] >> binoCalibFlag; 840 fs["binoCalibIn"] >> binoCalibIn; resetIO(binoCalibIn); 841 } 842 if (rdData) 843 { 844 if (rdMono) 845 { 846 fs["camK1"] >> calibMono1.camK; 847 fs["camK2"] >> calibMono2.camK; 848 fs["camD1"] >> calibMono1.camD; 849 fs["camD2"] >> calibMono2.camD; 850 fs["camAB1"] >> calibMono1.camAB; 851 fs["camAB2"] >> calibMono2.camAB; 852 } 853 fs["cam21R"] >> cam21R; 854 fs["cam21T"] >> cam21T; 855 fs["cam21E"] >> cam21E; 856 fs["cam21F"] >> cam21F; 857 fs["rmsView"] >> rmsView; 858 fs["rmsMean"] >> rmsMean; 859 fs["errPoints"] >> errPoints; 860 if (rdMono) 861 { 862 fs["oksView1"] >> detBD1->oksView; 863 fs["oksView2"] >> detBD2->oksView; 864 fs["point3D"] >> detBD1->point3D; 865 fs["point2D1"] >> detBD1->point2D; 866 fs["point2D2"] >> detBD2->point2D; 867 } 868 } 869 } 870 871 public: 872 Mat_<double> cam21R; 873 Mat_<double> cam21T; 874 Mat_<double> cam21E; 875 Mat_<double> cam21F; 876 Mat_<double> rmsView; 877 double rmsMean = -1; 878 vector<vector<Vec4d>> errPoints; 879 shared_ptr<DetBD> detBD1; 880 shared_ptr<DetBD> detBD2; 881 CalibMono calibMono1; 882 CalibMono calibMono2; 883 void calibrate() 884 { 885 //0.LoadMono 886 if (!utils::fs::exists(binoCalibIn)) spdlog::critical("{}: {} not exist", __FUNCTION__, binoCalibIn); 887 else if (!utils::fs::exists(detPath1)) spdlog::critical("{}: {} not exist", __FUNCTION__, detPath1); 888 else if (!utils::fs::exists(detPath2)) spdlog::critical("{}: {} not exist", __FUNCTION__, detPath2); 889 else if (!utils::fs::exists(calibPath1)) spdlog::critical("{}: {} not exist", __FUNCTION__, calibPath1); 890 else if (!utils::fs::exists(calibPath2)) spdlog::critical("{}: {} not exist", __FUNCTION__, calibPath2); 891 else spdlog::info("Current node: {}", __FUNCTION__); 892 calibMono1.load(calibPath1); 893 calibMono2.load(calibPath2); 894 if (calibMono1.calibBoard == CalibMono::CHBD) detBD1 = make_shared<DetCHBD>(); 895 if (calibMono1.calibBoard == CalibMono::CHBD) detBD2 = make_shared<DetCHBD>(); 896 detBD1->load(detPath1); 897 detBD2->load(detPath2); 898 899 //1.ExtractMono 900 vector<vector<Point3f>> point3D; 901 vector<vector<Point2f>> point2D1; 902 vector<vector<Point2f>> point2D2; 903 for (size_t k = 0; k < detBD1->oksView.size(); ++k) 904 { 905 if (detBD1->oksView[k] && detBD2->oksView[k]) point3D.push_back(detBD1->point3D[k]); 906 if (detBD1->oksView[k] && detBD2->oksView[k]) point2D1.push_back(detBD1->point2D[k]); 907 if (detBD1->oksView[k] && detBD2->oksView[k]) point2D2.push_back(detBD2->point2D[k]); 908 } 909 int calibModel = calibMono1.calibModel; 910 Size calibSize(calibMono1.calibCols, calibMono1.calibRows); 911 Mat_<double> camK1 = calibMono1.camK, camK2 = calibMono2.camK; 912 Mat_<double> camD1 = calibMono1.camD, camD2 = calibMono2.camD; 913 Mat_<double> camAB1 = calibMono1.camAB, camAB2 = calibMono2.camAB; 914 915 //2.CalibCamera 916 if (calibModel == CalibMono::RTCM) rmsMean = stereoCalibrate(point3D, point2D1, point2D2, camK1, camD1, camK2, camD2, calibSize, cam21R, cam21T, cam21E, cam21F, rmsView, binoCalibFlag, TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, 1e-5)); 917 if (calibModel == CalibMono::KBCM) rmsMean = fisheye::stereoCalibrate(point3D, point2D1, point2D2, camK1, camD1, camK2, camD2, calibSize, cam21R, cam21T, binoCalibFlag, TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 200, 1e-8)); 918 utils::fs::createDirectories(utils::fs::getParent(binoCalibOut)); 919 FileStorage fs(binoCalibOut, FileStorage::WRITE); 920 this->write(fs); 921 fs.release(); 922 } 923 }; 924 925 class RectBino 926 { 927 public://1.RectArgs 928 int binoRectRows = 480; 929 int binoRectCols = 640; 930 float binoRectFocusScale = 1.; 931 Vec3d binoRectReviewEuler = Vec3d(0, 0, 0); 932 933 public://2.IOConfig 934 string binoRectIn = "../data/calib/calib.yml"; 935 string binoRectOut = fmt::format("{}/rect.yml", utils::fs::getParent(binoRectIn)); 936 RectBino(string binoRectIn0 = "") { resetIO(binoRectIn0); } 937 void resetIO(string binoRectIn0 = "") { if (!binoRectIn0.empty()) { binoRectIn = binoRectIn0; binoRectOut = fmt::format("{}/rect.yml", utils::fs::getParent(binoRectIn)); } } 938 939 public://3.IOAction 940 void load(string inPath) { FileStorage fs(inPath, FileStorage::READ); read(fs); fs.release(); } 941 942 void write(FileStorage& fs, bool rdArgs = true, bool rdData = true) 943 { 944 if (rdArgs) 945 { 946 fs.writeComment("######RectBino######"); 947 fs << "binoRectRows" << binoRectRows; 948 fs << "binoRectCols" << binoRectCols; 949 fs << "binoRectFocusScale" << binoRectFocusScale; 950 fs << "binoRectReviewEuler" << binoRectReviewEuler; 951 fs << "binoRectIn" << binoRectIn; 952 } 953 if (rdData) 954 { 955 fs << "camP1" << camP1; 956 fs << "camP2" << camP2; 957 fs << "camR1" << camR1; 958 fs << "camR2" << camR2; 959 fs << "rectQ" << rectQ; 960 fs << "validRect1" << validRect1; 961 fs << "validRect2" << validRect2; 962 fs << "mapx1" << mapx1; 963 fs << "mapx2" << mapx2; 964 fs << "mapy1" << mapy1; 965 fs << "mapy2" << mapy2; 966 } 967 } 968 969 void read(FileStorage& fs, bool rdArgs = true, bool rdData = true) 970 { 971 if (rdArgs) 972 { 973 fs["binoRectRows"] >> binoRectRows; 974 fs["binoRectCols"] >> binoRectCols; 975 fs["binoRectFocusScale"] >> binoRectFocusScale; 976 fs["binoRectReviewEuler"] >> binoRectReviewEuler; 977 fs["binoRectIn"] >> binoRectIn; resetIO(binoRectIn); 978 } 979 if (rdData) 980 { 981 fs["camP1"] >> camP1; 982 fs["camP2"] >> camP2; 983 fs["camR1"] >> camR1; 984 fs["camR2"] >> camR2; 985 fs["rectQ"] >> rectQ; 986 fs["validRect1"] >> validRect1; 987 fs["validRect2"] >> validRect2; 988 fs["mapx1"] >> mapx1; 989 fs["mapx2"] >> mapx2; 990 fs["mapy1"] >> mapy1; 991 fs["mapy2"] >> mapy2; 992 } 993 } 994 995 public://4.CoreTask 996 Mat_<double> camP1; 997 Mat_<double> camP2; 998 Mat_<double> camR1; 999 Mat_<double> camR2; 1000 Mat_<double> rectQ; 1001 Rect validRect1; 1002 Rect validRect2; 1003 Mat_<float> mapx1; 1004 Mat_<float> mapx2; 1005 Mat_<float> mapy1; 1006 Mat_<float> mapy2; 1007 void rectify() 1008 { 1009 //0.LoadCalibration 1010 if (!utils::fs::exists(binoRectIn)) spdlog::critical("{}: {} not exist", __FUNCTION__, binoRectIn); else spdlog::info("Current node: {}", __FUNCTION__); 1011 CalibBino calibBino; 1012 calibBino.load(binoRectIn); 1013 1014 //1.ExtractCalibration 1015 int calibModel = calibBino.calibMono1.calibModel; 1016 Size calibSize(calibBino.calibMono1.calibCols, calibBino.calibMono1.calibRows); 1017 Mat_<double> camK1 = calibBino.calibMono1.camK, camK2 = calibBino.calibMono2.camK; 1018 Mat_<double> camD1 = calibBino.calibMono1.camD, camD2 = calibBino.calibMono2.camD; 1019 Mat_<double> camAB1 = calibBino.calibMono1.camAB, camAB2 = calibBino.calibMono2.camAB; 1020 Mat_<double> cam21R = calibBino.cam21R, cam21T = calibBino.cam21T; 1021 1022 //2.RectCamera 1023 Mat_<double> reviewRMat = Mat_<double>::eye(3, 3); 1024 ceres::EulerAnglesToRotationMatrix(binoRectReviewEuler.val, 0, reviewRMat.ptr<double>()); 1025 if (calibModel == CalibMono::RTCM) 1026 { 1027 stereoRectify(camK1, camD1, camK2, camD2, calibSize, cam21R, cam21T, camR1, camR2, camP1, camP2, rectQ, CALIB_ZERO_DISPARITY, binoRectFocusScale, Size(binoRectCols, binoRectRows), &validRect1, &validRect2); 1028 initUndistortRectifyMap(camK1, camD1, reviewRMat * camR1, camP1, Size(binoRectCols, binoRectRows), CV_32F, mapx1, mapy1); 1029 initUndistortRectifyMap(camK2, camD2, reviewRMat * camR2, camP2, Size(binoRectCols, binoRectRows), CV_32F, mapx2, mapy2); 1030 } 1031 if (calibModel == CalibMono::KBCM) 1032 { 1033 fisheye::stereoRectify(camK1, camD1, camK2, camD2, calibSize, cam21R, cam21T, camR1, camR2, camP1, camP2, rectQ, CALIB_ZERO_DISPARITY, Size(binoRectCols, binoRectRows), binoRectFocusScale, 1.0); 1034 fisheye::initUndistortRectifyMap(camK1, camD1, reviewRMat * camR1, camP1, Size(binoRectCols, binoRectRows), CV_32F, mapx1, mapy1); 1035 fisheye::initUndistortRectifyMap(camK2, camD2, reviewRMat * camR2, camP2, Size(binoRectCols, binoRectRows), CV_32F, mapx2, mapy2); 1036 } 1037 1038 //3.SaveResults 1039 utils::fs::createDirectories(utils::fs::getParent(binoRectOut)); 1040 FileStorage fs(binoRectOut, FileStorage::WRITE); 1041 this->write(fs); 1042 fs.release(); 1043 } 1044 }; 1045 1046 class UndBino 1047 { 1048 public://1.UndArgs 1049 int binoUndShowMS = 30; 1050 string binoUndPattern = "*"; 1051 string binoUndRectFile = "../data/calib/rect.yml"; 1052 1053 public://2.IOConfig 1054 string binoUndIn = "../data/calib/all"; 1055 string binoUndOut = fmt::format("{}/rst", binoUndIn); 1056 UndBino(string binoUndIn0 = "") { resetIO(binoUndIn0); } 1057 void resetIO(string binoUndIn0 = "") { if (!binoUndIn0.empty()) { binoUndIn = binoUndIn0; binoUndOut = fmt::format("{}/rst", binoUndIn); } } 1058 1059 public://3.IOAction 1060 void load(string inPath) { FileStorage fs(inPath, FileStorage::READ); read(fs); fs.release(); } 1061 1062 void write(FileStorage& fs, bool rdArgs = true, bool rdData = true) 1063 { 1064 if (rdArgs) 1065 { 1066 fs.writeComment("######UndBino######"); 1067 fs << "binoUndShowMS" << binoUndShowMS; 1068 fs << "binoUndPattern" << binoUndPattern; 1069 fs << "binoUndRectFile" << binoUndRectFile; 1070 fs << "binoUndIn" << binoUndIn; 1071 } 1072 } 1073 1074 void read(FileStorage& fs, bool rdArgs = true, bool rdData = true) 1075 { 1076 if (rdArgs) 1077 { 1078 fs["binoUndShowMS"] >> binoUndShowMS; 1079 fs["binoUndPattern"] >> binoUndPattern; 1080 fs["binoUndRectFile"] >> binoUndRectFile; 1081 fs["binoUndIn"] >> binoUndIn; resetIO(binoUndIn); 1082 } 1083 } 1084 1085 public://4.CoreTask 1086 void undistort() 1087 { 1088 //0.LoadRectification 1089 if (!utils::fs::exists(binoUndRectFile)) spdlog::critical("{}: {} not exist", __FUNCTION__, binoUndRectFile); else spdlog::info("Current node: {}", __FUNCTION__); 1090 RectBino rectBino; 1091 rectBino.load(binoUndRectFile); 1092 utils::fs::createDirectories(binoUndOut); 1093 1094 //1.ExtractRectification 1095 Mat_<float> mapx1 = rectBino.mapx1; 1096 Mat_<float> mapx2 = rectBino.mapx2; 1097 Mat_<float> mapy1 = rectBino.mapy1; 1098 Mat_<float> mapy2 = rectBino.mapy2; 1099 1100 //2.UndCamera 1101 if (binoUndShowMS >= 0) cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 1102 vector<string> filePaths = Tool2D::globPaths(binoUndIn, binoUndPattern, 0, false); 1103 for (size_t fileId = 0; fileId < filePaths.size(); ++fileId) 1104 { 1105 //2.1 1106 Mat_<Vec3b> color = imread(filePaths[fileId], 1); 1107 Mat_<uchar> gray1, gray2; 1108 cvtColor(color.colRange(0, color.cols >> 1), gray1, COLOR_BGR2GRAY); 1109 cvtColor(color.colRange(color.cols >> 1, color.cols), gray2, COLOR_BGR2GRAY); 1110 1111 //2.2 1112 Mat_<uchar> undIma1, undIma2; 1113 remap(gray1, undIma1, mapx1, mapy1, INTER_LINEAR); 1114 remap(gray2, undIma2, mapx2, mapy2, INTER_LINEAR); 1115 1116 //2.3 1117 Mat_<uchar> catIma; 1118 hconcat(undIma1, undIma2, catIma); 1119 cv::imwrite(fmt::format("{}/und_{}", binoUndOut, Tool2D::pathProps(filePaths[fileId])[4]), catIma); 1120 if (binoUndShowMS >= 0) cv::displayStatusBar(__FUNCTION__, "Undistorting " + filePaths[fileId], 1000); 1121 1122 //2.4 1123 if (binoUndShowMS < 0) continue; 1124 cv::imshow(__FUNCTION__, catIma); 1125 cv::waitKey(binoUndShowMS); 1126 } if (binoUndShowMS >= 0) cv::destroyWindow(__FUNCTION__); 1127 } 1128 }; 1129 1130 public://CalibAPP 1131 class CalibAPP 1132 { 1133 public: 1134 bool doDet = true; 1135 bool doCalib = true; 1136 bool doRect = true; 1137 bool doUnd = true; 1138 bool doBinoCalib = true; 1139 bool doBinoRect = true; 1140 bool doBinoUnd = true; 1141 DetCHBD detCHBD; 1142 CalibMono calibMono; 1143 RectMono rectMono; 1144 UndMono undMono; 1145 CalibBino calibBino; 1146 RectBino rectBino; 1147 UndBino undBino; 1148 void writeArgs(string cfgPath) 1149 { 1150 FileStorage fs(cfgPath, FileStorage::WRITE); 1151 fs.writeComment("######CalibAPP######"); 1152 fs << "doDet" << doDet; 1153 fs << "doCalib" << doCalib; 1154 fs << "doRect" << doRect; 1155 fs << "doUnd" << doUnd; 1156 fs << "doBinoCalib" << doBinoCalib; 1157 fs << "doBinoRect" << doBinoRect; 1158 fs << "doBinoUnd" << doBinoUnd; 1159 detCHBD.write(fs, true, false); 1160 calibMono.write(fs, true, false); 1161 rectMono.write(fs, true, false); 1162 undMono.write(fs, true, false); 1163 calibBino.write(fs, true, false, false); 1164 rectBino.write(fs, true, false); 1165 undBino.write(fs, true, false); 1166 fs.release(); 1167 } 1168 void readArgs(string cfgPath) 1169 { 1170 FileStorage fs(cfgPath, FileStorage::READ); 1171 fs["doDet"] >> doDet; 1172 fs["doCalib"] >> doCalib; 1173 fs["doRect"] >> doRect; 1174 fs["doUnd"] >> doUnd; 1175 fs["doBinoCalib"] >> doBinoCalib; 1176 fs["doBinoRect"] >> doBinoRect; 1177 fs["doBinoUnd"] >> doBinoUnd; 1178 detCHBD.read(fs, true, false); 1179 calibMono.read(fs, true, false); 1180 rectMono.read(fs, true, false); 1181 undMono.read(fs, true, false); 1182 calibBino.read(fs, true, false, false); 1183 rectBino.read(fs, true, false); 1184 undBino.read(fs, true, false); 1185 } 1186 void calibCam(string cfgPath = "../data/calib/cfg.yml") 1187 { 1188 if (cfgPath.empty()) { spdlog::critical("Config file must be given"); return; } 1189 if (!utils::fs::exists(cfgPath)) { spdlog::critical("Config file not exist and created {}", cfgPath); writeArgs(cfgPath); return; } 1190 readArgs(cfgPath); 1191 if (doDet) detCHBD.detect(); 1192 if (doCalib) calibMono.calibrate(); 1193 if (doRect) rectMono.rectify(); 1194 if (doUnd) undMono.undistort(); 1195 if (doBinoCalib) calibBino.calibrate(); 1196 if (doBinoRect) rectBino.rectify(); 1197 if (doBinoUnd) undBino.undistort(); 1198 } 1199 static void TestMe_CHBD_RTCM_KBCM(int argc, char** argv) 1200 { 1201 //0.AutoParams 1202 int calibRows[2] = { 480, 800 }; 1203 int calibCols[2] = { 640, 1280 }; 1204 int camModels[2] = { CalibMono::RTCM, CalibMono::KBCM }; 1205 int calibFlag[2] = { 0, fisheye::CALIB_RECOMPUTE_EXTRINSIC }; 1206 int rectRows[2] = { 480, 800 }; 1207 int rectCols[2] = { 640, 1280 }; 1208 string modDirs[2] = { "../data/calib/cvrtcm", "../data/calib/cvkbcm" };//from opencv/samples/data 1209 string camDirs[2] = { "cam1", "cam2" };//and opencv_extra/testdata/cv/cameracalibration/fisheye/calib-3_stereo_from_JY/ 1210 1211 //1.CalibCamera 1212 for (int i = 0; i < 2; ++i) 1213 { 1214 for (int j = 0; j < 2; ++j) 1215 { 1216 //1.1 ConfigApp 1217 CalibAPP calibApp; 1218 calibApp.doBinoCalib = false; 1219 calibApp.doBinoRect = false; 1220 calibApp.doBinoUnd = false; 1221 1222 //1.2 ConfigBoard 1223 calibApp.detCHBD.resetIO(fmt::format("{}/{}", modDirs[i], camDirs[j])); 1224 1225 //1.3 ConfigCalib 1226 calibApp.calibMono.resetIO(calibApp.detCHBD.detOut); 1227 calibApp.calibMono.calibRows = calibRows[i]; 1228 calibApp.calibMono.calibCols = calibCols[i]; 1229 calibApp.calibMono.calibModel = camModels[i]; 1230 calibApp.calibMono.calibFlag = calibFlag[i]; 1231 1232 //1.4 ConfigRect 1233 calibApp.rectMono.resetIO(calibApp.calibMono.calibOut); 1234 calibApp.rectMono.rectRows = rectRows[i]; 1235 calibApp.rectMono.rectCols = rectCols[i]; 1236 calibApp.rectMono.rectReviewEuler = Vec3d(30, 0, 0); 1237 1238 //1.5 ConfigUnd 1239 calibApp.undMono.resetIO(calibApp.detCHBD.detIn); 1240 calibApp.undMono.undRectFile = calibApp.rectMono.rectOut; 1241 1242 //1.6 CalibCam 1243 calibApp.writeArgs("../data/tmp.yml");//write config and then for read 1244 calibApp.calibCam("../data/tmp.yml"); 1245 } 1246 1247 //2.1 ConfigApp 1248 CalibAPP calibApp; 1249 calibApp.doDet = false; 1250 calibApp.doCalib = false; 1251 calibApp.doRect = false; 1252 calibApp.doUnd = false; 1253 1254 //2.2 ConfigCalib 1255 calibApp.calibBino.resetIO(modDirs[i]); 1256 calibApp.calibBino.binoCalibFlag = CALIB_FIX_INTRINSIC; 1257 1258 //2.3 ConfigRect 1259 calibApp.rectBino.resetIO(calibApp.calibBino.binoCalibOut); 1260 calibApp.rectBino.binoRectRows = rectRows[i]; 1261 calibApp.rectBino.binoRectCols = rectCols[i]; 1262 calibApp.rectBino.binoRectReviewEuler = Vec3d(30, 0, 0); 1263 1264 //2.4 ConfigUnd 1265 calibApp.undBino.resetIO(modDirs[i] + "/all"); 1266 calibApp.undBino.binoUndRectFile = calibApp.rectBino.binoRectOut; 1267 1268 //2.5 CalibCam 1269 calibApp.writeArgs("../data/tmp.yml");//write config and then for read 1270 calibApp.calibCam("../data/tmp.yml"); 1271 } 1272 } 1273 }; 1274 }; 1275 1276 #ifdef MPPCalibFrameTest 1277 int main(int argc, char** argv) { MPPCalibFrame::CalibAPP::TestMe_CHBD_RTCM_KBCM(argc, argv); return 0; } 1278 int main1(int argc, char** argv) { MPPCalibFrame::CapCam ccam; ccam.capRows = 480; ccam.capCols = 640; ccam.capSplitted = true; return 0; } 1279 int main2(int argc, char** argv) { MPPCalibFrame::CalibAPP::TestMe_CHBD_RTCM_KBCM(argc, argv); return 0; } 1280 #endif 1281 1282 #endif