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成像模型(编码为MUCM),解决UCM建模鱼眼镜头差的问题。
优化工程:于2013年,Heng和Li及Pollefeys。
完善工程:于2016年,Khomutenko和Garcia及Martinet提出EUCM成像模型,解决了UCM径向畸变和建模鱼眼镜头差的问题及MUCM非闭环解问题。
完善工程:于2018年,Usenko和Demmel及Cremers提出DSCM成像模型,相对于EUCM、MUCM、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、MUCM。其中RTCM、KBCM认可度最高,实现在Main模块,其次是MUCM,实现在Extra模块。
通过查看源码,MUCM实现较粗糙,如calibrateCamera和stereoCalibrate对CALIB_USE_GUESS都无效,查看源码发现initializeStereoCalibration忽视了此值,直接重新进行单目标定。然而,即使initializeStereoCalibration不忽视,你会发现单目标定calibrate也忽视了此值。
3.本文要点说明
本文基于OpenCV、Ceres、Spdlog来阐述以下数学模型:
(1)成像模型:RTCM、KBCM、MUCM、EUCM、DSCM,含所有模型的正向投影、逆向投影及偏导矩阵。
(2)模型转换:对MUCM/EUCM/DSCM系列的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+BasaltCM、CalibSolid+CamObservation、TestRTCM、TestKBCM、TestMUCM、TestEUCM、TestDSCM
(2)reproject、TestInvRTCM、TestInvKBCM、TestInvMUCM、TestInvEUCM、TestInvDSCM
(3)derivRTCM、、、、TestJacRTCM
(4)rectBino、rectTrino、TestRectBino、testRectTrino
测试来源:模型RTCM&MUCM对比OpenCV,模型KBCM&EUCM&DSCM对比basalt,偏导对比ceres。
遗留问题:逆投影和外参偏导数的完善和验证。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #ifndef __ModelCamera_h__ 2 #define __ModelCamera_h__ 3 4 #include <cscv/ModelRotation.h> 5 #include <opencv2/ccalib/omnidir.hpp> 6 #include <basalt/camera/unified_camera.hpp> 7 #include <basalt/camera/extended_camera.hpp> 8 #include <basalt/camera/double_sphere_camera.hpp> 9 #include <basalt/camera/kannala_brandt_camera4.hpp> 10 11 class ModelCamera 12 { 13 public: 14 struct VisionCM 15 { 16 enum { RTCM, KBCM, MUCM, EUCM, DSCM };//(1)EUCM and DSCM can use rt distortion (2)MUCM can be replaced with EUCM with nDist=4 and b=1 17 int nRot;//3 4 9 and for rvec quat rmat respectively 18 int nDist;//0 4 5 and echo AutoDiffCostFunction template parameter number except for 0. 0 uses overloaded operator. 19 int nModel;//0 1 2 and echo AutoDiffCostFunction template parameter number except for 0. 0 uses overloaded operator. 20 int camModel;//connect model with distortion 21 bool simPin;//valid only for XUCM 22 Mat_<Vec3d> point3D; 23 void inline checkInput() 24 { 25 if (camModel == RTCM && nModel != 0) assert(0); 26 if (camModel == KBCM && nModel != 0) assert(0); 27 if (camModel == MUCM && nModel != 1) assert(0); 28 if (camModel == EUCM && nModel != 2) assert(0); 29 if (camModel == DSCM && nModel != 2) assert(0); 30 if (nRot != 3 && nRot != 4 && nRot != 9) assert(0); 31 if (nDist != 0 && nDist != 4 && nDist != 5) assert(0); 32 if (nModel != 0 && nModel != 1 && nModel != 2) assert(0); 33 } 34 35 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); } 36 37 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); } 38 39 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 40 41 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 42 43 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 44 { 45 //1.Projection params 46 tp fx = K[0]; 47 tp fy = K[1]; 48 tp cx = K[2]; 49 tp cy = K[3]; 50 tp a; if (nModel > 0) a = ab[0]; 51 tp b; if (nModel > 1) b = ab[1]; 52 53 //2.Distortion params 54 tp k1, k2, p1, p2, k3; 55 if (nDist == 4) { k1 = D[0]; k2 = D[1]; p1 = D[2]; p2 = D[3]; } //k1=k1 k2=k2 p1=k3 p2=k4 k5=k3 for KBCM 56 else if (nDist == 5) { k1 = D[0]; k2 = D[1]; p1 = D[2]; p2 = D[3]; k3 = D[4];} 57 else if (nDist != 0) spdlog::critical("Invalid distortion configuration"); 58 59 //3.Rotation params 60 tp R[9]; if (nRot == 9) memcpy(R, rot, sizeof(R)); 61 else if (nRot == 4) ceres::QuaternionToRotation(rot, R); 62 else if (nRot == 3) ceres::AngleAxisToRotationMatrix(rot, ceres::RowMajorAdapter3x3(R)); 63 else spdlog::critical("Invalid rotation configuration"); 64 65 //4.Projection actions 66 Vec<tp, 2>* data2D = (Vec<tp, 2>*)data2D0; 67 const Vec3d* data3D = point3D.ptr<Vec3d>(); 68 for (int k = 0; k < point3D.rows; ++k) 69 { 70 //4.1 WorldPoint 71 double X = data3D[k][0]; 72 double Y = data3D[k][1]; 73 double Z = data3D[k][2]; 74 75 //4.2 CameraPoint 76 tp x = R[0] * X + R[1] * Y + R[2] * Z + t[0]; 77 tp y = R[3] * X + R[4] * Y + R[5] * Z + t[1]; 78 tp z = R[6] * X + R[7] * Y + R[8] * Z + t[2]; 79 80 //4.3 StandardPhysicsPoint 81 tp iz; if (camModel == RTCM) iz = tp(1) / z; 82 else if (camModel == KBCM) 83 { 84 if (z == tp(0)) { data2D[k][0] = data2D[k][1] = tp(0); continue; } 85 iz = tp(1) / z; 86 } 87 else if (camModel == MUCM) 88 { 89 tp d = sqrt(x * x + y * y + z * z); 90 iz = simPin ? tp(1) / (a * d + (tp(1) - a) * z) : tp(1) / (a * d + z); 91 //tp A = simPin ? a / (tp(1) - a) : a; 92 //tp flag = A <= tp(1) ? (A * d + z) : (d + A * z); 93 //if (flag <= tp(0)) { data2D[k][0] = data2D[k][1] = tp(0); continue; } 94 } 95 else if (camModel == EUCM) 96 { 97 tp d = sqrt(b * (x * x + y * y) + z * z); 98 iz = simPin ? tp(1) / (a * d + (tp(1) - a) * z) : tp(1) / (a * d + z); 99 //tp A = simPin ? a / (tp(1) - a) : a; 100 //tp flag = A <= tp(1) ? (A * d + z) : (d + A * z); 101 //if (flag <= tp(0)) { data2D[k][0] = data2D[k][1] = tp(0); continue; } 102 } 103 else if (camModel == DSCM) 104 { 105 tp d1 = sqrt(x * x + y * y + z * z); 106 tp d2 = sqrt(x * x + y * y + (b * d1 + z) * (b * d1 + z)); 107 iz = simPin ? tp(1) / (a * d2 + (tp(1) - a) * (b * d1 + z)) : tp(1) / (a * d2 + (b * d1 + z)); 108 tp A = simPin ? a / (tp(1) - a) : a; 109 tp flag = A <= tp(1) ? z * sqrt(tp(2) * A * b + b * b + tp(1)) + d1 * (A + b) : z * sqrt(tp(2) * A * b + A * A * (b * b + tp(1))) + d1 * (tp(1) + A * b); 110 if (flag <= tp(0)) { data2D[k][0] = data2D[k][1] = tp(0); continue; } 111 } 112 else spdlog::critical("Invalid model configuration"); 113 tp xn = x * iz; 114 tp yn = y * iz; 115 116 //4.4 DistortedPhysicsPoint 117 tp xd, yd; 118 if (camModel == KBCM)//KBDistortion 119 { 120 tp sgz = z > tp(0) ? tp(1) : tp(-1); 121 if (nDist == 0) 122 { 123 tp rn = sqrt(xn * xn + yn * yn); 124 tp irn = tp(1) / rn; 125 tp thetan = atan2(rn, sgz); 126 xd = xn * irn * thetan * sgz; 127 yd = yn * irn * thetan * sgz; 128 } 129 else 130 { 131 tp rn = sqrt(xn * xn + yn * yn); 132 tp irn = tp(1) / rn; 133 tp thetan = atan2(rn, sgz); 134 tp thetan2 = thetan * thetan; 135 tp thetan4 = thetan2 * thetan2; 136 tp thetan6 = thetan2 * thetan4; 137 tp thetan8 = thetan2 * thetan6; 138 tp thetad = thetan * (tp(1) + k1 * thetan2 + k2 * thetan4 + p1 * thetan6 + p2 * thetan8 + (nDist == 4 ? tp(0) : k3 * thetan2 * thetan8)); //p1=k3 p2=k4 k3=k5 here 139 xd = xn * irn * thetad * sgz; 140 yd = yn * irn * thetad * sgz; 141 } 142 } 143 else //RTDistortion 144 { 145 if (nDist == 0) { xd = xn; yd = yn; } 146 else 147 { 148 tp xn2 = xn * xn; 149 tp yn2 = yn * yn; 150 tp rn2 = xn2 + yn2; 151 tp rn4 = rn2 * rn2; 152 tp xnyn = tp(2) * xn * yn; 153 tp r2xn2 = rn2 + tp(2) * xn2; 154 tp r2yn2 = rn2 + tp(2) * yn2; 155 tp an = tp(1) + k1 * rn2 + k2 * rn4 + (nDist == 4 ? tp(0) : k3 * rn2 * rn4); 156 xd = xn * an + p1 * xnyn + p2 * r2xn2; 157 yd = yn * an + p2 * xnyn + p1 * r2yn2; 158 } 159 } 160 161 //4.5 DistortedPixelPoint 162 data2D[k][0] = xd * fx + cx; 163 data2D[k][1] = yd * fy + cy; 164 } 165 return true; 166 } 167 168 static double Mei2Pin(double a, double* fx = 0, double* fy = 0, double* s = 0, double* D = 0, int nDist = 4, bool forward = true) 169 { 170 a = forward ? a / (1 + a) : a / (1 - a); 171 double ratio1 = forward ? 1 - a : 1 + a; 172 double ratio2 = ratio1 * ratio1; 173 double ratio3 = ratio1 * ratio2; 174 double ratio4 = ratio1 * ratio3; 175 double ratio6 = ratio3 * ratio3; 176 if (fx) fx[0] *= ratio1; 177 if (fy) fy[0] *= ratio1; 178 if (s) s[0] = ratio1 * s[0]; 179 if (nDist > 0) D[0] *= ratio2;//k1 180 if (nDist > 1) D[1] *= ratio4;//k2 181 if (nDist > 2) D[2] *= ratio1;//p1 182 if (nDist > 3) D[3] *= ratio1;//p2 183 if (nDist > 4) D[4] *= ratio6;//k3 184 if (nDist > 5) D[5] *= ratio2;//k4 185 if (nDist > 6) D[6] *= ratio4;//k5 186 if (nDist > 7) D[7] *= ratio6;//k6 187 if (nDist > 8) D[8] *= ratio1;//s1 188 if (nDist > 9) D[9] *= ratio3;//s2 189 if (nDist > 10) D[10] *= ratio1;//s3 190 if (nDist > 11) D[11] *= ratio3;//s4 191 return a; 192 } 193 }; 194 195 struct BasaltCM : VisionCM 196 { 197 BasaltCM(float* data3D, int nPoint, int nRot0 = 9, int nDist0 = 4, int nModel0 = 1, int camModel0 = RTCM, bool simPin0 = false) : VisionCM(data3D, nPoint, nRot0, nDist0, nModel0, camModel0, simPin0) { } 198 199 BasaltCM(double* data3D, int nPoint, int nRot0 = 9, int nDist0 = 4, int nModel0 = 1, int camModel0 = RTCM, bool simPin0 = false) : VisionCM(data3D, nPoint, nRot0, nDist0, nModel0, camModel0, simPin0) { } 200 201 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 202 203 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 204 205 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 206 { 207 //1.Projection params 208 tp fx = K[0]; 209 tp fy = K[1]; 210 tp cx = K[2]; 211 tp cy = K[3]; 212 tp a; if (nModel > 0) a = ab[0]; 213 tp b; if (nModel > 1) b = ab[1]; 214 if(!simPin) assert(0); 215 216 //2.Distortion params 217 tp k1, k2, p1, p2, k3; 218 if (nDist == 4) { k1 = D[0]; k2 = D[1]; p1 = D[2]; p2 = D[3]; } //k1=k1 k2=k2 p1=k3 p2=k4 k5=k3 for KBCM 219 else if (nDist == 5) { k1 = D[0]; k2 = D[1]; p1 = D[2]; p2 = D[3]; k3 = D[4]; } 220 else if (nDist != 0) spdlog::critical("Invalid distortion configuration"); 221 222 //3.Rotation params 223 tp R[9]; if (nRot == 9) memcpy(R, rot, sizeof(R)); 224 else if (nRot == 4) ceres::QuaternionToRotation(rot, R); 225 else if (nRot == 3) ceres::AngleAxisToRotationMatrix(rot, ceres::RowMajorAdapter3x3(R)); 226 else spdlog::critical("Invalid rotation configuration"); 227 228 //4.Projection actions 229 Vec<tp, 2>* data2D = (Vec<tp, 2>*)data2D0; 230 const Vec3d* data3D = point3D.ptr<Vec3d>(); 231 shared_ptr<basalt::KannalaBrandtCamera4<tp>> kbcm; 232 shared_ptr<basalt::ExtendedUnifiedCamera<tp>> eucm; 233 shared_ptr<basalt::DoubleSphereCamera<tp>> dscm; 234 for (int k = 0; k < point3D.rows; ++k) 235 { 236 //4.1 WorldPoint 237 double X = data3D[k][0]; 238 double Y = data3D[k][1]; 239 double Z = data3D[k][2]; 240 241 //4.2 CameraPoint 242 tp x = R[0] * X + R[1] * Y + R[2] * Z + t[0]; 243 tp y = R[3] * X + R[4] * Y + R[5] * Z + t[1]; 244 tp z = R[6] * X + R[7] * Y + R[8] * Z + t[2]; 245 246 //4.3 Projection 247 if (camModel == KBCM) 248 { 249 if (k == 0) 250 { 251 basalt::KannalaBrandtCamera4<tp>::VecN camParam; camParam << fx, fy, cx, cy, k1, k2, p1, p2; 252 kbcm.reset(new basalt::KannalaBrandtCamera4<tp>(camParam)); 253 } 254 basalt::KannalaBrandtCamera4<tp>::Vec4 p3D(x, y, z, tp(1)); 255 basalt::KannalaBrandtCamera4<tp>::Vec2 p2D; 256 kbcm->project(p3D, p2D); 257 data2D[k][0] = p2D[0]; 258 data2D[k][1] = p2D[1]; 259 } 260 else if(camModel == EUCM) 261 { 262 if (k == 0) 263 { 264 basalt::ExtendedUnifiedCamera<tp>::VecN camParam; camParam << fx, fy, cx, cy, a, b; 265 eucm.reset(new basalt::ExtendedUnifiedCamera<tp>(camParam)); 266 } 267 basalt::ExtendedUnifiedCamera<tp>::Vec4 p3D(x, y, z, tp(1)); 268 basalt::ExtendedUnifiedCamera<tp>::Vec2 p2D; 269 eucm->project(p3D, p2D); 270 data2D[k][0] = p2D[0]; 271 data2D[k][1] = p2D[1]; 272 } 273 else if(camModel == DSCM) 274 { 275 if (k == 0) 276 { 277 basalt::DoubleSphereCamera<tp>::VecN camParam; camParam << fx, fy, cx, cy, a, b; 278 dscm.reset(new basalt::DoubleSphereCamera<tp>(camParam)); 279 } 280 basalt::DoubleSphereCamera<tp>::Vec4 p3D(x, y, z, tp(1)); 281 basalt::DoubleSphereCamera<tp>::Vec2 p2D; 282 dscm->project(p3D, p2D); 283 data2D[k][0] = p2D[0]; 284 data2D[k][1] = p2D[1]; 285 } 286 } 287 return true; 288 } 289 }; 290 291 struct CamSolid 292 { 293 double data[21] = { 0 }; 294 int& nDist = ((int*)data)[0];//same as VisionCM 295 int& nModel = ((int*)data)[1];//same as VisionCM 296 int& camModel = ((int*)data)[2];//same as VisionCM 297 int& simPin = ((int*)data)[3];//same as VisionCM 298 int& kbcmPi = ((int*)data)[4];//same as VisionCM 299 int& reserve = ((int*)data)[5]; 300 double* K = data + 3;//not use skew param 301 double* D = data + 7;//new max memory 302 double* ab = data + 19;//new max memory 303 CamSolid() {} 304 CamSolid(const CamSolid& other) { memcpy(data, other.data, sizeof(data)); } 305 CamSolid& operator=(const CamSolid& other) { memcpy(data, other.data, sizeof(data)); return *this; } 306 double limitFOV() 307 { 308 double A = simPin ? ab[0] / (1 - ab[0]) : ab[0]; 309 if (camModel == VisionCM::RTCM) return 180; 310 else if (camModel == VisionCM::KBCM) return 360; 311 else if (camModel == VisionCM::MUCM) return 2 * acos(-1 / A) * rad2deg; 312 else if (camModel == VisionCM::EUCM) return 2 * atan2(sqrt(A * A - 1), -sqrt(ab[1])) * rad2deg; 313 else if (camModel == VisionCM::DSCM) return 2 * acos(-1 / A) * rad2deg; 314 return 0; 315 } 316 Vec6d idealFOV(int rows, int cols) 317 { 318 double hfov1 = 0, hfov2 = 0, vfov1 = 0, vfov2 = 0; 319 if (camModel == VisionCM::RTCM)//geometric inference 320 { 321 hfov1 = atan(K[2] / K[0]); 322 hfov2 = atan((cols - K[2]) / K[0]); 323 vfov1 = atan(K[3] / K[1]); 324 vfov2 = atan((rows - K[3]) / K[1]); 325 } 326 else if (camModel == VisionCM::KBCM)//geometric inference 327 { 328 hfov1 = K[2] / K[0]; 329 hfov2 = (cols - K[2]) / K[0]; 330 vfov1 = K[3] / K[1]; 331 vfov2 = (rows - K[3]) / K[1]; 332 } 333 else if (camModel == VisionCM::MUCM || camModel == VisionCM::EUCM)//geometric inference 334 { 335 auto CalcHalfFOV = [](double F, double c, double A, double b)->double 336 { 337 double B = A * c / F; 338 double Y = A * (b * B * B - sqrt(A * A - b * A * A * B * B + b * B * B)); 339 double X = B * (A * A + sqrt(A * A - b * A * A * B * B + b * B * B)); 340 if (0) 341 { 342 Y = b * A * A * c * c - F * sqrt(A * A * F * F - b * A * A * A * A * c * c + b * A * A * c * c); 343 X = A * A * F * c + c * sqrt(A * A * F * F - b * A * A * A * A * c * c + b * A * A * c * c); 344 } 345 return atan2(1, -Y / X); 346 }; 347 auto CalcHalfFOV2 = [](double F, double c, double A, double b)->double//intersection angle formula 348 { 349 double ro = c / F; 350 Vec3d m(0, 0, 1 / (1 + A)); 351 Vec3d n(0, ro, (1 - A * A * b * ro * ro) / (1 + A * sqrt(1 + (1 - A * A) * b * ro * ro))); 352 double theta = acos(m.dot(n) / sqrt(m.dot(m)) / sqrt(n.dot(n))); 353 return theta; 354 }; 355 if (0) 356 { 357 vector<double> As(99), Fs(99), cs(99), bs(99); cv::randu(As, 0.1, 1.9); 358 cv::randu(Fs, 800, 1200); cv::randu(cs, 400, 600); cv::randu(bs, 0.1, 1.9); 359 for (int k = 0; k < As.size(); ++k) 360 { 361 double ang1 = CalcHalfFOV(Fs[k], cs[k], As[k], bs[k]) * rad2deg; 362 double ang2 = CalcHalfFOV2(Fs[k], cs[k], As[k], bs[k]) * rad2deg; 363 cout << endl << fmt::format("A1-A2: {} - {} = {:.6f}", ang1, ang2, ang1 - ang2) << endl; 364 } 365 } 366 double a = ab[0]; 367 double b = camModel == VisionCM::MUCM ? 1 : ab[1]; 368 hfov1 = CalcHalfFOV(simPin ? K[0] / (1 - a) : K[0], K[2], simPin ? a / (1 - a) : a, b); 369 hfov2 = CalcHalfFOV(simPin ? K[0] / (1 - a) : K[0], cols - K[2], simPin ? a / (1 - a) : a, b); 370 vfov1 = CalcHalfFOV(simPin ? K[1] / (1 - a) : K[1], K[3], simPin ? a / (1 - a) : a, b); 371 vfov2 = CalcHalfFOV(simPin ? K[1] / (1 - a) : K[1], rows - K[3], simPin ? a / (1 - a) : a, b); 372 } 373 else if (camModel == VisionCM::DSCM)//intersection angle formula 374 { 375 auto CalcHalfFOV = [](double F, double c, double A, double b)->double 376 { 377 double ro = c / F; 378 double zo = (1 - A * A * ro * ro) / (1 + A * sqrt(1 + (1 - A * A) * ro * ro)); 379 double wo = (b * zo + sqrt(zo * zo + (1 - b * b) * ro * ro)) / (zo * zo + ro * ro); 380 Vec3d m(0, 0, 1); 381 Vec3d n(0, wo * ro, wo * zo - b); 382 double theta = acos(m.dot(n) / sqrt(m.dot(m)) / sqrt(n.dot(n))); 383 return theta; 384 }; 385 hfov1 = CalcHalfFOV(simPin ? K[0] / (1 - ab[0]) : K[0], K[2], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 386 hfov2 = CalcHalfFOV(simPin ? K[0] / (1 - ab[0]) : K[0], cols - K[2], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 387 vfov1 = CalcHalfFOV(simPin ? K[1] / (1 - ab[0]) : K[1], K[3], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 388 vfov2 = CalcHalfFOV(simPin ? K[1] / (1 - ab[0]) : K[1], rows - K[3], simPin ? ab[0] / (1 - ab[0]) : ab[0], ab[1]); 389 } 390 return Vec6d(hfov1, hfov2, hfov1 + hfov2, vfov1, vfov2, vfov1 + vfov2) * (180 / 3.1415926535897932384626433832795); 391 } 392 string print(int rows = 0, int cols = 0) 393 { 394 string str; 395 str += fmt::format("M: [ {}, {}, {}, {} ]\n", nDist, nModel, camModel, simPin); 396 str += fmt::format("K: [ {}, {}, {}, {} ]\n", K[0], K[1], K[2], K[3]); 397 str += fmt::format("D: [ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} ]\n", D[0], D[1], D[2], D[3], D[4], D[5], D[6], D[7], D[8], D[9], D[10], D[11]); 398 str += fmt::format("ab: [ {}, {} ]\n", ab[0], ab[1]); 399 Vec6d fov; if (rows != 0 && cols != 0) fov = idealFOV(rows, cols); 400 str += fmt::format("FOV: [ {}, {}, {}, {}, {}, {}, {} ]\n", fov[0], fov[1], fov[2], fov[3], fov[4], fov[5], limitFOV()); 401 return str; 402 } 403 //inline double ellip2DLen(double r1, double r2) { return r1 > r2 ? 2 * pi * r2 + 4 * (r1 - r2) : 2 * pi * r1 + 4 * (r2 - r1); } 404 //inline double ellip2DArea(double r1, double r2) { return 4 * pi * r1 * r2; } 405 //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); } 406 //inline double ellip3DVolume(double r1, double r2, double r3) { return 4. / 3. * pi * r1 * r2 * r3; } 407 }; 408 409 using SolidPose = ModelRotation::SolidPose; 410 411 struct CamObservation 412 { 413 vector<SolidPose> poses; 414 vector<vector<Point3f>> point3D; 415 vector<vector<Point2f>> point2D; 416 vector<bool> oksView;//can be used for calibration 417 vector<vector<uint>> ids3D; //can be used for location and calibration 418 void clear() { poses.clear(); point3D.clear(); point2D.clear(); oksView.clear(); ids3D.clear(); } 419 }; 420 421 public: 422 static Mat reproject(Mat_<Vec2d> point2D, double* K, double* D, double* ab, int nDist, int nModel, int camModel,//Only support !simPin for UCMSerials and also just verify my formula with DSCMPapers' 423 double* R = 0, double* P = 0, TermCriteria criteria = TermCriteria(TermCriteria::COUNT, 5, 0.01)) //criteria is fixed as (10, 1e-8) in opencv4.5 KBCM 424 { 425 //0.Supported models 426 if (nDist != 0 && nDist != 4 && nDist != 5) assert(0); 427 if (nModel != 0 && nModel != 1 && nModel != 2) assert(0); 428 429 //1.Projection params 430 double fx = K[0], ifx = 1. / fx; 431 double fy = K[1], ify = 1. / fy; 432 double cx = K[2], icx = -ifx * cx; 433 double cy = K[3], icy = -ify * cy; 434 double a = 0; if (nModel > 0) a = ab[0]; 435 double b = 1; if (nModel > 1) b = ab[1]; 436 437 //2.Distortion params 438 double k1 = 0, k2 = 0, p1 = 0, p2 = 0, k3 = 0; 439 if (nDist == 4) { k1 = D[0]; k2 = D[1]; p1 = D[2]; p2 = D[3]; } 440 else if(nDist == 5) { k1 = D[0]; k2 = D[1]; p1 = D[2]; p2 = D[3]; k3 = D[4]; } 441 else spdlog::critical("Invalid distortion configuration"); 442 443 //3.Reprojection matrix 444 Matx33d A = A.eye(); 445 if (P != 0) A = A * Matx33d(P[0], 0, P[2], 0, P[1], P[3], 0, 0, 1); 446 if (R != 0) A = A * Matx33d(R); 447 448 //4.Reprojection actions 449 Mat unPoint(point2D.rows, 1, camModel == VisionCM::RTCM || camModel == VisionCM::KBCM ? CV_64FC2 : CV_64FC3); 450 Vec2d* data2D = unPoint.ptr<Vec2d>(); 451 Vec3d* data3D = unPoint.ptr<Vec3d>(); 452 for (int k = 0; k < point2D.rows; ++k) 453 { 454 //4.1 DistortedPixelPoint-->DistortedPhysicsPoint 455 double u = point2D(k)[0], xd = (u - cx) * ifx; 456 double v = point2D(k)[1], yd = (v - cy) * ify; 457 458 //4.2 DistortedPhysics-->StandardPhysicsPoint 459 double xn = xd, yn = yd; 460 if (camModel == VisionCM::KBCM) 461 { 462 if (nDist == 0) 463 { 464 double thetan = sqrt(xn * xn + yn * yn); 465 double scaled = tan(thetan) / thetan; 466 xn *= scaled; 467 yn *= scaled; 468 } 469 else 470 { 471 double thetad = sqrt(xd * xd + yd * yd), halfPI = 3.1415926535897932384626433832795 / 2.; 472 thetad = thetad > halfPI ? halfPI : thetad < -halfPI ? -halfPI : thetad;//same as opencv which only focuses on 0~180 fov 473 double thetan = thetad;//it not conflict with projection using thetan 474 int i = 0; 475 for (; i < criteria.maxCount; ++i) 476 { 477 double thetan2 = thetan * thetan, k1thetan2 = k1 * thetan2; 478 double thetan4 = thetan2 * thetan2, k2thetan4 = k2 * thetan4; 479 double thetan6 = thetan2 * thetan4, k3thetan6 = p1 * thetan6; 480 double thetan8 = thetan2 * thetan6, k4thetan8 = p2 * thetan8; //p1=k3 p2=k4 here 481 double deltaTheta = (thetan * (1 + k1thetan2 + k2thetan4 + k3thetan6 + k4thetan8) - thetad) / (1 + 3 * k1thetan2 + 5 * k2thetan4 + 7 * k3thetan6 + 9 * k4thetan8); 482 thetan -= deltaTheta; 483 if ((criteria.type & TermCriteria::EPS) && abs(deltaTheta) < criteria.epsilon) break; 484 } 485 if (i >= criteria.maxCount || thetad * thetan < 0) //same as opencv4.5 for consistence test and can be removed 486 { 487 data2D[k] = Vec2d(-1000000.0, -1000000.0); 488 continue; 489 } 490 double scaled = tan(thetan) / thetad; 491 xn *= scaled; 492 yn *= scaled; 493 } 494 } 495 else//RTCM&MUCM&EUCM&DSCM 496 { 497 if (nDist == 0) {/*nothing to do*/ } 498 else 499 { 500 for (int i = 0; i < criteria.maxCount; ++i) 501 { 502 double rn2 = xn * xn + yn * yn;//it not conflict with projection using rn2 anbn deltaX deltaY 503 double anbn = 1 / (1 + ((k3 * rn2 + k2) * rn2 + k1) * rn2); 504 if (anbn < 0) { xn = (u - cx) * ifx; yn = (v - cy) * ify; break; } // test: undistortPoints.regression_14583 505 double deltaX = 2 * p1 * xn * yn + p2 * (rn2 + 2 * xn * xn); 506 double deltaY = p1 * (rn2 + 2 * yn * yn) + 2 * p2 * xn * yn; 507 xn = (xd - deltaX) * anbn; 508 yn = (yd - deltaY) * anbn; 509 510 if (criteria.type & TermCriteria::EPS) 511 { 512 double xn2 = xn * xn; 513 double yn2 = yn * yn; 514 double rn2 = xn2 + yn2; 515 double rn4 = rn2 * rn2; 516 double xnyn = 2 * xn * yn; 517 double r2xn2 = rn2 + 2 * xn2; 518 double r2yn2 = rn2 + 2 * yn2; 519 double an = 1 + k1 * rn2 + k2 * rn4 + k3 * rn4 * rn2; 520 double xdd = xn * an + p1 * xnyn + p2 * r2xn2; 521 double ydd = yn * an + p1 * r2yn2 + p2 * xnyn; 522 double uerr = xdd * fx + cx - u; 523 double verr = ydd * fy + cy - v; 524 if (sqrt(uerr * uerr + verr * verr) < criteria.epsilon) break; 525 } 526 } 527 } 528 } 529 530 //4.3 StandardPhysicsPoint-->StandardPixelPoint 531 double xc = xn, yc = yn, zc = 1; 532 if (camModel == VisionCM::RTCM || camModel == VisionCM::KBCM) 533 { 534 double is = 1. / (A.val[6] * xc + A.val[7] * yc + A.val[8] * zc); 535 data2D[k][0] = (A.val[0] * xc + A.val[1] * yc + A.val[2] * zc) * is; 536 data2D[k][1] = (A.val[3] * xc + A.val[4] * yc + A.val[5] * zc) * is; 537 } 538 else 539 { 540 double rn2 = xn * xn + yn * yn; 541 if (camModel == VisionCM::DSCM) 542 { 543 zc = (1 - a * a * rn2) / (1 + a * sqrt(1 + (1 - a * a) * rn2)); 544 double wc = (b * zc + sqrt(zc * zc + (1 - b * b) * rn2)) / (zc * zc + rn2); 545 zc = zc - b / wc; 546 } 547 else zc = (1 - a * a * b * rn2) / (1 + a * sqrt(1 + (1 - a * a) * b * rn2));//MUCM&EUCM 548 549 //4.4 StandardPhysicsPoint-->ScaledCameraPoint 550 double xcc = xc, ycc = yc, zcc = zc; 551 if (R) 552 { 553 xcc = xc * R[0] + yc * R[1] + zc * R[2]; 554 ycc = xc * R[3] + yc * R[4] + zc * R[5]; 555 zcc = xc * R[6] + yc * R[7] + zc * R[8]; 556 } 557 data3D[k] = Vec3d(xcc, ycc, zcc); 558 } 559 } 560 return unPoint; 561 } 562 563 public: 564 static Mat_<Vec2d> derivRTCM(Mat_<Vec3d> point3D, double* r, double* t, double* K, double* D, int nDist, 565 Mat_<double>* dpdr = 0, Mat_<double>* dpdt = 0, Mat_<double>* dpdK = 0, Mat_<double>* dpdD = 0, Mat_<double>* dpdP = 0, 566 int derivRotWay = 0) 567 { 568 //0.FormatInput 569 if (dpdK) dpdK->release(); 570 if (dpdD) dpdD->release(); 571 if (dpdt) dpdt->release(); 572 if (dpdr) dpdr->release(); 573 if (dpdP) dpdP->release(); 574 Mat_<Vec2d> point2D(point3D.size()); 575 576 //1.Projection params 577 double fx = K[0]; 578 double fy = K[1]; 579 double cx = K[2]; 580 double cy = K[3]; 581 582 //2.Distortion params 583 double k1 = D[0]; 584 double k2 = D[1]; 585 double p1 = D[2]; 586 double p2 = D[3]; 587 double k3 = 0, k4 = 0, k5 = 0, k6 = 0; 588 double s1 = 0, s2 = 0, s3 = 0, s4 = 0; 589 if (nDist > 4) k3 = D[4]; 590 if (nDist > 5) { k4 = D[5]; k5 = D[6]; k6 = D[7]; } 591 if (nDist > 8) { s1 = D[8]; s2 = D[9]; s3 = D[10]; s4 = D[11]; } 592 593 //3.Rotation params 594 double R[9]; 595 Mat_<double> dRdr; 596 cv::Rodrigues(Mat_<double>(3, 1, r), Mat_<double>(3, 3, R), (dpdr && derivRotWay == 0) ? dRdr : noArray()); 597 if (dRdr.empty() == false) dRdr = dRdr.t(); 598 599 //4.Projection actions 600 Vec2d* data2D = point2D.ptr<Vec2d>(); 601 Vec3d* data3D = point3D.ptr<Vec3d>(); 602 for (int k = 0; k < point3D.rows; ++k) 603 { 604 //4.1 WorldPoint 605 double X = data3D[k][0]; 606 double Y = data3D[k][1]; 607 double Z = data3D[k][2]; 608 609 //4.2 CameraPoint 610 double x = R[0] * X + R[1] * Y + R[2] * Z + t[0]; 611 double y = R[3] * X + R[4] * Y + R[5] * Z + t[1]; 612 double z = R[6] * X + R[7] * Y + R[8] * Z + t[2]; 613 614 //4.3 StandardPhysicsPoint 615 double iz = z ? 1. / z : 1; 616 double xn = x * iz; 617 double yn = y * iz; 618 619 //4.4 DistortedPhysicsPoint 620 double xn2 = xn * xn; 621 double yn2 = yn * yn; 622 double rn2 = xn2 + yn2; 623 double xnyn = 2 * xn * yn; 624 double rn4 = rn2 * rn2; 625 double rn6 = rn2 * rn4; 626 double r2xn2 = rn2 + 2 * xn2; 627 double r2yn2 = rn2 + 2 * yn2; 628 double an = 1 + k1 * rn2 + k2 * rn4 + k3 * rn6; 629 double bn = 1. / (1 + k4 * rn2 + k5 * rn4 + k6 * rn6); 630 double xd = xn * an * bn + p1 * xnyn + p2 * r2xn2 + s1 * rn2 + s2 * rn4; 631 double yd = yn * an * bn + p2 * xnyn + p1 * r2yn2 + s3 * rn2 + s4 * rn4; 632 633 //4.5 DistortedPixelPoint 634 data2D[k][0] = xd * fx + cx; 635 data2D[k][1] = yd * fy + cy; 636 637 //5.6 dpdK: derivatives with respect to the camera parameters 638 if (dpdK) dpdK->push_back(Mat_<double>({ 2, 4 }, { xd, 0, 1, 0, 0, yd, 0, 1 })); 639 640 //5.7 dpdD: derivatives with respect to the distortion coefficients 641 if (dpdD) 642 { 643 Mat_<double> dpDdpCD(2, 2); 644 dpDdpCD << fx, 0, 0, fy; 645 646 Mat_<double> dpCDdD(2, nDist); 647 if (nDist < 5) 648 { 649 dpCDdD << 650 xn * rn2 * bn, xn* rn4* bn, xnyn, r2xn2, 651 yn* rn2* bn, yn* rn4* bn, r2yn2, xnyn; 652 } 653 else if (nDist < 8) 654 { 655 dpCDdD << 656 xn * rn2 * bn, xn* rn4* bn, xnyn, r2xn2, xn* rn6* bn, 657 yn* rn2* bn, yn* rn4* bn, r2yn2, xnyn, yn* rn6* bn; 658 } 659 else if (nDist < 12) 660 { 661 dpCDdD << 662 xn * rn2 * bn, xn* rn4* bn, xnyn, r2xn2, xn* rn6* bn, -xn * rn2 * an * bn * bn, -xn * rn4 * an * bn * bn, -xn * rn6 * an * bn * bn, 663 yn* rn2* bn, yn* rn4* bn, r2yn2, xnyn, yn* rn6* bn, -yn * rn2 * an * bn * bn, -yn * rn4 * an * bn * bn, -yn * rn6 * an * bn * bn; 664 } 665 else if (nDist < 14) 666 { 667 dpCDdD << 668 xn * rn2 * bn, xn* rn4* bn, xnyn, r2xn2, xn* rn6* bn, -xn * rn2 * an * bn * bn, -xn * rn4 * an * bn * bn, -xn * rn6 * an * bn * bn, rn2, rn4, 0, 0, 669 yn* rn2* bn, yn* rn4* bn, r2yn2, xnyn, yn* rn6* bn, -yn * rn2 * an * bn * bn, -yn * rn4 * an * bn * bn, -yn * rn6 * an * bn * bn, 0, 0, rn2, rn4; 670 } 671 dpdD->push_back(dpDdpCD * dpCDdD); 672 } 673 674 //5.8 dpdr&&dpdt: derivatives with respect to the pose 675 if (dpdr || dpdt || dpdP) 676 { 677 Mat_<double> dpDdpCD({ 2, 2 }, { fx, 0, 0, fy }); 678 679 Mat_<double> dpCDdh({ 2, 5 }, { 680 xn * bn, -xn * an * bn * bn, p2 + s1 + 2 * s2 * rn2, an * bn + 2 * p1 * yn + 4 * p2 * xn, 2 * p1 * xn, 681 yn * bn, -yn * an * bn * bn, p1 + s3 + 2 * s4 * rn2, 2 * p2 * yn, an * bn + 2 * p2 * xn + 4 * p1 * yn }); 682 683 Mat_<double> dhdpC({ 5, 2 }, { 684 xn * (2 * k1 + 4 * k2 * rn2 + 6 * k3 * rn4), yn * (2 * k1 + 4 * k2 * rn2 + 6 * k3 * rn4), 685 xn * (2 * k4 + 4 * k5 * rn2 + 6 * k6 * rn4), yn * (2 * k4 + 4 * k5 * rn2 + 6 * k6 * rn4), 686 2 * xn, 2 * yn, 687 1, 0, 688 0, 1 }); 689 690 Mat_<double> dpCdPC({ 2, 3 }, { 691 iz, 0, -xn * iz, 692 0, iz, -yn * iz }); 693 694 //1.dpdt: derivatives with respect to the translation vector 695 if (dpdt) dpdt->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC); 696 697 //2.dpdr: derivatives with respect to the rotation vector 698 if (dpdr) 699 { 700 //2.1 Exponential map 701 if (derivRotWay == 0) 702 { 703 Mat_<double> dPCdR({ 3, 9 }, { 704 X, Y, Z, 0, 0, 0, 0, 0, 0, 705 0, 0, 0, X, Y, Z, 0, 0, 0, 706 0, 0, 0, 0, 0, 0, X, Y, Z }); 707 dpdr->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * dPCdR * dRdr); 708 } 709 710 //2.2 BCH formula 711 else if (derivRotWay == 1) 712 { 713 Mat_<double> _skewPC({ 3, 3 }, { 714 0, (z - t[2]), -(y - t[1]), 715 -(z - t[2]), 0, (x - t[0]), 716 (y - t[1]), -(x - t[0]), 0 }); 717 double theta = sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]); 718 double itheta = 1 / theta; 719 double sn = sin(theta); 720 double cs1 = 1 - cos(theta); 721 Matx31d n(r[0] * itheta, r[1] * itheta, r[2] * itheta); 722 Matx33d nskew(0, -n.val[2], n.val[1], n.val[2], 0, -n.val[0], -n.val[1], n.val[0], 0); 723 Matx33d Jl = itheta * sn * Matx33d::eye() + itheta * cs1 * nskew + (1 - itheta * sn) * n * n.t(); 724 dpdr->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * _skewPC * Jl); 725 } 726 727 //2.3 Perturbance without LocalParameterization::ComputeJacobian 728 else if (derivRotWay == 2) 729 { 730 Mat_<double> _skewPC({ 3, 3 }, { 731 0, (z - t[2]), -(y - t[1]), 732 -(z - t[2]), 0, (x - t[0]), 733 (y - t[1]), -(x - t[0]), 0 }); 734 dpdr->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * _skewPC); 735 } 736 737 //2.4 Perturbance with LocalParameterization::ComputeJacobian 738 if (derivRotWay == 3) 739 { 740 Mat_<double> dPCdR({ 3, 9 }, { 741 X, Y, Z, 0, 0, 0, 0, 0, 0, 742 0, 0, 0, X, Y, Z, 0, 0, 0, 743 0, 0, 0, 0, 0, 0, X, Y, Z }); 744 dpdr->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * dPCdR); 745 } 746 } 747 748 //3.dpdP: derivatives with respect to the worldcoordinate 749 if (dpdP) dpdP->push_back(dpDdpCD * dpCDdh * dhdpC * dpCdPC * Mat_<double>(3, 3, R)); 750 } 751 } 752 return point2D; 753 } 754 755 public: 756 static void rectBino(double* R1, double* R2, double* R, double* t, double* delta = 0, double ratio1 = 0.5, double* tnew = 0) 757 { 758 //1.Let left and right image plane be coplanar 759 Matx31d r; cv::Rodrigues(Matx33d(R), r); 760 Matx33d RCop1; cv::Rodrigues(ratio1 * r, RCop1); 761 Matx33d RCop2; cv::Rodrigues(ratio1 * r - r, RCop2); 762 Matx31d tCop = RCop2 * Matx31d(t); 763 764 //2.Let left and right image plane are epipolar 765 Matx31d xAxis(tCop(0) > 0 ? 1 : -1, 0, 0); 766 Matx31d rEpi = Vec3d(tCop.val).cross(Vec3d(xAxis.val)); 767 if (norm(rEpi, NORM_L2) > 0.) rEpi *= (acos(fabs(tCop(0)) / norm(tCop, NORM_L2)) / norm(rEpi, NORM_L2)); 768 Matx33d REpi; cv::Rodrigues(rEpi, REpi); 769 770 //3.Additional rotation for reprojection at expectation angle 771 Matx33d RDelta = RDelta.eye(); 772 if (delta != 0) cv::Rodrigues(Vec3d(delta), RDelta); 773 774 //4.Final transformation 775 memcpy(R1, (RDelta * REpi * RCop1).val, 9 * sizeof(double)); 776 memcpy(R2, (RDelta * REpi * RCop2).val, 9 * sizeof(double)); 777 if (tnew != 0) memcpy(tnew, (Matx33d(R2) * Matx31d(t)).val, 3 * sizeof(double)); 778 } 779 780 static void rectBino2(double* R1, double* R2, double* R, double* t, double* delta = 0, double ratio1 = 0.0, double* tnew = 0) 781 { 782 //1.Let left and right image plane be coplanar 783 Matx31d r; cv::Rodrigues(Matx33d(R), r); 784 Matx33d RCop1; cv::Rodrigues(r * ratio1, RCop1); 785 Matx33d RCop2; cv::Rodrigues(r * ratio1 - r, RCop2); 786 Matx31d tCop = RCop2 * Matx31d(t); 787 788 //2.Let left and right image plane be epipolar 789 Vec3d e1((-tCop).val); 790 e1 = e1 / cv::norm(e1); 791 Vec3d e2(-e1(1), e1(0), 0); 792 e2 = e2 / cv::norm(e2); 793 Vec3d e3 = e1.cross(e2); 794 e3 = e3 / cv::norm(e3); 795 Matx33d REpi(e1(0), e1(1), e1(2), e2(0), e2(1), e2(2), e3(0), e3(1), e3(2)); 796 797 //3.Additional rotation for reprojection at expectation angle 798 Matx33d RDelta = RDelta.eye(); 799 if (delta != 0) cv::Rodrigues(Vec3d(delta), RDelta); 800 801 //4.Final transformation 802 memcpy(R1, (RDelta * REpi * RCop1).val, 9 * sizeof(double)); 803 memcpy(R2, (RDelta * REpi * RCop2).val, 9 * sizeof(double)); 804 if (tnew != 0) memcpy(tnew, (Matx33d(R2) * Matx31d(t)).val, 3 * sizeof(double)); 805 806 if (1)//OpenCV codes 807 { 808 //1.Let left and right image plane be coplanar 809 Matx33d RCop = Matx33d(R).t(); 810 Matx31d tCop = -Matx33d(R).t() * Matx31d(t);//here should not has minus sign and use tCop(0) sign later like stereoRectify and fisheye::stereoRectify 811 812 //2.Let left and right image plane be epipolar 813 Vec3d e1(tCop.val); 814 e1 = e1 / cv::norm(e1); 815 Vec3d e2(-e1(1), e1(0), 0); 816 e2 = e2 / cv::norm(e2); 817 Vec3d e3 = e1.cross(e2); 818 e3 = e3 / cv::norm(e3); 819 Matx33d REpi(e1(0), e1(1), e1(2), e2(0), e2(1), e2(2), e3(0), e3(1), e3(2)); 820 821 //3.Additional rotation for reprojection at expectation angle 822 Matx33d RDelta = RDelta.eye(); 823 if (delta != 0) cv::Rodrigues(Vec3d(delta), RDelta); 824 825 //4.Final transformation 826 memcpy(R1, (RDelta * REpi).val, 9 * sizeof(double)); 827 memcpy(R2, (RDelta * REpi * RCop).val, 9 * sizeof(double)); 828 if (tnew != 0) memcpy(tnew, (Matx33d(R2) * Matx31d(t)).val, 3 * sizeof(double)); 829 } 830 } 831 832 public: 833 static void TestRTCM(int argc = 0, char** argv = 0) 834 { 835 for (int k = 0; k < 999; ++k) 836 { 837 //0.GenerateData 838 static const int nDist = 5;//fix nRot=3 nModel=0 839 Mat_<Vec3d> point3D(2, 1); cv::randu(point3D, -999, 999); 840 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 841 Mat_<double> D(nDist, 1); cv::randu(D, -1.0, 1.0); 842 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 843 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 844 845 //1.CalcByOpenCV 846 Mat_<Vec2d> point2D1; 847 Mat_<double> dpdKDT1; 848 projectPoints(point3D, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, point2D1, dpdKDT1); 849 Mat_<double> dpdr1 = dpdKDT1.colRange(0, 3); 850 Mat_<double> dpdt1 = dpdKDT1.colRange(3, 6); 851 Mat_<double> dpdK1 = dpdKDT1.colRange(6, 10); 852 Mat_<double> dpdD1 = dpdKDT1.colRange(10, 10 + D.rows); 853 854 //2.CalcByCeresExp 855 Mat_<double> dpdr2(point3D.rows * 2, 3); 856 Mat_<double> dpdt2(point3D.rows * 2, 3); 857 Mat_<double> dpdK2(point3D.rows * 2, 4); 858 Mat_<double> dpdD2(point3D.rows * 2, nDist); 859 Mat_<Vec2d> point2D2(point3D.rows, 1); 860 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); 861 double* parameters[4] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>() }; 862 double* jacobians[4] = { dpdr2.ptr<double>(), dpdt2.ptr<double>(), dpdK2.ptr<double>(), dpdD2.ptr<double>() }; 863 double* residuals = point2D2.ptr<double>(); 864 autoDeriv.Evaluate(parameters, residuals, jacobians); 865 866 //3.AnalyzeError 867 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 868 double infdpdr1r2 = norm(dpdr1, dpdr2, NORM_INF); 869 double infdpdt1t2 = norm(dpdt1, dpdt2, NORM_INF); 870 double infdpdK1K2 = norm(dpdK1, dpdK2, NORM_INF); 871 double infdpdD1D2 = norm(dpdD1, dpdD2, NORM_INF); 872 873 //4.PrintError 874 cout << fmt::format("LoopCount: {}\n", k); 875 if (infpts1pts2 > 1e-9 || infdpdr1r2 > 1e-9 || infdpdt1t2 > 1e-9 || infdpdK1K2 > 1e-9 || infdpdD1D2 > 1e-9) 876 { 877 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 878 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 879 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 880 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 881 cout << endl << "infdpdD1D2: " << infdpdD1D2 << endl; 882 if (0) 883 { 884 //cout << endl << "K: " << K.t() << endl; 885 //cout << endl << "D: " << D.t() << endl; 886 //cout << endl << "r: " << r.t() << endl; 887 //cout << endl << "t: " << t.t() << endl; 888 cout << endl << "point3D: " << point3D << endl; 889 cout << endl << "point2D1: " << point2D1 << endl; 890 cout << endl << "point2D2: " << point2D2 << endl; 891 } 892 cout << "Press any key to continue" << endl; getchar(); 893 } 894 } 895 } 896 static void TestKBCM(int argc = 0, char** argv = 0) 897 { 898 for (int k = 0; k < 999; ++k) 899 { 900 //0.GenerateData //fix nRot=3 nDist=4 nModel=0 901 Mat_<Vec3d> point3D(2, 1); cv::randu(point3D, -999, 999); 902 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 903 Mat_<double> D(4, 1); cv::randu(D, -1.0, 1.0); 904 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 905 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 906 907 //1.CalcByBasaltExp 908 Mat_<double> dpdr1(point3D.rows * 2, 3); 909 Mat_<double> dpdt1(point3D.rows * 2, 3); 910 Mat_<double> dpdK1(point3D.rows * 2, 4); 911 Mat_<double> dpdD1(point3D.rows * 2, 4); 912 Mat_<Vec2d> point2D1(point3D.rows, 1); 913 ceres::AutoDiffCostFunction<BasaltCM, ceres::DYNAMIC, 3, 3, 4, 4> autoDeriv1(new BasaltCM(point3D.ptr<double>(), point3D.rows, 3, 4, 0, BasaltCM::KBCM, true), point3D.rows * 2); 914 double* parameters1[4] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>() }; 915 double* jacobians1[4] = { dpdr1.ptr<double>(), dpdt1.ptr<double>(), dpdK1.ptr<double>(), dpdD1.ptr<double>() }; 916 double* residuals1 = point2D1.ptr<double>(); 917 autoDeriv1.Evaluate(parameters1, residuals1, jacobians1); 918 919 //2.CalcByCeresExp 920 Mat_<double> dpdr2(point3D.rows * 2, 3); 921 Mat_<double> dpdt2(point3D.rows * 2, 3); 922 Mat_<double> dpdK2(point3D.rows * 2, 4); 923 Mat_<double> dpdD2(point3D.rows * 2, 4); 924 Mat_<Vec2d> point2D2(point3D.rows, 1); 925 ceres::AutoDiffCostFunction<VisionCM, ceres::DYNAMIC, 3, 3, 4, 4> autoDeriv2(new VisionCM(point3D.ptr<double>(), point3D.rows, 3, 4, 0, VisionCM::KBCM, true), point3D.rows * 2); 926 double* parameters2[4] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>() }; 927 double* jacobians2[4] = { dpdr2.ptr<double>(), dpdt2.ptr<double>(), dpdK2.ptr<double>(), dpdD2.ptr<double>() }; 928 double* residuals2 = point2D2.ptr<double>(); 929 autoDeriv2.Evaluate(parameters2, residuals2, jacobians2); 930 931 //3.AnalyzeError 932 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 933 double infdpdr1r2 = norm(dpdr1, dpdr2, NORM_INF); 934 double infdpdt1t2 = norm(dpdt1, dpdt2, NORM_INF); 935 double infdpdK1K2 = norm(dpdK1, dpdK2, NORM_INF); 936 double infdpdD1D2 = norm(dpdD1, dpdD2, NORM_INF); 937 938 //4.PrintError 939 cout << fmt::format("LoopCount: {}\n", k); 940 if (infpts1pts2 > 1e-9 || infdpdr1r2 > 1e-9 || infdpdt1t2 > 1e-9 || infdpdK1K2 > 1e-9 || infdpdD1D2 > 1e-9) 941 { 942 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 943 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 944 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 945 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 946 cout << endl << "infdpdD1D2: " << infdpdD1D2 << endl; 947 if (0) 948 { 949 //cout << endl << "K: " << K.t() << endl; 950 //cout << endl << "D: " << D.t() << endl; 951 //cout << endl << "r: " << r.t() << endl; 952 //cout << endl << "t: " << t.t() << endl; 953 cout << endl << "point3D: " << point3D << endl; 954 cout << endl << "point2D1: " << point2D1 << endl; 955 cout << endl << "point2D2: " << point2D2 << endl; 956 } 957 cout << "Press any key to continue" << endl; getchar(); 958 } 959 } 960 } 961 962 static void TestMUCM(int argc = 0, char** argv = 0) 963 { 964 for (int k = 0; k < 999; ++k) 965 { 966 //0.GenerateData //fix nRot=3 nDist=4 nModel=1 967 Mat_<Vec3d> point3D(2, 1); cv::randu(point3D, -999, 999); 968 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 969 Mat_<double> D(4, 1); cv::randu(D, -1, 1); 970 Mat_<double> a(1, 1); cv::randu(a, 1, 9); double A = a(0);//only for fisheye and so A>1 971 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 972 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 973 974 //1.CalcByOpenCV(nRot=3&&&simPin=false) 975 Mat_<Vec2d> point2D1; 976 Mat_<double> dpdKDT1; 977 omnidir::projectPoints(point3D, point2D1, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), a(0), D.rowRange(0, 4), dpdKDT1); 978 Mat_<double> dpdr1 = dpdKDT1.colRange(0, 3); 979 Mat_<double> dpdt1 = dpdKDT1.colRange(3, 6); 980 Mat_<double> dpdK1 = dpdKDT1.colRange(6, 11); 981 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)); 982 Mat_<double> dpda1 = dpdKDT1.colRange(11, 12); 983 Mat_<double> dpdD1 = dpdKDT1.colRange(12, 16); 984 985 //2.CalcByCeresExpSameAsOpenCV(nRot=3&&simPin=false) 986 Mat_<double> dpdr2(point3D.rows * 2, 3); 987 Mat_<double> dpdt2(point3D.rows * 2, 3); 988 Mat_<double> dpdK2(point3D.rows * 2, 4); 989 Mat_<double> dpdD2(point3D.rows * 2, 4); 990 Mat_<double> dpda2(point3D.rows * 2, 1); 991 Mat_<Vec2d> point2D2(point3D.rows, 1); 992 ceres::AutoDiffCostFunction<VisionCM, ceres::DYNAMIC, 3, 3, 4, 4, 1> autoDeriv2(new VisionCM(point3D.ptr<double>(), point3D.rows, 3, 4, 1, VisionCM::MUCM, false), point3D.rows * 2); 993 double* parameters2[5] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>(), a.ptr<double>() }; 994 double* jacobians2[5] = { dpdr2.ptr<double>(), dpdt2.ptr<double>(), dpdK2.ptr<double>(), dpdD2.ptr<double>(), dpda2.ptr<double>() }; 995 double* residuals2 = point2D2.ptr<double>(); 996 autoDeriv2.Evaluate(parameters2, residuals2, jacobians2); 997 998 //3.CalcByCeresExpSameAsOpenCV(nRot=3&&simPin=true) 999 a(0) = VisionCM::Mei2Pin(a(0), K.ptr<double>(), K.ptr<double>() + 1, 0, D.ptr<double>(), D.rows, true); 1000 Mat_<double> dpdr3(point3D.rows * 2, 3); 1001 Mat_<double> dpdt3(point3D.rows * 2, 3); 1002 Mat_<double> dpdK3(point3D.rows * 2, 4); 1003 Mat_<double> dpdD3(point3D.rows * 2, 4); 1004 Mat_<double> dpda3(point3D.rows * 2, 1); 1005 Mat_<Vec2d> point2D3(point3D.rows, 1); 1006 ceres::AutoDiffCostFunction<VisionCM, ceres::DYNAMIC, 3, 3, 4, 4, 1> autoDeriv3(new VisionCM(point3D.ptr<double>(), point3D.rows, 3, 4, 1, VisionCM::MUCM, true), point3D.rows * 2); 1007 double* parameters3[5] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>(), a.ptr<double>() }; 1008 double* jacobians3[5] = { dpdr3.ptr<double>(), dpdt3.ptr<double>(), dpdK3.ptr<double>(), dpdD3.ptr<double>(), dpda3.ptr<double>() }; 1009 double* residuals3 = point2D3.ptr<double>(); 1010 autoDeriv3.Evaluate(parameters3, residuals3, jacobians3); 1011 1012 //4.AnalyzeError 1013 double infpts1pts2 = cv::norm(point2D1, point2D2, NORM_INF); 1014 double infpts1pts3 = cv::norm(point2D1, point2D3, NORM_INF); 1015 double infpts2pts3 = cv::norm(point2D2, point2D3, NORM_INF); 1016 double infdpdr1r2 = cv::norm(dpdr1, dpdr2, NORM_INF); 1017 double infdpdt1t2 = cv::norm(dpdt1, dpdt2, NORM_INF); 1018 double infdpdK1K2 = cv::norm(dpdK1.colRange(0, 4), dpdK2, NORM_INF); 1019 double infdpdD1D2 = cv::norm(dpdD1, dpdD2, NORM_INF); 1020 double infdpda1a2 = cv::norm(dpda1, dpda2, NORM_INF); 1021 1022 //5.PrintError 1023 cout << fmt::format("LoopCount: {} A: {:.12f} a: {:.12f}\n", k, A, a(0)); 1024 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) 1025 { 1026 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 1027 cout << endl << "infpts1pts3: " << infpts1pts3 << endl; 1028 cout << endl << "infpts2pts3: " << infpts2pts3 << endl; 1029 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 1030 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 1031 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 1032 cout << endl << "infdpdD1D2: " << infdpdD1D2 << endl; 1033 cout << endl << "infdpda1a2: " << infdpda1a2 << endl; 1034 if (0) 1035 { 1036 //cout << endl << "K: " << K.t() << endl; 1037 //cout << endl << "D: " << D.t() << endl; 1038 //cout << endl << "ab: " << ab.t() << endl; 1039 //cout << endl << "r: " << r.t() << endl; 1040 //cout << endl << "t: " << t.t() << endl; 1041 cout << endl << "point3D: " << point3D << endl; 1042 cout << endl << "point2D1:" << point2D1 << endl; 1043 cout << endl << "point2D2:" << point2D2 << endl; 1044 cout << endl << "point2D3:" << point2D3 << endl; 1045 } 1046 cout << "Press any key to continue" << endl; getchar(); 1047 } 1048 1049 } 1050 } 1051 1052 static void TestEUCM(int argc = 0, char** argv = 0) 1053 { 1054 for (int k = 0; k < 999; ++k) 1055 { 1056 //0.GenerateData //fix nRot=3 nDist=0 nModel=2 1057 Mat_<Vec3d> point3D(22, 1); cv::randu(point3D, -999, 999); 1058 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 1059 Mat_<double> ab(2, 1); cv::randu(ab.row(0), 0.5, 0.91); cv::randu(ab.row(1), 0, 9.9); //only for fisheye and so A>1 1060 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 1061 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 1062 1063 //1.CalcByBasaltExp 1064 Mat_<double> dpdr1(point3D.rows * 2, 3); 1065 Mat_<double> dpdt1(point3D.rows * 2, 3); 1066 Mat_<double> dpdK1(point3D.rows * 2, 4); 1067 Mat_<double> dpdab1(point3D.rows * 2, 2); 1068 Mat_<Vec2d> point2D1(point3D.rows, 1); 1069 ceres::AutoDiffCostFunction<BasaltCM, ceres::DYNAMIC, 3, 3, 4, 2> autoDeriv1(new BasaltCM(point3D.ptr<double>(), point3D.rows, 3, 0, 2, BasaltCM::EUCM, true), point3D.rows * 2); 1070 double* parameters1[4] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), ab.ptr<double>() }; 1071 double* jacobians1[4] = { dpdr1.ptr<double>(), dpdt1.ptr<double>(), dpdK1.ptr<double>(), dpdab1.ptr<double>() }; 1072 double* residuals1 = point2D1.ptr<double>(); 1073 autoDeriv1.Evaluate(parameters1, residuals1, jacobians1); 1074 1075 //2.CalcByCeresExp 1076 Mat_<double> dpdr2(point3D.rows * 2, 3); 1077 Mat_<double> dpdt2(point3D.rows * 2, 3); 1078 Mat_<double> dpdK2(point3D.rows * 2, 4); 1079 Mat_<double> dpdab2(point3D.rows * 2, 2); 1080 Mat_<Vec2d> point2D2(point3D.rows, 1); 1081 ceres::AutoDiffCostFunction<VisionCM, ceres::DYNAMIC, 3, 3, 4, 2> autoDeriv2(new VisionCM(point3D.ptr<double>(), point3D.rows, 3, 0, 2, VisionCM::EUCM, true), point3D.rows * 2); 1082 double* parameters2[4] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), ab.ptr<double>() }; 1083 double* jacobians2[4] = { dpdr2.ptr<double>(), dpdt2.ptr<double>(), dpdK2.ptr<double>(),dpdab2.ptr<double>() }; 1084 double* residuals2 = point2D2.ptr<double>(); 1085 autoDeriv2.Evaluate(parameters2, residuals2, jacobians2); 1086 1087 //3.AnalyzeError 1088 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 1089 double infdpdr1r2 = norm(dpdr1, dpdr2, NORM_INF); 1090 double infdpdt1t2 = norm(dpdt1, dpdt2, NORM_INF); 1091 double infdpdK1K2 = norm(dpdK1, dpdK2, NORM_INF); 1092 double infdpdab1ab2 = norm(dpdab1, dpdab2, NORM_INF); 1093 1094 //4.PrintError 1095 cout << fmt::format("LoopCount: {}\n", k); 1096 if (infpts1pts2 > 1e-9 || infdpdr1r2 > 1e-9 || infdpdt1t2 > 1e-9 || infdpdK1K2 > 1e-9 || infdpdab1ab2 > 1e-9) 1097 { 1098 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 1099 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 1100 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 1101 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 1102 cout << endl << "infdpdab1ab2: " << infdpdab1ab2 << endl; 1103 //if (0) 1104 { 1105 //cout << endl << "K: " << K.t() << endl; 1106 //cout << endl << "D: " << D.t() << endl; 1107 //cout << endl << "r: " << r.t() << endl; 1108 //cout << endl << "t: " << t.t() << endl; 1109 cout << endl << "point3D: " << point3D << endl; 1110 cout << endl << "point2D1: " << point2D1 << endl; 1111 cout << endl << "point2D2: " << point2D2 << endl; 1112 } 1113 cout << "Press any key to continue" << endl; getchar(); 1114 } 1115 } 1116 } 1117 static void TestDSCM(int argc = 0, char** argv = 0) 1118 { 1119 for (int k = 0; k < 999; ++k) 1120 { 1121 //0.GenerateData //fix nRot=3 nDist=0 nModel=2 1122 Mat_<Vec3d> point3D(22, 1); cv::randu(point3D, -999, 999); 1123 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 1124 Mat_<double> ab(2, 1); cv::randu(ab.row(0), 0.5, 0.91); cv::randu(ab.row(1), -9.9, 9.9); //only for fisheye and so A>1 1125 Mat_<double> ba({ 2, 1 }, { ab(1), ab(0) });//Basalt use different order 1126 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 1127 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 1128 1129 //1.CalcByBasaltExp 1130 Mat_<double> dpdr1(point3D.rows * 2, 3); 1131 Mat_<double> dpdt1(point3D.rows * 2, 3); 1132 Mat_<double> dpdK1(point3D.rows * 2, 4); 1133 Mat_<double> dpdab1(point3D.rows * 2, 2); 1134 Mat_<Vec2d> point2D1(point3D.rows, 1); 1135 ceres::AutoDiffCostFunction<BasaltCM, ceres::DYNAMIC, 3, 3, 4, 2> autoDeriv1(new BasaltCM(point3D.ptr<double>(), point3D.rows, 3, 0, 2, BasaltCM::DSCM, true), point3D.rows * 2); 1136 double* parameters1[4] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), ba.ptr<double>() }; 1137 double* jacobians1[4] = { dpdr1.ptr<double>(), dpdt1.ptr<double>(), dpdK1.ptr<double>(), dpdab1.ptr<double>() }; 1138 double* residuals1 = point2D1.ptr<double>(); 1139 autoDeriv1.Evaluate(parameters1, residuals1, jacobians1); 1140 1141 //2.CalcByCeresExp 1142 Mat_<double> dpdr2(point3D.rows * 2, 3); 1143 Mat_<double> dpdt2(point3D.rows * 2, 3); 1144 Mat_<double> dpdK2(point3D.rows * 2, 4); 1145 Mat_<double> dpdab2(point3D.rows * 2, 2); 1146 Mat_<Vec2d> point2D2(point3D.rows, 1); 1147 ceres::AutoDiffCostFunction<VisionCM, ceres::DYNAMIC, 3, 3, 4, 2> autoDeriv2(new VisionCM(point3D.ptr<double>(), point3D.rows, 3, 0, 2, VisionCM::DSCM, true), point3D.rows * 2); 1148 double* parameters2[4] = { r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), ab.ptr<double>() }; 1149 double* jacobians2[4] = { dpdr2.ptr<double>(), dpdt2.ptr<double>(), dpdK2.ptr<double>(),dpdab2.ptr<double>() }; 1150 double* residuals2 = point2D2.ptr<double>(); 1151 autoDeriv2.Evaluate(parameters2, residuals2, jacobians2); 1152 1153 //3.AnalyzeError 1154 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 1155 double infdpdr1r2 = norm(dpdr1, dpdr2, NORM_INF); 1156 double infdpdt1t2 = norm(dpdt1, dpdt2, NORM_INF); 1157 double infdpdK1K2 = norm(dpdK1, dpdK2, NORM_INF); 1158 double infdpdab1ab2 = max(norm(dpdab1.col(0), dpdab2.col(1), NORM_INF), norm(dpdab1.col(1), dpdab2.col(0), NORM_INF)); 1159 1160 //4.PrintError 1161 cout << fmt::format("LoopCount: {}\n", k); 1162 if (infpts1pts2 > 1e-9 || infdpdr1r2 > 1e-9 || infdpdt1t2 > 1e-9 || infdpdK1K2 > 1e-9 || infdpdab1ab2 > 1e-9) 1163 { 1164 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 1165 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 1166 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 1167 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 1168 cout << endl << "infdpdab1ab2: " << infdpdab1ab2 << endl; 1169 //if (0) 1170 { 1171 //cout << endl << "K: " << K.t() << endl; 1172 //cout << endl << "D: " << D.t() << endl; 1173 //cout << endl << "r: " << r.t() << endl; 1174 //cout << endl << "t: " << t.t() << endl; 1175 cout << endl << "point3D: " << point3D << endl; 1176 cout << endl << "point2D1: " << point2D1 << endl; 1177 cout << endl << "point2D2: " << point2D2 << endl; 1178 } 1179 cout << "Press any key to continue" << endl; getchar(); 1180 } 1181 } 1182 } 1183 1184 public: 1185 static void TestInvRTCM(int argc = 0, char** argv = 0) 1186 { 1187 for (int k = 0; k < 999; ++k) 1188 { 1189 //0.GenerateData 1190 const int nDist = k % 2 == 0 ? 4 : 5; 1191 Mat_<Vec3d> point3D(22, 1); cv::randu(point3D, -999, 999); 1192 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 1193 Mat_<double> D(nDist, 1); cv::randu(D, -0.2, 0.2); 1194 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 1195 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 1196 Mat_<Vec2d> point2D; projectPoints(point3D, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, point2D); 1197 1198 //1.CalcGT 1199 Mat_<Vec2d> point2D0; projectPoints(point3D, r, t, Mat_<double>::eye(3, 3), Mat_<double>::zeros(D.rows, 1), point2D0); 1200 1201 //2.CalcByOpenCV 1202 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)); 1203 1204 //3.CalcByDIY 1205 Mat_<Vec2d> point2D2 = reproject(point2D, K.ptr<double>(), D.ptr<double>(), 0, D.rows, 0, VisionCM::RTCM, 0, 0, TermCriteria(TermCriteria::COUNT, 8, 1e-3)); 1206 1207 //4.AnalyzeError 1208 double infpts0pts1 = norm(point2D0, point2D1, NORM_INF); 1209 double infpts0pts2 = norm(point2D0, point2D2, NORM_INF); 1210 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 1211 TermCriteria criteria(TermCriteria::COUNT + TermCriteria::EPS, k % 20 + 2, 1. / pow(10, k % 10 + 2)); 1212 Mat_<double> P(4, 1); cv::randu(P, 111, 999); 1213 Mat_<double> R(3, 3); cv::Rodrigues(Vec3d::randu(-999, 999), R); 1214 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); 1215 Mat_<Vec2d> point2D4 = reproject(point2D, K.ptr<double>(), D.ptr<double>(), 0, D.rows, 0, VisionCM::RTCM, R.ptr<double>(), P.ptr<double>(), criteria); 1216 double infpts3pts4 = norm(point2D3, point2D4, NORM_INF); 1217 1218 //5.PrintError 1219 cout << endl << "LoopCount: " << k << endl; 1220 if (/*infpts0pts1 > 1E-12 || infpts0pts2 > 1E-12 || */infpts1pts2 > 1E-12 || infpts3pts4 > 1E-12) 1221 { 1222 cout << endl << "infpts0pts1: " << infpts0pts1 << endl; 1223 cout << endl << "infpts0pts2: " << infpts0pts2 << endl; 1224 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 1225 cout << endl << "infpts3pts4: " << infpts3pts4 << endl; 1226 if (0) 1227 { 1228 cout << endl << "point3D: " << point3D << endl; 1229 cout << endl << "point2D0: " << point2D0 << endl; 1230 cout << endl << "point2D1: " << point2D1 << endl; 1231 cout << endl << "point2D2: " << point2D2 << endl; 1232 cout << endl << "point2D3: " << point2D3 << endl; 1233 cout << endl << "point2D4: " << point2D4 << endl; 1234 } 1235 cout << endl << "Press any key to continue" << endl; getchar(); 1236 } 1237 } 1238 } 1239 static void TestInvKBCM(int argc = 0, char** argv = 0) 1240 { 1241 for (int k = 0; k < 999; ++k) 1242 { 1243 //0.GenerateData 1244 static const int nDist = 4; 1245 Mat_<Vec3d> point3D(22, 1); cv::randu(point3D, -999, 999); 1246 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 1247 Mat_<double> D(4, 1); cv::randu(D, -0.2, 0.2); 1248 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 1249 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 1250 Mat_<Vec2d> point2D; fisheye::projectPoints(point3D, point2D, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D); 1251 1252 //1.CalcGT 1253 Mat_<Vec2d> point2D0; fisheye::projectPoints(point3D, point2D0, r, t, Mat_<double>::eye(3, 3), Mat_<double>::zeros(D.rows, 1)); 1254 1255 //2.CalcByOpenCV 1256 Mat_<Vec2d> point2D1; fisheye::undistortPoints(point2D, point2D1, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, noArray(), noArray()); 1257 1258 //3.CalcByDIY 1259 Mat_<Vec2d> point2D2 = reproject(point2D, K.ptr<double>(), D.ptr<double>(), 0, D.rows, 0, VisionCM::KBCM, 0, 0, 1260 TermCriteria(TermCriteria::COUNT + TermCriteria::COUNT, 10, 1e-8));//criteria is fixed as (10, 1e-8) in opencv4.5 KBCM 1261 1262 //4.AnalyzeError 1263 double infpts0pts1 = norm(point2D0, point2D1, NORM_INF); 1264 double infpts0pts2 = norm(point2D0, point2D2, NORM_INF); 1265 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 1266 TermCriteria criteria(TermCriteria::COUNT + TermCriteria::EPS, k % 20 + 2, 1. / pow(10, k % 10 + 2)); 1267 Mat_<double> P(4, 1); cv::randu(P, 111, 999); 1268 Mat_<double> R(3, 3); cv::Rodrigues(Vec3d::randu(-999, 999), R); 1269 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)); 1270 Mat_<Vec2d> point2D4 = reproject(point2D, K.ptr<double>(), D.ptr<double>(), 0, D.rows, 0, VisionCM::KBCM, R.ptr<double>(), P.ptr<double>(), 1271 TermCriteria(TermCriteria::COUNT + TermCriteria::COUNT, 10, 1e-8));//criteria is fixed as (10, 1e-8) in opencv4.5 KBCM 1272 double infpts3pts4 = norm(point2D3, point2D4, NORM_INF); 1273 1274 //5.PrintError 1275 cout << endl << "LoopCount: " << k << endl; 1276 if (/*infpts0pts1 > 1E-9 || infpts0pts2 > 1E-9 ||*/ infpts1pts2 > 1E-9 || infpts3pts4 > 1E-9) 1277 { 1278 cout << endl << "infpts0pts1: " << infpts0pts1 << endl; 1279 cout << endl << "infpts0pts2: " << infpts0pts2 << endl; 1280 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 1281 cout << endl << "infpts3pts4: " << infpts3pts4 << endl; 1282 if (0) 1283 { 1284 cout << endl << "point3D: " << point3D << endl; 1285 cout << endl << "point2D0: " << point2D0 << endl; 1286 cout << endl << "point2D1: " << point2D1 << endl; 1287 cout << endl << "point2D2: " << point2D2 << endl; 1288 cout << endl << "point2D3: " << point2D3 << endl; 1289 cout << endl << "point2D4: " << point2D4 << endl; 1290 } 1291 cout << endl << "Press any key to continue" << endl; getchar(); 1292 } 1293 } 1294 } 1295 1296 static void TestInvMUCM() 1297 { 1298 auto omnidir_undistortPoints = [](InputArray distorted, OutputArray undistorted, InputArray K, InputArray D, InputArray xi, InputArray R)->Mat 1299 { 1300 CV_Assert(distorted.type() == CV_64FC2 || distorted.type() == CV_32FC2); 1301 CV_Assert(R.empty() || (!R.empty() && (R.size() == Size(3, 3) || R.total() * R.channels() == 3) 1302 && (R.depth() == CV_64F || R.depth() == CV_32F))); 1303 CV_Assert((D.depth() == CV_64F || D.depth() == CV_32F) && D.total() == 4); 1304 CV_Assert(K.size() == Size(3, 3) && (K.depth() == CV_64F || K.depth() == CV_32F)); 1305 CV_Assert(xi.total() == 1 && (xi.depth() == CV_64F || xi.depth() == CV_32F)); 1306 1307 undistorted.create(distorted.size(), distorted.type()); 1308 1309 cv::Vec2d f, c; 1310 double s = 0.0; 1311 if (K.depth() == CV_32F) 1312 { 1313 Matx33f camMat = K.getMat(); 1314 f = Vec2f(camMat(0, 0), camMat(1, 1)); 1315 c = Vec2f(camMat(0, 2), camMat(1, 2)); 1316 s = (double)camMat(0, 1); 1317 } 1318 else if (K.depth() == CV_64F) 1319 { 1320 Matx33d camMat = K.getMat(); 1321 f = Vec2d(camMat(0, 0), camMat(1, 1)); 1322 c = Vec2d(camMat(0, 2), camMat(1, 2)); 1323 s = camMat(0, 1); 1324 } 1325 1326 Vec4d kp = D.depth() == CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>() : (Vec4d)*D.getMat().ptr<Vec4d>(); 1327 Vec2d k = Vec2d(kp[0], kp[1]); 1328 Vec2d p = Vec2d(kp[2], kp[3]); 1329 1330 double _xi = xi.depth() == CV_32F ? (double)*xi.getMat().ptr<float>() : *xi.getMat().ptr<double>(); 1331 cv::Matx33d RR = cv::Matx33d::eye(); 1332 // R is om 1333 if (!R.empty() && R.total() * R.channels() == 3) 1334 { 1335 cv::Vec3d rvec; 1336 R.getMat().convertTo(rvec, CV_64F); 1337 cv::Rodrigues(rvec, RR); 1338 } 1339 else if (!R.empty() && R.size() == Size(3, 3)) 1340 { 1341 R.getMat().convertTo(RR, CV_64F); 1342 } 1343 1344 const cv::Vec2d* srcd = distorted.getMat().ptr<cv::Vec2d>(); 1345 const cv::Vec2f* srcf = distorted.getMat().ptr<cv::Vec2f>(); 1346 1347 cv::Vec2d* dstd = undistorted.getMat().ptr<cv::Vec2d>(); 1348 cv::Vec2f* dstf = undistorted.getMat().ptr<cv::Vec2f>(); 1349 1350 Mat_<Vec3d> p3D;//############################################################Add 1351 int n = (int)distorted.total(); 1352 for (int i = 0; i < n; i++) 1353 { 1354 Vec2d pii = distorted.depth() == CV_32F ? (Vec2d)srcf[i] : (Vec2d)srcd[i]; // image point 1355 Vec2d pp((pii[0] * f[1] - c[0] * f[1] - s * (pii[1] - c[1])) / (f[0] * f[1]), (pii[1] - c[1]) / f[1]); //plane 1356 Vec2d pu = pp; // points without distortion 1357 1358 // remove distortion iteratively 1359 for (int j = 0; j < 20; j++) 1360 { 1361 double r2 = pu[0] * pu[0] + pu[1] * pu[1]; 1362 double r4 = r2 * r2; 1363 pu[0] = (pp[0] - 2 * p[0] * pu[0] * pu[1] - p[1] * (r2 + 2 * pu[0] * pu[0])) / (1 + k[0] * r2 + k[1] * r4); 1364 pu[1] = (pp[1] - 2 * p[1] * pu[0] * pu[1] - p[0] * (r2 + 2 * pu[1] * pu[1])) / (1 + k[0] * r2 + k[1] * r4); 1365 } 1366 1367 // project to unit sphere 1368 double r2 = pu[0] * pu[0] + pu[1] * pu[1]; 1369 double a = (r2 + 1); 1370 double b = 2 * _xi * r2; 1371 double cc = r2 * _xi * _xi - 1; 1372 double Zs = (-b + sqrt(b * b - 4 * a * cc)) / (2 * a); 1373 Vec3d Xw = Vec3d(pu[0] * (Zs + _xi), pu[1] * (Zs + _xi), Zs); 1374 1375 // rotate 1376 Xw = RR * Xw; 1377 p3D.push_back(Xw);//############################################################Add 1378 1379 // project back to sphere 1380 Vec3d Xs = Xw / cv::norm(Xw); 1381 1382 // reproject to camera plane 1383 Vec3d ppu = Vec3d(Xs[0] / (Xs[2] + _xi), Xs[1] / (Xs[2] + _xi), 1.0); 1384 if (undistorted.depth() == CV_32F) 1385 { 1386 dstf[i] = Vec2f((float)ppu[0], (float)ppu[1]); 1387 } 1388 else if (undistorted.depth() == CV_64F) 1389 { 1390 dstd[i] = Vec2d(ppu[0], ppu[1]); 1391 } 1392 } 1393 return p3D;//############################################################Add 1394 }; 1395 for (int k = 0; k < 999; ++k) 1396 { 1397 //0.GenerateData 1398 Mat_<Vec3d> point3D(4, 1); cv::randu(point3D, -999, 999); 1399 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 1400 Mat_<double> D(4, 1); cv::randu(D, -0, 0); //distortion is tested by testInvRTCM 1401 Mat_<double> a(1, 1); cv::randu(a, 1, 9); //only for fisheye and so A>1 1402 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 1403 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 1404 Mat_<Vec2d> point2D; omnidir::projectPoints(point3D, point2D, r, t, Mat_<double>({ 3, 3 }, { K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1 }), a(0), D); 1405 Mat_<double> R; cv::Rodrigues(r, R); 1406 1407 //1.CalcGT 1408 Mat_<Vec3d> point3D0; 1409 for (int i = 0; i < point3D.rows; ++i) point3D0.push_back(Vec3d(Mat_<double>(R * Mat_<double>(3, 1, point3D(i).val) + t).ptr<double>())); 1410 1411 //2.CalcByOpenCV 1412 Mat_<Vec3d> point3D1 = ModelCamera::reproject(point2D, K.ptr<double>(), D.ptr<double>(), a.ptr<double>(), 4, 1, ModelCamera::VisionCM::MUCM, 0, 0); 1413 1414 //3.CalcByDIY 1415 Mat unPt; 1416 Mat_<Vec3d> point3D2 = omnidir_undistortPoints(point2D, unPt, Mat_<double>({ 3, 3 }, { K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1 }), D, a, Mat_<double>::eye(3, 3)); 1417 1418 //4.AnalyzeError 1419 Mat_<Vec3d> point3D3 = ModelCamera::reproject(point2D, K.ptr<double>(), D.ptr<double>(), a.ptr<double>(), 4, 1, ModelCamera::VisionCM::MUCM, R.ptr<double>(), 0); 1420 Mat_<Vec3d> point3D4 = omnidir_undistortPoints(point2D, unPt, Mat_<double>({ 3, 3 }, { K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1 }), D, a, R); 1421 Mat_<double> infP0P1P2; 1422 for (int i = 0; i < point3D.rows; ++i) 1423 { 1424 Vec3d P0 = point3D0(i); 1425 Vec3d P1 = point3D1(i), P2 = point3D2(i); 1426 Vec3d P3 = point3D3(i), P4 = point3D4(i); 1427 double ang1 = acos(P0.dot(P1) / sqrt(P0.dot(P0) * P1.dot(P1))) * rad2deg; 1428 double ang2 = acos(P0.dot(P2) / sqrt(P0.dot(P0) * P2.dot(P2))) * rad2deg; 1429 double ang12 = acos(P1.dot(P2) / sqrt(P1.dot(P1) * P2.dot(P2))) * rad2deg; 1430 double ang34 = acos(P3.dot(P4) / sqrt(P3.dot(P3) * P4.dot(P4))) * rad2deg; 1431 infP0P1P2.push_back(Vec4d(ang1, ang2, ang12, ang34)); 1432 } 1433 1434 //5.PrintError 1435 cout << endl << "LoopCount: " << k << endl;//Half part is not close to ground truth because of random input 1436 if (infP0P1P2(0) > 1E-12 || infP0P1P2(1) > 1E-12 || infP0P1P2(2) > 1E-12) 1437 { 1438 cout << endl << "infP0P1: " << infP0P1P2(0) << endl; 1439 cout << endl << "infP0P2: " << infP0P1P2(1) << endl; 1440 cout << endl << "infP1P2: " << infP0P1P2(2) << endl; 1441 cout << endl << "infP3P4: " << infP0P1P2(3) << endl; 1442 if (0) 1443 { 1444 cout << endl << "point3D0: " << point3D0 << endl; 1445 cout << endl << "point3D1: " << point3D1 << endl; 1446 cout << endl << "point3D2: " << point3D2 << endl; 1447 cout << endl << "point3D3: " << point3D3 << endl; 1448 cout << endl << "point3D4: " << point3D4 << endl; 1449 } 1450 cout << "Press any key to continue" << endl; getchar(); 1451 } 1452 } 1453 } 1454 1455 static void TestInvEUCM(int argc = 0, char** argv = 0) {} 1456 static void TestInvDSCM(int argc = 0, char** argv = 0) {} 1457 1458 public: 1459 static void TestJacRTCM(int argc = 0, char** argv = 0) 1460 { 1461 for (int k = 0; k < 999; ++k) 1462 { 1463 static const int nRot = 3;//fixed 1464 static const int nDist = 5;//varied 1465 static const int nModel = 0;//fixed 1466 Mat_<Vec3d> point3D(2, 1); cv::randu(point3D, -999, 999); 1467 Mat_<double> K(4, 1); cv::randu(K, 111, 999); 1468 Mat_<double> D(nDist, 1); cv::randu(D, -1.0, 1.0); 1469 Mat_<double> r(3, 1); cv::randu(r, -999, 999); 1470 Mat_<double> t(3, 1); cv::randu(t, -999, 999); 1471 1472 //1.CalcByOpenCV 1473 Mat_<Vec2d> point2D1; 1474 Mat_<double> dpdKDT1; 1475 projectPoints(point3D, r, t, Matx33d(K(0), 0, K(2), 0, K(1), K(3), 0, 0, 1), D, point2D1, dpdKDT1); 1476 Mat_<double> dpdr1 = dpdKDT1.colRange(0, 3); 1477 Mat_<double> dpdt1 = dpdKDT1.colRange(3, 6); 1478 Mat_<double> dpdK1 = dpdKDT1.colRange(6, 10); 1479 Mat_<double> dpdD1 = dpdKDT1.colRange(10, 10 + D.rows); 1480 1481 //2.CalcByManuMap 1482 Mat_<double> dpdr2, dpdt2, dpdK2, dpdD2; 1483 Mat_<Vec2d> point2D2 = derivRTCM(point3D, r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>(), D.rows, 1484 &dpdr2, &dpdt2, &dpdK2, &dpdD2, 0, 0); 1485 1486 //2.CalcByManuBCH 1487 Mat_<double> dpdr3, dpdt3, dpdK3, dpdD3; 1488 Mat_<Vec2d> point2D3 = derivRTCM(point3D, r.ptr<double>(), t.ptr<double>(), K.ptr<double>(), D.ptr<double>(), D.rows, 1489 &dpdr3, &dpdt3, &dpdK3, &dpdD3, 0, 1); 1490 1491 //4.AnalyzeError 1492 double infpts1pts2 = norm(point2D1, point2D2, NORM_INF); 1493 double infpts1pts3 = norm(point2D1, point2D3, NORM_INF); 1494 double infdpdr1r2 = norm(dpdr1, dpdr2, NORM_INF); 1495 double infdpdr1r3 = norm(dpdr1, dpdr3, NORM_INF); 1496 double infdpdr2r3 = norm(dpdr2, dpdr3, NORM_INF); 1497 double infdpdt1t2 = norm(dpdt1, dpdt2, NORM_INF); 1498 double infdpdt1t3 = norm(dpdt1, dpdt3, NORM_INF); 1499 double infdpdt2t3 = norm(dpdt2, dpdt3, NORM_INF); 1500 double infdpdK1K2 = norm(dpdK1, dpdK2, NORM_INF); 1501 double infdpdK1K3 = norm(dpdK1, dpdK3, NORM_INF); 1502 double infdpdK2K3 = norm(dpdK2, dpdK3, NORM_INF); 1503 double infdpdD1D2 = norm(dpdD1, dpdD2, NORM_INF); 1504 double infdpdD1D3 = norm(dpdD1, dpdD3, NORM_INF); 1505 double infdpdD2D3 = norm(dpdD2, dpdD3, NORM_INF); 1506 1507 //5.PrintError 1508 cout << fmt::format("LoopCount: {}\n", k); 1509 if (infpts1pts2 > 1e-9 || infpts1pts3 > 1e-9 || 1510 infdpdr1r2 > 1e-9 || infdpdr1r3 > 1e-9 || infdpdr2r3 > 1e-9 || infdpdt1t2 > 1e-9 || infdpdt1t3 > 1e-9 || infdpdt2t3 > 1e-9 || 1511 infdpdK1K2 > 1e-9 || infdpdK1K3 > 1e-9 || infdpdK2K3 > 1e-9 || infdpdD1D2 > 1e-9 || infdpdD1D3 > 1e-9 || infdpdD2D3 > 1e-9) 1512 { 1513 cout << endl << "infpts1pts2: " << infpts1pts2 << endl; 1514 cout << endl << "infpts1pts3: " << infpts1pts3 << endl; 1515 cout << endl << "infdpdr1r2: " << infdpdr1r2 << endl; 1516 cout << endl << "infdpdr1r3: " << infdpdr1r3 << endl; 1517 cout << endl << "infdpdr2r3: " << infdpdr2r3 << endl; 1518 cout << endl << "infdpdt1t2: " << infdpdt1t2 << endl; 1519 cout << endl << "infdpdt1t3: " << infdpdt1t3 << endl; 1520 cout << endl << "infdpdt2t3: " << infdpdt2t3 << endl; 1521 cout << endl << "infdpdK1K2: " << infdpdK1K2 << endl; 1522 cout << endl << "infdpdK1K3: " << infdpdK1K3 << endl; 1523 cout << endl << "infdpdK2K3: " << infdpdK2K3 << endl; 1524 cout << endl << "infdpdD1D2: " << infdpdD1D2 << endl; 1525 cout << endl << "infdpdD1D3: " << infdpdD1D3 << endl; 1526 cout << endl << "infdpdD2D3: " << infdpdD2D3 << endl; 1527 if (0) 1528 { 1529 cout << "point2D1: " << point2D1 << endl; 1530 cout << "point2D2: " << point2D2 << endl; 1531 cout << "point2D3: " << point2D3 << endl; 1532 } 1533 cout << "Press any key to continue" << endl; getchar(); 1534 } 1535 } 1536 } 1537 1538 public: 1539 static void TestRectBino(int argc = 0, char** argv = 0) 1540 { 1541 for (int k = 0; k < 999; ++k) 1542 { 1543 //0.GenerateData 1544 Mat_<double> R(3, 3); ceres::EulerAnglesToRotationMatrix(Matx31d::randu(-15, 15).val, 0, R.ptr<double>()); 1545 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 1546 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 1547 1548 //1.CalcByOpenCVPin 1549 Matx33d R11, R12; 1550 stereoRectify(Matx33d::eye(), Mat(), Matx33d::eye(), Mat(), Size(480, 640), R, t, R11, R12, Matx34d(), Matx34d(), noArray()); 1551 double infP11P12 = cv::norm(R11 * P, R12 * R * P, NORM_INF); 1552 1553 //2.CalcByOpenCVKB 1554 Matx33d R21, R22; 1555 fisheye::stereoRectify(Matx33d::eye(), Matx41d::zeros(), Matx33d::eye(), Matx41d::zeros(), Size(480, 640), R, t, R21, R22, Matx34d(), Matx34d(), noArray(), 0); 1556 double infP21P22 = cv::norm(R21 * P, R22 * R * P, NORM_INF); 1557 1558 //3.CalcByDIYPin 1559 Matx33d R31, R32; 1560 rectBino(R31.val, R32.val, R.ptr<double>(), t.ptr<double>()); 1561 double infP31P32 = cv::norm(R31 * P, R32 * R * P, NORM_INF); 1562 1563 //4.CalcByDIYMUCM 1564 Matx33d R41, R42; 1565 rectBino2(R41.val, R42.val, R.ptr<double>(), t.ptr<double>()); 1566 double infP41P42 = cv::norm(R41 * P, R42 * R * P, NORM_INF); 1567 1568 //5.CalcByOpenCVMUCM 1569 Matx33d R51, R52; 1570 omnidir::stereoRectify(R, t, R51, R52); 1571 double infP51P52 = cv::norm(R51 * P, R52 * R * P, NORM_INF); 1572 string ss = " It is OK to rectify the last line {_R2 = R21 * _R1} in omnidir::stereoRectify {_R2 = _R1 * R21}"; 1573 1574 //6.AnalyzeError 1575 double infR11R21 = norm(R11, R21, NORM_INF); 1576 double infR11R31 = norm(R11, R31, NORM_INF); 1577 double infR41R51 = norm(R41, R51, NORM_INF); 1578 double infR12R22 = norm(R12, R22, NORM_INF); 1579 double infR12R32 = norm(R12, R32, NORM_INF); 1580 double infR42R52 = norm(R42, R52, NORM_INF); 1581 1582 //7.PrintError 1583 cout << endl << "LoopCount: " << k << ss << endl; 1584 if (infP11P12 > 1E-12 || infP21P22 > 1E-12 || infP31P32 > 1E-12 || infP41P42 > 1E-12 || //infP51P52 > 1E-12 || 1585 infR11R21 > 1E-12 || infR11R31 > 1E-12 || infR12R22 > 1E-12 || infR12R32 > 1E-12 /*|| infR41R51 > 1E-12 || infR42R52 > 1E-12*/) 1586 { 1587 cout << endl << "infP11P12: " << infP11P12 << endl; 1588 cout << endl << "infP21P22: " << infP21P22 << endl; 1589 cout << endl << "infP31P32: " << infP31P32 << endl; 1590 cout << endl << "infP41P42: " << infP41P42 << endl; 1591 cout << endl << "infP51P52: " << infP51P52 << endl; 1592 cout << endl << "infR11R21: " << infR11R21 << endl; 1593 cout << endl << "infR11R31: " << infR11R31 << endl; 1594 cout << endl << "infR11R51: " << infR41R51 << endl; 1595 cout << endl << "infR12R22: " << infR12R22 << endl; 1596 cout << endl << "infR12R32: " << infR12R32 << endl; 1597 cout << endl << "infR12R52: " << infR42R52 << endl; 1598 if (1) 1599 { 1600 cout << endl << "R: " << R << endl << endl; 1601 cout << endl << "t: " << t << endl << endl; 1602 cout << endl << "P: " << P << endl << endl; 1603 //cout << endl << "R11: " << R11 << endl; 1604 //cout << endl << "R12: " << R12 << endl; 1605 //cout << endl << "R21: " << R21 << endl; 1606 //cout << endl << "R22: " << R22 << endl; 1607 //cout << endl << "R31: " << R31 << endl; 1608 //cout << endl << "R32: " << R32 << endl; 1609 //cout << endl << "R41: " << R41 << endl; 1610 //cout << endl << "R42: " << R42 << endl; 1611 //cout << endl << "R51: " << R51 << endl; 1612 //cout << endl << "R52: " << R52 << endl; 1613 } 1614 cout << endl << "Press any key to continue" << endl; getchar(); 1615 } 1616 } 1617 } 1618 }; 1619 1620 #ifdef ModelCameraTest 1621 int main(int argc, char** argv) { ModelCamera::TestRectBino(argc, argv); return 0; } 1622 int main1(int argc, char** argv) { ModelCamera::TestRTCM(argc, argv); return 0; } 1623 int main2(int argc, char** argv) { ModelCamera::TestKBCM(argc, argv); return 0; } 1624 int main3(int argc, char** argv) { ModelCamera::TestMUCM(argc, argv); return 0; } 1625 int main4(int argc, char** argv) { ModelCamera::TestEUCM(argc, argv); return 0; } 1626 int main5(int argc, char** argv) { ModelCamera::TestDSCM(argc, argv); return 0; } 1627 int main6(int argc, char** argv) { ModelCamera::TestInvRTCM(argc, argv); return 0; } 1628 int main7(int argc, char** argv) { ModelCamera::TestInvKBCM(argc, argv); return 0; } 1629 int main8(int argc, char** argv) { ModelCamera::TestInvMUCM(argc, argv); return 0; } 1630 int main9(int argc, char** argv) { ModelCamera::TestInvEUCM(argc, argv); return 0; } 1631 int main10(int argc, char** argv) { ModelCamera::TestInvDSCM(argc, argv); return 0; } 1632 int main11(int argc, char** argv) { ModelCamera::TestJacRTCM(argc, argv); return 0; } 1633 int main21(int argc, char** argv) { ModelCamera::TestRectBino(argc, argv); return 0; } 1634 #endif 1635 1636 #endif