1.视觉成像模型发展历程
透视模型:于2000年,Zhang贡献(编码为RTCM=RadialTangentialCameraModel)。
开创理论:于2000年,Geyer和Daniilidis提出UCM成像模型,可用于建模平面、抛物、椭圆及双曲反射相机。
完善理论:于2001年,Barreto和Araujo优化UCM成像模型,从而简化了标定算法的开发。
理论向工程过渡:于2006年,Scaramuzza和Martinelli及Siegwart基于泰勒展开式提出用多项式来近似所有成像模型。
等距模型:于2006年,Kannala和Brandt贡献(编码为KBCM)。
开创工程:于2007年,Mei和Rives融合径向切向畸变到UCM成像模型(编码为RTUCM),解决UCM建模鱼眼镜头差的问题。
优化工程:于2013年,Heng和Li及Pollefeys。
完善工程:于2016年,Khomutenko和Garcia及Martinet提出EUCM成像模型,解决了UCM径向畸变和建模鱼眼镜头差的问题及RTUCM非闭环解问题。
完善工程:于2018年,Usenko和Demmel及Cremers提出DSCM成像模型,相对于EUCM、RTUCM、KBCM等算法,在精度和速度上取得了折中。
***就工程实现和框架把握而言,2007年和2018年的这两篇论文最佳:
2007:Single view point omnidirectional camera calibration from planar grids
2018:The Double Sphere Camera Model
***成像理论基础阅读资料:
Models for the various classical lens projections
Computer Generated Angular Fisheye Projections
About the various projections of the photographic objective lenses
2.OpenCV中的标定算法
OpenCV实现了RTCM、KBCM、RTUCM。其中RTCM、KBCM认可度最高,实现在Main模块,其次是RTUCM,实现在Extra模块。
通过查看源码,RTUCM实现较粗糙,如calibrateCamera和stereoCalibrate对CALIB_USE_GUESS都无效,查看源码发现initializeStereoCalibration忽视了此值,直接重新进行单目标定。然而,即使initializeStereoCalibration不忽视,你会发现单目标定calibrate也忽视了此值。
3.本文要点说明
本文基于OpenCV、Ceres、Spdlog来阐述以下数学模型:
(1)成像模型:RTCM、KBCM、EUCM/RTEUCM、DSCM/RTDSCM,含所有模型的正向投影、逆向投影及偏导矩阵。
(2)模型转换:对EUCM/RTEUCM和DSCM/RTDSCM系列的UCM模型,含Pin形式、Mei形式及参数转换公式。
(3)几何分析:基于FOV从几何角度分析模型的成像过程,阐述成像模型中每个参数的几何物理意义。
(4)多视模型:双目模型、三目模型。
在projectPoints中针对旋转使用了四种求导方式:
(1)基于指数映射对旋转向量的加法导数,Ceres优化时不用LocalParameterization。
(2)基于BCH公式对旋转向量的加法导数,Ceres优化时不用LocalParameterization。
(3)基于李代数论对旋转向量的乘法导数,Ceres优化时重载LocalParameterization::Plus实现乘法增量的叠加。
(4)对四元数的加法导数,Ceres优化时重载LocalParameterization::ComputeJacobian实现四元数对旋转向量的导数(若ComputeJacobian是乘法导数还需要重载Plus实现乘法增量的叠加)。
(5)对旋转矩阵的加法导数,Ceres优化时重载LocalParameterization::ComputeJacobian实现旋转矩阵对旋转向量的导数(若ComputeJacobian是乘法导数还需要重载Plus实现乘法增量的叠加)。
以上五种求导方式,计算量依次先增后减,中间者计算量最小。因此,直接对旋转向量的乘法导数是视觉定位中最常使用的。
4.实验测试代码
依赖于OpenCV、Ceres和Spdlog,封装在类ModelCamera:
(1)VisionCM+Mei2Pin、CalibSolid+CamObservation、TestRTCM、TestKBCM、TestRTEUCM(仅四畸变的UCM)、、、、
(2)reprojectPoints、TestInvRTCM、TestInvKBCM、TestInvRTEUCM(仅四畸变的UCM)、、、、
(3)calcJacobians、calcJacobiansOfRTCM、、、、TestJacobianRTCM
(4)rectBino、rectTrino、TestRectBino、testRectTrino
1 #include <ceres/ModelRotation.h> 2 #include <opencv2/ccalib/omnidir.hpp> 3 4 class ModelCamera 5 { 6 public: 7 struct VisionCM 8 { 9 enum { RTCM, KBCM, RTEUCM, RTDSCM }; 10 int nRot;//3 4 9 and for rvec quat rmat respectively 11 int nDist;//0 2 4 5 8 12 and echo AutoDiffCostFunction template parameter number except for 0. 0 uses overloaded operator. 12 int nModel;//0 1 2 and echo AutoDiffCostFunction template parameter number except for 0. 0 uses overloaded operator. 13 int camModel;//connect model with distortion 14 bool simPin;//valid only for RTUCM RTEUCM RTDSCM 15 Mat_<Vec3d> point3D; 16 void inline checkInput() 17 { 18 if (camModel == RTCM && nModel != 0) assert(0); 19 if (camModel == KBCM && nModel != 0) assert(0); 20 if (camModel == RTEUCM && nModel != 2) assert(0); 21 if (camModel == RTDSCM && nModel != 2) assert(0); 22 } 23 VisionCM(float* data3D, int nPoint, int nRot0 = 9, int nDist0 = 4, int nModel0 = 1, int camModel0 = RTCM, bool simPin0 = false) : nRot(nRot0), nDist(nDist0), nModel(nModel0), camModel(camModel0), simPin(simPin0) { checkInput(); Mat_<Vec3f>(nPoint, 1, (Vec3f*)data3D).copyTo(point3D); } 24 VisionCM(double* data3D, int nPoint, int nRot0 = 9, int nDist0 = 4, int nModel0 = 1, int camModel0 = RTCM, bool simPin0 = false) : nRot(nRot0), nDist(nDist0), nModel(nModel0), camModel(camModel0), simPin(simPin0) { checkInput(); Mat_<Vec3d>(nPoint, 1, (Vec3d*)data3D).copyTo(point3D); } 25 template <typename tp> bool operator()(const tp* const rot, const tp* const t, const tp* const K, tp* data2D0) const { return (*this)(rot, t, K, (tp*)0, (tp*)0, data2D0); }//fix D=0 and a=0 and b=1 if using full format 26 template <typename tp> bool operator()(const tp* const rot, const tp* const t, const tp* const K, const tp* const DorAB, tp* data2D0) const { return (nModel == 0 ? (*this)(rot, t, K, DorAB, (tp*)0, data2D0) : (*this)(rot, t, K, (tp*)0, DorAB, data2D0)); }//fix D=0 or a=0 or b=1 if using full format 27 template <typename tp> bool operator()(const tp* const rot, const tp* const t, const tp* const K, const tp* const D, const tp* const ab, tp* data2D0) const 28 { 29 //1.Projection params 30 tp fx = K[0]; 31 tp fy = K[1]; 32 tp cx = K[2]; 33 tp cy = K[3]; 34 tp a; if (nModel > 0) a = ab[0]; 35 tp b; if (nModel > 1) b = ab[1]; 36 37 //2.Distortion params 38 tp k1, k2, p1, p2, k3, k4, k5, k6, s1, s2, s3, s4; 39 if (nDist > 1) { k1 = D[0]; k2 = D[1]; } 40 if (nDist > 2) { p1 = D[2]; p2 = D[3]; } //k1=k1 k2=k2 p1=k3 p2=k4 for KB 41 if (nDist > 4) k3 = D[4]; 42 if (nDist > 5) { k4 = D[5]; k5 = D[6]; k6 = D[7]; } 43 if (nDist > 8) { s1 = D[8]; s2 = D[9]; s3 = D[10]; s4 = D[11]; } 44 45 //3.Rotation params 46 tp R[9]; if (nRot == 9) memcpy(R, rot, sizeof(R)); 47 else if (nRot == 4) ceres::QuaternionToRotation(rot, R); 48 else if (nRot == 3) ceres::AngleAxisToRotationMatrix(rot, ceres::RowMajorAdapter3x3(R)); 49 else spdlog::critical("Invalid rotation configuration"); 50 51 //4.Projection actions 52 Vec<tp, 2>* data2D = (Vec<tp, 2>*)data2D0; 53 const Vec3d* data3D = point3D.ptr<Vec3d>(); 54 for (int k = 0; k < point3D.rows; ++k) 55 { 56 //4.1 WorldPoint 57 double X = data3D[k][0]; 58 double Y = data3D[k][1]; 59 double Z = data3D[k][2]; 60 61 //4.2 CameraPoint 62 tp x = R[0] * X + R[1] * Y + R[2] * Z + t[0]; 63 tp y = R[3] * X + R[4] * Y + R[5] * Z + t[1]; 64 tp z = R[6] * X + R[7] * Y + R[8] * Z + t[2]; 65 66 //4.3 StandardPhysicsPoint 67 tp iz; if (camModel == RTCM || camModel == KBCM) iz = 1. / z;//Refraction model 68 else if (camModel == RTEUCM) 69 { 70 tp d = sqrt(b * (x * x + y * y) + z * z); 71 iz = simPin ? 1. / (a * d + (1. - a) * z) : 1. / (a * d + z); 72 } 73 else if (camModel == RTDSCM) 74 { 75 tp d1 = sqrt(x * x + y * y + z * z); 76 tp d2 = sqrt(x * x + y * y + (b * d1 + z) * (b * d1 + z)); 77 iz = simPin ? 1. / (a * d2 + (1. - a) * (b * d1 + z)) : 1. / (a * d2 + (b * d1 + z)); 78 } 79 else spdlog::critical("Invalid model configuration"); 80 tp xn = x * iz; 81 tp yn = y * iz; 82 83 //4.4 DistortedPhysicsPoint 84 tp xd, yd; 85 if (camModel == KBCM)//KBDistortion 86 { 87 if (nDist == 0) 88 { 89 tp r = sqrt(xn * xn + yn * yn); 90 tp ir = 1. / r; 91 tp theta = atan(r); 92 xd = xn * ir * theta; 93 yd = yn * ir * theta; 94 } 95 else 96 { 97 tp r = sqrt(xn * xn + yn * yn); 98 tp ir = 1. / r; 99 tp theta = atan(r);//same as opencv which only focuses on 0~180 fov 100 tp theta2 = theta * theta; 101 tp theta4 = theta2 * theta2; 102 tp theta6 = theta2 * theta4; 103 tp theta8 = theta2 * theta6; 104 tp thetad = theta * (1. + k1 * theta2 + k2 * theta4 + p1 * theta6 + p2 * theta8); //p1=k3 p2=k4 here 105 xd = xn * ir * thetad; 106 yd = yn * ir * thetad; 107 } 108 } 109 else //RTDistortion 110 { 111 if (nDist == 0) { xd = xn; yd = yn; } 112 else 113 { 114 tp xn2, yn2, r2, r4, r6; 115 tp xnyn, r2xn2, r2yn2; 116 tp an, bn; 117 if (nDist > 1) 118 { 119 xn2 = xn * xn; 120 yn2 = yn * yn; 121 r2 = xn2 + yn2; 122 r4 = r2 * r2; 123 r6 = r2 * r4; 124 } 125 if (nDist > 2) 126 { 127 xnyn = 2. * xn * yn; 128 r2xn2 = r2 + 2. * xn2; 129 r2yn2 = r2 + 2. * yn2; 130 } 131 if (nDist == 2) 132 { 133 an = 1. + k1 * r2 + k2 * r4; 134 xd = xn * an; 135 yd = yn * an; 136 } 137 else if (nDist == 4) 138 { 139 an = 1. + k1 * r2 + k2 * r4; 140 xd = xn * an + p1 * xnyn + p2 * r2xn2; 141 yd = yn * an + p2 * xnyn + p1 * r2yn2; 142 } 143 else if (nDist == 5) 144 { 145 an = 1. + k1 * r2 + k2 * r4 + k3 * r6; 146 xd = xn * an + p1 * xnyn + p2 * r2xn2; 147 yd = yn * an + p2 * xnyn + p1 * r2yn2; 148 } 149 else if (nDist == 8) 150 { 151 an = 1. + k1 * r2 + k2 * r4 + k3 * r6; 152 bn = 1. / (1. + k4 * r2 + k5 * r4 + k6 * r6);//if denominator==0 153 xd = xn * an * bn + p1 * xnyn + p2 * r2xn2; 154 yd = yn * an * bn + p2 * xnyn + p1 * r2yn2; 155 } 156 else if (nDist == 12) 157 { 158 an = 1. + k1 * r2 + k2 * r4 + k3 * r6; 159 bn = 1. / (1. + k4 * r2 + k5 * r4 + k6 * r6);//if denominator==0 160 xd = xn * an * bn + p1 * xnyn + p2 * r2xn2 + s1 * r2 + s2 * r4; 161 yd = yn * an * bn + p2 * xnyn + p1 * r2yn2 + s3 * r2 + s4 * r4; 162 } 163 else spdlog::critical("Invalid distortion configuration"); 164 } 165 } 166 167 //4.5 DistortedPixelPoint 168 data2D[k][0] = xd * fx + cx; 169 data2D[k][1] = yd * fy + cy; 170 } 171 return true; 172 } 173 static double Mei2Pin(double a, double* fx = 0, double* fy = 0, double* s = 0, double* D = 0, int nDist = 4, bool forward = true) 174 { 175 a = forward ? a / (1 + a) : a / (1 - a); 176 double ratio1 = forward ? 1 - a : 1 + a; 177 double ratio2 = ratio1 * ratio1; 178 double ratio3 = ratio1 * ratio2; 179 double ratio4 = ratio1 * ratio3; 180 double ratio6 = ratio3 * ratio3; 181 if (fx) fx[0] *= ratio1; 182 if (fy) fy[0] *= ratio1; 183 if (s) s[0] = ratio1 * s[0]; 184 if (nDist > 0) D[0] *= ratio2;//k1 185 if (nDist > 1) D[1] *= ratio4;//k2 186 if (nDist > 2) D[2] *= ratio1;//p1 187 if (nDist > 3) D[3] *= ratio1;//p2 188 if (nDist > 4) D[4] *= ratio6;//k3 189 if (nDist > 5) D[5] *= ratio2;//k4 190 if (nDist > 6) D[6] *= ratio4;//k5 191 if (nDist > 7) D[7] *= ratio6;//k6 192 if (nDist > 8) D[8] *= ratio1;//s1 193 if (nDist > 9) D[9] *= ratio3;//s2 194 if (nDist > 10) D[10] *= ratio1;//s3 195 if (nDist > 11) D[11] *= ratio3;//s4 196 return a; 197 } 198 }; 199 200 struct CamSolid 201 { 202 short nDist;//same as VisionCM 203 short nModel;//same as VisionCM 204 short camModel;//same as VisionCM 205 short simPin;//same as VisionCM 206 double K[4] = { 0 };//not use skew param 207 double D[12] = { 0 };//new max memory 208 double ab[2] = { 0 };//new max memory 209 inline double* data() { return (double*)(this); }//convenient for IO 210 double limitFOV() 211 { 212 double rad2deg = 180 / 3.1415926535897932384626433832795; 213 double A = simPin ? ab[0] / (1 - ab[0]) : ab[0]; 214 if (camModel == VisionCM::RTCM) return 180; 215 else if (camModel == VisionCM::KBCM) return 360; 216 else if (camModel == VisionCM::RTEUCM) return 2 * atan2(sqrt(A * A - 1), -sqrt(ab[1])) * rad2deg;//can be simplified as 2 * acos(-1/A) for b=1 217 else if (camModel == VisionCM::RTDSCM) return 2 * acos(-1 / A) * rad2deg; 218 return 0; 219 } 220 Vec6d idealFOV(int rows, int cols) 221 { 222 double hfov1 = 0, hfov2 = 0, vfov1 = 0, vfov2 = 0; 223 if (camModel == VisionCM::RTCM)//geometric inference 224 { 225 hfov1 = atan(K[2] / K[0]); 226 hfov2 = atan((cols - K[2]) / K[0]); 227 vfov1 = atan(K[3] / K[1]); 228 vfov2 = atan((rows - K[3]) / K[1]); 229 } 230 else if (camModel == VisionCM::KBCM)//geometric inference 231 { 232 hfov1 = K[2] / K[0]; 233 hfov2 = (cols - K[2]) / K[0]; 234 vfov1 = K[3] / K[1]; 235 vfov2 = (rows - K[3]) / K[1]; 236 } 237 else if (camModel == VisionCM::RTEUCM)//geometric inference 238 { 239 auto CalcHalfFOV = [](double F, double c, double A, double b)->double 240 { 241 const double pi = 3.1415926535897932384626433832795; 242 double B = A * c / F; 243 double Y = A * (b * B * B - sqrt(A * A - b * A * A * B * B + b * B * B)); 244 double X = B * (A * A + sqrt(A * A - b * A * A * B * B + b * B * B)); 245 if (0) 246 { 247 Y = b * A * A * c * c - F * sqrt(A * A * F * F - b * A * A * A * A * c * c + b * A * A * c * c); 248 X = A * A * F * c + c * sqrt(A * A * F * F - b * A * A * A * A * c * c + b * A * A * c * c); 249 } 250 return atan2(1, -Y / X); 251 }; 252 auto CalcHalfFOV2 = [](double F, double c, double A, double b)->double//intersection angle formula 253 { 254 double ro = c / F; 255 Vec3d m(0, 0, 1 / (1 + A)); 256 Vec3d n(0, ro, (1 - A * A * b * ro * ro) / (1 + A * sqrt(1 + (1 - A * A) * b * ro * ro))); 257 double theta = acos(m.dot(n) / sqrt(m.dot(m)) / sqrt(n.dot(n))); 258 return theta; 259 }; 260 if (0) 261 { 262 vector<double> As(99), Fs(99), cs(99), bs(99); cv::randu(As, 0.1, 1.9); 263 cv::randu(Fs, 800, 1200); cv::randu(cs, 400, 600); cv::randu(bs, 0.1, 1.9); 264 for (int k = 0; k < As.size(); ++k) 265 { 266 const double rad2deg = 180 / 3.1415926535897932384626433832795; 267 double ang1 = CalcHalfFOV(Fs[k], cs[k], As[k], bs[k]) * rad2deg; 268 double ang2 = CalcHalfFOV2(Fs[k], cs[k], As[k], bs[k]) * rad2deg; 269 cout << endl << fmt::format("A1-A2: {} - {} = {:.6f}", ang1, ang2, ang1 - ang2) << endl; 270 } 271 } 272 hfov1 = CalcHalfFOV(simPin ? K[0] / (1 - ab[0]) : K[0], K[2], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 273 hfov2 = CalcHalfFOV(simPin ? K[0] / (1 - ab[0]) : K[0], cols - K[2], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 274 vfov1 = CalcHalfFOV(simPin ? K[1] / (1 - ab[0]) : K[1], K[3], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 275 vfov2 = CalcHalfFOV(simPin ? K[1] / (1 - ab[0]) : K[1], rows - K[3], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 276 } 277 else if (camModel == VisionCM::RTDSCM)//intersection angle formula 278 { 279 auto CalcHalfFOV = [](double F, double c, double A, double b)->double 280 { 281 double ro = c / F; 282 double zo = (1 - A * A * ro * ro) / (1 + A * sqrt(1 + (1 - A * A) * ro * ro)); 283 double wo = (b * zo + sqrt(zo * zo + (1 - b * b) * ro * ro)) / (zo * zo + ro * ro); 284 Vec3d m(0, 0, 1); 285 Vec3d n(0, wo * ro, wo * zo - b); 286 double theta = acos(m.dot(n) / sqrt(m.dot(m)) / sqrt(n.dot(n))); 287 return theta; 288 }; 289 hfov1 = CalcHalfFOV(simPin ? K[0] / (1 - ab[0]) : K[0], K[2], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 290 hfov2 = CalcHalfFOV(simPin ? K[0] / (1 - ab[0]) : K[0], cols - K[2], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 291 vfov1 = CalcHalfFOV(simPin ? K[1] / (1 - ab[0]) : K[1], K[3], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 292 vfov2 = CalcHalfFOV(simPin ? K[1] / (1 - ab[0]) : K[1], rows - K[3], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 293 } 294 return Vec6d(hfov1, hfov2, hfov1 + hfov2, vfov1, vfov2, vfov1 + vfov2) * (180 / 3.1415926535897932384626433832795); 295 } 296 string print(int rows = 0, int cols = 0) 297 { 298 string str; 299 str += fmt::format("M: [ {}, {}, {}, {} ] ", nDist, nModel, camModel, simPin); 300 str += fmt::format("K: [ {}, {}, {}, {} ] ", K[0], K[1], K[2], K[3]); 301 str += fmt::format("D: [ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} ] ", D[0], D[1], D[2], D[3], D[4], D[5], D[6], D[7], D[8], D[9], D[10], D[11]); 302 str += fmt::format("ab: [ {}, {} ] ", ab[0], ab[1]); 303 Vec6d fov; if (rows != 0 && cols != 0) fov = idealFOV(rows, cols); 304 str += fmt::format("FOV: [ {}, {}, {}, {}, {}, {}, {} ] ", fov[0], fov[1], fov[2], fov[3], fov[4], fov[5], limitFOV()); 305 return str; 306 } 307 //const double pi = 3.1415926535897932384626433832795; 308 //const double deg2rad = pi / 180; 309 //const double rad2deg = 180 / pi; 310 //inline double ellip2DLen(double r1, double r2) { return r1 > r2 ? 2 * pi * r2 + 4 * (r1 - r2) : 2 * pi * r1 + 4 * (r2 - r1); } 311 //inline double ellip2DArea(double r1, double r2) { return 4 * pi * r1 * r2; } 312 //inline double ellip3DArea(double r1, double r2, double r3) { double p = 1.6075; return (r1 == r2 && r2 == r3) ? 4 * pi * r1 * r1 : 4 * pi * pow((pow(r1 * r2, p) + pow(r1 * r3, p) + pow(r2 * r3, p)) / 3., 1. / p); } 313 //inline double ellip3DVolume(double r1, double r2, double r3) { return 4. / 3. * pi * r1 * r2 * r3; } 314 }; 315 316 using SolidPose = ModelRotation::SolidPose; 317 318 struct CamObservation 319 { 320 vector<SolidPose> poses; 321 vector<vector<Point3f>> point3D; 322 vector<vector<Point2f>> point2D; 323 vector<bool> oksView;//can be used for calibration 324 vector<vector<uint>> ids3D; //can be used for location and calibration 325 void clear() { poses.clear(); point3D.clear(); point2D.clear(); oksView.clear(); ids3D.clear(); } 326 }; 327 328 public: 329 static Mat_<Vec2d> reprojectPoints(Mat_<Vec2d> point2D, double* K, double* D, double* ab, int nDist, int nModel, int camModel, bool simPin, double* R = 0, double* P = 0, 330 Mat_<Vec3d>* point3DDir = 0, Mat_<Vec3d>* point3DCir = 0, TermCriteria criteria = TermCriteria(TermCriteria::COUNT, 5, 0.01)) //criteria is fixed as (10, 1e-8) in opencv4.5 KB 331 { 332 //1.Projection params 333 double fx = K[0], ifx = 1. / fx; 334 double fy = K[1], ify = 1. / fy; 335 double cx = K[2], icx = -ifx * cx; 336 double cy = K[3], icy = -ify * cy; 337 double a = 0; if (nModel > 0) a = ab[0]; 338 double b = 1; if (nModel > 1) b = ab[1]; 339 340 //2.Distortion params 341 double k1 = 0, k2 = 0, p1 = 0, p2 = 0; 342 double k3 = 0, k4 = 0, k5 = 0, k6 = 0; 343 double s1 = 0, s2 = 0, s3 = 0, s4 = 0; 344 if (nDist > 0) { k1 = D[0]; k2 = D[1]; } 345 if (nDist > 2) { p1 = D[2]; p2 = D[3]; } 346 if (nDist > 4) k3 = D[4]; 347 if (nDist > 5) { k4 = D[5]; k5 = D[6]; k6 = D[7]; } 348 if (nDist > 8) { s1 = D[8]; s2 = D[9]; s3 = D[10]; s4 = D[11]; } 349 350 //3.Reprojection matrix 351 Matx33d A = A.eye(); 352 if (P != 0) A = A * Matx33d(P[0], 0, P[2], 0, P[1], P[3], 0, 0, 1); 353 if (R != 0) A = A * Matx33d(R); 354 355 //4.Reprojection actions 356 Mat_<Vec2d> point2DNorm(point2D.rows, 1); 357 if (point3DDir != 0 && point3DDir->size() != point2D.size()) point3DDir->create(point2D.size()); 358 if (point3DCir != 0 && point3DCir->size() != point2D.size()) point3DCir->create(point2D.size()); 359 for (int k = 0; k < point2D.rows; ++k) 360 { 361 //4.1 DistortedPixelPoint-->DistortedPhysicsPoint 362 double u = point2D(k)[0], xd = (u - cx) * ifx; 363 double v = point2D(k)[1], yd = (v - cy) * ify; 364 365 //4.2 DistortedPhysics-->StandardPhysicsPoint 366 double xn = xd, yn = yd; 367 if (camModel == VisionCM::KBCM) 368 { 369 if (nDist == 0) 370 { 371 372 } 373 else 374 { 375 double thetad = sqrt(xd * xd + yd * yd), halfPI = 3.1415926535897932384626433832795 / 2.; 376 thetad = thetad > halfPI ? halfPI : thetad < -halfPI ? -halfPI : thetad;//same as opencv which only focuses on 0~180 fov 377 double thetan = thetad;//it not conflict with projection using thetan 378 int i = 0; 379 for (; i < criteria.maxCount; ++i) 380 { 381 double thetan2 = thetan * thetan, k1thetan2 = k1 * thetan2; 382 double thetan4 = thetan2 * thetan2, k2thetan4 = k2 * thetan4; 383 double thetan6 = thetan2 * thetan4, k3thetan6 = p1 * thetan6; 384 double thetan8 = thetan2 * thetan6, k4thetan8 = p2 * thetan8; //p1=k3 p2=k4 here 385 double deltaTheta = (thetan * (1 + k1thetan2 + k2thetan4 + k3thetan6 + k4thetan8) - thetad) / (1 + 3 * k1thetan2 + 5 * k2thetan4 + 7 * k3thetan6 + 9 * k4thetan8); 386 thetan -= deltaTheta; 387 if ((criteria.type & TermCriteria::EPS) && abs(deltaTheta) < criteria.epsilon) break; 388 } 389 if (i >= criteria.maxCount || thetad * thetan < 0) //same as opencv4.5 for consistence test and can be removed 390 { 391 point2DNorm(k) = Vec2d(-1000000.0, -1000000.0); 392 if (point3DDir) (*point3DDir)(k) = Vec3d(0, 0, 0); 393 if (point3DCir) (*point3DCir)(k) = Vec3d(0, 0, 0); 394 continue; 395 } 396 double scaled = tan(thetan) / thetad; 397 xn *= scaled; 398 yn *= scaled; 399 } 400 } 401 else 402 { 403 if (nDist == 0) {/*nothing to do*/ } 404 else 405 { 406 for (int i = 0; i < criteria.maxCount; ++i) 407 { 408 double rn2 = xn * xn + yn * yn;//it not conflict with projection using rn2 anbn deltaX deltaY 409 double anbn = (1 + ((k6 * rn2 + k5) * rn2 + k4) * rn2) / (1 + ((k3 * rn2 + k2) * rn2 + k1) * rn2); 410 if (anbn < 0) { xn = (u - cx) * ifx; yn = (v - cy) * ify; break; } // test: undistortPoints.regression_14583 411 double deltaX = 2 * p1 * xn * yn + p2 * (rn2 + 2 * xn * xn) + s1 * rn2 + s2 * rn2 * rn2; 412 double deltaY = p1 * (rn2 + 2 * yn * yn) + 2 * p2 * xn * yn + s3 * rn2 + s4 * rn2 * rn2; 413 xn = (xd - deltaX) * anbn; 414 yn = (yd - deltaY) * anbn; 415 416 if (criteria.type & TermCriteria::EPS) 417 { 418 double xn2 = xn * xn; 419 double yn2 = yn * yn; 420 double r2 = xn2 + yn2; 421 double xnyn = 2 * xn * yn; 422 double r4 = r2 * r2; 423 double r6 = r4 * r2; 424 double r2xn2 = r2 + 2 * xn2; 425 double r2yn2 = r2 + 2 * yn2; 426 double an = 1 + k1 * r2 + k2 * r4 + k3 * r6; 427 double bn = 1. / (1 + k4 * r2 + k5 * r4 + k6 * r6); 428 double xdd = xn * an * bn + p1 * xnyn + p2 * r2xn2 + s1 * r2 + s2 * r4; 429 double ydd = yn * an * bn + p1 * r2yn2 + p2 * xnyn + s3 * r2 + s4 * r4; 430 double uerr = xdd * fx + cx - u; 431 double verr = ydd * fy + cy - v; 432 if (sqrt(uerr * uerr + verr * verr) < criteria.epsilon) break; 433 } 434 } 435 } 436 } 437 438 //4.3 StandardPhysicsPoint-->StandardPixelPoint 439 double rn2 = xn * xn + yn * yn; 440 double xc = xn, yc = yn, zc = (1 - a * a * b * rn2) / (1 + a * sqrt(1 + (1 - a * a) * b * rn2)); 441 double is = 1. / (A.val[6] * xc + A.val[7] * yc + A.val[8] * zc); 442 point2DNorm(k)[0] = (A.val[0] * xc + A.val[1] * yc + A.val[2] * zc) * is; 443 point2DNorm(k)[1] = (A.val[3] * xc + A.val[4] * yc + A.val[5] * zc) * is; 444 445 //4.4 StandardPhysicsPoint-->ScaledCameraPoint 446 if (point3DDir || point3DCir) 447 { 448 double xcc = xc, ycc = yc, zcc = zc; 449 if (R) 450 { 451 xcc = xc * R[0] + yc * R[1] + zc * R[2]; 452 ycc = xc * R[3] + yc * R[4] + zc * R[5]; 453 zcc = xc * R[6] + yc * R[7] + zc * R[8]; 454 } 455 if (point3DDir != 0) (*point3DDir)(k) = Vec3d(xcc, ycc, zcc); 456 if (point3DCir != 0) { double ratio = 1. / sqrt(xcc * xcc + ycc * ycc + zcc * zcc); (*point3DCir)(k) = Vec3d(xcc * ratio, ycc * ratio, zcc * ratio); } 457 } 458 } 459 return point2DNorm; 460 } 461 462 public: 463 static Mat_<Vec2d> calcJacobians(Mat_<Vec3d> point3D, double* r, double* t, double* K, double* D, int nDist, int nModel, int idDist, int idModel, bool simPin, Mat_<double>& jacobians) { return Mat_<Vec2d>(); } 464 465 static Mat_<Vec2d> calcJacobiansOfRTCM(Mat_<Vec3d> point3D, double* r, double* t, double* K, double* D, int nDist, 466 Mat_<double>* dpdr = 0, Mat_<double>* dpdt = 0, Mat_<double>* dpdK = 0, Mat_<double>* dpdD = 0, Mat_<double>* dpdP = 0, 467 int derivRotWay = 0) 468 { 469 //0.FormatInput 470 if (dpdK) dpdK->release(); 471 if (dpdD) dpdD->release(); 472 if (dpdt) dpdt->release(); 473 if (dpdr) dpdr->release(); 474 if (dpdP) dpdP->release(); 475 Mat_<Vec2d> point2D(point3D.size()); 476 477 //1.Projection params 478 double fx = K[0]; 479 double fy = K[1]; 480 double cx = K[2]; 481 double cy = K[3]; 482 483 //2.Distortion params 484 double k1 = D[0]; 485 double k2 = D[1]; 486 double p1 = D[2]; 487 double p2 = D[3]; 488 double k3 = 0, k4 = 0, k5 = 0, k6 = 0; 489 double s1 = 0, s2 = 0, s3 = 0, s4 = 0; 490 if (nDist > 4) k3 = D[4]; 491 if (nDist > 5) { k4 = D[5]; k5 = D[6]; k6 = D[7]; } 492 if (nDist > 8) { s1 = D[8]; s2 = D[9]; s3 = D[10]; s4 = D[11]; } 493 494 //3.Rotation params 495 double R[9]; 496 Mat_<double> dRdr; 497 cv::Rodrigues(Mat_<double>(3, 1, r), Mat_<double>(3, 3, R), (dpdr && derivRotWay == 0) ? dRdr : noArray()); 498 if (dRdr.empty() == false) dRdr = dRdr.t(); 499 500 //4.Projection actions 501 Vec2d* data2D = point2D.ptr<Vec2d>(); 502 Vec3d* data3D = point3D.ptr<Vec3d>(); 503 for (int k = 0; k < point3D.rows; ++k) 504 { 505 //4.1 WorldPoint 506 double X = data3D[k][0]; 507 double Y = data3D[k][1]; 508 double Z = data3D[k][2]; 509 510 //4.2 CameraPoint 511 double x = R[0] * X + R[1] * Y + R[2] * Z + t[0]; 512 double y = R[3] * X + R[4] * Y + R[5] * Z + t[1]; 513 double z = R[6] * X + R[7] * Y + R[8] * Z + t[2]; 514 515 //4.3 StandardPhysicsPoint 516 double iz = z ? 1. / z : 1; 517 double xn = x * iz; 518 double yn = y * iz; 519 520 //4.4 DistortedPhysicsPoint 521 double xn2 = xn * xn; 522 double yn2 = yn * yn; 523 double r2 = xn2 + yn2; 524 double xnyn = 2 * xn * yn; 525 double r4 = r2 * r2; 526 double r6 = r2 * r4; 527 double r2xn2 = r2 + 2 * xn2; 528 double r2yn2 = r2 + 2 * yn2; 529 double an = 1 + k1 * r2 + k2 * r4 + k3 * r6; 530 double bn = 1. / (1 + k4 * r2 + k5 * r4 + k6 * r6); 531 double xd = xn * an * bn + p1 * xnyn + p2 * r2xn2 + s1 * r2 + s2 * r4; 532 double yd = yn * an * bn + p2 * xnyn + p1 * r2yn2 + s3 * r2 + s4 * r4; 533 534 //4.5 DistortedPixelPoint 535 data2D[k][0] = xd * fx + cx; 536 data2D[k][1] = yd * fy + cy; 537 538 //5.6 dpdK: derivatives with respect to the camera parameters 539 if (dpdK) dpdK->push_back(Mat_<double>({ 2, 4 }, { xd, 0, 1, 0, 0, yd, 0, 1 })); 540 541 //5.7 dpdD: derivatives with respect to the distortion coefficients 542 if (dpdD) 543 { 544 Mat_<double> dpDdpCD(2, 2); 545 dpDdpCD << fx, 0, 0, fy; 546 547 Mat_<double> dpCDdD(2, nDist); 548 if (nDist < 5) 549 { 550 dpCDdD << 551 xn * r2 * bn, xn* r4* bn, xnyn, r2xn2, 552 yn* r2* bn, yn* r4* bn, r2yn2, xnyn; 553 } 554 else if (nDist < 8) 555 { 556 dpCDdD << 557 xn * r2 * bn, xn* r4* bn, xnyn, r2xn2, xn* r6* bn, 558 yn* r2* bn, yn* r4* bn, r2yn2, xnyn, yn* r6* bn; 559 } 560 else if (nDist < 12) 561 { 562 dpCDdD << 563 xn * r2 * bn, xn* r4* bn, xnyn, r2xn2, xn* r6* bn, -xn * r2 * an * bn * bn, -xn * r4 * an * bn * bn, -xn * r6 * an * bn * bn, 564 yn* r2* bn, yn* r4* bn, r2yn2, xnyn, yn* r6* bn, -yn * r2 * an * bn * bn, -yn * r4 * an * bn * bn, -yn * r6 * an * bn * bn; 565 } 566 else if (nDist < 14) 567 { 568 dpCDdD << 569 xn * r2 * bn, xn* r4* bn, xnyn, r2xn2, xn* r6* bn, -xn * r2 * an * bn * bn, -xn * r4 * an * bn * bn, -xn * r6 * an * bn * bn, r2, r4, 0, 0, 570 yn* r2* bn, yn* r4* bn, r2yn2, xnyn, yn* r6* bn, -yn * r2 * an * bn * bn, -yn * r4 * an * bn * bn, -yn * r6 * an * bn * bn, 0, 0, r2, r4; 571 } 572 dpdD->push_back(dpDdpCD * dpCDdD); 573 } 574 575 //5.8 dpdr&&dpdt: derivatives with respect to the pose 576 if (dpdr || dpdt || dpdP) 577 { 578 Mat_<double> dpDdpCD({ 2, 2 }, { fx, 0, 0, fy }); 579 580 Mat_<double> dpCDdh({ 2, 5 }, { 581 xn * bn, -xn * an * bn * bn, p2 + s1 + 2 * s2 * r2, an * bn + 2 * p1 * yn + 4 * p2 * xn, 2 * p1 * xn, 582 yn * bn, -yn * an * bn * bn, p1 + s3 + 2 * s4 * r2, 2 * p2 * yn, an * bn + 2 * p2 * xn + 4 * p1 * yn }); 583 584 Mat_<double> dhdpC({ 5, 2 }, { 585 xn * (2 * k1 + 4 * k2 * r2 + 6 * k3 * r4), yn * (2 * k1 + 4 * k2 * r2 + 6 * k3 * r4), 586 xn * (2 * k4 + 4 * k5 * r2 + 6 * k6 * r4), yn * (2 * k4 + 4 * k5 * r2 + 6 * k6 * r4), 587 2 * xn, 2 * yn, 588 1, 0, 589 0, 1 }); 590 591 Mat_<double> dpCdPC({ 2, 3 }, { 592 iz, 0, -xn * iz, 593 0, iz, -yn * iz }); 594 595 //1.dpdt: derivatives with respect to the translation vector 596 if (dpdt) dpdt->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC); 597 598 //2.dpdr: derivatives with respect to the rotation vector 599 if (dpdr) 600 { 601 //2.1 Exponential map 602 if (derivRotWay == 0) 603 { 604 Mat_<double> dPCdR({ 3, 9 }, { 605 X, Y, Z, 0, 0, 0, 0, 0, 0, 606 0, 0, 0, X, Y, Z, 0, 0, 0, 607 0, 0, 0, 0, 0, 0, X, Y, Z }); 608 dpdr->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * dPCdR * dRdr); 609 } 610 611 //2.2 BCH formula 612 else if (derivRotWay == 1) 613 { 614 Mat_<double> _skewPC({ 3, 3 }, { 615 0, (z - t[2]), -(y - t[1]), 616 -(z - t[2]), 0, (x - t[0]), 617 (y - t[1]), -(x - t[0]), 0 }); 618 double theta = sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]); 619 double itheta = 1 / theta; 620 double sn = sin(theta); 621 double cs1 = 1 - cos(theta); 622 Matx31d n(r[0] * itheta, r[1] * itheta, r[2] * itheta); 623 Matx33d nskew(0, -n.val[2], n.val[1], n.val[2], 0, -n.val[0], -n.val[1], n.val[0], 0); 624 Matx33d Jl = itheta * sn * Matx33d::eye() + itheta * cs1 * nskew + (1 - itheta * sn) * n * n.t(); 625 dpdr->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * _skewPC * Jl); 626 } 627 628 //2.3 Perturbance without LocalParameterization::ComputeJacobian 629 else if (derivRotWay == 2) 630 { 631 Mat_<double> _skewPC({ 3, 3 }, { 632 0, (z - t[2]), -(y - t[1]), 633 -(z - t[2]), 0, (x - t[0]), 634 (y - t[1]), -(x - t[0]), 0 }); 635 dpdr->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * _skewPC); 636 } 637 638 //2.4 Perturbance with LocalParameterization::ComputeJacobian 639 if (derivRotWay == 3) 640 { 641 Mat_<double> dPCdR({ 3, 9 }, { 642 X, Y, Z, 0, 0, 0, 0, 0, 0, 643 0, 0, 0, X, Y, Z, 0, 0, 0, 644 0, 0, 0, 0, 0, 0, X, Y, Z }); 645 dpdr->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * dPCdR); 646 } 647 } 648 649 //3.dpdP: derivatives with respect to the worldcoordinate 650 if (dpdP) dpdP->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * Mat_<double>(3, 3, R)); 651 } 652 } 653 return point2D; 654 } 655 656 public: 657 static void rectBino(double* R1, double* R2, double* R, double* t, double* delta = 0, double ratio1 = 0.5, double* tnew = 0) 658 { 659 //1.Let left and right image plane be coplanar 660 Matx31d r; cv::Rodrigues(Matx33d(R), r); 661 Matx33d RCop1; cv::Rodrigues(ratio1 * r, RCop1); 662 Matx33d RCop2; cv::Rodrigues(ratio1 * r - r, RCop2); 663 Matx31d tCop = RCop2 * Matx31d(t); 664 665 //2.Let left and right image plane are epipolar 666 Matx31d xAxis(tCop(0) > 0 ? 1 : -1, 0, 0); 667 Matx31d rEpi = Vec3d(tCop.val).cross(Vec3d(xAxis.val)); 668 if (norm(rEpi, NORM_L2) > 0.) rEpi *= (acos(fabs(tCop(0)) / norm(tCop, NORM_L2)) / norm(rEpi, NORM_L2)); 669 Matx33d REpi; cv::Rodrigues(rEpi, REpi); 670 671 //3.Additional rotation for reprojection at expectation angle 672 Matx33d RDelta = RDelta.eye(); 673 if (delta != 0) cv::Rodrigues(Vec3d(delta), RDelta); 674 675 //4.Final transformation 676 memcpy(R1, (RDelta * REpi * RCop1).val, 9 * sizeof(double)); 677 memcpy(R2, (RDelta * REpi * RCop2).val, 9 * sizeof(double)); 678 if (tnew != 0) memcpy(tnew, (Matx33d(R2) * Matx31d(t)).val, 3 * sizeof(double)); 679 } 680 681 static void rectBino2(double* R1, double* R2, double* R, double* t, double* delta = 0, double ratio1 = 0.0, double* tnew = 0) 682 { 683 //1.Let left and right image plane be coplanar 684 Matx31d r; cv::Rodrigues(Matx33d(R), r); 685 Matx33d RCop1; cv::Rodrigues(r * ratio1, RCop1); 686 Matx33d RCop2; cv::Rodrigues(r * ratio1 - r, RCop2); 687 Matx31d tCop = RCop2 * Matx31d(t); 688 689 //2.Let left and right image plane be epipolar 690 Vec3d e1((-tCop).val); 691 e1 = e1 / cv::norm(e1); 692 Vec3d e2(-e1(1), e1(0), 0); 693 e2 = e2 / cv::norm(e2); 694 Vec3d e3 = e1.cross(e2); 695 e3 = e3 / cv::norm(e3); 696 Matx33d REpi(e1(0), e1(1), e1(2), e2(0), e2(1), e2(2), e3(0), e3(1), e3(2)); 697 698 //3.Additional rotation for reprojection at expectation angle 699 Matx33d RDelta = RDelta.eye(); 700 if (delta != 0) cv::Rodrigues(Vec3d(delta), RDelta); 701 702 //4.Final transformation 703 memcpy(R1, (RDelta * REpi * RCop1).val, 9 * sizeof(double)); 704 memcpy(R2, (RDelta * REpi * RCop2).val, 9 * sizeof(double)); 705 if (tnew != 0) memcpy(tnew, (Matx33d(R2) * Matx31d(t)).val, 3 * sizeof(double)); 706 707 if (1)//OpenCV codes 708 { 709 //1.Let left and right image plane be coplanar 710 Matx33d RCop = Matx33d(R).t(); 711 Matx31d tCop = -Matx33d(R).t() * Matx31d(t);//here should not has minus sign and use tCop(0) sign later like stereoRectify and fisheye::stereoRectify 712 713 //2.Let left and right image plane be epipolar 714 Vec3d e1(tCop.val); 715 e1 = e1 / cv::norm(e1); 716 Vec3d e2(-e1(1), e1(0), 0); 717 e2 = e2 / cv::norm(e2); 718 Vec3d e3 = e1.cross(e2); 719 e3 = e3 / cv::norm(e3); 720 Matx33d REpi(e1(0), e1(1), e1(2), e2(0), e2(1), e2(2), e3(0), e3(1), e3(2)); 721 722 //3.Additional rotation for reprojection at expectation angle 723 Matx33d RDelta = RDelta.eye(); 724 if (delta != 0) cv::Rodrigues(Vec3d(delta), RDelta); 725 726 //4.Final transformation 727 memcpy(R1, (RDelta * REpi).val, 9 * sizeof(double)); 728 memcpy(R2, (RDelta * REpi * RCop).val, 9 * sizeof(double)); 729 if (tnew != 0) memcpy(tnew, (Matx33d(R2) * Matx31d(t)).val, 3 * sizeof(double)); 730 } 731 } 732 733 public: 734 static void TestRTCM(int argc = 0, char** argv = 0) 735 { 736 int N = 999; 737 for (int k = 0; k < N; ++k) 738 { 739 //0.GenerateData 740 static const int nDist = 5;//fix nRot=3 nModel=0 741 Mat_<Vec3d> point3D(2, 1); cv::randu(point3D, -999, 999); 742 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 743 Mat_<double> D(nDist, 1); cv::randu(D, -1.0, 1.0); 744 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 745 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 746 747 //1.CalcByOpenCV 748 Mat_<Vec2d> point2D1; 749 Mat_<double> dpdKDT1; 750 projectPoints(point3D, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, point2D1, dpdKDT1); 751 Mat_<double> dpdr1 = dpdKDT1.colRange(0, 3); 752 Mat_<double> dpdt1 = dpdKDT1.colRange(3, 6); 753 Mat_<double> dpdK1 = dpdKDT1.colRange(6, 10); 754 Mat_<double> dpdD1 = dpdKDT1.colRange(10, 10 + D.rows); 755 756 //2.CalcByCeresExp 757 Mat_<double> dpdr2(point3D.rows * 2, 3); 758 Mat_<double> dpdt2(point3D.rows * 2, 3); 759 Mat_<double> dpdK2(point3D.rows * 2, 4); 760 Mat_<double> dpdD2(point3D.rows * 2, nDist); 761 Mat_<Vec2d> point2D2(point3D.rows, 1); 762 ceres::AutoDiffCostFunction<VisionCM, ceres::DYNAMIC, 3, 3, 4, nDist> autoDeriv(new VisionCM(point3D.ptr<double>(), point3D.rows, 3, nDist, 0, VisionCM::RTCM, true), point3D.rows * 2); 763 double* parameters[4] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>() }; 764 double* jacobians[4] = { dpdr2.ptr<double>(), dpdt2.ptr<double>(), dpdK2.ptr<double>(), dpdD2.ptr<double>() }; 765 double* residuals = point2D2.ptr<double>(); 766 autoDeriv.Evaluate(parameters, residuals, jacobians); 767 768 //3.AnalyzeError 769 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 770 double infdpdr1r2 = norm(dpdr1, dpdr2, NORM_INF); 771 double infdpdt1t2 = norm(dpdt1, dpdt2, NORM_INF); 772 double infdpdK1K2 = norm(dpdK1, dpdK2, NORM_INF); 773 double infdpdD1D2 = norm(dpdD1, dpdD2, NORM_INF); 774 775 //4.PrintError 776 cout << fmt::format("LoopCount: {} ", k); 777 if (infpts1pts2 > 1e-9 || infdpdr1r2 > 1e-9 || infdpdt1t2 > 1e-9 || infdpdK1K2 > 1e-9 || infdpdD1D2 > 1e-9) 778 { 779 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 780 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 781 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 782 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 783 cout << endl << "infdpdD1D2: " << infdpdD1D2 << endl; 784 if (0) 785 { 786 //cout << endl << "K: " << K.t() << endl; 787 //cout << endl << "D: " << D.t() << endl; 788 //cout << endl << "r: " << r.t() << endl; 789 //cout << endl << "t: " << t.t() << endl; 790 cout << endl << "point3D: " << point3D << endl; 791 cout << endl << "point2D1: " << point2D1 << endl; 792 cout << endl << "point2D2: " << point2D2 << endl; 793 } 794 cout << "Press any key to continue" << endl; getchar(); 795 } 796 } 797 } 798 static void TestKBCM(int argc = 0, char** argv = 0) 799 { 800 int N = 999; 801 for (int k = 0; k < N; ++k) 802 { 803 //0.GenerateData //fix nRot=3 nDist=4 nModel=0 804 Mat_<Vec3d> point3D(2, 1); cv::randu(point3D, -999, 999); 805 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 806 Mat_<double> D(4, 1); cv::randu(D, -1.0, 1.0); 807 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 808 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 809 810 //1.CalcByOpenCV 811 Mat_<Vec2d> point2D1; 812 Mat_<double> dpdKDT1; 813 fisheye::projectPoints(point3D, point2D1, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, 0, dpdKDT1); 814 Mat_<double> dpdK1 = dpdKDT1.colRange(0, 4); 815 Mat_<double> dpdD1 = dpdKDT1.colRange(4, 8); 816 Mat_<double> dpdr1 = dpdKDT1.colRange(8, 11); 817 Mat_<double> dpdt1 = dpdKDT1.colRange(11, 14); 818 819 //2.CalcByCeresExp 820 Mat_<double> dpdr2(point3D.rows * 2, 3); 821 Mat_<double> dpdt2(point3D.rows * 2, 3); 822 Mat_<double> dpdK2(point3D.rows * 2, 4); 823 Mat_<double> dpdD2(point3D.rows * 2, 4); 824 Mat_<double> dpda2(point3D.rows * 2, 1); 825 Mat_<Vec2d> point2D2(point3D.rows, 1); 826 ceres::AutoDiffCostFunction<VisionCM, ceres::DYNAMIC, 3, 3, 4, 4> autoDeriv(new VisionCM(point3D.ptr<double>(), point3D.rows, 3, 4, 0, VisionCM::KBCM, true), point3D.rows * 2); 827 double* parameters[4] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>() }; 828 double* jacobians[4] = { dpdr2.ptr<double>(), dpdt2.ptr<double>(), dpdK2.ptr<double>(), dpdD2.ptr<double>() }; 829 double* residuals = point2D2.ptr<double>(); 830 autoDeriv.Evaluate(parameters, residuals, jacobians); 831 832 //3.AnalyzeError 833 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 834 double infdpdr1r2 = norm(dpdr1, dpdr2, NORM_INF); 835 double infdpdt1t2 = norm(dpdt1, dpdt2, NORM_INF); 836 double infdpdK1K2 = norm(dpdK1, dpdK2, NORM_INF); 837 double infdpdD1D2 = norm(dpdD1, dpdD2, NORM_INF); 838 839 //4.PrintError 840 cout << fmt::format("LoopCount: {} ", k); 841 if (infpts1pts2 > 1e-9 || infdpdr1r2 > 1e-9 || infdpdt1t2 > 1e-9 || infdpdK1K2 > 1e-9 || infdpdD1D2 > 1e-9) 842 { 843 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 844 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 845 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 846 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 847 cout << endl << "infdpdD1D2: " << infdpdD1D2 << endl; 848 if (0) 849 { 850 //cout << endl << "K: " << K.t() << endl; 851 //cout << endl << "D: " << D.t() << endl; 852 //cout << endl << "r: " << r.t() << endl; 853 //cout << endl << "t: " << t.t() << endl; 854 cout << endl << "point3D: " << point3D << endl; 855 cout << endl << "point2D1: " << point2D1 << endl; 856 cout << endl << "point2D2: " << point2D2 << endl; 857 } 858 cout << "Press any key to continue" << endl; getchar(); 859 } 860 } 861 } 862 863 static void TestRTEUCM(int argc = 0, char** argv = 0) 864 { 865 int N = 999; 866 for (int k = 0; k < N; ++k) 867 { 868 //0.GenerateData 869 static const int nDist = 4;//fix nRot=3 nModel=2 870 Mat_<Vec3d> point3D(2, 1); cv::randu(point3D, -999, 999); 871 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 872 Mat_<double> D(nDist, 1); cv::randu(D, -1, 1); 873 Mat_<double> ab(2, 1); cv::randu(ab, 0, 9); double A = ab(0); if (k % 2 == 0) ab(1) = 1; 874 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 875 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 876 877 //1.CalcByOpenCV(nRot=3&&nDist=4&&&simPin=false) 878 Mat_<Vec2d> point2D1; 879 Mat_<double> dpdKDT1; 880 omnidir::projectPoints(point3D, point2D1, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), ab(0), D.rowRange(0, 4), dpdKDT1); 881 Mat_<double> dpdr1 = dpdKDT1.colRange(0, 3); 882 Mat_<double> dpdt1 = dpdKDT1.colRange(3, 6); 883 Mat_<double> dpdK1 = dpdKDT1.colRange(6, 11); 884 Mat_<double> dpds1 = dpdK1.col(2).clone(); dpdK1.col(3).copyTo(dpdK1.col(2)); dpdK1.col(4).copyTo(dpdK1.col(3)); dpds1.copyTo(dpdK1.col(4)); 885 Mat_<double> dpda1 = dpdKDT1.colRange(11, 12); 886 Mat_<double> dpdD1 = dpdKDT1.colRange(12, 16); 887 888 //2.CalcByCeresExpSameAsOpenCV(nRot=3&&nDist=4&&&&simPin=false) 889 Mat_<double> dpdr2(point3D.rows * 2, 3); 890 Mat_<double> dpdt2(point3D.rows * 2, 3); 891 Mat_<double> dpdK2(point3D.rows * 2, 4); 892 Mat_<double> dpdD2(point3D.rows * 2, nDist); 893 Mat_<double> dpdab2(point3D.rows * 2, 2); 894 Mat_<Vec2d> point2D2(point3D.rows, 1); 895 ceres::AutoDiffCostFunction<VisionCM, ceres::DYNAMIC, 3, 3, 4, nDist, 2> autoDeriv2(new VisionCM(point3D.ptr<double>(), point3D.rows, 3, nDist, 2, VisionCM::RTEUCM, false), point3D.rows * 2); 896 double* parameters2[5] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>(), ab.ptr<double>() }; 897 double* jacobians2[5] = { dpdr2.ptr<double>(), dpdt2.ptr<double>(), dpdK2.ptr<double>(), dpdD2.ptr<double>(), dpdab2.ptr<double>() }; 898 double* residuals2 = point2D2.ptr<double>(); 899 autoDeriv2.Evaluate(parameters2, residuals2, jacobians2); 900 901 //3.CalcByCeresExpSameAsOpenCV(nRot=3&&nDist=4&&simPin=true) 902 ab(0) = VisionCM::Mei2Pin(ab(0), K.ptr<double>(), K.ptr<double>() + 1, 0, D.ptr<double>(), D.rows, true); 903 Mat_<double> dpdr3(point3D.rows * 2, 3); 904 Mat_<double> dpdt3(point3D.rows * 2, 3); 905 Mat_<double> dpdK3(point3D.rows * 2, 4); 906 Mat_<double> dpdD3(point3D.rows * 2, nDist); 907 Mat_<double> dpdab3(point3D.rows * 2, 2); 908 Mat_<Vec2d> point2D3(point3D.rows, 1); 909 ceres::AutoDiffCostFunction<VisionCM, ceres::DYNAMIC, 3, 3, 4, nDist, 2> autoDeriv3(new VisionCM(point3D.ptr<double>(), point3D.rows, 3, nDist, 2, VisionCM::RTEUCM, true), point3D.rows * 2); 910 double* parameters3[5] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>(), ab.ptr<double>() }; 911 double* jacobians3[5] = { dpdr3.ptr<double>(), dpdt3.ptr<double>(), dpdK3.ptr<double>(), dpdD3.ptr<double>(), dpdab3.ptr<double>() }; 912 double* residuals3 = point2D3.ptr<double>(); 913 autoDeriv3.Evaluate(parameters3, residuals3, jacobians3); 914 915 //4.AnalyzeError 916 double infpts1pts2 = (nDist == 4 && ab(1) == 1) ? cv::norm(point2D1, point2D2, NORM_INF) : 0; 917 double infpts1pts3 = (nDist == 4 && ab(1) == 1) ? cv::norm(point2D1, point2D3, NORM_INF) : 0; 918 double infpts2pts3 = cv::norm(point2D2, point2D3, NORM_INF); 919 double infdpdr1r2 = (nDist == 4 && ab(1) == 1) ? cv::norm(dpdr1, dpdr2, NORM_INF) : 0; 920 double infdpdt1t2 = (nDist == 4 && ab(1) == 1) ? cv::norm(dpdt1, dpdt2, NORM_INF) : 0; 921 double infdpdK1K2 = (nDist == 4 && ab(1) == 1) ? cv::norm(dpdK1.colRange(0, 4), dpdK2, NORM_INF) : 0; 922 double infdpdD1D2 = (nDist == 4 && ab(1) == 1) ? cv::norm(dpdD1, dpdD2, NORM_INF) : 0; 923 double infdpda1a2 = (nDist == 4 && ab(1) == 1) ? cv::norm(dpda1, dpdab2.col(0), NORM_INF) : 0; 924 925 //5.PrintError 926 cout << fmt::format("LoopCount: {} A: {:.12f} a: {:.12f} b: {:.12f} ", k, A, ab(0), ab(1), nDist); 927 if (infpts1pts2 > 1e-9 || infpts1pts3 > 1e-9 || infpts2pts3 > 1e-9 || infdpdr1r2 > 1e-9 || infdpdt1t2 > 1e-9 || infdpdK1K2 > 1e-9 || infdpdD1D2 > 1e-9 || infdpda1a2 > 1e-9) 928 { 929 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 930 cout << endl << "infpts1pts3: " << infpts1pts3 << endl; 931 cout << endl << "infpts2pts3: " << infpts2pts3 << endl; 932 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 933 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 934 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 935 cout << endl << "infdpdD1D2: " << infdpdD1D2 << endl; 936 cout << endl << "infdpda1a2: " << infdpda1a2 << endl; 937 if (0) 938 { 939 //cout << endl << "K: " << K.t() << endl; 940 //cout << endl << "D: " << D.t() << endl; 941 //cout << endl << "ab: " << ab.t() << endl; 942 //cout << endl << "r: " << r.t() << endl; 943 //cout << endl << "t: " << t.t() << endl; 944 cout << endl << "point3D: " << point3D << endl; 945 cout << endl << "point2D1:" << point2D1 << endl; 946 cout << endl << "point2D2:" << point2D2 << endl; 947 cout << endl << "point2D3:" << point2D3 << endl; 948 } 949 cout << "Press any key to continue" << endl; getchar(); 950 } 951 952 } 953 } 954 static void TestRTDSCM(int argc = 0, char** argv = 0) {} 955 956 public: 957 static void TestInvRTCM(int argc = 0, char** argv = 0) 958 { 959 int N = 999; 960 for (int k = 0; k < N; ++k) 961 { 962 //0.GenerateData 963 const int nDist = k % 4 == 0 ? 4 : k % 4 == 1 ? 5 : k % 4 == 2 ? 8 : 12; 964 Mat_<Vec3d> point3D(22, 1); cv::randu(point3D, -999, 999); 965 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 966 Mat_<double> D(nDist, 1); cv::randu(D, -0.2, 0.2); 967 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 968 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 969 Mat_<Vec2d> point2D; projectPoints(point3D, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, point2D); 970 971 //1.CalcGT 972 Mat_<Vec2d> point2D0; projectPoints(point3D, r, t, Mat_<double>::eye(3, 3), Mat_<double>::zeros(D.rows, 1), point2D0); 973 974 //2.CalcByOpenCV 975 Mat_<Vec2d> point2D1; undistortPoints(point2D, point2D1, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, noArray(), noArray(), TermCriteria(TermCriteria::COUNT, 8, 1e-3)); 976 977 //3.CalcByDIY 978 Mat_<Vec2d> point2D2 = reprojectPoints(point2D, K.ptr<double>(), D.ptr<double>(), 0, D.rows, 0, VisionCM::RTCM, true, 0, 0, 0, 0, TermCriteria(TermCriteria::COUNT, 8, 1e-3)); 979 980 //4.AnalyzeError 981 double infpts0pts1 = norm(point2D0, point2D1, NORM_INF); 982 double infpts0pts2 = norm(point2D0, point2D2, NORM_INF); 983 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 984 TermCriteria criteria(TermCriteria::COUNT + TermCriteria::EPS, k % 20 + 2, 1. / pow(10, k % 10 + 2)); 985 Mat_<double> P(4, 1); cv::randu(P, 111, 999); 986 Mat_<double> R(3, 3); cv::Rodrigues(Vec3d::randu(-1, 1), R); 987 Mat_<Vec2d> point2D3; undistortPoints(point2D, point2D3, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, R, Matx33d(P(0), 0, P(2), 0, P(1), P(3), 0, 0, 1), criteria); 988 Mat_<Vec2d> point2D4 = reprojectPoints(point2D, K.ptr<double>(), D.ptr<double>(), 0, D.rows, 0, VisionCM::RTCM, true, R.ptr<double>(), P.ptr<double>(), 0, 0, criteria); 989 double infpts3pts4 = norm(point2D3, point2D4, NORM_INF); 990 991 //5.PrintError 992 cout << endl << "LoopCount: " << k << endl; 993 if (/*infpts0pts1 > 1E-12 || infpts0pts2 > 1E-12 || */infpts1pts2 > 1E-12 || infpts3pts4 > 1E-12) 994 { 995 cout << endl << "infpts0pts1: " << infpts0pts1 << endl; 996 cout << endl << "infpts0pts2: " << infpts0pts2 << endl; 997 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 998 cout << endl << "infpts3pts4: " << infpts3pts4 << endl; 999 if (0) 1000 { 1001 cout << endl << "point3D: " << point3D << endl; 1002 cout << endl << "point2D0: " << point2D0 << endl; 1003 cout << endl << "point2D1: " << point2D1 << endl; 1004 cout << endl << "point2D2: " << point2D2 << endl; 1005 cout << endl << "point2D3: " << point2D3 << endl; 1006 cout << endl << "point2D4: " << point2D4 << endl; 1007 } 1008 cout << endl << "Press any key to continue" << endl; getchar(); 1009 } 1010 } 1011 } 1012 static void TestInvKBCM(int argc = 0, char** argv = 0) 1013 { 1014 int N = 999; 1015 for (int k = 0; k < N; ++k) 1016 { 1017 //0.GenerateData 1018 static const int nDist = 4; 1019 Mat_<Vec3d> point3D(22, 1); cv::randu(point3D, -999, 999); 1020 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 1021 Mat_<double> D(4, 1); cv::randu(D, -0.2, 0.2); 1022 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 1023 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 1024 Mat_<Vec2d> point2D; fisheye::projectPoints(point3D, point2D, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D); 1025 1026 //1.CalcGT 1027 Mat_<Vec2d> point2D0; fisheye::projectPoints(point3D, point2D0, r, t, Mat_<double>::eye(3, 3), Mat_<double>::zeros(D.rows, 1)); 1028 1029 //2.CalcByOpenCV 1030 Mat_<Vec2d> point2D1; fisheye::undistortPoints(point2D, point2D1, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, noArray(), noArray()); 1031 1032 //3.CalcByDIY 1033 Mat_<Vec2d> point2D2 = reprojectPoints(point2D, K.ptr<double>(), D.ptr<double>(), 0, D.rows, 0, VisionCM::KBCM, false, 0, 0, 0, 0, 1034 TermCriteria(TermCriteria::COUNT + TermCriteria::COUNT, 10, 1e-8));//criteria is fixed as (10, 1e-8) in opencv4.5 KB 1035 1036 //4.AnalyzeError 1037 double infpts0pts1 = norm(point2D0, point2D1, NORM_INF); 1038 double infpts0pts2 = norm(point2D0, point2D2, NORM_INF); 1039 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 1040 TermCriteria criteria(TermCriteria::COUNT + TermCriteria::EPS, k % 20 + 2, 1. / pow(10, k % 10 + 2)); 1041 Mat_<double> P(4, 1); cv::randu(P, 111, 999); 1042 Mat_<double> R(3, 3); cv::Rodrigues(Vec3d::randu(-1, 1), R); 1043 Mat_<Vec2d> point2D3; fisheye::undistortPoints(point2D, point2D3, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, R, Matx33d(P(0), 0, P(2), 0, P(1), P(3), 0, 0, 1)); 1044 Mat_<Vec2d> point2D4 = reprojectPoints(point2D, K.ptr<double>(), D.ptr<double>(), 0, D.rows, 0, VisionCM::KBCM, false, R.ptr<double>(), P.ptr<double>(), 0, 0, 1045 TermCriteria(TermCriteria::COUNT + TermCriteria::COUNT, 10, 1e-8));//criteria is fixed as (10, 1e-8) in opencv4.5 KB 1046 double infpts3pts4 = norm(point2D3, point2D4, NORM_INF); 1047 1048 //5.PrintError 1049 cout << endl << "LoopCount: " << k << endl; 1050 if (/*infpts0pts1 > 1E-9 || infpts0pts2 > 1E-9 ||*/ infpts1pts2 > 1E-9 || infpts3pts4 > 1E-9) 1051 { 1052 cout << endl << "infpts0pts1: " << infpts0pts1 << endl; 1053 cout << endl << "infpts0pts2: " << infpts0pts2 << endl; 1054 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 1055 cout << endl << "infpts3pts4: " << infpts3pts4 << endl; 1056 if (0) 1057 { 1058 cout << endl << "point3D: " << point3D << endl; 1059 cout << endl << "point2D0: " << point2D0 << endl; 1060 cout << endl << "point2D1: " << point2D1 << endl; 1061 cout << endl << "point2D2: " << point2D2 << endl; 1062 cout << endl << "point2D3: " << point2D3 << endl; 1063 cout << endl << "point2D4: " << point2D4 << endl; 1064 } 1065 cout << endl << "Press any key to continue" << endl; getchar(); 1066 } 1067 } 1068 } 1069 1070 static void TestInvRTEUCM(int argc = 0, char** argv = 0) {} 1071 static void TestInvRTDSCM(int argc = 0, char** argv = 0) {} 1072 1073 public: 1074 static void TestJacobianRTCM(int argc = 0, char** argv = 0) 1075 { 1076 int N = 999; 1077 for (int k = 0; k < N; ++k) 1078 { 1079 static const int nRot = 3;//fixed 1080 static const int nDist = 5;//varied 1081 static const int nModel = 0;//fixed 1082 Mat_<Vec3d> point3D(2, 1); cv::randu(point3D, -999, 999); 1083 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 1084 Mat_<double> D(nDist, 1); cv::randu(D, -1.0, 1.0); 1085 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 1086 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 1087 1088 //1.CalcByOpenCV 1089 Mat_<Vec2d> point2D1; 1090 Mat_<double> dpdKDT1; 1091 projectPoints(point3D, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, point2D1, dpdKDT1); 1092 Mat_<double> dpdr1 = dpdKDT1.colRange(0, 3); 1093 Mat_<double> dpdt1 = dpdKDT1.colRange(3, 6); 1094 Mat_<double> dpdK1 = dpdKDT1.colRange(6, 10); 1095 Mat_<double> dpdD1 = dpdKDT1.colRange(10, 10 + D.rows); 1096 1097 //2.CalcByManuMap 1098 Mat_<double> dpdr2, dpdt2, dpdK2, dpdD2; 1099 Mat_<Vec2d> point2D2 = calcJacobiansOfRTCM(point3D, r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>(), D.rows, 1100 &dpdr2, &dpdt2, &dpdK2, &dpdD2, 0, 0); 1101 1102 //2.CalcByManuBCH 1103 Mat_<double> dpdr3, dpdt3, dpdK3, dpdD3; 1104 Mat_<Vec2d> point2D3 = calcJacobiansOfRTCM(point3D, r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>(), D.rows, 1105 &dpdr3, &dpdt3, &dpdK3, &dpdD3, 0, 1); 1106 1107 //4.AnalyzeError 1108 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 1109 double infpts1pts3 = norm(point2D1, point2D3, NORM_INF); 1110 double infdpdr1r2 = norm(dpdr1, dpdr2, NORM_INF); 1111 double infdpdr1r3 = norm(dpdr1, dpdr3, NORM_INF); 1112 double infdpdr2r3 = norm(dpdr2, dpdr3, NORM_INF); 1113 double infdpdt1t2 = norm(dpdt1, dpdt2, NORM_INF); 1114 double infdpdt1t3 = norm(dpdt1, dpdt3, NORM_INF); 1115 double infdpdt2t3 = norm(dpdt2, dpdt3, NORM_INF); 1116 double infdpdK1K2 = norm(dpdK1, dpdK2, NORM_INF); 1117 double infdpdK1K3 = norm(dpdK1, dpdK3, NORM_INF); 1118 double infdpdK2K3 = norm(dpdK2, dpdK3, NORM_INF); 1119 double infdpdD1D2 = norm(dpdD1, dpdD2, NORM_INF); 1120 double infdpdD1D3 = norm(dpdD1, dpdD3, NORM_INF); 1121 double infdpdD2D3 = norm(dpdD2, dpdD3, NORM_INF); 1122 1123 //5.PrintError 1124 cout << fmt::format("LoopCount: {} ", k); 1125 if (infpts1pts2 > 1e-9 || infpts1pts3 > 1e-9 || 1126 infdpdr1r2 > 1e-9 || infdpdr1r3 > 1e-9 || infdpdr2r3 > 1e-9 || infdpdt1t2 > 1e-9 || infdpdt1t3 > 1e-9 || infdpdt2t3 > 1e-9 || 1127 infdpdK1K2 > 1e-9 || infdpdK1K3 > 1e-9 || infdpdK2K3 > 1e-9 || infdpdD1D2 > 1e-9 || infdpdD1D3 > 1e-9 || infdpdD2D3 > 1e-9) 1128 { 1129 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 1130 cout << endl << "infpts1pts3: " << infpts1pts3 << endl; 1131 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 1132 cout << endl << "infdpdr1r3: " << infdpdr1r3 << endl; 1133 cout << endl << "infdpdr2r3: " << infdpdr2r3 << endl; 1134 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 1135 cout << endl << "infdpdt1t3: " << infdpdt1t3 << endl; 1136 cout << endl << "infdpdt2t3: " << infdpdt2t3 << endl; 1137 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 1138 cout << endl << "infdpdK1K3: " << infdpdK1K3 << endl; 1139 cout << endl << "infdpdK2K3: " << infdpdK2K3 << endl; 1140 cout << endl << "infdpdD1D2: " << infdpdD1D2 << endl; 1141 cout << endl << "infdpdD1D3: " << infdpdD1D3 << endl; 1142 cout << endl << "infdpdD2D3: " << infdpdD2D3 << endl; 1143 if (0) 1144 { 1145 cout << "point2D1: " << point2D1 << endl; 1146 cout << "point2D2: " << point2D2 << endl; 1147 cout << "point2D3: " << point2D3 << endl; 1148 } 1149 cout << "Press any key to continue" << endl; getchar(); 1150 } 1151 } 1152 } 1153 1154 public: 1155 static void TestRectBino(int argc = 0, char** argv = 0) 1156 { 1157 for (int k = 0; k < 999; ++k) 1158 { 1159 //0.GenerateData 1160 Mat_<double> R(3, 3); ceres::EulerAnglesToRotationMatrix(Matx31d::randu(-15, 15).val, 0, R.ptr<double>()); 1161 Mat_<double> t({ 3, 1 }, { -Matx21d::randu(10, 20)(0), Matx21d::randu(-5, 5)(0), Matx21d::randu(-5, 5)(0) });//left camera x must be negative in right camera system which echoes opencv 1162 Matx31d P = P.randu(-999, 999); if ((P(2) < 0)) P(2) = -P(2);//P from phyCam1 to virCam1 == P from phyCam1 to phyCam2 to virCam2. virCam1 == virCam2 when no translation 1163 1164 //1.CalcByOpenCVPin 1165 Matx33d R11, R12; 1166 stereoRectify(Matx33d::eye(), Mat(), Matx33d::eye(), Mat(), Size(480, 640), R, t, R11, R12, Matx34d(), Matx34d(), noArray()); 1167 double infP11P12 = cv::norm(R11 * P, R12 * R * P, NORM_INF); 1168 1169 //2.CalcByOpenCVKB 1170 Matx33d R21, R22; 1171 fisheye::stereoRectify(Matx33d::eye(), Matx41d::zeros(), Matx33d::eye(), Matx41d::zeros(), Size(480, 640), R, t, R21, R22, Matx34d(), Matx34d(), noArray(), 0); 1172 double infP21P22 = cv::norm(R21 * P, R22 * R * P, NORM_INF); 1173 1174 //3.CalcByDIYPin 1175 Matx33d R31, R32; 1176 rectBino(R31.val, R32.val, R.ptr<double>(), t.ptr<double>()); 1177 double infP31P32 = cv::norm(R31 * P, R32 * R * P, NORM_INF); 1178 1179 //4.CalcByDIYMUCM 1180 Matx33d R41, R42; 1181 rectBino2(R41.val, R42.val, R.ptr<double>(), t.ptr<double>()); 1182 double infP41P42 = cv::norm(R41 * P, R42 * R * P, NORM_INF); 1183 1184 //5.CalcByOpenCVMUCM 1185 Matx33d R51, R52; 1186 omnidir::stereoRectify(R, t, R51, R52); 1187 double infP51P52 = cv::norm(R51 * P, R52 * R * P, NORM_INF); 1188 string ss = " It is OK to rectify the last line {_R2 = R21 * _R1} in omnidir::stereoRectify {_R2 = _R1 * R21}"; 1189 1190 //6.AnalyzeError 1191 double infR11R21 = norm(R11, R21, NORM_INF); 1192 double infR11R31 = norm(R11, R31, NORM_INF); 1193 double infR41R51 = norm(R41, R51, NORM_INF); 1194 double infR12R22 = norm(R12, R22, NORM_INF); 1195 double infR12R32 = norm(R12, R32, NORM_INF); 1196 double infR42R52 = norm(R42, R52, NORM_INF); 1197 1198 //7.PrintError 1199 cout << endl << "LoopCount: " << k << ss << endl; 1200 if (infP11P12 > 1E-12 || infP21P22 > 1E-12 || infP31P32 > 1E-12 || infP41P42 > 1E-12 || //infP51P52 > 1E-12 || 1201 infR11R21 > 1E-12 || infR11R31 > 1E-12 || infR12R22 > 1E-12 || infR12R32 > 1E-12 /*|| infR41R51 > 1E-12 || infR42R52 > 1E-12*/) 1202 { 1203 cout << endl << "infP11P12: " << infP11P12 << endl; 1204 cout << endl << "infP21P22: " << infP21P22 << endl; 1205 cout << endl << "infP31P32: " << infP31P32 << endl; 1206 cout << endl << "infP41P42: " << infP41P42 << endl; 1207 cout << endl << "infP51P52: " << infP51P52 << endl; 1208 cout << endl << "infR11R21: " << infR11R21 << endl; 1209 cout << endl << "infR11R31: " << infR11R31 << endl; 1210 cout << endl << "infR11R51: " << infR41R51 << endl; 1211 cout << endl << "infR12R22: " << infR12R22 << endl; 1212 cout << endl << "infR12R32: " << infR12R32 << endl; 1213 cout << endl << "infR12R52: " << infR42R52 << endl; 1214 if (1) 1215 { 1216 cout << endl << "R: " << R << endl << endl; 1217 cout << endl << "t: " << t << endl << endl; 1218 cout << endl << "P: " << P << endl << endl; 1219 //cout << endl << "R11: " << R11 << endl; 1220 //cout << endl << "R12: " << R12 << endl; 1221 //cout << endl << "R21: " << R21 << endl; 1222 //cout << endl << "R22: " << R22 << endl; 1223 //cout << endl << "R31: " << R31 << endl; 1224 //cout << endl << "R32: " << R32 << endl; 1225 //cout << endl << "R41: " << R41 << endl; 1226 //cout << endl << "R42: " << R42 << endl; 1227 //cout << endl << "R51: " << R51 << endl; 1228 //cout << endl << "R52: " << R52 << endl; 1229 } 1230 cout << endl << "Press any key to continue" << endl; getchar(); 1231 } 1232 } 1233 } 1234 }; 1235 1236 #if 0 1237 int main(int argc, char** argv) { ModelCamera::TestRectBino(argc, argv); return 0; } 1238 int main1(int argc, char** argv) { ModelCamera::TestRTCM(argc, argv); return 0; } 1239 int main2(int argc, char** argv) { ModelCamera::TestKBCM(argc, argv); return 0; } 1240 int main3(int argc, char** argv) { ModelCamera::TestRTEUCM(argc, argv); return 0; } 1241 int main4(int argc, char** argv) { ModelCamera::TestRTDSCM(argc, argv); return 0; } 1242 int main5(int argc, char** argv) { ModelCamera::TestInvRTCM(argc, argv); return 0; } 1243 int main6(int argc, char** argv) { ModelCamera::TestInvKBCM(argc, argv); return 0; } 1244 int main7(int argc, char** argv) { ModelCamera::TestInvRTEUCM(argc, argv); return 0; } 1245 int main8(int argc, char** argv) { ModelCamera::TestInvRTDSCM(argc, argv); return 0; } 1246 int main11(int argc, char** argv) { ModelCamera::TestJacobianRTCM(argc, argv); return 0; } 1247 int main21(int argc, char** argv) { ModelCamera::TestRectBino(argc, argv); return 0; } 1248 #endif