Ceres-Solver学习日志:捆绑优化使用样例与单目视觉中的尺度问题

         前面通过几个篇幅阐述了Ceres的基本应用,本篇基于之前的样例展示一个稍加复杂的样例。在给出使用样例之前,我们先来谈谈捆绑优化(Bundle Adjustment)和单目视觉中的尺度问题。

1.捆绑优化

         捆绑优化的概念追溯久远,要从其原始出处说起,还真不是一两话能说清楚,涉及视觉领域相当专业的概念。也许大多数视觉领域的研究者都理解捆绑优化是什么,用得也如火纯青,但真要通俗易懂的说出来就不知道有几人啦,相关书籍和博文也很少有对捆绑优化的通俗阐述。这里,我根据自己的理解,避开视觉中的概念,从更广义的角度来说说自己对捆绑优化的理解。

         假设有优化模型A(a)=0,这里A代表线性或非线性或两者混合的方程组,a代表被优化的变量组成的向量。常规而言,只要方程的数量足够,迭代优化即可得到最优值。然而,有时候方程数量再多,得到的解也可能不正确或者说不够精确,至于其原由这里不展开,从哲学的角度来说,大意就是你一直站在角度A看问题,不够全面,自然得到结果a也不全面(即不精确),如果能同时站在其它角度B、C、D来看问题,你看到的a是否会更正确了?答案很明显。但是当你站在B、C、D的角度来看问题时,包含的变量通常就不只是a了,我们假设站在B的角度会增加变量b、站在C的角度会增加变量c、站在D的角度会增加变量d。于是优化模型变为:

         A(a)=0;B(a,b)=0;C(a,c)=0;D(a,d)=0

以上是比较理想的情况,也有可能是如下情况:

         A(a)=0;B(a,b)=0;C(a, b,c)=0; D(a,b,c,d)=0。

         或

         A(a)=0;B(a,b)=0;C(b,c)=0;D(c,b)=0。

         具体形式,取决于实际模型。但至少需要存在一个中间角度将a和后续变量关联起来,才有意义。

         于是问题转化为求解最优abcd。这里将abcd捆绑优化,也正好呼应了捆绑优化这一概念。

         我们来分析之前和本篇的使用样例:

         OptimizeRt:这是之前的样例,基于一次观察(即一个角度)计算Rt,这还不能算捆绑优化。

         OptimizeKDRt:这是之前的样例,基于多次观察(即多个角度)计算KD和所有Rt,KD出现在所有观察,每个Rt对应一组观察,这就是典型的捆绑优化。

         OptimizeKDTP:这是本篇的样例,基于多次观察(即多个角度)计算KD、所有Rt及所有Point3D,KD出现在所有观察,每个Rt对应一组观察,每个Point3D对应至少N组(N大于等于2)观察,这也是典型的捆绑优化。

2.单目视觉中的尺度

         单目视觉中,当我们要基于多次观察计算每次观察的Rt和每次观察中某些(这里不说所有是因为一个三维点通常需要被多次观察才能很好地确定,对于没有被多次观察到的则将其排除)三维点时,必须首先得到一些确定的Rt或一些确定的三维点才能使模型收敛到正确的结果。通常有两种方案:

         (1)给定位姿:给定一次观察的Rt和另一次观察的t(或t模值)、或给定一次观察的Rt和两次观察的之间的相对t(或t模值)。

         (2)给定三维点:给定三个至少能出现在N次(N大于等于2)观察中的Point3D。

         以上要求的是最少数量,数量越多越好。关于原理分析,可参见视觉Slam那些古老的论文。

3.使用样例

         提供捆绑优化K、D、R、t、XYZ的使用样例,命名为OptimizeKDTP,这是在OptimizeKDRt的基础上增加了对世界坐标的优化。对此使用样例作如下说明:

         (1)提供参数设置以决定是否固定K或D或R或t或P,以模拟视觉开发中的各种应用场景(如定位、标定、重建等)。

         (2)当要优化世界坐标时,提供参数设置以决定选用位姿真值还是三维点真值来估计尺度。

         以下是详细代码,依赖于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 OptimizeKDTP
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             //0.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             static const int nDist = 5;
608             motionSim.nCamDist = nDist;
609             motionSim.runMotion(world2D, false, rotMode);
610             //motionSim.visMotion();
611             Mat_<double> rs0; for (size_t k = 0; k < motionSim.motionViews.size(); ++k) rs0.push_back(-motionSim.motionViews[k].r);
612             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);
613             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) });
614             Mat_<double> D0 = motionSim.motionViews[0].D.clone();
615             double errRatio = 0.9;
616             double errRatioTrans = 0.99;
617             const int keyViewId = 0;
618             const int nMinMatch = 8;
619             Mat_<Vec3d> point3D0;
620             vector<vector<std::pair<int, int>>> idsPosePoint2DForAll3D;
621             vector<bool> rtsHitted(motionSim.motionViews.size(), false);
622             {
623                 //1.Get approapriate world points and its all observations             
624                 for (int k = 0; k < motionSim.motionViews[keyViewId].point3DIds.rows; ++k)
625                 {
626                     int which3D = motionSim.motionViews[keyViewId].point3DIds(k);
627                     vector<std::pair<int, int>> idsPosePoint2DForCur3D;
628                     for (int i = 0; i < motionSim.motionViews.size(); ++i)//WhichPose==WhichView
629                         for (int j = 0; j < motionSim.motionViews[i].point3DIds.rows; ++j)//WhichPoint3D=WhichPoint2D
630                             if (which3D == motionSim.motionViews[i].point3DIds(j)) idsPosePoint2DForCur3D.push_back(std::pair<int, int>(i, j));
631                     if (idsPosePoint2DForCur3D.size() > nMinMatch)
632                     {
633                         point3D0.push_back(motionSim.motionViews[keyViewId].point3D(k));
634                         idsPosePoint2DForAll3D.push_back(idsPosePoint2DForCur3D);
635                     }
636                 }
637                 //2.Check which views are used and check above world points whether echo its all observations
638                 Scalar sumDBG = 0;
639                 for (int k = 0; k < point3D0.rows; ++k)
640                     for (int i = 0; i < idsPosePoint2DForAll3D[k].size(); ++i)
641                     {
642                         int whichView = idsPosePoint2DForAll3D[k][i].first;
643                         int whichPoint2D = idsPosePoint2DForAll3D[k][i].second;
644                         sumDBG += cv::sum(motionSim.motionViews[whichView].point3D(whichPoint2D) - point3D0(k));
645                         Mat_<Vec2d> pt; cv::projectPoints(point3D0(k), rs0.rowRange(whichView * 3, whichView * 3 + 3), ts0.rowRange(whichView * 3, whichView * 3 + 3), Matx33d(K0(0), 0, K0(2), 0, K0(1), K0(3), 0, 0, 1), D0, pt);
646                         sumDBG += cv::sum(motionSim.motionViews[whichView].point2D(whichPoint2D) - pt(0));
647                         rtsHitted[whichView] = true;
648                     }
649                 if (sumDBG[0] + sumDBG[1] + sumDBG[2] + sumDBG[3] != 0) spdlog::error("Error input: {}", sumDBG[0] + sumDBG[1] + sumDBG[2] + sumDBG[3]);
650             }
651 
652             //1.CalcByCeresWithAutoDeriv
653             bool fixK = false;
654             bool fixD = false;
655             bool fixRot = false;
656             bool fixTrans = false;
657             bool fixPoint3D = false;
658             int scaleFrom = 1;//1=fromPose 2=fromPoint3D
659             Mat_<double> Rs1, ts1;
660             for (int k = 0; k < rs0.rows; k += 3)
661             {
662                 double errRot = errRatio;
663                 double errTrans = errRatioTrans;
664                 if (fixRot || rtsHitted[k / 3] == false) errRot = 1.;
665                 if (fixTrans || rtsHitted[k / 3] == false) errTrans = 1.;
666                 if (fixPoint3D == false && scaleFrom == 1)
667                 {
668                     if (k / 3 == keyViewId) errRot = 1.;//Fix at least one rotation frame for scale
669                     if (k / 3 == keyViewId || k / 3 == keyViewId + 1) errTrans = 1.;//Fix at least two translation frames for scale
670                 }
671                 Mat_<double> _R; cv::Rodrigues(rs0.rowRange(k, k + 3) * errRot, _R);
672                 Rs1.push_back(_R);
673                 ts1.push_back(ts0.rowRange(k, k + 3) * errTrans);
674             }
675             Mat_<double> K1 = K0 * (fixK ? 1. : errRatio);
676             Mat_<double> D1 = D0 * (fixD ? 1. : errRatio);
677             Mat_<Vec3d> point3D1 = point3D0 * (fixPoint3D ? 1. : errRatio);
678             if (fixPoint3D == false && scaleFrom == 2) for (int k = 0; k < 3; ++k) point3D1(k) = point3D0(k);//Fix at least three world points for scale
679             ceres::Problem problem1;
680             for (int k = 0; k < point3D1.rows; ++k)
681                 for (int i = 0; i < idsPosePoint2DForAll3D[k].size(); ++i)
682                 {
683                     int whichView = idsPosePoint2DForAll3D[k][i].first;
684                     int whichPoint2D = idsPosePoint2DForAll3D[k][i].second;
685                     problem1.AddResidualBlock(new ceres::AutoDiffCostFunction<ProjectionModelKDTP, 2, 3, 9, 3, 4, nDist>(
686                         new ProjectionModelKDTP(motionSim.motionViews[whichView].point2D.row(whichPoint2D), motionSim.nCamDist)),
687                         NULL, point3D1(k).val, Rs1.ptr<double>(whichView * 3), ts1.ptr<double>(whichView * 3), K1.ptr<double>(), D1.ptr<double>());
688 
689                     problem1.SetParameterization(Rs1.ptr<double>(whichView * 3), new LocalParamRWithGeneralJ);//it is to be set more than one time
690                     if (fixK) problem1.SetParameterBlockConstant(K1.ptr<double>());
691                     if (fixD) problem1.SetParameterBlockConstant(D1.ptr<double>());
692                     if (fixRot) problem1.SetParameterBlockConstant(Rs1.ptr<double>(whichView * 3));
693                     if (fixTrans) problem1.SetParameterBlockConstant(ts1.ptr<double>(whichView * 3));
694                     if (fixPoint3D) problem1.SetParameterBlockConstant(point3D1(k).val);
695                     if (fixPoint3D == false && scaleFrom == 1)
696                     {
697                         if (whichView == keyViewId) problem1.SetParameterBlockConstant(Rs1.ptr<double>(whichView * 3));//Fix at least one rotation and two translations for scale
698                         if (whichView == keyViewId || whichView == keyViewId + 1) problem1.SetParameterBlockConstant(ts1.ptr<double>(whichView * 3));
699                     }
700                     if (fixPoint3D == false && scaleFrom == 2 && k < 3) problem1.SetParameterBlockConstant(point3D1(k).val);//Fix at least three world points for scale
701                 }
702             ceres::Solver::Options options;
703             ceres::Solver::Summary summary;
704             ceres::Solve(options, &problem1, &summary);
705             Mat_<double> rs1; for (int k = 0; k < Rs1.rows; k += 3) { Mat_<double> _r; cv::Rodrigues(Rs1.rowRange(k, k + 3), _r); rs1.push_back(_r); }
706             int nIter1 = (int)summary.iterations.size();
707 
708             //2.AnalyzeError
709             double infrs0rs0 = norm(rs0, rs0 * errRatio, NORM_INF);
710             double infrs0rs1 = norm(rs0, rs1, NORM_INF);
711             double infts0ts0 = norm(ts0, ts0 * errRatioTrans, NORM_INF);
712             double infts0ts1 = norm(ts0, ts1, NORM_INF);
713             double infK0K0 = norm(K0, K0 * errRatio, NORM_INF);
714             double infK0K1 = norm(K0, K1, NORM_INF);
715             double infD0D0 = norm(D0, D0 * errRatio, NORM_INF);
716             double infD0D1 = norm(D0, D1, NORM_INF);
717             double infPTS0PTS0 = norm(point3D0, point3D0 * errRatio, NORM_INF);
718             double infPTS0PTS1 = norm(point3D0, point3D1, NORM_INF);
719 
720             //3.PrintError
721             cout << fmt::format("LoopCount: {}      AutoDeriv.iters: {}
", k, nIter1);
722             //if (infrs0rs1 > 1e-8 || infts0ts1 > 1e-8 || infK0K1 > 1e-8 || infD0D1 > 1e-8 )
723             {
724                 cout << fmt::format("infrs0rs1: {:<15.9}		{:<15.9}
", infrs0rs1, infrs0rs0);
725                 cout << fmt::format("infts0ts1: {:<15.9}		{:<15.9}
", infts0ts1, infts0ts0);
726                 cout << fmt::format("infK0K1  : {:<15.9}		{:<15.9}
", infK0K1, infK0K0);
727                 cout << fmt::format("infD0D1  : {:<15.9}		{:<15.9}
", infD0D1, infD0D0);
728                 cout << fmt::format("infPTS0PTS1  : {:<15.9}		{:<15.9}
", infPTS0PTS1, infPTS0PTS0);
729                 if (1)
730                 {
731                     //cout << endl << "" << << endl;
732                 }
733                 cout << "Press any key to continue" << endl; std::getchar();
734             }
735         }
736     }
737 
738 public:
739     struct ProjectionModelKDTP
740     {
741         const int nDist;
742         const Mat_<Vec2d> point2D;
743         ProjectionModelKDTP(const Mat_<Vec2d> point2D0, const int nDist0) : point2D(point2D0), nDist(nDist0) {}
744         template <typename tp> bool operator()(const tp* const point3D, const tp* const rot, const tp* const t, const tp* const K, const tp* const D, tp* errPoint2D) const
745         {
746             //1.Projection params
747             tp fx = K[0];
748             tp fy = K[1];
749             tp cx = K[2];
750             tp cy = K[3];
751 
752             //2.Distortion params
753             tp k1 = D[0];
754             tp k2 = D[1];
755             tp p1 = D[2];
756             tp p2 = D[3];
757             tp k3, k4, k5, k6;
758             tp s1, s2, s3, s4;
759             if (nDist > 4) k3 = D[4];
760             if (nDist > 5) { k4 = D[5]; k5 = D[6]; k6 = D[7]; }
761             if (nDist > 8) { s1 = D[8]; s2 = D[9]; s3 = D[10]; s4 = D[11]; }
762 
763             //3.Translation params
764             tp tx = t[0];
765             tp ty = t[1];
766             tp tz = t[2];
767 
768             //4.Rotation params
769             tp R11, R12, R13, R21, R22, R23, R31, R32, R33;
770             {
771                 R11 = rot[0]; R12 = rot[1]; R13 = rot[2];
772                 R21 = rot[3]; R22 = rot[4]; R23 = rot[5];
773                 R31 = rot[6]; R32 = rot[7]; R33 = rot[8];
774             }
775 
776             //5.ReProjection
777             const Vec2d* data2D = point2D.ptr<Vec2d>();
778             Vec<tp, 3>* data3D = (Vec<tp, 3>*)point3D;
779             Vec<tp, 2>* err2D = (Vec<tp, 2>*)errPoint2D;
780             for (int k = 0; k < point2D.rows; ++k)
781             {
782                 //5.1 WorldCoordinate
783                 tp X = data3D[k][0];
784                 tp Y = data3D[k][1];
785                 tp Z = data3D[k][2];
786 
787                 //5.2 CameraCoordinate
788                 tp x = R11 * X + R12 * Y + R13 * Z + tx;
789                 tp y = R21 * X + R22 * Y + R23 * Z + ty;
790                 tp z = R31 * X + R32 * Y + R33 * Z + tz;
791 
792                 //5.3 StandardPhysicsCoordinate
793                 tp iz = 1. / z; //if denominator==0
794                 tp xc = x * iz;
795                 tp yc = y * iz;
796 
797                 //5.4 DistortionPhysicsCoordinate
798                 tp xc2 = xc * xc;
799                 tp yc2 = yc * yc;
800                 tp d2 = xc2 + yc2;
801                 tp xcyc = 2. * xc * yc;
802                 tp d4 = d2 * d2;
803                 tp d6 = d2 * d4;
804                 tp d2xc2 = d2 + 2. * xc2;
805                 tp d2yc2 = d2 + 2. * yc2;
806                 tp nu, de, xd, yd;
807                 if (nDist < 5)
808                 {
809                     nu = 1. + k1 * d2 + k2 * d4;
810                     xd = xc * nu + p1 * xcyc + p2 * d2xc2;
811                     yd = yc * nu + p2 * xcyc + p1 * d2yc2;
812                 }
813                 else if (nDist < 8)
814                 {
815                     nu = 1. + k1 * d2 + k2 * d4 + k3 * d6;
816                     xd = xc * nu + p1 * xcyc + p2 * d2xc2;
817                     yd = yc * nu + p2 * xcyc + p1 * d2yc2;
818                 }
819                 else if (nDist < 12)
820                 {
821                     nu = 1. + k1 * d2 + k2 * d4 + k3 * d6;
822                     de = 1. / (1. + k4 * d2 + k5 * d4 + k6 * d6);//if denominator==0
823                     xd = xc * nu * de + p1 * xcyc + p2 * d2xc2;
824                     yd = yc * nu * de + p2 * xcyc + p1 * d2yc2;
825                 }
826                 else if (nDist < 14)
827                 {
828                     nu = 1. + k1 * d2 + k2 * d4 + k3 * d6;
829                     de = 1. / (1. + k4 * d2 + k5 * d4 + k6 * d6);//if denominator==0
830                     xd = xc * nu * de + p1 * xcyc + p2 * d2xc2 + s1 * d2 + s2 * d4;
831                     yd = yc * nu * de + p2 * xcyc + p1 * d2yc2 + s3 * d2 + s4 * d4;
832                 }
833                 err2D[k][0] = xd * fx + cx - data2D[k][0];
834                 err2D[k][1] = yd * fy + cy - data2D[k][1];
835             }
836             return true;
837         }
838     };
839     struct LocalParamRWithGeneralJ : public ceres::LocalParameterization
840     {
841         bool Plus(const double* preParam, const double* deltaParam, double* newParam) const
842         {
843             Matx33d ambientPre(preParam);
844             Matx33d ambientDelta; cv::Rodrigues(Matx31d(deltaParam), ambientDelta);
845             Matx33d ambientNew = ambientDelta * ambientPre;
846             memcpy(newParam, ambientNew.val, sizeof(double) * 9);
847             return true;
848         }
849 
850         bool ComputeJacobian(const double* param, double* jacobian) const
851         {
852             Mat_<double> dRdr(9, 3, jacobian);
853             dRdr << 0, param[6], -param[3], 0, param[7], -param[4], 0, param[8], -param[5],
854                 -param[6], 0, param[0], -param[7], 0, param[1], -param[8], 0, param[2],
855                 param[3], -param[0], 0, param[4], -param[1], 0, param[5], -param[2], 0;
856             return true;
857 
858             Matx33d R(param);
859             Mat_<double> dRdrT({ 3, 9 }, {
860                 0, 0, 0, -R(2, 0), -R(2, 1), -R(2, 2), R(1, 0), R(1, 1), R(1, 2),
861                 R(2, 0), R(2, 1), R(2, 2), 0, 0, 0, -R(0, 0), -R(0, 1), -R(0, 2),
862                 -R(1, 0), -R(1, 1), -R(1, 2), R(0, 0), R(0, 1), R(0, 2), 0, 0, 0 });
863             cout << endl << cv::norm(dRdr, dRdrT.t(), NORM_INF) << endl;
864         }
865 
866         int GlobalSize() const { return 9; }
867         int LocalSize() const { return 3; }
868     };
869 };
870 
871 int main(int argc, char** argv) { OptimizeKDTP::TestMe(argc, argv); return 0; }
View Code
原文地址:https://www.cnblogs.com/dzyBK/p/14016309.html