1.本文要点说明
(1)测试旋转模型:旋转向量NRot=3(仅加法导数)、旋转四元数NRot=4(先加法导数后乘法导数)、旋转矩阵NRot=9(先加法导数后乘法导数)。
(2)测试成像模型:透视模型RTCM、等距模型KBCM、全向模型MUCM、椭球模型EUCM、双球模型DSCM。
(3)测试径切畸变:仅针对透视模型RTCM,NDist=4、5、8、12。
(4)测试针孔效应:仅针对统一模型XUCM,SIMPin为真时收敛更快,正如DSCM论文所说具有更好的数值性。
(5)测试稀疏效应:USEBatch为真时将每次观察的所有点作为一个残差块,USEBatch为假时将每次观察的每个点作为一个残差块。
测试数据来源opencv/samples/data和opencv_extra/testdata/cv/cameracalibration/fisheye/calib-3_stereo_from_JY,复制到../data/calib/cvrtcm/cam1|cam2|all和../data/calib/cvkbcm/cam1|cam2|all。
2.实验测试代码
依赖于OpenCV、Ceres和Spdlog,封装在类ModelCalibration:
(0)CalibConfigs:NRot、CAMModel、NDist、FIXDist、NModel、FIXModel、SIMPin、USEBatch、{MONO_INTRINSIC_GUESS, BINO_INTRINSIC_FIXED, BINO_EXTRINSIC_GUESS}
(1)CeresModels:旋转矩阵参数化DIY::RotationMatrixParameterization用于NRot=9,单位四元数参数化ceres::QuaternionParameterization用于NRot=4,旋转向量参数化DIY::RotationVectorParameterization未使用(需要自定义代价函数),对NRot=3,默认即可。
(2)CalibModels:单目成像残差模型MonoVCM,双目成像残差模型BinoVCM。
(3)CalibUtils:calcBestPinCAM、rectAsPinMAP。
(4)MonoCalib:单目成像解算模型,flags/imaSize/point3D/point2D、camSolid/camPoses/rmsMean/rmsView/errPoints、create/calibrate、init/optimize/updateErr、initRTCM/initKBCM/initXUCM、solvePnP。
(5)BinoCalib:双目成像解算模型,calib1/calib2、cam21Pose/rmsMean/rmsView/errPoints、create/calibrate、init/optimize/updateErr。
(6)CalibAPP:单目成像解算样例MonoAPP,双目解算样例BinoAPP。
遗留问题:initXUCM只针对棋盘板、initRTCM和initKBCM未执行,solvePnP未执行,CalibFlags未执行。
1 #ifndef __ModelCalibration_h__ 2 #define __ModelCalibration_h__ 3 4 #include <cscv/Motion2D.h> 5 6 class ModelCalibration 7 { 8 private://CalibConfigs 9 using SolidPose = ModelRotation::SolidPose; 10 using VisionCM = ModelCamera::VisionCM; 11 using CamSolid = ModelCamera::CamSolid; 12 static const int NProject = 4; //cannot be modified and only for clarity 13 static const int NRot = 4; //support 3 4 9 for rotation vector and quaternion and rotation matrix 14 static const int CAMModel = VisionCM::MUCM; 15 //RTCM: NDist=5 NModel>0 FIXDist=0 FIXModel=1 16 //KBCM: NDist=4 NModle>0 FIXDist=0 FIXModel=1 17 //MUCM: NDist=4 NModel=1 FIXDist=0 FIXModel=0 18 //EUCM: NDist>0 NModel=2 FIXDist=1 FIXModel=0 19 //DSCM: NDist>0 NModel=2 FIXDist=1 FIXModel=0 20 static const int NDist = 4, FIXDist = false; 21 static const int NModel = 1, FIXModel = false; 22 static const int SIMPin = false; 23 static const int USEBatch = false; 24 enum { MONO_INTRINSIC_GUESS, BINO_INTRINSIC_FIXED, BINO_EXTRINSIC_GUESS }; //not used yet 25 26 public://CeresModels 27 //RotationMatrixParameterization: Half Addition Derivative + Half Multiplication Derivative + AutoCostFunction 28 //QuaternionParameterization: Half Addition Derivative + Half Multiplication Derivative + AutoCostFunction 29 //RotationVectorParameterization: Full Multiplication Derivative + ManuCostFunction 30 struct RotationVectorParameterization : public ceres::LocalParameterization 31 { 32 bool Plus(const double* x, const double* delta, double* x_plus_delta) const 33 { 34 #if 1 //Coversion by quaternion 35 double q_x[4], q_delta[4], q_x_plus_delta[4]; 36 ceres::AngleAxisToQuaternion(x, q_x); 37 ceres::AngleAxisToQuaternion(delta, q_delta); 38 ceres::QuaternionProduct(q_delta, q_x, q_x_plus_delta); 39 ceres::QuaternionToAngleAxis(q_x_plus_delta, x_plus_delta); 40 return true; 41 #else //Conversion by matrix 42 double m_x[9], m_delta[9], m_x_plus_delta[9]; 43 ceres::AngleAxisToRotationMatrix(x, ceres::RowMajorAdapter3x3(m_x)); 44 ceres::AngleAxisToRotationMatrix(delta, ceres::RowMajorAdapter3x3(m_delta)); 45 m_x_plus_delta[0] = m_delta[0] * m_x[0] + m_delta[1] * m_x[3] + m_delta[2] * m_x[6]; 46 m_x_plus_delta[1] = m_delta[0] * m_x[1] + m_delta[1] * m_x[4] + m_delta[2] * m_x[7]; 47 m_x_plus_delta[2] = m_delta[0] * m_x[2] + m_delta[1] * m_x[5] + m_delta[2] * m_x[8]; 48 m_x_plus_delta[3] = m_delta[3] * m_x[0] + m_delta[4] * m_x[3] + m_delta[5] * m_x[6]; 49 m_x_plus_delta[4] = m_delta[3] * m_x[1] + m_delta[4] * m_x[4] + m_delta[5] * m_x[7]; 50 m_x_plus_delta[5] = m_delta[3] * m_x[2] + m_delta[4] * m_x[5] + m_delta[5] * m_x[8]; 51 m_x_plus_delta[6] = m_delta[6] * m_x[0] + m_delta[7] * m_x[3] + m_delta[8] * m_x[6]; 52 m_x_plus_delta[7] = m_delta[6] * m_x[1] + m_delta[7] * m_x[4] + m_delta[8] * m_x[7]; 53 m_x_plus_delta[8] = m_delta[6] * m_x[2] + m_delta[7] * m_x[5] + m_delta[8] * m_x[8]; 54 ceres::RotationMatrixToAngleAxis(ceres::RowMajorAdapter3x3((const double*)m_x_plus_delta), x_plus_delta); 55 return true; 56 #endif 57 } 58 59 bool ComputeJacobian(const double* x, double* jacobian) const 60 { 61 jacobian[0] = 1; jacobian[1] = 0; jacobian[2] = 0; 62 jacobian[3] = 0; jacobian[4] = 1; jacobian[5] = 0; 63 jacobian[6] = 0; jacobian[7] = 0; jacobian[8] = 1; 64 return true; 65 } 66 67 int GlobalSize() const { return 3; } 68 69 int LocalSize() const { return 3; } 70 }; 71 72 struct RotationMatrixParameterization : public ceres::LocalParameterization 73 { 74 bool Plus(const double* x, const double* delta, double* x_plus_delta) const 75 { 76 double m_delta[9]; 77 ceres::AngleAxisToRotationMatrix(delta, ceres::RowMajorAdapter3x3(m_delta)); 78 x_plus_delta[0] = m_delta[0] * x[0] + m_delta[1] * x[3] + m_delta[2] * x[6]; 79 x_plus_delta[1] = m_delta[0] * x[1] + m_delta[1] * x[4] + m_delta[2] * x[7]; 80 x_plus_delta[2] = m_delta[0] * x[2] + m_delta[1] * x[5] + m_delta[2] * x[8]; 81 x_plus_delta[3] = m_delta[3] * x[0] + m_delta[4] * x[3] + m_delta[5] * x[6]; 82 x_plus_delta[4] = m_delta[3] * x[1] + m_delta[4] * x[4] + m_delta[5] * x[7]; 83 x_plus_delta[5] = m_delta[3] * x[2] + m_delta[4] * x[5] + m_delta[5] * x[8]; 84 x_plus_delta[6] = m_delta[6] * x[0] + m_delta[7] * x[3] + m_delta[8] * x[6]; 85 x_plus_delta[7] = m_delta[6] * x[1] + m_delta[7] * x[4] + m_delta[8] * x[7]; 86 x_plus_delta[8] = m_delta[6] * x[2] + m_delta[7] * x[5] + m_delta[8] * x[8]; 87 return true; 88 } 89 90 bool ComputeJacobian(const double* x, double* jacobian) const 91 { 92 jacobian[0] = 0; jacobian[1] = x[6]; jacobian[2] = -x[3]; 93 jacobian[3] = 0; jacobian[4] = x[7]; jacobian[5] = -x[4]; 94 jacobian[6] = 0; jacobian[7] = x[8]; jacobian[8] = -x[5]; 95 jacobian[9] = -x[6]; jacobian[10] = 0; jacobian[11] = x[0]; 96 jacobian[12] = -x[7]; jacobian[13] = 0; jacobian[14] = x[1]; 97 jacobian[15] = -x[8]; jacobian[16] = 0; jacobian[17] = x[2]; 98 jacobian[18] = x[3]; jacobian[19] = -x[0]; jacobian[20] = 0; 99 jacobian[21] = x[4]; jacobian[22] = -x[1]; jacobian[23] = 0; 100 jacobian[24] = x[5]; jacobian[25] = -x[2]; jacobian[26] = 0; 101 return true; 102 } 103 104 int GlobalSize() const { return 9; } 105 106 int LocalSize() const { return 3; } 107 }; 108 109 public://CalibModels 110 struct MonoVCM 111 { 112 int nPoint; 113 VisionCM vcm; 114 Mat_<Vec2d> point2D; 115 MonoVCM(double* data3D0, double* data2D0, int nPoint0, int nRot0, int nDist0, int nModel0, int camModel0, bool simPin0) : nPoint(nPoint0), 116 vcm(data3D0, nPoint, nRot0, nDist0, nModel0, camModel0, simPin0) { 117 if (data2D0) Mat_<Vec2d>(nPoint, 1, (Vec2d*)data2D0).copyTo(point2D); 118 } 119 MonoVCM(float* data3D0, float* data2D0, int nPoint0, int nRot0, int nDist0, int nModel0, int camModel0, bool simPin0) : nPoint(nPoint0), 120 vcm(data3D0, nPoint, nRot0, nDist0, nModel0, camModel0, simPin0) { 121 if (data2D0) Mat_<Vec2f>(nPoint, 1, (Vec2f*)data2D0).copyTo(point2D); 122 } 123 template <typename tp> double calcErr(const tp* const rot, const tp* const t, const tp* const K, const tp* const D, const tp* const ab, tp* err2D0 = 0) const 124 { 125 //1.CalcResiduals 126 vector<tp> buf; 127 if (err2D0 == 0) { buf.resize(nPoint * 2); err2D0 = buf.data(); } 128 (*this)(rot, t, K, D, ab, err2D0); 129 130 //2.CalcMeanRms 131 Vec<tp, 2>* err2D = (Vec<tp, 2>*)err2D0; 132 double rmsMean = 0; for (int k = 0; k < nPoint; ++k) rmsMean += sqrt(err2D[k][0] * err2D[k][0] + err2D[k][1] * err2D[k][1]); 133 return rmsMean / nPoint; 134 } 135 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* err2D0) const 136 { 137 //1.ProjectPoints 138 vector<Vec<tp, 2>> point2DEva(nPoint); 139 vcm(rot, t, K, D, ab, point2DEva.data()->val); 140 141 //2.CalcResiduals 142 Vec<tp, 2>* eva2D = point2DEva.data(); 143 Vec<tp, 2>* err2D = (Vec<tp, 2>*)err2D0; 144 const Vec2d* data2D = point2D.ptr<Vec2d>(); 145 for (int k = 0; k < nPoint; ++k) err2D[k] = Vec<tp, 2>(eva2D[k][0] - data2D[k][0], eva2D[k][1] - data2D[k][1]); 146 return true; 147 } 148 }; 149 150 struct BinoVCM 151 { 152 MonoVCM mvcm1; 153 MonoVCM mvcm2; 154 BinoVCM(double* data3D0, double* data2D10, double* data2D20, int nPoint, int nRot0, int nDist0, int nModel0, int camModel0, bool simPin0) : 155 mvcm1(data3D0, data2D10, nPoint, nRot0, nDist0, nModel0, camModel0, simPin0), 156 mvcm2(data3D0, data2D20, nPoint, nRot0, nDist0, nModel0, camModel0, simPin0) { } 157 BinoVCM(float* data3D0, float* data2D10, float* data2D20, int nPoint, int nRot0, int nDist0, int nModel0, int camModel0, bool simPin0) : 158 mvcm1(data3D0, data2D10, nPoint, nRot0, nDist0, nModel0, camModel0, simPin0), 159 mvcm2(data3D0, data2D20, nPoint, nRot0, nDist0, nModel0, camModel0, simPin0) { } 160 template <typename tp> double calcErr(const tp* const rot12, const tp* const t12, const tp* const rot1, const tp* const t1, 161 const tp* const K1, const tp* const D1, const tp* const ab1, 162 const tp* const K2, const tp* const D2, const tp* const ab2, tp* err4D0) const 163 { 164 //1.CalcResiduals 165 vector<tp> buf; 166 if (err4D0 == 0) { buf.resize(mvcm1.nPoint * 4); err4D0 = buf.data(); } 167 (*this)(rot12, t12, rot1, t1, K1, D1, ab1, K2, D2, ab2, err4D0); 168 169 //2.CalcMeanRms 170 Vec<tp, 4>* err4D = (Vec<tp, 4>*)err4D0; 171 double rmsMean = 0; for (int k = 0; k < mvcm1.nPoint; ++k) rmsMean += sqrt(err4D[k][0] * err4D[k][0] + err4D[k][1] * err4D[k][1] + err4D[k][2] * err4D[k][2] + err4D[k][3] * err4D[k][3]); 172 return rmsMean / mvcm1.nPoint; 173 } 174 template <typename tp> bool operator()(const tp* const rot21, const tp* const t21, const tp* const rot1, const tp* const t1, 175 const tp* const K1, const tp* const D1, const tp* const ab1, 176 const tp* const K2, const tp* const D2, const tp* const ab2, tp* err4D0) const 177 { 178 //0. 179 int nRot = mvcm1.vcm.nRot; 180 int nPoint = mvcm1.nPoint; 181 182 //1.ProjectCamera1 183 vector<Vec<tp, 2>> point2D1Eva(nPoint); 184 mvcm1.vcm(rot1, t1, K1, D1, ab1, point2D1Eva.data()->val); 185 186 //2.CalcCam21Pose 187 tp rot2[9], t2[3]; 188 if (nRot == 9) 189 { 190 rot2[0] = rot21[0] * rot1[0] + rot21[1] * rot1[3] + rot21[2] * rot1[6]; 191 rot2[1] = rot21[0] * rot1[1] + rot21[1] * rot1[4] + rot21[2] * rot1[7]; 192 rot2[2] = rot21[0] * rot1[2] + rot21[1] * rot1[5] + rot21[2] * rot1[8]; 193 194 rot2[3] = rot21[3] * rot1[0] + rot21[4] * rot1[3] + rot21[5] * rot1[6]; 195 rot2[4] = rot21[3] * rot1[1] + rot21[4] * rot1[4] + rot21[5] * rot1[7]; 196 rot2[5] = rot21[3] * rot1[2] + rot21[4] * rot1[5] + rot21[5] * rot1[8]; 197 198 rot2[6] = rot21[6] * rot1[0] + rot21[7] * rot1[3] + rot21[8] * rot1[6]; 199 rot2[7] = rot21[6] * rot1[1] + rot21[7] * rot1[4] + rot21[8] * rot1[7]; 200 rot2[8] = rot21[6] * rot1[2] + rot21[7] * rot1[5] + rot21[8] * rot1[8]; 201 202 t2[0] = rot21[0] * t1[0] + rot21[1] * t1[1] + rot21[2] * t1[2] + t21[0]; 203 t2[1] = rot21[3] * t1[0] + rot21[4] * t1[1] + rot21[5] * t1[2] + t21[1]; 204 t2[2] = rot21[6] * t1[0] + rot21[7] * t1[1] + rot21[8] * t1[2] + t21[2]; 205 } 206 else if (nRot == 4) 207 { 208 ceres::QuaternionProduct(rot21, rot1, rot2);//R2=R21*R1 209 ceres::QuaternionRotatePoint(rot21, t1, t2);//t2=R21*t1+t21 210 t2[0] += t21[0]; t2[1] += t21[1]; t2[2] += t21[2]; 211 } 212 else if (nRot == 3) 213 { 214 tp tmp[12]; 215 ceres::AngleAxisToQuaternion(rot21, tmp); 216 ceres::AngleAxisToQuaternion(rot1, tmp + 4); 217 ceres::QuaternionProduct(tmp, tmp + 4, tmp + 8); 218 ceres::QuaternionRotatePoint(tmp, t1, t2); 219 t2[0] += t21[0]; t2[1] += t21[1]; t2[2] += t21[2]; 220 ceres::QuaternionToAngleAxis(tmp + 8, rot2); 221 } 222 else spdlog::critical("Invalid rotation configuration"); 223 224 //3.ProjectCamera2 225 vector<Vec<tp, 2>> point2D2Eva(nPoint); 226 mvcm2.vcm(rot2, t2, K2, D2, ab2, point2D2Eva.data()->val); 227 228 //3.CalcResiduals 229 Vec<tp, 2>* eva2D1 = point2D1Eva.data(); 230 Vec<tp, 2>* eva2D2 = point2D2Eva.data(); 231 Vec<tp, 4>* err4D = (Vec<tp, 4>*)err4D0; 232 const Vec2d* data2D1 = mvcm1.point2D.ptr<Vec2d>(); 233 const Vec2d* data2D2 = mvcm2.point2D.ptr<Vec2d>(); 234 for (int k = 0; k < nPoint; ++k) err4D[k] = Vec<tp, 4>(eva2D1[k][0] - data2D1[k][0], eva2D1[k][1] - data2D1[k][1], eva2D2[k][0] - data2D2[k][0], eva2D2[k][1] - data2D2[k][1]); 235 return true; 236 } 237 }; 238 239 public://CalibUtils 240 static void calcBestPinCAM(Mat_<double> K, Mat_<double> D, Mat_<double> ab, int camModel, bool simPin, Size srcSize, Size newSize, Mat_<double> R, Mat_<double>& P) {} 241 242 static void rectAsPinMAP(Mat_<double> K, Mat_<double> D, Mat_<double> ab, int camModel, bool simPin, Mat_<double> R, Mat_<double> P, Size newSize, Mat_<float>& mapx, Mat_<float>& mapy) 243 { 244 //0.FormatInput 245 if (K(0, 1) != 0) { spdlog::critical("Skew must be zero"); assert(0); } 246 if (D.total() != 5) { spdlog::critical("Number of distortion must be five"); assert(0); } 247 if (ab.total() != 2) { spdlog::critical("Number of model must be two"); assert(0); } 248 if (mapx.rows != newSize.height || mapx.cols != newSize.width) mapx.create(newSize); 249 if (mapy.rows != newSize.height || mapy.cols != newSize.width) mapy.create(newSize); 250 double fx = K(0, 0), fy = K(1, 1), cx = K(0, 2), cy = K(1, 2); 251 double a = ab(0), b = ab(1); 252 double k1 = D(0), k2 = D(1), p1 = D(2), p2 = D(3), k3 = D(4); 253 254 //1.MainTask 255 double sumD = k1 + k2 + p1 + p2 + k3; 256 Matx33d iRiK = Mat_<double>((P * R).inv()); 257 for (int v = 0; v < newSize.height; ++v) 258 for (int u = 0; u < newSize.width; ++u) 259 { 260 //CamPoint 261 double x = u * iRiK.val[0] + v * iRiK.val[1] + iRiK.val[2]; 262 double y = u * iRiK.val[3] + v * iRiK.val[4] + iRiK.val[5]; 263 double z = u * iRiK.val[6] + v * iRiK.val[7] + iRiK.val[8]; 264 265 //NormPoint 266 double iz = 0; 267 if (camModel == VisionCM::RTCM || camModel == VisionCM::KBCM) iz = 1. / z; 268 else if (camModel == VisionCM::MUCM || camModel == VisionCM::EUCM) iz = 1. / (a * sqrt(b * (x * x + y * y) + z * z) + (1. - (simPin ? a : 0)) * z); 269 else if (camModel == VisionCM::DSCM) 270 { 271 double d1 = sqrt(x * x + y * y + z * z); 272 double d2 = sqrt(x * x + y * y + (b * d1 + z) * (b * d1 + z)); 273 iz = 1. / (a * d2 + (1. - (simPin ? a : 0)) * (b * d1 + z)); 274 } 275 else spdlog::critical("Invalid model configuration"); 276 277 double xn = x * iz; 278 double yn = y * iz; 279 280 //DistPoint 281 double xd = xn; 282 double yd = yn; 283 if (sumD != 0) 284 { 285 double xn2 = xn * xn; 286 double yn2 = yn * yn; 287 double r2 = xn2 + yn2; 288 double xnyn = 2 * xn * yn; 289 double r4 = r2 * r2; 290 double r6 = r2 * r4; 291 double r2xn2 = r2 + 2 * xn2; 292 double r2yn2 = r2 + 2 * yn2; 293 double an = 1 + k1 * r2 + k2 * r4 + k3 * r6; 294 xd = xn * an + p1 * xnyn + p2 * r2xn2; 295 yd = yn * an + p2 * xnyn + p1 * r2yn2; 296 } 297 298 //PixelPoint 299 mapx(v, u) = float(fx * xd + cx); 300 mapy(v, u) = float(fy * yd + cy); 301 } 302 } 303 304 public://MonoCalib 305 class MonoCalib 306 { 307 public://ForeEndArgs(Variable) 308 Size chSizeCross; 309 310 public://BackEndArgs(Constant) 311 int flags; 312 Size imaSize; 313 vector<vector<Point3f>> point3D; 314 vector<vector<Point2f>> point2D; 315 void create(vector<vector<Point3f>> p3D, vector<vector<Point2f>> p2D, Size imaSz, Size chSz) 316 { 317 point3D = p3D; point2D = p2D; imaSize = imaSz; chSizeCross = chSz; 318 camSolid.nDist = NDist; 319 camSolid.nModel = NModel; 320 camSolid.camModel = CAMModel; 321 camSolid.simPin = SIMPin; 322 camPoses.resize(point3D.size()); 323 rmsView.create(int(point3D.size()), 1); 324 errPoints.resize(point3D.size()); 325 for (size_t k = 0; k < point3D.size(); ++k) errPoints[k].resize(point3D[k].size()); 326 } 327 328 public://FinalResults(Constant) 329 CamSolid camSolid; 330 vector<SolidPose> camPoses; 331 double rmsMean = -1; 332 Mat_<double> rmsView; 333 vector<vector<Vec2d>> errPoints; 334 335 public://CalibFuncs(Constant) 336 void calibrate() { init(); optimize(); } 337 338 void init(/*Based on rotation vector*/) { camSolid.camModel == VisionCM::RTCM ? initRTCM() : camSolid.camModel == VisionCM::KBCM ? initKBCM() : initXUCM(); } 339 340 void optimize(/*Based on given rotation*/) 341 { 342 //1.CreateModel 343 ceres::Problem problem; 344 for (int k = 0; k < point3D.size(); ++k) 345 { 346 if (USEBatch) 347 { 348 ceres::CostFunction* cereCost = 349 new ceres::AutoDiffCostFunction<MonoVCM, ceres::DYNAMIC, NRot, 3, NProject, NDist, NModel>( 350 new MonoVCM((float*)point3D[k].data(), (float*)point2D[k].data(), int(point3D[k].size()), NRot, camSolid.nDist, camSolid.nModel, camSolid.camModel, camSolid.simPin), 351 int(point3D[k].size() * 2), ceres::TAKE_OWNERSHIP); 352 problem.AddResidualBlock(cereCost, new ceres::CauchyLoss(4.431969), camPoses[k].getRot(NRot), camPoses[k].tdata, camSolid.K, camSolid.D, camSolid.ab); 353 } 354 else 355 for (int i = 0; i < point3D[k].size(); ++i) 356 { 357 ceres::CostFunction* cereCost = 358 new ceres::AutoDiffCostFunction<MonoVCM, ceres::DYNAMIC, NRot, 3, NProject, NDist, NModel>( 359 new MonoVCM((float*)(point3D[k].data() + i), (float*)(point2D[k].data() + i), 1, NRot, camSolid.nDist, camSolid.nModel, camSolid.camModel, camSolid.simPin), 360 2, ceres::TAKE_OWNERSHIP); 361 problem.AddResidualBlock(cereCost, new ceres::CauchyLoss(1.0), camPoses[k].getRot(NRot), camPoses[k].tdata, camSolid.K, camSolid.D, camSolid.ab); 362 } 363 if (NRot == 4) problem.SetParameterization(camPoses[k].getRot(NRot), new ceres::QuaternionParameterization); 364 if (NRot == 9) problem.SetParameterization(camPoses[k].getRot(NRot), new RotationMatrixParameterization); 365 } 366 if (FIXDist) problem.SetParameterBlockConstant(camSolid.D); 367 if (FIXModel) problem.SetParameterBlockConstant(camSolid.ab); 368 369 //2.SolveModel 370 ceres::Solver::Options options; 371 options.num_threads = 8; 372 options.max_num_iterations = 256; 373 options.minimizer_progress_to_stdout = true; 374 ceres::Solver::Summary summary; 375 ceres::Solve(options, &problem, &summary); 376 for (size_t k = 0; k < camPoses.size(); ++k) camPoses[k].setRot(camPoses[k].getRot(NRot), NRot); 377 spdlog::info("Optimization done and report:{}", summary.FullReport()); 378 379 //3.UpdateState 380 updateErr(); //0.2279609352654616 381 spdlog::info("Optimization done and reprojection rms: {}", rmsMean); 382 } 383 384 void updateErr(/*Based on rotation matrix*/) 385 { 386 for (int k = 0; k < point3D.size(); ++k) 387 rmsView(k) = MonoVCM((float*)(point3D[k].data()), (float*)(point2D[k].data()), int(point3D[k].size()), 9, camSolid.nDist, camSolid.nModel, camSolid.camModel, camSolid.simPin).calcErr( 388 camPoses[k].mdata, camPoses[k].tdata, camSolid.K, camSolid.D, camSolid.ab, (double*)errPoints[k].data()); 389 rmsMean = cv::sum(rmsView)[0] / point3D.size();//0.2279609352654616 390 } 391 392 public: 393 void initRTCM() {} 394 void initKBCM() {} 395 void initXUCM(/*Based on rotation vector*/) 396 { 397 //1.BasicInit 398 camSolid.ab[0] = 1;//let degraded to UCM 399 camSolid.ab[1] = camSolid.camModel == VisionCM::DSCM ? 0 : 1;//let degraded to UCM 400 camSolid.K[2] = imaSize.width / 2.0; 401 camSolid.K[3] = imaSize.height / 2.0; 402 for (int k = 0; k < camSolid.nDist; ++k) camSolid.D[k] = 0; 403 404 //2.CoreInit 405 rmsMean = DBL_MAX; 406 const double cx = camSolid.K[2]; 407 const double cy = camSolid.K[3]; 408 for (int k = 0; k < point2D.size(); ++k) 409 for (int i = 0; i < chSizeCross.height; ++i) 410 { 411 //2.1 GetEquations 412 Mat_<double> P(chSizeCross.width, 4); 413 for (int j = 0; j < chSizeCross.width; ++j) 414 { 415 Point2f pt = point2D[k][i * chSizeCross.width + j]; 416 double u = pt.x - cx; 417 double v = pt.y - cy; 418 P(j, 0) = u; 419 P(j, 1) = v; 420 P(j, 2) = 0.5; 421 P(j, 3) = -0.5 * (u * u + v * v); 422 } 423 424 //2.2 SolveEquations 425 Mat_<double> C; 426 SVD::solveZ(P, C); 427 double t = C(0) * C(0) + C(1) * C(1) + C(2) * C(3); 428 if (t < 0.0) continue; 429 430 //2.3 CalcCamParam 431 double d = std::sqrt(1.0 / t); 432 double nx = C(0) * d; 433 double ny = C(1) * d; 434 if (std::hypot(nx, ny) > 0.95) continue; 435 double ffk = std::sqrt(C(2) / C(3)); 436 437 //2.4 CalcCamPose 438 double rmsMean0 = 0; 439 Mat_<double> rmsView0(rmsView.size(), 0.); 440 vector<SolidPose> camPoses0(camPoses.size()); 441 vector<vector<Vec2d>> errPoints0(errPoints.size()); 442 for (int kk = 0; kk < point3D.size(); ++kk) 443 { 444 //1.CalcCamPose 445 vector<Point3f> point3DEx; 446 vector<Point2f> point2DEx; 447 for (int ii = 0; ii < point2D[kk].size(); ++ii) 448 { 449 double x = (point2D[kk][ii].x - cx) / ffk; 450 double y = (point2D[kk][ii].y - cy) / ffk; 451 double z = (1 - x * x - y * y) / 2;//because of a=b=1 D=0 452 if (z < 0.01) continue; // { spdlog::warn("Skip point3DRay[{}] ({}, {}, {}) which is close to or more than 180: z<0.01", ii, x, y, z); continue; } 453 point3DEx.push_back(point3D[kk][ii]); 454 point2DEx.push_back(Point2f(float(x / z), float(y / z))); 455 } 456 if (point3DEx.size() < 8) { spdlog::warn("View{}---Line{}---View{}--ValidPoints{}", k, i, kk, point3DEx.size()); continue; } 457 cv::solvePnP(point3DEx, point2DEx, Mat_<double>::eye(3, 3), noArray(), Mat_<double>(3, 1, camPoses0[kk].vdata), Mat_<double>(3, 1, camPoses0[kk].tdata)); 458 459 //2.AnalyzeCamPose 460 errPoints0[kk].resize(point3D[kk].size()); 461 Matx33d R; cv::Rodrigues(Mat_<double>(3, 1, camPoses0[kk].vdata), R); 462 for (int ii = 0; ii < point3D[kk].size(); ++ii) 463 { 464 double x = R.val[0] * point3D[kk][ii].x + R.val[1] * point3D[kk][ii].y + R.val[2] * point3D[kk][ii].z + camPoses0[kk].tdata[0]; 465 double y = R.val[3] * point3D[kk][ii].x + R.val[4] * point3D[kk][ii].y + R.val[5] * point3D[kk][ii].z + camPoses0[kk].tdata[1]; 466 double z = R.val[6] * point3D[kk][ii].x + R.val[7] * point3D[kk][ii].y + R.val[8] * point3D[kk][ii].z + camPoses0[kk].tdata[2]; 467 double d = sqrt(x * x + y * y + z * z);//because of a=b=1 D=0 468 double iz = 1. / (d + z); //if denominator==0 469 double xn = x * iz; 470 double yn = y * iz; 471 errPoints0[kk][ii] = Vec2d(ffk * xn + cx - point2D[kk][ii].x, ffk * yn + cy - point2D[kk][ii].y); 472 rmsView0(kk) += cv::norm(errPoints0[kk][ii]); 473 } 474 rmsView0(kk) = rmsView0(kk) / point3D[kk].size(); 475 } 476 477 //2.5 ChooseBetterPose 478 rmsMean0 = cv::sum(rmsView0)[0] / point3D.size(); 479 if (rmsMean0 < rmsMean) //1.4185722111399552 480 { 481 rmsMean = rmsMean0; 482 rmsView = rmsView0; 483 camPoses = camPoses0; 484 errPoints = errPoints0; 485 camSolid.K[0] = camSolid.K[1] = ffk; 486 } 487 } 488 489 //3.UpdateState 490 for (size_t k = 0; k < camPoses.size(); ++k) camPoses[k].setRot(camPoses[k].vdata, 3);//Here must be roation vector 491 spdlog::info("Initialization done and reprojection rms: {}", rmsMean);//1.3384170570917158 492 } 493 494 public: 495 void sovlePnP() {} 496 }; 497 498 public://BinoCalib 499 class BinoCalib 500 { 501 public://MonoFuncs(Constant) 502 MonoCalib calib1; 503 MonoCalib calib2; 504 505 public://FinalResults(Constant) 506 SolidPose cam21Pose; 507 double rmsMean = -1; 508 Mat_<double> rmsView; 509 vector<vector<Vec4d>> errPoints; 510 void create(vector<vector<Point3f>> p3D, vector<vector<Point2f>> p2D1, vector<vector<Point2f>> p2D2, Size imaSz, Size chSz) 511 { 512 calib1.create(p3D, p2D1, imaSz, chSz); 513 calib2.create(p3D, p2D2, imaSz, chSz); 514 rmsView.create(int(p3D.size()), 1); 515 errPoints.resize(calib1.point3D.size()); 516 for (size_t k = 0; k < calib1.point3D.size(); ++k) errPoints[k].resize(calib1.point3D[k].size()); 517 } 518 519 public://CalibFuncs(Constant) 520 void calibrate() { init(); optimize(); } 521 522 void init(/*Based on unit quaternion*/) 523 { 524 //1.BasicInit 525 const ModelCamera::CamSolid& camSolid = calib1.camSolid; 526 const vector<vector<Point3f>>& point3D = calib1.point3D; 527 const vector<vector<Point2f>>& point2D1 = calib1.point2D; 528 const vector<vector<Point2f>>& point2D2 = calib2.point2D; 529 calib1.calibrate(); //0.228574 530 calib2.calibrate(); //0.273371 531 532 //2.CoreInit 533 this->rmsMean = DBL_MAX; 534 for (int k = 0; k < point3D.size(); ++k) 535 { 536 //2.1 GetCam1PoseAndCam2Pose 537 const SolidPose& cam1Pose = calib1.camPoses[k]; 538 const SolidPose& cam2Pose = calib2.camPoses[k]; 539 540 //2.2 CalcCam21Pose 541 SolidPose cam21Pose0; 542 ceres::QuaternionProduct(cam2Pose.qdata, Vec4d(cam1Pose.qdata[0], -cam1Pose.qdata[1], -cam1Pose.qdata[2], -cam1Pose.qdata[3]).val, cam21Pose0.qdata);//R21=R2*Inv(R1) 543 ceres::QuaternionRotatePoint(cam21Pose0.qdata, cam1Pose.tdata, cam21Pose0.tdata);//t12=-R21*t1+t2 544 for (int t = 0; t < 3; ++t) cam21Pose0.tdata[t] = -cam21Pose0.tdata[t] + cam2Pose.tdata[t]; 545 546 //2.3 CalcCam2Pose 547 vector<SolidPose> cam2Poses0(point3D.size()); 548 for (int i = 0; i < point3D.size(); ++i) 549 { 550 const double* cam1Q = calib1.camPoses[i].qdata; 551 const double* cam1T = calib1.camPoses[i].tdata; 552 ceres::QuaternionProduct(cam21Pose0.qdata, cam1Q, cam2Poses0[i].qdata);//R2=R21*R1 553 ceres::QuaternionRotatePoint(cam21Pose0.qdata, cam1T, cam2Poses0[i].tdata);//t2=R21*t1+t21 554 for (int t = 0; t < 3; ++t) cam2Poses0[i].tdata[t] += cam21Pose0.tdata[t]; 555 } 556 557 //2.4 AnalyzeCam21Pose 558 Mat_<double> rmsView0(rmsView.size(), 0.); 559 vector<vector<Vec2d>> errPoints0(errPoints.size()); 560 for (int i = 0; i < point3D.size(); ++i) 561 rmsView0(i) = MonoVCM((float*)point3D[i].data(), (float*)point2D2[i].data(), int(point3D[i].size()), 4, camSolid.nDist, camSolid.nModel, camSolid.camModel, camSolid.simPin).calcErr( 562 cam2Poses0[i].qdata, cam2Poses0[i].tdata, calib2.camSolid.K, calib2.camSolid.D, calib2.camSolid.ab);//Here must be quaternion 563 564 //2.5 ChooseBetterPose 565 double rmsMean0 = cv::sum(rmsView0)[0] / point3D.size(); 566 if (rmsMean0 < this->rmsMean)//0.814502 567 { 568 this->rmsMean = rmsMean0; 569 this->rmsView = rmsView0; 570 this->cam21Pose = cam21Pose0; 571 } 572 } 573 574 //3.UpdateState 575 this->cam21Pose.setRot(cam21Pose.qdata, 4);//0.999380396759363, -0.0031299638455660334, -0.002043827564730205, -0.0349978380519228 //Here must be quaternion 576 spdlog::info("Initialization done and quat: {}, {}, {}, {}", this->cam21Pose.qdata[0], this->cam21Pose.qdata[1], this->cam21Pose.qdata[2], this->cam21Pose.qdata[3]); 577 spdlog::info("Initialization done and tvec: {}, {}, {}", this->cam21Pose.tdata[0], this->cam21Pose.tdata[1], this->cam21Pose.tdata[2]);//-203.4071041001514, 5.738333250873239, -0.39390447090420366 578 spdlog::info("Initialization done and reprojection rms2: {}", this->rmsMean);//0.5493172947025333 579 } 580 581 void optimize(/*Based on given rotation*/) 582 { 583 //0.GetInput 584 const ModelCamera::CamSolid& camSolid = calib1.camSolid; 585 const vector<vector<Point3f>>& point3D = calib1.point3D; 586 const vector<vector<Point2f>>& point2D1 = calib1.point2D; 587 const vector<vector<Point2f>>& point2D2 = calib2.point2D; 588 589 //1.CreateModel 590 ceres::Problem problem; 591 for (int k = 0; k < point3D.size(); ++k) 592 { 593 if (USEBatch) 594 { 595 ceres::CostFunction* cereCost = 596 new ceres::AutoDiffCostFunction<BinoVCM, ceres::DYNAMIC, NRot, 3, NRot, 3, NProject, NDist, NModel, NProject, NDist, NModel>( 597 new BinoVCM((float*)point3D[k].data(), (float*)point2D1[k].data(), (float*)point2D2[k].data(), int(point3D[k].size()), NRot, camSolid.nDist, camSolid.nModel, camSolid.camModel, camSolid.simPin), 598 int(point3D[k].size() * 4), ceres::TAKE_OWNERSHIP); 599 problem.AddResidualBlock(cereCost, new ceres::CauchyLoss(5.5), 600 this->cam21Pose.getRot(NRot), this->cam21Pose.tdata, calib1.camPoses[k].getRot(NRot), calib1.camPoses[k].tdata, 601 calib1.camSolid.K, calib1.camSolid.D, calib1.camSolid.ab, 602 calib2.camSolid.K, calib2.camSolid.D, calib2.camSolid.ab); 603 } 604 else 605 for (size_t i = 0; i < point3D[k].size(); ++i) 606 { 607 ceres::CostFunction* cereCost = 608 new ceres::AutoDiffCostFunction<BinoVCM, ceres::DYNAMIC, NRot, 3, NRot, 3, NProject, NDist, NModel, NProject, NDist, NModel>( 609 new BinoVCM((float*)(point3D[k].data() + i), (float*)(point2D1[k].data() + i), (float*)(point2D2[k].data() + i), 1, NRot, camSolid.nDist, camSolid.nModel, camSolid.camModel, camSolid.simPin), 610 4, ceres::TAKE_OWNERSHIP); 611 problem.AddResidualBlock(cereCost, new ceres::CauchyLoss(1.0), 612 this->cam21Pose.getRot(NRot), this->cam21Pose.tdata, calib1.camPoses[k].getRot(NRot), calib1.camPoses[k].tdata, 613 calib1.camSolid.K, calib1.camSolid.D, calib1.camSolid.ab, 614 calib2.camSolid.K, calib2.camSolid.D, calib2.camSolid.ab); 615 } 616 if(NRot == 4) problem.SetParameterization(calib1.camPoses[k].getRot(NRot), new ceres::QuaternionParameterization); 617 if(NRot == 9) problem.SetParameterization(calib1.camPoses[k].getRot(NRot), new RotationMatrixParameterization); 618 } 619 if (NRot == 4) problem.SetParameterization(this->cam21Pose.getRot(NRot), new ceres::QuaternionParameterization); 620 if (NRot == 9) problem.SetParameterization(this->cam21Pose.getRot(NRot), new RotationMatrixParameterization); 621 if (FIXDist) problem.SetParameterBlockConstant(calib1.camSolid.D); 622 if (FIXDist) problem.SetParameterBlockConstant(calib2.camSolid.D); 623 if (FIXModel) problem.SetParameterBlockConstant(calib1.camSolid.ab); 624 if (FIXModel) problem.SetParameterBlockConstant(calib2.camSolid.ab); 625 626 //2.SolveModel 627 ceres::Solver::Options options; 628 options.num_threads = 8; 629 options.max_num_iterations = 256; 630 options.minimizer_progress_to_stdout = true; 631 ceres::Solver::Summary summary; 632 ceres::Solve(options, &problem, &summary); 633 this->cam21Pose.setRot(this->cam21Pose.getRot(NRot), NRot); 634 for (int k = 0; k < point3D.size(); ++k)//UpdateCam2Pose 635 { 636 calib1.camPoses[k].setRot(calib1.camPoses[k].getRot(NRot), NRot); 637 ceres::QuaternionProduct(this->cam21Pose.qdata, calib1.camPoses[k].qdata, calib2.camPoses[k].qdata);//R2=R21*R1 638 ceres::QuaternionRotatePoint(this->cam21Pose.qdata, calib1.camPoses[k].tdata, calib2.camPoses[k].tdata);//t2=R21*t1+t12 639 for (int t = 0; t < 3; ++t) calib2.camPoses[k].tdata[t] += this->cam21Pose.tdata[t]; 640 calib2.camPoses[k].setRot(calib2.camPoses[k].qdata, 4);//Here must be quaternion 641 } 642 spdlog::info("Optimization done and report:{}", summary.FullReport()); 643 644 //3.UpdateState 645 this->updateErr(); 646 calib1.updateErr(); 647 calib2.updateErr(); 648 spdlog::info("Optimization done and reprojection rms: {}", this->rmsMean);//0.346291 649 spdlog::info("Optimization done and reprojection rms1: {}", calib1.rmsMean);//0.237126 650 spdlog::info("Optimization done and reprojection rms2: {}", calib2.rmsMean);//0.284363 651 } 652 653 void updateErr(/*Based on rotation matrix*/) 654 { 655 const ModelCamera::CamSolid& camSolid = calib1.camSolid; 656 for (int k = 0; k < calib1.point3D.size(); ++k) 657 rmsView(k) = BinoVCM((float*)(calib1.point3D[k].data()), (float*)(calib1.point2D[k].data()), (float*)(calib2.point2D[k].data()), int(calib1.point3D.size()), 9, camSolid.nDist, camSolid.nModel, camSolid.camModel, camSolid.simPin).calcErr( 658 this->cam21Pose.mdata, this->cam21Pose.tdata, calib1.camPoses[k].mdata, calib1.camPoses[k].tdata, 659 calib1.camSolid.K, calib1.camSolid.D, calib1.camSolid.ab, 660 calib2.camSolid.K, calib2.camSolid.D, calib2.camSolid.ab, (double*)errPoints[k].data()); 661 this->rmsMean = cv::sum(rmsView)[0] / calib1.point3D.size(); 662 } 663 }; 664 665 public://CalibAPP 666 class MonoAPP 667 { 668 public://CalibArgs 669 bool doCalib = true; 670 bool doRect = true; 671 bool doUnd = true; 672 string detDir = "../data/calib/cvkbcm/cam1"; 673 Size calibSize = Size(1280, 800); 674 int chSqrSide = 50; 675 Size chSizeCross = Size(8, 6); 676 int chDetFlag = 3; 677 678 public://RectArgs 679 Size rectSize = calibSize; 680 Vec4d rectPlan = Vec4d(0.4, 30, 0, 0); 681 void resetViewPose(Vec4d rectPlan0) { rectPlan = rectPlan0; rectPath = fmt::format("{}/rst/RectF{}R{}P{}Y{}.yml", detDir, rectPlan[0], rectPlan[1], rectPlan[2], rectPlan[3]); } 682 683 public://AutoParams 684 string calibPath = fmt::format("{}/rst/Calib.yml", detDir); 685 string rectPath = fmt::format("{}/rst/RectF{}R{}P{}Y{}.yml", detDir, rectPlan[0], rectPlan[1], rectPlan[2], rectPlan[3]); 686 687 public://CalibParams 688 int camModel; 689 int simPin; 690 Mat_<double> K = Mat_<double>(3, 3, 0.); 691 Mat_<double> D = Mat_<double>(5, 1, 0.); 692 Mat_<double> ab = Mat_<double>(2, 1, 0.); 693 Mat_<double> poses; 694 Mat_<double> rmsView; 695 double rmsMean; 696 vector<vector<Vec2d>> errPoints; 697 vector<vector<Point3f>> point3D; 698 vector<vector<Point2f>> point2D; 699 700 public://RectParams 701 Mat_<double> P; 702 Mat_<double> R; 703 Mat_<float> mapx; 704 Mat_<float> mapy; 705 706 public: 707 void save(string path) 708 { 709 FileStorage fs(path, FileStorage::WRITE); 710 fs << "camModel" << camModel; 711 fs << "simPin" << simPin; 712 fs << "K" << K; 713 fs << "D" << D; 714 fs << "ab" << ab; 715 fs << "P" << P; 716 fs << "R" << R; 717 fs << "poses" << poses; 718 fs << "rmsMean" << rmsMean; 719 fs << "rmsView" << rmsView; 720 fs << "errPoints" << errPoints; 721 fs << "point3D" << point3D; 722 fs << "point2D" << point2D; 723 fs << "mapx" << mapx; 724 fs << "mapy" << mapy; 725 fs.release(); 726 } 727 728 void read(string path) 729 { 730 FileStorage fs(path, FileStorage::READ); 731 fs["camModel"] >> camModel; 732 fs["simPin"] >> simPin; 733 fs["K"] >> K; 734 fs["D"] >> D; 735 fs["ab"] >> ab; 736 fs["P"] >> P; 737 fs["R"] >> R; 738 fs["poses"] >> poses; 739 fs["rmsMean"] >> rmsMean; 740 fs["rmsView"] >> rmsView; 741 fs["errPoints"] >> errPoints; 742 fs["point3D"] >> point3D; 743 fs["point2D"] >> point2D; 744 fs["mapx"] >> mapx; 745 fs["mapy"] >> mapy; 746 fs.release(); 747 }; 748 749 public: 750 ModelCalibration::MonoCalib monoCalib; 751 void run() 752 { 753 //1.Calib 754 if (doCalib) 755 { 756 //1.1 Detection 757 monoDetCHBD(detDir, chSizeCross, float(chSqrSide), chDetFlag, point2D, point3D); 758 monoCalib.create(point3D, point2D, calibSize, chSizeCross); 759 760 //1.2 Calibration 761 monoCalib.calibrate(); 762 camModel = monoCalib.camSolid.camModel; 763 simPin = monoCalib.camSolid.simPin; 764 K << monoCalib.camSolid.K[0], 0, monoCalib.camSolid.K[2], 0, monoCalib.camSolid.K[1], monoCalib.camSolid.K[3], 0, 0, 1; 765 for (int k = 0; k < monoCalib.camSolid.nDist; ++k) D(k) = monoCalib.camSolid.D[k]; 766 ab(0) = monoCalib.camSolid.ab[0]; ab(1) = monoCalib.camSolid.ab[1]; 767 poses = Mat_<double>(int(monoCalib.camPoses.size()), sizeof(monoCalib.camPoses[0].data) / sizeof(double), (double*)monoCalib.camPoses.data()); 768 rmsView = monoCalib.rmsView; 769 rmsMean = monoCalib.rmsMean; 770 errPoints = monoCalib.errPoints; 771 utils::fs::createDirectories(utils::fs::getParent(calibPath)); save(calibPath); 772 } 773 else read(calibPath); 774 775 //2.Rect 776 if (doRect) 777 { 778 R.create(3, 3); ceres::EulerAnglesToRotationMatrix(Vec3d(rectPlan[1], rectPlan[2], rectPlan[3]).val, 3, R.ptr<double>()); 779 P.create(3, 3); P << K(0, 0) * rectPlan[0] / (1 + (simPin ? 0 : ab(0))), 0, rectSize.width * 0.5, 0, K(1, 1)* rectPlan[0] / (1 + (simPin ? 0 : ab(0))), rectSize.height * 0.5, 0, 0, 1; 780 ModelCalibration::rectAsPinMAP(K, D, ab, camModel, simPin, R, P, rectSize, mapx, mapy); 781 save(rectPath); 782 } 783 784 //3.Undistort 785 if (doUnd) monoUnd(mapx, mapy, detDir); 786 } 787 788 public: 789 static void monoDetCHBD(string detDir, Size chSizeCross, float chSqrSide, int chDetFlag, 790 vector<vector<Point2f>>& point2D, vector<vector<Point3f>>& point3D, int chWinSize = 5, string chPattern = "*", int chShowMS = 10) 791 { 792 //0.Initialize 793 point2D.clear(); 794 point3D.clear(); 795 if (chShowMS >= 0) cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 796 797 //1.DetectPoint2D 798 int cnt = 0; 799 vector<string> filePaths = Tool2D::globPaths(detDir, chPattern, 0, false); 800 for (size_t fileId = 0; fileId < filePaths.size(); ++fileId) 801 { 802 //1.1 803 Mat_<Vec3b> color = imread(filePaths[fileId]); 804 Mat_<uchar> gray; cvtColor(color, gray, COLOR_BGR2GRAY); 805 806 //1.2 807 vector<Point2f> corner2D; 808 bool found = findChessboardCorners(gray, chSizeCross, corner2D, chDetFlag); 809 if (found == false) 810 { 811 Mat_<uchar> tmpIma; resize(gray, tmpIma, gray.size() / 2); 812 found = findChessboardCorners(tmpIma, chSizeCross, corner2D, chDetFlag); 813 for (size_t k = 0; k < corner2D.size(); ++k) corner2D[k] *= 2; 814 } 815 816 //1.3 817 if (found) cornerSubPix(gray, corner2D, Size(chWinSize, chWinSize), Size(-1, -1), TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, 0.01)); 818 if (found) drawChessboardCorners(color, chSizeCross, corner2D, found); 819 if (found) point2D.push_back(corner2D); 820 string tips = fmt::format("{} detect {} and current vaild views {}", found ? "Could" : "Cannot", filePaths[fileId], cnt += found); 821 if (!found) spdlog::warn(tips); 822 if (chShowMS >= 0) cv::displayStatusBar(__FUNCTION__, tips, 1000); 823 824 //1.4 825 if (chShowMS < 0) continue; 826 cv::imshow(__FUNCTION__, color); 827 cv::waitKey(chShowMS); 828 } if (chShowMS >= 0) cv::destroyWindow(__FUNCTION__); 829 830 //2.CalcPoint3D 831 vector<Point3f> corner3D; 832 for (int i = 0; i < chSizeCross.height; ++i) 833 for (int j = 0; j < chSizeCross.width; ++j) 834 corner3D.push_back(Point3f(j * chSqrSide, i * chSqrSide, 0)); 835 point3D.assign(cnt, corner3D); 836 } 837 838 static void monoUnd(Mat_<float> mapx, Mat_<float> mapy, string undDir, string undPattern = "*", int undShowMS = 10) 839 { 840 //0.Initialize 841 string undOut = undDir + "/rst"; 842 utils::fs::createDirectories(undOut); 843 if (undShowMS >= 0) cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 844 845 //1.UndCamera 846 vector<string> filePaths = Tool2D::globPaths(undDir, undPattern, 0, false); 847 for (size_t fileId = 0; fileId < filePaths.size(); ++fileId) 848 { 849 //1.1 850 Mat_<Vec3b> color = imread(filePaths[fileId], 1); 851 Mat_<uchar> gray; cvtColor(color, gray, COLOR_BGR2GRAY); 852 853 //1.2 854 Mat_<uchar> undIma; 855 remap(gray, undIma, mapx, mapy, INTER_LINEAR); 856 857 //1.3 858 Mat_<uchar> catIma; 859 if (gray.size() != undIma.size()) resize(gray, gray, undIma.size()); 860 hconcat(gray, undIma, catIma); 861 cv::imwrite(fmt::format("{}/und_{}", undOut, Tool2D::pathProps(filePaths[fileId])[4]), catIma); 862 if (undShowMS >= 0) cv::displayStatusBar(__FUNCTION__, "Undistorting " + filePaths[fileId], 1000); 863 864 //1.4 865 if (undShowMS < 0) continue; 866 cv::imshow(__FUNCTION__, catIma); 867 cv::waitKey(undShowMS); 868 } if (undShowMS >= 0) cv::destroyWindow(__FUNCTION__); 869 } 870 }; 871 872 class BinoAPP 873 { 874 public://CalibArgs 875 bool doCalib = true; 876 bool doRect = true; 877 string detDir = "../data/calib/cvkbcm/all"; 878 Size calibSize = Size(1280, 800); 879 int chSqrSide = 50; 880 Size chSizeCross = Size(8, 6); 881 int chDetFlag = 3; 882 883 public://RectArgs 884 Size rectSize = calibSize; 885 Vec4d rectPlan = Vec4d(0.4, 30, 0, 0); 886 string calibPath = fmt::format("{}/rst/Calib.yml", detDir); 887 string rectPath = fmt::format("{}/rst/RectF{}R{}P{}Y{}.yml", detDir, rectPlan[0], rectPlan[1], rectPlan[2], rectPlan[3]); 888 889 public://CalibParams 890 int camModel; 891 int simPin; 892 Mat_<double> K1 = Mat_<double>(3, 3, 0.); 893 Mat_<double> K2 = Mat_<double>(3, 3, 0.); 894 Mat_<double> D1 = Mat_<double>(5, 1, 0.); 895 Mat_<double> D2 = Mat_<double>(5, 1, 0.); 896 Mat_<double> ab1 = Mat_<double>(2, 1, 0.); 897 Mat_<double> ab2 = Mat_<double>(2, 1, 0.); 898 Mat_<double> R; 899 Mat_<double> T; 900 Mat_<double> rmsView; 901 double rmsMean; 902 vector<vector<Vec4d>> errPoints; 903 vector<vector<Point2f>> point2D1; 904 vector<vector<Point2f>> point2D2; 905 vector<vector<Point3f>> point3D; 906 907 public://RectParams 908 Mat_<double> P1; 909 Mat_<double> P2; 910 Mat_<double> R1; 911 Mat_<double> R2; 912 Mat_<float> mapx1; 913 Mat_<float> mapy1; 914 Mat_<float> mapx2; 915 Mat_<float> mapy2; 916 917 public://IOActions 918 void save(string path) 919 { 920 FileStorage fs(path, FileStorage::WRITE); 921 fs << "camModel" << camModel; 922 fs << "simPin" << simPin; 923 fs << "K1" << K1; 924 fs << "K2" << K2; 925 fs << "D1" << D1; 926 fs << "D2" << D2; 927 fs << "ab1" << ab1; 928 fs << "ab2" << ab2; 929 fs << "R" << R; 930 fs << "T" << T; 931 fs << "P1" << P1; 932 fs << "P2" << P2; 933 fs << "R1" << R1; 934 fs << "R2" << R2; 935 fs << "rmsMean" << rmsMean; 936 fs << "rmsView" << rmsView; 937 fs << "errPoints" << errPoints; 938 fs << "point3D" << point3D; 939 fs << "point2D1" << point2D1; 940 fs << "point2D2" << point2D2; 941 fs << "mapx1" << mapx1; 942 fs << "mapy1" << mapy1; 943 fs << "mapx2" << mapx2; 944 fs << "mapy2" << mapy2; 945 fs.release(); 946 } 947 948 void read(string path) 949 { 950 FileStorage fs(path, FileStorage::READ); 951 fs["camModel"] >> camModel; 952 fs["simPin"] >> simPin; 953 fs["K1"] >> K1; 954 fs["K2"] >> K2; 955 fs["D1"] >> D1; 956 fs["D2"] >> D2; 957 fs["ab1"] >> ab1; 958 fs["ab2"] >> ab2; 959 fs["R"] >> R; 960 fs["T"] >> T; 961 fs["P1"] >> P1; 962 fs["P2"] >> P2; 963 fs["R1"] >> R1; 964 fs["R2"] >> R2; 965 fs["rmsMean"] >> rmsMean; 966 fs["rmsView"] >> rmsView; 967 fs["errPoints"] >> errPoints; 968 fs["point3D"] >> point3D; 969 fs["point2D1"] >> point2D1; 970 fs["point2D2"] >> point2D2; 971 fs["mapx1"] >> mapx1; 972 fs["mapy1"] >> mapy1; 973 fs["mapx2"] >> mapx2; 974 fs["mapy2"] >> mapy2; 975 fs.release(); 976 }; 977 978 public://MainTask 979 ModelCalibration::BinoCalib binoCalib; 980 void run() 981 { 982 //1.DetectionAndCalibbration 983 if (doCalib) 984 { 985 //1.1 Detection 986 binoDetCHBD(detDir, chSizeCross, float(chSqrSide), chDetFlag, point2D1, point2D2, point3D); 987 binoCalib.create(point3D, point2D1, point2D2, calibSize, chSizeCross); 988 989 //1.2 Calibbration 990 binoCalib.calibrate(); 991 camModel = binoCalib.calib1.camSolid.camModel; 992 simPin = binoCalib.calib2.camSolid.simPin; 993 K1 << binoCalib.calib1.camSolid.K[0], 0, binoCalib.calib1.camSolid.K[2], 0, binoCalib.calib1.camSolid.K[1], binoCalib.calib1.camSolid.K[3], 0, 0, 1; 994 K2 << binoCalib.calib2.camSolid.K[0], 0, binoCalib.calib2.camSolid.K[2], 0, binoCalib.calib2.camSolid.K[1], binoCalib.calib2.camSolid.K[3], 0, 0, 1; 995 for (int k = 0; k < binoCalib.calib1.camSolid.nDist; ++k) D1(k) = binoCalib.calib1.camSolid.D[k]; 996 for (int k = 0; k < binoCalib.calib2.camSolid.nDist; ++k) D2(k) = binoCalib.calib2.camSolid.D[k]; 997 ab1(0) = binoCalib.calib1.camSolid.ab[0]; ab1(1) = binoCalib.calib1.camSolid.ab[1]; 998 ab2(0) = binoCalib.calib2.camSolid.ab[0]; ab2(1) = binoCalib.calib2.camSolid.ab[1]; 999 R = Mat_<double>(3, 3, binoCalib.cam21Pose.mdata); 1000 T = Mat_<double>(3, 1, binoCalib.cam21Pose.tdata); 1001 rmsView = binoCalib.rmsView; 1002 rmsMean = binoCalib.rmsMean; 1003 errPoints = binoCalib.errPoints; 1004 utils::fs::createDirectories(utils::fs::getParent(calibPath)); save(calibPath); 1005 } 1006 else read(calibPath); 1007 1008 //2.RectificationAndUndistortion 1009 if (doRect) 1010 { 1011 //2.1 Rectification 1012 stereoRectify(Matx33d::eye(), Matx41d(), Matx33d::eye(), Matx41d(), Size(640, 480), R, T, R1, R2, Matx34d(), Matx34d(), Matx44d()); 1013 Mat_<double> viewR(3, 3); ceres::EulerAnglesToRotationMatrix(Vec3d(rectPlan[1], rectPlan[2], rectPlan[3]).val, 3, viewR.ptr<double>()); 1014 R1 = viewR * R1; 1015 R2 = viewR * R2; 1016 P1.create(3, 3); P1 << K1(0, 0) * rectPlan[0] / (1 + (simPin ? 0 : ab1(0))), 0, rectSize.width * 0.5, 0, K1(1, 1)* rectPlan[0] / (1 + (simPin ? 0 : ab1(0))), rectSize.height * 0.5, 0, 0, 1; 1017 P2.create(3, 3); P2 << K2(0, 0) * rectPlan[0] / (1 + (simPin ? 0 : ab2(0))), 0, rectSize.width * 0.5, 0, K2(1, 1)* rectPlan[0] / (1 + (simPin ? 0 : ab2(0))), rectSize.height * 0.5, 0, 0, 1; 1018 P1 = P2 = (P1 + P2) * 0.5; 1019 ModelCalibration::rectAsPinMAP(K1, D1, ab1, camModel, simPin, R1, P1, rectSize, mapx1, mapy1); 1020 ModelCalibration::rectAsPinMAP(K2, D2, ab2, camModel, simPin, R2, P2, rectSize, mapx2, mapy2); 1021 save(rectPath); 1022 1023 //2.2 Undistortion 1024 binoUnd(mapx1, mapy1, mapx2, mapy2, detDir); 1025 } 1026 } 1027 1028 public://Utils 1029 static void binoDetCHBD(string detDir, Size chSizeCross, float chSqrSide, int chDetFlag, 1030 vector<vector<Point2f>>& point2D1, vector<vector<Point2f>>& point2D2, vector<vector<Point3f>>& point3D, int chWinSize = 5, string chPattern = "*", int chShowMS = 10) 1031 { 1032 //0.Initialize 1033 point2D1.clear(); 1034 point2D2.clear(); 1035 point3D.clear(); 1036 if (chShowMS >= 0) cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 1037 1038 //1.DetectPoint2D 1039 int cnt = 0; 1040 vector<string> filePaths = Tool2D::globPaths(detDir, chPattern, 0, false); 1041 for (size_t fileId = 0; fileId < filePaths.size(); ++fileId) 1042 { 1043 //1.1 1044 Mat_<Vec3b> color = imread(filePaths[fileId]); 1045 Mat_<uchar> gray1; cvtColor(color.colRange(0, color.cols >> 1), gray1, COLOR_BGR2GRAY); 1046 Mat_<uchar> gray2; cvtColor(color.colRange(color.cols >> 1, color.cols), gray2, COLOR_BGR2GRAY); 1047 1048 //1.2 1049 vector<Point2f> corner2D1; 1050 vector<Point2f> corner2D2; 1051 bool found1 = findChessboardCorners(gray1, chSizeCross, corner2D1, chDetFlag); 1052 bool found2 = findChessboardCorners(gray2, chSizeCross, corner2D2, chDetFlag); 1053 if (found1 == false) 1054 { 1055 Mat_<uchar> tmpIma1; resize(gray1, tmpIma1, gray1.size() / 2); 1056 found1 = findChessboardCorners(tmpIma1, chSizeCross, corner2D1, chDetFlag); 1057 for (size_t k = 0; k < corner2D1.size(); ++k) corner2D1[k] *= 2; 1058 } 1059 if (found2 == false) 1060 { 1061 Mat_<uchar> tmpIma2; resize(gray2, tmpIma2, gray2.size() / 2); 1062 found2 = findChessboardCorners(tmpIma2, chSizeCross, corner2D2, chDetFlag); 1063 for (size_t k = 0; k < corner2D2.size(); ++k) corner2D2[k] *= 2; 1064 } 1065 1066 //2.4 1067 if (found1 == true && found2 == true) 1068 { 1069 cornerSubPix(gray1, corner2D1, Size(chWinSize, chWinSize), Size(-1, -1), TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, 0.01)); 1070 cornerSubPix(gray2, corner2D2, Size(chWinSize, chWinSize), Size(-1, -1), TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, 0.01)); 1071 drawChessboardCorners(color.colRange(0, color.cols >> 1), chSizeCross, corner2D1, found1); 1072 drawChessboardCorners(color.colRange(color.cols >> 1, color.cols), chSizeCross, corner2D2, found2); 1073 point2D1.push_back(corner2D1); 1074 point2D2.push_back(corner2D2); 1075 } 1076 string tips = fmt::format("{} detect {} and current vaild views {}", found1 && found2 ? "Could" : "Cannot", filePaths[fileId], cnt += (found1 && found2)); 1077 if (!found1 || !found2) spdlog::warn(tips); 1078 if (chShowMS >= 0) cv::displayStatusBar(__FUNCTION__, tips, 1000); 1079 1080 //2.5 1081 if (chShowMS < 0) continue; 1082 cv::imshow(__FUNCTION__, color); 1083 cv::waitKey(chShowMS); 1084 } if (chShowMS >= 0) cv::destroyWindow(__FUNCTION__); 1085 1086 //3. CalcPoint3D 1087 vector<Point3f> corner3D; 1088 for (int i = 0; i < chSizeCross.height; ++i) 1089 for (int j = 0; j < chSizeCross.width; ++j) 1090 corner3D.push_back(Point3f(j * chSqrSide, i * chSqrSide, 0)); 1091 point3D.assign(cnt, corner3D); 1092 } 1093 1094 static void binoUnd(Mat_<float> mapx1, Mat_<float> mapy1, Mat_<float> mapx2, Mat_<float> mapy2, string undDir, string undPattern = "*", int undShowMS = 10) 1095 { 1096 //1.Initialize 1097 string undOut = undDir + "/rst"; 1098 utils::fs::createDirectories(undOut); 1099 if (undShowMS >= 0) cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 1100 1101 //2.UndCamera 1102 vector<string> filePaths = Tool2D::globPaths(undDir, undPattern, 0, false); 1103 for (size_t fileId = 0; fileId < filePaths.size(); ++fileId) 1104 { 1105 //2.1 1106 Mat_<Vec3b> color = imread(filePaths[fileId], 1); 1107 Mat_<uchar> gray1; cvtColor(color.colRange(0, color.cols >> 1), gray1, COLOR_BGR2GRAY); 1108 Mat_<uchar> gray2; cvtColor(color.colRange(color.cols >> 1, color.cols), gray2, COLOR_BGR2GRAY); 1109 1110 //2.2 1111 Mat_<uchar> undIma1; remap(gray1, undIma1, mapx1, mapy1, INTER_LINEAR); 1112 Mat_<uchar> undIma2; remap(gray2, undIma2, mapx2, mapy2, INTER_LINEAR); 1113 1114 //2.3 1115 Mat_<uchar> catIma; 1116 hconcat(undIma1, undIma2, catIma); 1117 cv::imwrite(fmt::format("{}/und_{}", undOut, Tool2D::pathProps(filePaths[fileId])[4]), catIma); 1118 if (undShowMS >= 0) cv::displayStatusBar(__FUNCTION__, "Undistorting " + filePaths[fileId], 1000); 1119 1120 //2.4 1121 if (undShowMS < 0) continue; 1122 cv::imshow(__FUNCTION__, catIma); 1123 cv::waitKey(undShowMS); 1124 } if (undShowMS >= 0) cv::destroyWindow(__FUNCTION__); 1125 } 1126 }; 1127 }; 1128 1129 #ifdef ModelCalibrationTest 1130 int main(int argc, char** argv) { ModelCalibration::MonoAPP monoApp; monoApp.run(); ModelCalibration::BinoAPP binoApp; binoApp.run(); return 0; } 1131 int main1(int argc, char** argv) { ModelCalibration::MonoAPP monoApp; monoApp.run(); return 0; } 1132 int main2(int argc, char** argv) { ModelCalibration::BinoAPP binoApp; binoApp.run(); return 0; } 1133 #endif 1134 1135 #endif