zoukankan      html  css  js  c++  java
  • Ceres-Solver学习日志:手动求导使用样例与cvLMSolver使用对比

             之前已经介绍过Ceres自动求导的使用样例,详见《Ceres-Solver学习日志:自动求导使用样例与针孔成像器的应用》,这里介绍手动求导使用样例,并对比与cv::LMSolver的使用差异。

    1.定义代价类

             手动求导的核心就是继承CostFunction或DynamicCostFunction或SizedCostFunction,继承后的主要工作就是重载Evaluate函数,实现残差和雅可比矩阵的计算。

             继承CostFunction或DynamicCostFunction适用于在参数数量和残差数量都不能在编译时确定的情况,两者的区别是设置参数数量和残差数量的接口不一样,DynamicCostFuntion的设置接口就是对CostFunction设置接口的简单封装,查看源码可知无本质区别,之所以定义DynamicCostFuntion类可能是为了与SizedCostFunction对应。

             继承SizedCostFunction适用于至少参数数量能在编译时确定的情况,残差数量未知则将对应的模板参数设为DYNAMIC即可。

             定义多少个代价类的规则同定义多少个残差类,具体参见自动求导使用样例一文。

    2.使用代价类

             与如何使用残差类相似,具体参见自动求导使用样例一文。

    3.使用cv::LMSolver

             对比使用样例代码即可知晓。

    4.使用样例

             提供两个使用样例,封装为两个类:

             OptimizeRt:基于单次观察,优化此次观察的外参,定义了CeresCostRt和CvCallbackRt两个类。

             OptimizeKDRt:基于多次观察,优化相机内参和所有观察的外参,定义了CeresCostKDRt和CvCallbackKDRt两个类。

             其中类MotionSim用于生成仿真数据,使用说明参见《CV学习日志:CV开发之三维仿真器》。

             存在可能不收敛的测试结果,属于正常现象,可修改初值精度来增加收敛性。

             关于测试结果说明:从基于仿真数据的测试结果来看,Ceres收敛次数比cv::LMSolver多,但在收敛的情况下,cv::LMSolver在迭代次数和收敛精度上比Ceres好。

             以下是详细代码,依赖于C++14、OpenCV4.x、Ceres和Spdlog。

      1 #include <opencv2/opencv.hpp>
      2 #include <opencv2/viz.hpp>
      3 #include <spdlog/spdlog.h>
      4 #include <ceres/ceres.h>
      5 using namespace std;
      6 using namespace cv;
      7 
      8 class MotionSim
      9 {
     10 public:
     11     static void TestMe(int argc, char** argv)
     12     {
     13         MotionSim motionSim(false);
     14         motionSim.camFovX = 45;
     15         motionSim.camFovY = 30;
     16         motionSim.camRand = 10;
     17         motionSim.enableVerbose = false;
     18         motionSim.runMotion(false, false, 7);
     19         motionSim.visMotion();
     20     }
     21 
     22 public:
     23     struct MotionView
     24     {
     25         Mat_<double> r = Mat_<double>(3, 1);
     26         Mat_<double> t = Mat_<double>(3, 1);
     27         Mat_<double> q = Mat_<double>(4, 1);
     28         Mat_<double> rt = Mat_<double>(6, 1);
     29         Mat_<double> radian = Mat_<double>(3, 1);
     30         Mat_<double> degree = Mat_<double>(3, 1);
     31         Mat_<double> R = Mat_<double>(3, 3);
     32         Mat_<double> T = Mat_<double>(3, 4);
     33         Mat_<double> K;
     34         Mat_<double> D;
     35         Mat_<Vec3d> point3D;
     36         Mat_<Vec2d> point2D;
     37         Mat_<int> point3DIds;
     38         string print(string savePath = "")
     39         {
     40             string str;
     41             str += fmt::format("r: {}
    ", cvarr2str(r.t()));
     42             str += fmt::format("t: {}
    ", cvarr2str(t.t()));
     43             str += fmt::format("q: {}
    ", cvarr2str(q.t()));
     44             str += fmt::format("rt: {}
    ", cvarr2str(rt.t()));
     45             str += fmt::format("radian: {}
    ", cvarr2str(radian.t()));
     46             str += fmt::format("degree: {}
    ", cvarr2str(degree.t()));
     47             str += fmt::format("R: {}
    ", cvarr2str(R));
     48             str += fmt::format("T: {}
    ", cvarr2str(T));
     49             str += fmt::format("K: {}
    ", cvarr2str(K));
     50             str += fmt::format("D: {}
    ", cvarr2str(D.t()));
     51             if (savePath.empty() == false) { FILE* out = fopen(savePath.c_str(), "w"); fprintf(out, str.c_str()); fclose(out); }
     52             return str;
     53         }
     54     };
     55     static string cvarr2str(InputArray v)
     56     {
     57         Ptr<Formatted> fmtd = cv::format(v, Formatter::FMT_DEFAULT);
     58         string dst; fmtd->reset();
     59         for (const char* str = fmtd->next(); str; str = fmtd->next()) dst += string(str);
     60         return dst;
     61     }
     62     static void euler2matrix(double e[3], double R[9], bool forward = true, int argc = 0, char** argv = 0)
     63     {
     64         if (argc > 0)
     65         {
     66             int N = 999;
     67             for (int k = 0; k < N; ++k)//OpenCV not better than DIY
     68             {
     69                 //1.GenerateData
     70                 Matx31d radian0 = radian0.randu(-3.14159265358979323846, 3.14159265358979323846);
     71                 Matx33d R; euler2matrix(radian0.val, R.val, true);
     72                 const double deg2rad = 3.14159265358979323846 * 0.0055555555555555556;
     73                 const double rad2deg = 180 * 0.3183098861837906715;
     74 
     75                 //2.CalcByOpenCV
     76                 Matx31d radian1 = cv::RQDecomp3x3(R, Matx33d(), Matx33d()) * deg2rad;
     77 
     78                 //3.CalcByDIY
     79                 Matx31d radian2; euler2matrix(R.val, radian2.val, false);
     80 
     81                 //4.AnalyzeError
     82                 double infRadian0Radian1 = norm(radian0, radian1, NORM_INF);
     83                 double infRadian1Radian2 = norm(radian1, radian2, NORM_INF);
     84 
     85                 //5.PrintError
     86                 cout << endl << "LoopCount: " << k << endl;
     87                 if (infRadian0Radian1 > 0 || infRadian1Radian2 > 0)
     88                 {
     89                     cout << endl << "5.1PrintError" << endl;
     90                     cout << endl << "infRadian0Radian1: " << infRadian0Radian1 << endl;
     91                     cout << endl << "infRadian1Radian2: " << infRadian1Radian2 << endl;
     92                     if (0)
     93                     {
     94                         cout << endl << "5.2PrintDiff" << endl;
     95                         cout << endl << "radian0-degree0:" << endl << radian0.t() << endl << radian0.t() * rad2deg << endl;
     96                         cout << endl << "radian1-degree1:" << endl << radian1.t() << endl << radian1.t() * rad2deg << endl;
     97                         cout << endl << "radian2-degree2:" << endl << radian2.t() << endl << radian2.t() * rad2deg << endl;
     98                         cout << endl << "5.3PrintOthers" << endl;
     99                         cout << endl << "R:" << endl << R << endl;
    100                     }
    101                     cout << endl << "Press any key to continue" << endl; std::getchar();
    102                 }
    103             }
    104             return;
    105         }
    106         if (forward)//check with 3D Rotation Converter
    107         {
    108             double sinR = std::sin(e[0]);
    109             double sinP = std::sin(e[1]);
    110             double sinY = std::sin(e[2]);
    111             double cosR = std::cos(e[0]);
    112             double cosP = std::cos(e[1]);
    113             double cosY = std::cos(e[2]);
    114 
    115             //RPY indicates: first Yaw aroundZ, second Pitch aroundY, third Roll aroundX
    116             R[0] = cosY * cosP; R[1] = cosY * sinP * sinR - sinY * cosR; R[2] = cosY * sinP * cosR + sinY * sinR;
    117             R[3] = sinY * cosP; R[4] = sinY * sinP * sinR + cosY * cosR; R[5] = sinY * sinP * cosR - cosY * sinR;
    118             R[6] = -sinP;       R[7] = cosP * sinR;                      R[8] = cosP * cosR;
    119         }
    120         else
    121         {
    122             double vs1 = std::abs(R[6] - 1.);
    123             double vs_1 = std::abs(R[6] + 1.);
    124             if (vs1 > 1E-9 && vs_1 > 1E-9)
    125             {
    126                 e[2] = std::atan2(R[3], R[0]); //Yaw aroundZ
    127                 e[1] = std::asin(-R[6]);//Pitch aroundY
    128                 e[0] = std::atan2(R[7], R[8]); //Roll aroundX
    129             }
    130             else if (vs_1 <= 1E-9)
    131             {
    132                 e[2] = 0; //Yaw aroundZ
    133                 e[1] = 3.14159265358979323846 * 0.5;//Pitch aroundY
    134                 e[0] = e[2] + atan2(R[1], R[2]); //Roll aroundX
    135             }
    136             else
    137             {
    138                 e[2] = 0; //Yaw aroundZ
    139                 e[1] = -3.14159265358979323846 * 0.5;//Pitch aroundY
    140                 e[0] = -e[2] + atan2(-R[1], -R[2]); //Roll aroundX
    141             }
    142         }
    143     };
    144     static void quat2matrix(double q[4], double R[9], bool forward = true)
    145     {
    146         if (forward)//refer to qglviwer
    147         {
    148             double L1 = std::sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
    149             if (std::abs(L1 - 1) > 1E-9) { std::printf("Not uint quaternion: NormQ=%.9f
    ", L1); abort(); }
    150 
    151             double xx = 2.0 * q[1] * q[1];
    152             double yy = 2.0 * q[2] * q[2];
    153             double zz = 2.0 * q[3] * q[3];
    154 
    155             double xy = 2.0 * q[1] * q[2];
    156             double xz = 2.0 * q[1] * q[3];
    157             double wx = 2.0 * q[1] * q[0];
    158 
    159             double yz = 2.0 * q[2] * q[3];
    160             double wy = 2.0 * q[2] * q[0];
    161 
    162             double wz = 2.0 * q[3] * q[0];
    163 
    164             R[0] = 1.0 - yy - zz;
    165             R[4] = 1.0 - xx - zz;
    166             R[8] = 1.0 - xx - yy;
    167 
    168             R[1] = xy - wz;
    169             R[3] = xy + wz;
    170 
    171             R[2] = xz + wy;
    172             R[6] = xz - wy;
    173 
    174             R[5] = yz - wx;
    175             R[7] = yz + wx;
    176         }
    177         else
    178         {
    179             double onePlusTrace = 1.0 + R[0] + R[4] + R[8];// Compute one plus the trace of the matrix
    180             if (onePlusTrace > 1E-9)
    181             {
    182                 double s = sqrt(onePlusTrace) * 2.0;
    183                 double is = 1 / s;
    184                 q[0] = 0.25 * s;
    185                 q[1] = (R[7] - R[5]) * is;
    186                 q[2] = (R[2] - R[6]) * is;
    187                 q[3] = (R[3] - R[1]) * is;
    188             }
    189             else
    190             {
    191                 std::printf("1+trace(R)=%.9f is too small and (R11,R22,R33)=(%.9f,%.9f,%.9f)
    ", onePlusTrace, R[0], R[4], R[8]);
    192                 if ((R[0] > R[4]) && (R[0] > R[8]))//max(R00, R11, R22)=R00
    193                 {
    194                     double s = sqrt(1.0 + R[0] - R[4] - R[8]) * 2.0;
    195                     double is = 1 / s;
    196                     q[0] = (R[5] - R[7]) * is;
    197                     q[1] = 0.25 * s;
    198                     q[2] = (R[1] + R[3]) * is;
    199                     q[3] = (R[2] + R[6]) * is;
    200                 }
    201                 else if (R[4] > R[8])//max(R00, R11, R22)=R11
    202                 {
    203                     double s = sqrt(1.0 - R[0] + R[4] - R[8]) * 2.0;
    204                     double is = 1 / s;
    205                     q[0] = (R[2] - R[6]) * is;
    206                     q[1] = (R[1] + R[3]) * is;
    207                     q[2] = 0.25 * s;
    208                     q[3] = (R[5] + R[7]) * is;
    209                 }
    210                 else//max(R00, R11, R22)=R22
    211                 {
    212                     double s = sqrt(1.0 - R[0] - R[4] + R[8]) * 2.0;
    213                     double is = 1 / s;
    214                     q[0] = (R[1] - R[3]) * is;
    215                     q[1] = (R[2] + R[6]) * is;
    216                     q[2] = (R[5] + R[7]) * is;
    217                     q[3] = 0.25 * s;
    218                 }
    219             }
    220             double L1 = std::sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
    221             if (L1 < 1e-9) { std::printf("Wrong rotation matrix: NormQ=%.9f
    ", L1); abort(); }
    222             else { L1 = 1 / L1; q[0] *= L1; q[1] *= L1; q[2] *= L1; q[3] *= L1; }
    223         }
    224     }
    225     static void vec2quat(double r[3], double q[4], bool forward = true)
    226     {
    227         if (forward)//refer to qglviwer
    228         {
    229             double theta = std::sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]);
    230             if (std::abs(theta) < 1E-9)
    231             {
    232                 q[0] = 1; q[1] = q[2] = q[3] = 0;
    233                 std::printf("Rotation approximates zero: Theta=%.9f
    ", theta);
    234             };
    235 
    236             q[0] = std::cos(theta * 0.5);
    237             double ss = std::sin(theta * 0.5) / theta;
    238             q[1] = r[0] * ss;
    239             q[2] = r[1] * ss;
    240             q[3] = r[2] * ss;
    241         }
    242         else
    243         {
    244             double L1 = std::sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
    245             if (std::abs(L1 - 1) > 1E-9) { std::printf("Not uint quaternion: NormQ=%.9f
    ", L1); abort(); }
    246 
    247             double theta = 2 * acos(q[0]);
    248             if (theta > 3.14159265358979323846) theta = 2 * 3.14159265358979323846 - theta;
    249             double thetaEx = theta / std::sin(theta * 0.5);
    250             r[0] = q[1] * thetaEx;
    251             r[1] = q[2] * thetaEx;
    252             r[2] = q[3] * thetaEx;
    253         }
    254     }
    255     static void vec2matrix(double r[3], double R[9], bool forward = true, int argc = 0, char** argv = 0)
    256     {
    257         if (argc > 0)
    258         {
    259             int N = 999;
    260             for (int k = 0; k < N; ++k) //refer to the subsequent article for more details
    261             {
    262                 //1.GenerateData
    263                 Matx31d r0 = r0.randu(-999, 999);
    264                 Matx33d R0; cv::Rodrigues(r0, R0);
    265 
    266                 //2.CalcByOpenCV
    267                 Matx33d R1;
    268                 Matx31d r1;
    269                 cv::Rodrigues(r0, R1);
    270                 cv::Rodrigues(R0, r1);
    271 
    272                 //3.CalcByDIY
    273                 Matx33d R2;
    274                 Matx31d r2;
    275                 vec2matrix(r0.val, R2.val, true);
    276                 vec2matrix(r2.val, R0.val, false);
    277 
    278                 //4.AnalyzeError
    279                 double infR1R2 = norm(R1, R2, NORM_INF);
    280                 double infr1r2 = norm(r1, r2, NORM_INF);
    281 
    282                 //5.PrintError
    283                 cout << endl << "LoopCount: " << k << endl;
    284                 if (infR1R2 > 1E-12 || infr1r2 > 1E-12)
    285                 {
    286                     cout << endl << "5.1PrintError" << endl;
    287                     cout << endl << "infR1R2: " << infR1R2 << endl;
    288                     cout << endl << "infr1r2: " << infr1r2 << endl;
    289                     if (0)
    290                     {
    291                         cout << endl << "5.2PrintDiff" << endl;
    292                         cout << endl << "R1: " << endl << R1 << endl;
    293                         cout << endl << "R2: " << endl << R2 << endl;
    294                         cout << endl;
    295                         cout << endl << "r1: " << endl << r1.t() << endl;
    296                         cout << endl << "r2: " << endl << r2.t() << endl;
    297                         cout << endl << "5.3PrintOthers" << endl;
    298                     }
    299                     cout << endl << "Press any key to continue" << endl; std::getchar();
    300                 }
    301             }
    302             return;
    303         }
    304 
    305         if (forward)
    306         {
    307             double theta = std::sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]);
    308             if (theta < 1E-9)
    309             {
    310                 R[0] = R[4] = R[8] = 1.0;
    311                 R[1] = R[2] = R[3] = R[5] = R[6] = R[7] = 0.0;
    312                 std::printf("Rotation approximates zero: Theta=%.9f
    ", theta);
    313                 return;
    314             }
    315             double cs = cos(theta);
    316             double sn = sin(theta);
    317             double itheta = 1. / theta;
    318             double cs1 = 1 - cs;
    319             double nx = r[0] * itheta;
    320             double ny = r[1] * itheta;
    321             double nz = r[2] * itheta;
    322 
    323             double nxnx = nx * nx, nyny = ny * ny, nznz = nz * nz;
    324             double nxny = nx * ny, nxnz = nx * nz, nynz = ny * nz;
    325             double nxsn = nx * sn, nysn = ny * sn, nzsn = nz * sn;
    326 
    327             R[0] = nxnx * cs1 + cs;
    328             R[3] = nxny * cs1 + nzsn;
    329             R[6] = nxnz * cs1 - nysn;
    330 
    331             R[1] = nxny * cs1 - nzsn;
    332             R[4] = nyny * cs1 + cs;
    333             R[7] = nynz * cs1 + nxsn;
    334 
    335             R[2] = nxnz * cs1 + nysn;
    336             R[5] = nynz * cs1 - nxsn;
    337             R[8] = nznz * cs1 + cs;
    338 
    339             if (0)
    340             {
    341                 Mat_<double> dRdu({ 9, 4 }, {
    342                     2 * nx * cs1, 0, 0, (nxnx - 1) * sn,
    343                     ny * cs1, nx * cs1, -sn, nxny * sn - nz * cs,
    344                     nz * cs1, sn, nx * cs1, nxnz * sn + ny * cs,
    345                     ny * cs1, nx * cs1, sn, nxny * sn + nz * cs,
    346                     0, 2 * ny * cs1, 0, (nyny - 1) * sn,
    347                     -sn, nz * cs1, ny * cs1, nynz * sn - nx * cs,
    348                     nz * cs1, -sn, nx * cs1, nxnz * sn - ny * cs,
    349                     sn, nz * cs1, ny * cs1, nynz * sn + nx * cs,
    350                     0, 0, 2 * nz * cs1, (nznz - 1) * sn });
    351 
    352                 Mat_<double> dudv({ 4, 4 }, {
    353                     itheta, 0, 0, -nx * itheta,
    354                     0, itheta, 0, -ny * itheta,
    355                     0, 0, itheta, -nz * itheta,
    356                     0, 0, 0, 1 });
    357 
    358                 Mat_<double> dvdr({ 4, 3 }, {
    359                     1, 0, 0,
    360                     0, 1, 0,
    361                     0, 0, 1,
    362                     nx, ny, nz });
    363 
    364                 Mat_<double> Jacobian = dRdu * dudv * dvdr;//rows=9 cols=3
    365             }
    366         }
    367         else
    368         {
    369             double sx = R[7] - R[5];
    370             double sy = R[2] - R[6];
    371             double sz = R[3] - R[1];
    372             double sn = sqrt(sx * sx + sy * sy + sz * sz) * 0.5;
    373             double cs = (R[0] + R[4] + R[8] - 1) * 0.5;
    374             double theta = acos(cs);
    375             double ss = 2 * sn;
    376             double iss = 1. / ss;
    377             double tss = theta * iss;
    378             r[0] = tss * sx;
    379             r[1] = tss * sy;
    380             r[2] = tss * sz;
    381 
    382             if (0)
    383             {
    384                 Mat_<double> drdu({ 3, 4 }, {
    385                     tss, 0, 0, (sn - theta * cs) * iss * iss * sx * 2,
    386                     0, tss, 0, (sn - theta * cs) * iss * iss * sy * 2,
    387                     0, 0, tss, (sn - theta * cs) * iss * iss * sz * 2 });
    388 
    389                 Mat_<double> dudR({ 4, 9 }, {
    390                     0, 0, 0, 0, 0, -1, 0, 1, 0,
    391                     0, 0, 1, 0, 0, 0, -1, 0, 0,
    392                     0, -1, 0, 1, 0, 0, 0, 0, 0,
    393                     -iss, 0, 0, 0, -iss, 0, 0, 0, -iss });
    394 
    395                 Mat_<double> Jacobian = drdu * dudR;//rows=3 cols=9
    396             }
    397         }
    398     }
    399 
    400 private:
    401     const int nHorPoint3D = 100;
    402     const int nVerPoint3D = 100;
    403     const double varPoint3DXY = 10.;
    404     const double minPoint3DZ = 1.;
    405     const double maxPoint3DZ = 99.;
    406     const double minCamZ = 101.;
    407     const double maxCamZ = 150.;
    408     const double varCamDegree = 10.;
    409     Mat_<Vec3d> allPoint3D = Mat_<Vec3d>(nVerPoint3D * nHorPoint3D, 1);
    410     Mat_<double> allPoint3DZ = Mat_<double>(nVerPoint3D * nHorPoint3D, 1);
    411     Mat_<double> K;
    412     Mat_<double> D;
    413     const double deg2rad = 3.14159265358979323846 * 0.0055555555555555556;
    414     const double rad2deg = 180 * 0.3183098861837906715;
    415 
    416 public:
    417     int camRows = 480;
    418     int camCols = 640;
    419     int camFovY = 90;
    420     int camFovX = 90;
    421     int camRand = 10;//append random[0,camRand] to camera intrinsics
    422     int nCamDist = 5;//refer to opencv for value domain
    423     int nMinMotion = 32; // no less than X motion views
    424     int nMaxMotion = INT_MAX; // no more than X motion views
    425     int nPoint2DThenExit = 32;//exit when less than X pixies
    426     int rotMode = 1 + 2 + 4;//0=noRot 1=xAxis 2=yAxis 4=zAxis
    427     bool noTrans = false;//translate or not while motion
    428     bool world2D = false;//planar world or not
    429     bool rndSeek = true;//use random seek or not
    430     bool enableVerbose = false;//check motions one by one or not
    431     vector<MotionView> motionViews;//World Information: RightX, FrontY, DownZ
    432     MotionSim(bool run = true, bool world2D0 = false, bool noTrans0 = false, int rotMode0 = 7) { if (run) runMotion(world2D0, noTrans0, rotMode0); }
    433 
    434 public:
    435     void runMotion(bool world2D0 = false, bool noTrans0 = false, int rotMode0 = 7)
    436     {
    437         world2D = world2D0;
    438         noTrans = noTrans0;
    439         rotMode = rotMode0;
    440         motionViews.clear();
    441         if (rndSeek) cv::setRNGSeed(clock());
    442         while (motionViews.size() < nMinMotion)
    443         {
    444             //1.GetAllPoint3D
    445             if (world2D) allPoint3DZ = 0.;
    446             else cv::randu(allPoint3DZ, -maxPoint3DZ, -minPoint3DZ);//DownZ
    447             for (int i = 0, k = 0; i < nVerPoint3D; ++i)
    448                 for (int j = 0; j < nHorPoint3D; ++j, ++k)
    449                     allPoint3D(k) = Vec3d((j + cv::randu<double>()) * varPoint3DXY, (i + cv::randu<double>()) * varPoint3DXY, allPoint3DZ(i, j));
    450 
    451             //2.GetCamParams
    452             double camFx = camCols / 2. / std::tan(camFovX / 2. * deg2rad) + cv::randu<double>() * camRand;
    453             double camFy = camRows / 2. / std::tan(camFovY / 2. * deg2rad) + cv::randu<double>() * camRand;
    454             double camCx = camCols / 2. + cv::randu<double>() * camRand;
    455             double camCy = camRows / 2. + cv::randu<double>() * camRand;
    456             K.create(3, 3); K << camFx, 0, camCx, 0, camFy, camCy, 0, 0, 1;
    457             D.create(nCamDist, 1); cv::randu(D, -1.0, 1.0);
    458 
    459             //3.GetAllMotionView
    460             motionViews.clear();
    461             for (int64 k = 0; ; ++k)
    462             {
    463                 //3.1 JoinCamParams
    464                 MotionView view;
    465                 view.K = K.clone();
    466                 view.D = D.clone();
    467 
    468                 //3.2 GetCamTrans
    469                 if (k == 0) view.t(0) = view.t(1) = 0;
    470                 else
    471                 {
    472                     view.t(0) = motionViews[k - 1].t(0) + cv::randu<double>() * varPoint3DXY;
    473                     view.t(1) = motionViews[k - 1].t(1) + cv::randu<double>() * varPoint3DXY;
    474                 }
    475                 view.t(2) = minCamZ + cv::randu<double>() * (maxCamZ - minCamZ);
    476                 view.t(2) = -view.t(2);//DownZ
    477                 if (noTrans && k != 0) { view.t(0) = motionViews[0].t(0); view.t(1) = motionViews[0].t(1); view.t(2) = motionViews[0].t(2); }
    478 
    479                 //3.3 GetCamRot: degree-->radian-->matrix-->vector&quaternion
    480                 view.degree = 0.;
    481                 if (rotMode & 1) view.degree(0) = cv::randu<double>() * varCamDegree;
    482                 if (rotMode & 2) view.degree(1) = cv::randu<double>() * varCamDegree;
    483                 if (rotMode & 4) view.degree(2) = cv::randu<double>() * varCamDegree;
    484                 view.radian = view.degree * deg2rad;
    485                 euler2matrix(view.radian.ptr<double>(), view.R.ptr<double>());
    486                 cv::Rodrigues(view.R, view.r);
    487                 quat2matrix(view.q.ptr<double>(), view.R.ptr<double>(), false);
    488                 cv::hconcat(view.R, view.t, view.T);
    489                 cv::vconcat(view.r, view.t, view.rt);
    490 
    491                 //3.4 GetPoint3DAndPoint2D
    492                 Mat_<Vec2d> allPoint2D;
    493                 cv::projectPoints(allPoint3D, -view.r, -view.R.t() * view.t, view.K, view.D, allPoint2D);
    494                 for (int k = 0; k < allPoint2D.total(); ++k)
    495                     if (allPoint2D(k)[0] > 0 && allPoint2D(k)[0] < camCols && allPoint2D(k)[1] > 0 && allPoint2D(k)[1] < camRows)
    496                     {
    497                         view.point2D.push_back(allPoint2D(k));
    498                         view.point3D.push_back(allPoint3D(k));
    499                         view.point3DIds.push_back(k);
    500                     }
    501 
    502                 //3.5 PrintDetails
    503                 motionViews.push_back(view);
    504                 if (enableVerbose)
    505                 {
    506                     cout << endl << view.print();
    507                     cout << fmt::format("view={}   features={}
    ", k, view.point2D.rows);
    508                     double minV = 0, maxV = 0;//Distortion makes some minV next to maxV
    509                     int minId = 0, maxId = 0;
    510                     cv::minMaxIdx(allPoint2D.reshape(1, int(allPoint2D.total()) * allPoint2D.channels()), &minV, &maxV, &minId, &maxId);
    511                     cout << fmt::format("minInfo:({}, {})", minId, minV) << allPoint3D(minId / 2) << allPoint2D(minId / 2) << endl;
    512                     cout << fmt::format("maxInfo:({}, {})", maxId, maxV) << allPoint3D(maxId / 2) << allPoint2D(maxId / 2) << endl;
    513                     cout << "Press any key to continue" << endl; std::getchar();
    514                 }
    515                 if (view.point2D.rows < nPoint2DThenExit || motionViews.size() > nMaxMotion) break;
    516             }
    517         }
    518     }
    519     void visMotion()
    520     {
    521         //1.CreateWidgets
    522         Size2d validSize(nHorPoint3D * varPoint3DXY, nVerPoint3D * varPoint3DXY);
    523         Mat_<cv::Affine3d> camPoses(int(motionViews.size()), 1); for (int k = 0; k < camPoses.rows; ++k) camPoses(k) = cv::Affine3d(motionViews[k].T);
    524         viz::WText worldInfo(fmt::format("nMotionView: {}
    K: {}
    D: {}", motionViews.size(), cvarr2str(K), cvarr2str(D)), Point(10, 240), 10);
    525         viz::WCoordinateSystem worldCSys(1000);
    526         viz::WPlane worldGround(Point3d(validSize.width / 2, validSize.height / 2, 0), Vec3d(0, 0, 1), Vec3d(0, 1, 0), validSize);
    527         viz::WCloud worldPoints(allPoint3D, Mat_<Vec3b>(allPoint3D.size(), Vec3b(0, 255, 0)));
    528         viz::WTrajectory camTraj1(camPoses, viz::WTrajectory::FRAMES, 8);
    529         viz::WTrajectorySpheres camTraj2(camPoses, 100, 2);
    530         viz::WTrajectoryFrustums camTraj3(camPoses, Matx33d(K), 4., viz::Color::yellow());
    531         worldCSys.setRenderingProperty(viz::OPACITY, 0.1);
    532         worldGround.setRenderingProperty(viz::OPACITY, 0.1);
    533         camTraj2.setRenderingProperty(viz::OPACITY, 0.6);
    534 
    535         //2.ShowWidgets
    536         static viz::Viz3d viz3d(__FUNCTION__);
    537         viz3d.showWidget("worldInfo", worldInfo);
    538         viz3d.showWidget("worldCSys", worldCSys);
    539         viz3d.showWidget("worldGround", worldGround);
    540         viz3d.showWidget("worldPoints", worldPoints);
    541         viz3d.showWidget("camTraj1", camTraj1);
    542         viz3d.showWidget("camTraj2", camTraj2);
    543         viz3d.showWidget("camTraj3", camTraj3);
    544 
    545         //3.UpdateWidghts
    546         static const vector<MotionView>& views = motionViews;
    547         viz3d.registerKeyboardCallback([](const viz::KeyboardEvent& keyboarEvent, void* pVizBorad)->void
    548             {
    549                 if (keyboarEvent.action != viz::KeyboardEvent::KEY_DOWN) return;
    550                 static int pos = 0;
    551                 if (keyboarEvent.code == ' ')
    552                 {
    553                     size_t num = views.size();
    554                     size_t ind = pos % num;
    555                     double xmin3D = DBL_MAX, ymin3D = DBL_MAX, xmin2D = DBL_MAX, ymin2D = DBL_MAX;
    556                     double xmax3D = -DBL_MAX, ymax3D = -DBL_MAX, xmax2D = -DBL_MAX, ymax2D = -DBL_MAX;
    557                     for (size_t k = 0; k < views[ind].point3D.rows; ++k)
    558                     {
    559                         Vec3d pt3 = views[ind].point3D(int(k));
    560                         Vec2d pt2 = views[ind].point2D(int(k));
    561                         if (pt3[0] < xmin3D) xmin3D = pt3[0];
    562                         if (pt3[0] > xmax3D) xmax3D = pt3[0];
    563                         if (pt3[1] < ymin3D) ymin3D = pt3[1];
    564                         if (pt3[1] > ymax3D) ymax3D = pt3[1];
    565                         if (pt2[0] < xmin2D) xmin2D = pt2[0];
    566                         if (pt2[0] > xmax2D) xmax2D = pt2[0];
    567                         if (pt2[1] < ymin2D) ymin2D = pt2[1];
    568                         if (pt2[1] > ymax2D) ymax2D = pt2[1];
    569                     }
    570                     if (pos != 0)
    571                     {
    572                         for (int k = 0; k < views[ind == 0 ? num - 1 : ind - 1].point3D.rows; ++k) viz3d.removeWidget("active" + std::to_string(k));
    573                         viz3d.removeWidget("viewInfo");
    574                         viz3d.removeWidget("camSolid");
    575                     }
    576                     for (int k = 0; k < views[ind].point3D.rows; ++k) viz3d.showWidget("active" + std::to_string(k), viz::WSphere(views[ind].point3D(k), 5, 10));
    577                     viz3d.showWidget("viewInfo", viz::WText(fmt::format("CurrentMotion: {}
    ValidPoints: {}
    Min3DXY_Min2DXY: {}, {}, {}, {}
    Max3DXY_Max2DXY: {}, {}, {}, {}
    Rot_Trans_Euler: {}
    ",
    578                         ind, views[ind].point3D.rows, xmin3D, ymin3D, xmin2D, ymin2D, xmax3D, ymax3D, xmax2D, ymax2D,
    579                         cvarr2str(views[ind].r.t()) + cvarr2str(views[ind].t.t()) + cvarr2str(views[ind].degree.t())), Point(10, 10), 10));
    580                     viz3d.showWidget("camSolid", viz::WCameraPosition(Matx33d(views[ind].K), 10, viz::Color::yellow()), cv::Affine3d(views[ind].T));
    581                     ++pos;
    582                 }
    583             }, 0);
    584         viz3d.spin();
    585     }
    586 };
    587 
    588 class OptimizeRt
    589 {
    590 public:
    591     using MotionView = MotionSim::MotionView;
    592     static void TestMe(int argc = 0, char** argv = 0)
    593     {
    594         int N = 99;
    595         for (int k = 0; k < N; ++k)
    596         {
    597             //1.GenerateData
    598             bool world2D = k % 2;
    599             int rotMode = k % 7 + 1;
    600             MotionSim motionSim(false);
    601             motionSim.camFovX = 90;
    602             motionSim.camFovY = 90;
    603             motionSim.camRand = 10;
    604             motionSim.nMinMotion = 16;//2
    605             motionSim.nMaxMotion = 32;//4
    606             motionSim.rndSeek = false;
    607             motionSim.nCamDist = 5;
    608             motionSim.runMotion(world2D, false, rotMode);
    609             //motionSim.visMotion();
    610             int rndInd = int(motionSim.motionViews.size() * cv::randu<double>());
    611             Mat_<double> r0 = -motionSim.motionViews[rndInd].r;
    612             Mat_<double> t0 = -motionSim.motionViews[rndInd].R.t() * motionSim.motionViews[rndInd].t;
    613             const MotionView& motionView = motionSim.motionViews[rndInd];
    614             double errRatio = 0.9;
    615 
    616             //2.CalcByCeres
    617             Mat_<double> param1; cv::vconcat(r0, t0, param1); param1 *= errRatio;//use one group of param for LMSolver param format
    618             ceres::Problem problem;
    619             problem.AddResidualBlock(new CeresCostRt(motionView), NULL, param1.ptr<double>());
    620             ceres::Solver::Options options;
    621             ceres::Solver::Summary summary;
    622             ceres::Solve(options, &problem, &summary);
    623             int nIter1 = (int)summary.iterations.size();
    624 
    625             //3.CalcByOpenCV
    626             Mat_<double> param2; cv::vconcat(r0, t0, param2); param2 *= errRatio;
    627             Ptr<cv::LMSolver::Callback> callback = makePtr<CvCallbackRt>(motionView);
    628             Ptr<cv::LMSolver> lmSolver2 = cv::LMSolver::create(callback, 50);
    629             int nIter2 = lmSolver2->run(param2);
    630 
    631             //4.AnalyzeError
    632             double infr0r0 = norm(r0, r0 * errRatio, NORM_INF);
    633             double infr0r1 = norm(r0, param1.rowRange(0, 3), NORM_INF);
    634             double infr0r2 = norm(r0, param2.rowRange(0, 3), NORM_INF);
    635             double inft0t0 = norm(t0, t0 * errRatio, NORM_INF);
    636             double inft0t1 = norm(t0, param1.rowRange(3, 6), NORM_INF);
    637             double inft0t2 = norm(t0, param2.rowRange(3, 6), NORM_INF);
    638             double infr1r2 = norm(param1.rowRange(0, 3), param2.rowRange(0, 3), NORM_INF);
    639             double inft1t2 = norm(param1.rowRange(3, 6), param2.rowRange(3, 6), NORM_INF);
    640 
    641             //5.PrintError
    642             cout << fmt::format("LoopCount: {}      CeresSolver.iters: {}      CVLMSolver.iters: {}
    ", k, nIter1, nIter2);
    643             if (infr0r1 > 1e-8 || infr0r2 > 1e-8 || inft0t1 > 1e-8 || inft0t2 > 1e-8)
    644             {
    645                 cout << fmt::format("infr0r1: {:<15.9}		{:<15.9}
    ", infr0r1, infr0r0);
    646                 cout << fmt::format("infr0r2: {:<15.9}		{:<15.9}
    ", infr0r2, infr0r0);
    647                 cout << fmt::format("inft0t1: {:<15.9}		{:<15.9}
    ", inft0t1, inft0t0);
    648                 cout << fmt::format("inft0t2: {:<15.9}		{:<15.9}
    ", inft0t2, inft0t0);
    649                 cout << fmt::format("infr1r2: {:<15.9}		
    ", infr1r2);
    650                 cout << fmt::format("inft1t2: {:<15.9}		
    ", inft1t2);
    651                 cout << "Press any key to continue" << endl; std::getchar();
    652             }
    653         }
    654     }
    655 
    656 public:
    657     struct CeresCostRt : public ceres::CostFunction
    658     {
    659         const MotionView& motionView;//use K&D&point2D&point3D as groundtruth
    660         CeresCostRt(const MotionView& motionView0) : motionView(motionView0)
    661         {
    662             this->set_num_residuals(motionView.point2D.rows * 2);
    663             this->mutable_parameter_block_sizes()->push_back(6);//Also can use two groups of params: r and t. Refer to OptimizeKDRt. And two groups can contribute to less jacobians computation.
    664         }
    665         bool Evaluate(double const* const* params, double* residuals, double** jacobians) const
    666         {
    667             //1.ExtractInput
    668             Vec3d r(params[0]);
    669             Vec3d t(params[0] + 3);
    670             Mat_<Vec2d> errPoint2D(motionView.point2D.rows, 1, (Vec2d*)(residuals));
    671             Mat_<double> dpdrt; if (jacobians && jacobians[0]) dpdrt = Mat_<double>(motionView.point2D.rows * 2, 6, jacobians[0]);
    672 
    673             //2.CalcJacAndErr
    674             Mat_<double> dpdKDT;
    675             Mat_<Vec2d> point2DEva;
    676             cv::projectPoints(motionView.point3D, r, t, motionView.K, motionView.D, point2DEva, dpdrt.empty() ? noArray() : dpdKDT);
    677             errPoint2D = point2DEva - motionView.point2D;
    678             if (dpdrt.empty() == false) dpdKDT.colRange(0, 6).copyTo(dpdrt);
    679             return true;
    680         }
    681     };
    682 
    683 public:
    684     struct CvCallbackRt : public cv::LMSolver::Callback
    685     {
    686         const MotionView& motionView;//use K&D&point2D&point3D as groundtruth
    687         CvCallbackRt(const MotionView& motionView0) : motionView(motionView0) {}
    688         bool compute(InputArray params, OutputArray residuals, OutputArray jacobians) const
    689         {
    690             //1.ExtractInput
    691             Vec3d r(params.getMat().ptr<double>());
    692             Vec3d t(params.getMat().ptr<double>() + 3);
    693             if (residuals.empty()) residuals.create(motionView.point2D.rows * 2, 1, CV_64F);
    694             if (jacobians.needed() && jacobians.empty()) jacobians.create(motionView.point2D.rows * 2, params.rows(), CV_64F);
    695             Mat_<Vec2d> errPoint2D = residuals.getMat();
    696             Mat_<double> dpdrt = jacobians.getMat();
    697 
    698             //2.CalcJacAndErr
    699             Mat_<double> dpdKDT;
    700             Mat_<Vec2d> point2DEva;
    701             cv::projectPoints(motionView.point3D, r, t, motionView.K, motionView.D, point2DEva, dpdrt.empty() ? noArray() : dpdKDT);
    702             errPoint2D = point2DEva - motionView.point2D;
    703             if (dpdrt.empty() == false) dpdKDT.colRange(0, 6).copyTo(dpdrt);
    704             return true;
    705         }
    706     };
    707 };
    708 
    709 class OptimizeKDRt
    710 {
    711 public:
    712     using MotionView = MotionSim::MotionView;
    713     static void TestMe(int argc = 0, char** argv = 0)
    714     {
    715         int N = 99;
    716         for (int k = 0; k < N; ++k)
    717         {
    718             //1.GenerateData
    719             bool world2D = k % 2;
    720             int rotMode = k % 7 + 1;
    721             MotionSim motionSim(false);
    722             motionSim.camFovX = 90;
    723             motionSim.camFovY = 90;
    724             motionSim.camRand = 10;
    725             motionSim.nMinMotion = 16;//2
    726             motionSim.nMaxMotion = 32;//4
    727             motionSim.rndSeek = false;
    728             motionSim.nCamDist = 5;
    729             motionSim.runMotion(world2D, false, rotMode);
    730             //motionSim.visMotion();
    731             Mat_<double> rs0; for (size_t k = 0; k < motionSim.motionViews.size(); ++k) rs0.push_back(-motionSim.motionViews[k].r);
    732             Mat_<double> ts0; for (size_t k = 0; k < motionSim.motionViews.size(); ++k) ts0.push_back(-motionSim.motionViews[k].R.t() * motionSim.motionViews[k].t);
    733             Mat_<double> K0({ 4, 1 }, { motionSim.motionViews[0].K(0, 0), motionSim.motionViews[0].K(1, 1), motionSim.motionViews[0].K(0, 2), motionSim.motionViews[0].K(1, 2) });
    734             Mat_<double> D0 = motionSim.motionViews[0].D.clone();
    735             double errRatio = 0.9;
    736             double errRatioTrans = 0.99;
    737 
    738             //2.CalcByCeres
    739             Mat_<double> params1, rs1, ts1;//use one group of param for LMSolver param format
    740             for (int k = 0; k < rs0.rows; k += 3) { params1.push_back(rs0.rowRange(k, k + 3) * errRatio); params1.push_back(ts0.rowRange(k, k + 3) * errRatioTrans); }
    741             params1.push_back(K0 * errRatio);
    742             params1.push_back(D0 * errRatio);
    743             ceres::Problem problem;
    744             for (int k = 0; k < motionSim.motionViews.size(); ++k)
    745                 problem.AddResidualBlock(new CeresCostKDRt(motionSim.motionViews[k]), NULL, params1.ptr<double>(k * 6), params1.ptr<double>(k * 6 + 3),
    746                     params1.ptr<double>(params1.rows - 4 - D0.rows), params1.ptr<double>(params1.rows - D0.rows));
    747             ceres::Solver::Options options;
    748             ceres::Solver::Summary summary;
    749             ceres::Solve(options, &problem, &summary);
    750             int nIter1 = (int)summary.iterations.size();
    751             for (int k = 0; k < params1.rows - 4 - D0.rows; k += 6) { rs1.push_back(params1.rowRange(k, k + 3)); ts1.push_back(params1.rowRange(k + 3, k + 6)); }
    752             Mat_<double> K1 = params1.rowRange(params1.rows - 4 - D0.rows, params1.rows - D0.rows).clone();
    753             Mat_<double> D1 = params1.rowRange(params1.rows - D0.rows, params1.rows).clone();
    754 
    755             //3.CalcByOpenCV
    756             Mat_<double> params2, rs2, ts2;
    757             for (int k = 0; k < rs0.rows; k += 3) { params2.push_back(rs0.rowRange(k, k + 3) * errRatio); params2.push_back(ts0.rowRange(k, k + 3) * errRatioTrans); }
    758             params2.push_back(K0 * errRatio);
    759             params2.push_back(D0 * errRatio);
    760             Ptr<cv::LMSolver::Callback> callback = makePtr<CvCallbackKDRt>(motionSim.motionViews);
    761             Ptr<cv::LMSolver> lmSolver2 = cv::LMSolver::create(callback, 50);
    762             int nIter2 = lmSolver2->run(params2);
    763             for (int k = 0; k < params2.rows - 4 - D0.rows; k += 6) { rs2.push_back(params2.rowRange(k, k + 3)); ts2.push_back(params2.rowRange(k + 3, k + 6)); }
    764             Mat_<double> K2 = params2.rowRange(params2.rows - 4 - D0.rows, params2.rows - D0.rows).clone();
    765             Mat_<double> D2 = params2.rowRange(params2.rows - D0.rows, params2.rows).clone();
    766 
    767             //4.AnalyzeError
    768             double infrs0rs0 = norm(rs0, rs0 * errRatio, NORM_INF);
    769             double infrs0rs1 = norm(rs0, rs1, NORM_INF);
    770             double infrs0rs2 = norm(rs0, rs2, NORM_INF);
    771             double infts0ts0 = norm(ts0, ts0 * errRatioTrans, NORM_INF);
    772             double infts0ts1 = norm(ts0, ts1, NORM_INF);
    773             double infts0ts2 = norm(ts0, ts2, NORM_INF);
    774             double infK0K0 = norm(K0, K0 * errRatio, NORM_INF);
    775             double infK0K1 = norm(K0, K1, NORM_INF);
    776             double infK0K2 = norm(K0, K2, NORM_INF);
    777             double infD0D0 = norm(D0, D0 * errRatio, NORM_INF);
    778             double infD0D1 = norm(D0, D1, NORM_INF);
    779             double infD0D2 = norm(D0, D2, NORM_INF);
    780             double infrs1rs2 = norm(rs1, rs2, NORM_INF);
    781             double infts1ts2 = norm(ts1, ts2, NORM_INF);
    782             double infK1K2 = norm(K1, K2, NORM_INF);
    783             double infD1D2 = norm(D1, D2, NORM_INF);
    784 
    785             //5.PrintError
    786             cout << fmt::format("LoopCount: {}      CeresSolver.iters: {}      CVLMSolver.iters: {}
    ", k, nIter1, nIter2);
    787             if (infrs0rs1 > 1e-8 || infrs0rs2 > 1e-8 || infts0ts1 > 1e-8 || infts0ts2 > 1e-8 || infK0K1 > 1e-8 || infK0K2 > 1e-8 || infD0D1 > 1e-8 || infD0D2 > 1e-8)
    788             {
    789                 cout << fmt::format("infrs0rs1: {:<15.9}		{:<15.9}
    ", infrs0rs1, infrs0rs0);
    790                 cout << fmt::format("infrs0rs2: {:<15.9}		{:<15.9}
    ", infrs0rs2, infrs0rs0);
    791                 cout << fmt::format("infts0ts1: {:<15.9}		{:<15.9}
    ", infts0ts1, infts0ts0);
    792                 cout << fmt::format("infts0ts2: {:<15.9}		{:<15.9}
    ", infts0ts2, infts0ts0);
    793                 cout << fmt::format("infK0K1  : {:<15.9}		{:<15.9}
    ", infK0K1, infK0K0);
    794                 cout << fmt::format("infK0K2  : {:<15.9}		{:<15.9}
    ", infK0K2, infK0K0);
    795                 cout << fmt::format("infD0D1  : {:<15.9}		{:<15.9}
    ", infD0D1, infD0D0);
    796                 cout << fmt::format("infD0D2  : {:<15.9}		{:<15.9}
    ", infD0D2, infD0D0);
    797                 cout << fmt::format("infrs1rs2: {:<15.9}		
    ", infrs1rs2);
    798                 cout << fmt::format("infts1ts2: {:<15.9}		
    ", infts1ts2);
    799                 cout << fmt::format("infK1D2  : {:<15.9}		
    ", infK1K2);
    800                 cout << fmt::format("infD1D2  : {:<15.9}		
    ", infD1D2);
    801                 cout << "Press any key to continue" << endl; std::getchar();
    802             }
    803         }
    804     }
    805 
    806 public:
    807     struct CeresCostKDRt : public ceres::CostFunction
    808     {
    809         const MotionView& motionView;//use K&D&point2D&point3D as groundtruth
    810         CeresCostKDRt(const MotionView& motionView0) : motionView(motionView0)
    811         {
    812             this->set_num_residuals(motionView.point2D.rows * 2);
    813             this->mutable_parameter_block_sizes()->push_back(3);
    814             this->mutable_parameter_block_sizes()->push_back(3);
    815             this->mutable_parameter_block_sizes()->push_back(4);
    816             this->mutable_parameter_block_sizes()->push_back(motionView.D.rows);
    817         }
    818         bool Evaluate(double const* const* params, double* residuals, double** jacobians) const
    819         {
    820             //1.ExtractInput
    821             Vec3d r(params[0]);
    822             Vec3d t(params[1]);
    823             Matx33d K(params[2][0], 0, params[2][2], 0, params[2][1], params[2][3], 0, 0, 1);
    824             Mat_<double> D(motionView.D.rows, 1); for (int k = 0; k < D.rows; ++k) D(k) = params[3][k];
    825             Mat_<Vec2d> errPoint2D(motionView.point2D.rows, 1, (Vec2d*)(residuals));
    826             Mat_<double> dpdr; if (jacobians && jacobians[0]) dpdr = Mat_<double>(motionView.point2D.rows * 2, 3, jacobians[0]);
    827             Mat_<double> dpdt; if (jacobians && jacobians[1]) dpdt = Mat_<double>(motionView.point2D.rows * 2, 3, jacobians[1]);
    828             Mat_<double> dpdK; if (jacobians && jacobians[2]) dpdK = Mat_<double>(motionView.point2D.rows * 2, 4, jacobians[2]);
    829             Mat_<double> dpdD; if (jacobians && jacobians[3]) dpdD = Mat_<double>(motionView.point2D.rows * 2, motionView.D.rows, jacobians[3]);
    830 
    831             //2.CalcJacAndErr
    832             Mat_<double> dpdKDT;
    833             Mat_<Vec2d> point2DEva;
    834             cv::projectPoints(motionView.point3D, r, t, K, D, point2DEva, dpdr.empty() && dpdt.empty() && dpdK.empty() && dpdD.empty() ? noArray() : dpdKDT);
    835             errPoint2D = point2DEva - motionView.point2D;
    836             if (dpdr.empty() == false) dpdKDT.colRange(0, 3).copyTo(dpdr);
    837             if (dpdt.empty() == false) dpdKDT.colRange(3, 6).copyTo(dpdt);
    838             if (dpdK.empty() == false) dpdKDT.colRange(6, 10).copyTo(dpdK);
    839             if (dpdD.empty() == false) dpdKDT.colRange(10, 10 + D.rows).copyTo(dpdD);
    840             return true;
    841         }
    842     };
    843 
    844 public:
    845     struct CvCallbackKDRt : public cv::LMSolver::Callback
    846     {
    847         int nResidual = 0;
    848         const vector<MotionView>& motionViews;//use K&D&point2D&point3D as groundtruth
    849         CvCallbackKDRt(const vector<MotionView>& motionViews0) : motionViews(motionViews0) { for (size_t i = 0; i < motionViews.size(); ++i) nResidual += motionViews[i].point2D.rows * 2; }
    850         bool compute(InputArray params, OutputArray residuals, OutputArray jacobians) const
    851         {
    852             //1.ExtractInputGlobal
    853             Mat_<double> cvParams = params.getMat();
    854             double* rsdata = params.getMat().ptr<double>();
    855             double* tsdata = params.getMat().ptr<double>() + 3;
    856             double* KData = params.getMat().ptr<double>() + motionViews.size() * 2 * 3;
    857             double* DData = params.getMat().ptr<double>() + motionViews.size() * 2 * 3 + 4;
    858             if (residuals.empty()) residuals.create(nResidual, 1, CV_64F);
    859             if (jacobians.needed() && jacobians.empty()) jacobians.create(nResidual, params.rows(), CV_64F);
    860             Mat_<Vec2d> errPoint2Ds = residuals.getMat();
    861             Mat_<double> dpdKDTs = jacobians.getMat();
    862 
    863             //2.CalcJacAndErrGlobal
    864             for (int k = 0, row1 = 0, row2 = 0, nView = int(motionViews.size()); k < nView; ++k, row1 = row2)
    865             {
    866                 const MotionView& motionView = motionViews[k];
    867                 //2.1 ExtractInput
    868                 Vec3d r(rsdata + k * 6);
    869                 Vec3d t(tsdata + k * 6);
    870                 Matx33d K(KData[0], 0, KData[2], 0, KData[1], KData[3], 1);
    871                 Mat_<double> D(motionView.D.rows, 1, DData);
    872                 Mat_<Vec2d> errPoint2D = errPoint2Ds.rowRange(row1, row2 = row1 + motionView.point2D.rows);
    873                 Mat_<double> dpdr; if (dpdKDTs.empty() == false) dpdr = dpdKDTs.rowRange(row1 * 2, row2 * 2).colRange(k * 6, k * 6 + 3);
    874                 Mat_<double> dpdt; if (dpdKDTs.empty() == false) dpdt = dpdKDTs.rowRange(row1 * 2, row2 * 2).colRange(k * 6 + 3, k * 6 + 6);
    875                 Mat_<double> dpdK; if (dpdKDTs.empty() == false) dpdK = dpdKDTs.rowRange(row1 * 2, row2 * 2).colRange(nView * 2 * 3, nView * 2 * 3 + 4);
    876                 Mat_<double> dpdD; if (dpdKDTs.empty() == false) dpdD = dpdKDTs.rowRange(row1 * 2, row2 * 2).colRange(nView * 2 * 3 + 4, dpdKDTs.cols);
    877 
    878                 //2.2 CalcJacAndErr
    879                 Mat_<double> dpdKDT;
    880                 Mat_<Vec2d> point2DEva;
    881                 cv::projectPoints(motionView.point3D, r, t, K, D, point2DEva, dpdKDTs.empty() ? noArray() : dpdKDT);
    882                 errPoint2D = point2DEva - motionView.point2D;
    883                 if (dpdr.empty() == false) dpdKDT.colRange(0, 3).copyTo(dpdr);
    884                 if (dpdt.empty() == false) dpdKDT.colRange(3, 6).copyTo(dpdt);
    885                 if (dpdK.empty() == false) dpdKDT.colRange(6, 10).copyTo(dpdK);
    886                 if (dpdD.empty() == false) dpdKDT.colRange(10, 10 + D.rows).copyTo(dpdD);
    887                 if (0)//for DEBUG
    888                 {
    889                     cout << norm(point2DEva - motionView.point2D, errPoint2Ds.rowRange(row1, row2 = row1 + motionView.point2D.rows), NORM_INF) << "	";
    890                     if (dpdr.empty() == false) cout << norm(dpdKDT.colRange(0, 3), dpdKDTs.rowRange(row1 * 2, row2 * 2).colRange(k * 6, k * 6 + 3), NORM_INF) << "	";
    891                     if (dpdt.empty() == false) cout << norm(dpdKDT.colRange(3, 6), dpdKDTs.rowRange(row1 * 2, row2 * 2).colRange(k * 6 + 3, k * 6 + 6), NORM_INF) << "	";
    892                     if (dpdK.empty() == false) cout << norm(dpdKDT.colRange(6, 10), dpdKDTs.rowRange(row1 * 2, row2 * 2).colRange(nView * 2 * 3, nView * 2 * 3 + 4), NORM_INF) << "	";
    893                     if (dpdK.empty() == false) cout << norm(dpdKDT.colRange(10, 10 + D.rows), dpdKDTs.rowRange(row1 * 2, row2 * 2).colRange(nView * 2 * 3 + 4, dpdKDTs.cols), NORM_INF) << endl;
    894                 }
    895             }
    896             return true;
    897         }
    898     };
    899 };
    900 
    901 int main(int argc, char** argv) { OptimizeKDRt::TestMe(argc, argv); return 0; }
    902 int main1(int argc, char** argv) { OptimizeRt::TestMe(argc, argv); return 0; }
    903 int main2(int argc, char** argv) { OptimizeKDRt::TestMe(argc, argv); return 0; }
    View Code
  • 相关阅读:
    glibcxx升级
    content security policy directive
    创建AD域用户,并用之在客户端操作远程桌面
    windows安装ad域
    Python切片
    Python字符串格式化
    一段比较有意思的C++代码
    MySql数据库导入新的表里面 php操作,网站文章批量导入到新网站
    [P6672] [清华集训2016] 你的生命已如风中残烛 题解
    软件测试基础之测试分类
  • 原文地址:https://www.cnblogs.com/dzyBK/p/13961866.html
Copyright © 2011-2022 走看看