导航

OpenCV-3D学习日志:搭建成像解算框架

Posted on 2020-05-01 22:32  dzyBK  阅读(259)  评论(0编辑  收藏  举报

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
View Code