test_cameracalibration_tilt.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. /*M///////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
  4. //
  5. // By downloading, copying, installing or using the software you agree to this license.
  6. // If you do not agree to this license, do not download, install,
  7. // copy or use the software.
  8. //
  9. //
  10. // License Agreement
  11. // For Open Source Computer Vision Library
  12. //
  13. // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
  14. // Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
  15. // Third party copyrights are property of their respective owners.
  16. //
  17. // Redistribution and use in source and binary forms, with or without modification,
  18. // are permitted provided that the following conditions are met:
  19. //
  20. // * Redistribution's of source code must retain the above copyright notice,
  21. // this list of conditions and the following disclaimer.
  22. //
  23. // * Redistribution's in binary form must reproduce the above copyright notice,
  24. // this list of conditions and the following disclaimer in the documentation
  25. // and/or other materials provided with the distribution.
  26. //
  27. // * The name of the copyright holders may not be used to endorse or promote products
  28. // derived from this software without specific prior written permission.
  29. //
  30. // This software is provided by the copyright holders and contributors "as is" and
  31. // any express or implied warranties, including, but not limited to, the implied
  32. // warranties of merchantability and fitness for a particular purpose are disclaimed.
  33. // In no event shall the Intel Corporation or contributors be liable for any direct,
  34. // indirect, incidental, special, exemplary, or consequential damages
  35. // (including, but not limited to, procurement of substitute goods or services;
  36. // loss of use, data, or profits; or business interruption) however caused
  37. // and on any theory of liability, whether in contract, strict liability,
  38. // or tort (including negligence or otherwise) arising in any way out of
  39. // the use of this software, even if advised of the possibility of such damage.
  40. //
  41. //M*/
  42. #include "test_precomp.hpp"
  43. #include "opencv2/ts/cuda_test.hpp" // EXPECT_MAT_NEAR
  44. namespace opencv_test { namespace {
  45. #define NUM_DIST_COEFF_TILT 14
  46. /**
  47. Some conventions:
  48. - the first camera determines the world coordinate system
  49. - y points down, hence top means minimal y value (negative) and
  50. bottom means maximal y value (positive)
  51. - the field of view plane is tilted around x such that it
  52. intersects the xy-plane in a line with a large (positive)
  53. y-value
  54. - image sensor and object are both modelled in the halfspace
  55. z > 0
  56. **/
  57. class cameraCalibrationTiltTest : public ::testing::Test {
  58. protected:
  59. cameraCalibrationTiltTest()
  60. : m_toRadian(acos(-1.0)/180.0)
  61. , m_toDegree(180.0/acos(-1.0))
  62. {}
  63. virtual void SetUp();
  64. protected:
  65. static const cv::Size m_imageSize;
  66. static const double m_pixelSize;
  67. static const double m_circleConfusionPixel;
  68. static const double m_lensFocalLength;
  69. static const double m_lensFNumber;
  70. static const double m_objectDistance;
  71. static const double m_planeTiltDegree;
  72. static const double m_pointTargetDist;
  73. static const int m_pointTargetNum;
  74. /** image distance corresponding to working distance */
  75. double m_imageDistance;
  76. /** image tilt angle corresponding to the tilt of the object plane */
  77. double m_imageTiltDegree;
  78. /** center of the field of view, near and far plane */
  79. std::vector<cv::Vec3d> m_fovCenter;
  80. /** normal of the field of view, near and far plane */
  81. std::vector<cv::Vec3d> m_fovNormal;
  82. /** points on a plane calibration target */
  83. std::vector<cv::Point3d> m_pointTarget;
  84. /** rotations for the calibration target */
  85. std::vector<cv::Vec3d> m_pointTargetRvec;
  86. /** translations for the calibration target */
  87. std::vector<cv::Vec3d> m_pointTargetTvec;
  88. /** camera matrix */
  89. cv::Matx33d m_cameraMatrix;
  90. /** distortion coefficients */
  91. cv::Vec<double, NUM_DIST_COEFF_TILT> m_distortionCoeff;
  92. /** random generator */
  93. cv::RNG m_rng;
  94. /** degree to radian conversion factor */
  95. const double m_toRadian;
  96. /** radian to degree conversion factor */
  97. const double m_toDegree;
  98. /**
  99. computes for a given distance of an image or object point
  100. the distance of the corresponding object or image point
  101. */
  102. double opticalMap(double dist) {
  103. return m_lensFocalLength*dist/(dist - m_lensFocalLength);
  104. }
  105. /** magnification of the optical map */
  106. double magnification(double dist) {
  107. return m_lensFocalLength/(dist - m_lensFocalLength);
  108. }
  109. /**
  110. Changes given distortion coefficients randomly by adding
  111. a uniformly distributed random variable in [-max max]
  112. \param coeff input
  113. \param max limits for the random variables
  114. */
  115. void randomDistortionCoeff(
  116. cv::Vec<double, NUM_DIST_COEFF_TILT>& coeff,
  117. const cv::Vec<double, NUM_DIST_COEFF_TILT>& max)
  118. {
  119. for (int i = 0; i < coeff.rows; ++i)
  120. coeff(i) += m_rng.uniform(-max(i), max(i));
  121. }
  122. /** numerical jacobian */
  123. void numericalDerivative(
  124. cv::Mat& jac,
  125. double eps,
  126. const std::vector<cv::Point3d>& obj,
  127. const cv::Vec3d& rvec,
  128. const cv::Vec3d& tvec,
  129. const cv::Matx33d& camera,
  130. const cv::Vec<double, NUM_DIST_COEFF_TILT>& distor);
  131. /** remove points with projection outside the sensor array */
  132. void removeInvalidPoints(
  133. std::vector<cv::Point2d>& imagePoints,
  134. std::vector<cv::Point3d>& objectPoints);
  135. /** add uniform distribute noise in [-halfWidthNoise, halfWidthNoise]
  136. to the image points and remove out of range points */
  137. void addNoiseRemoveInvalidPoints(
  138. std::vector<cv::Point2f>& imagePoints,
  139. std::vector<cv::Point3f>& objectPoints,
  140. std::vector<cv::Point2f>& noisyImagePoints,
  141. double halfWidthNoise);
  142. };
  143. /** Number of Pixel of the sensor */
  144. const cv::Size cameraCalibrationTiltTest::m_imageSize(1600, 1200);
  145. /** Size of a pixel in mm */
  146. const double cameraCalibrationTiltTest::m_pixelSize(.005);
  147. /** Diameter of the circle of confusion */
  148. const double cameraCalibrationTiltTest::m_circleConfusionPixel(3);
  149. /** Focal length of the lens */
  150. const double cameraCalibrationTiltTest::m_lensFocalLength(16.4);
  151. /** F-Number */
  152. const double cameraCalibrationTiltTest::m_lensFNumber(8);
  153. /** Working distance */
  154. const double cameraCalibrationTiltTest::m_objectDistance(200);
  155. /** Angle between optical axis and object plane normal */
  156. const double cameraCalibrationTiltTest::m_planeTiltDegree(55);
  157. /** the calibration target are points on a square grid with this side length */
  158. const double cameraCalibrationTiltTest::m_pointTargetDist(5);
  159. /** the calibration target has (2*n + 1) x (2*n + 1) points */
  160. const int cameraCalibrationTiltTest::m_pointTargetNum(15);
  161. void cameraCalibrationTiltTest::SetUp()
  162. {
  163. m_imageDistance = opticalMap(m_objectDistance);
  164. m_imageTiltDegree = m_toDegree * atan2(
  165. m_imageDistance * tan(m_toRadian * m_planeTiltDegree),
  166. m_objectDistance);
  167. // half sensor height
  168. double tmp = .5 * (m_imageSize.height - 1) * m_pixelSize
  169. * cos(m_toRadian * m_imageTiltDegree);
  170. // y-Value of tilted sensor
  171. double yImage[2] = {tmp, -tmp};
  172. // change in z because of the tilt
  173. tmp *= sin(m_toRadian * m_imageTiltDegree);
  174. // z-values of the sensor lower and upper corner
  175. double zImage[2] = {
  176. m_imageDistance + tmp,
  177. m_imageDistance - tmp};
  178. // circle of confusion
  179. double circleConfusion = m_circleConfusionPixel*m_pixelSize;
  180. // aperture of the lense
  181. double aperture = m_lensFocalLength/m_lensFNumber;
  182. // near and far factor on the image side
  183. double nearFarFactorImage[2] = {
  184. aperture/(aperture - circleConfusion),
  185. aperture/(aperture + circleConfusion)};
  186. // on the object side - points that determine the field of
  187. // view
  188. std::vector<cv::Vec3d> fovBottomTop(6);
  189. std::vector<cv::Vec3d>::iterator itFov = fovBottomTop.begin();
  190. for (size_t iBottomTop = 0; iBottomTop < 2; ++iBottomTop)
  191. {
  192. // mapping sensor to field of view
  193. *itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]);
  194. *itFov *= magnification((*itFov)(2));
  195. ++itFov;
  196. for (size_t iNearFar = 0; iNearFar < 2; ++iNearFar, ++itFov)
  197. {
  198. // scaling to the near and far distance on the
  199. // image side
  200. *itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]) *
  201. nearFarFactorImage[iNearFar];
  202. // scaling to the object side
  203. *itFov *= magnification((*itFov)(2));
  204. }
  205. }
  206. m_fovCenter.resize(3);
  207. m_fovNormal.resize(3);
  208. for (size_t i = 0; i < 3; ++i)
  209. {
  210. m_fovCenter[i] = .5*(fovBottomTop[i] + fovBottomTop[i+3]);
  211. m_fovNormal[i] = fovBottomTop[i+3] - fovBottomTop[i];
  212. m_fovNormal[i] = cv::normalize(m_fovNormal[i]);
  213. m_fovNormal[i] = cv::Vec3d(
  214. m_fovNormal[i](0),
  215. -m_fovNormal[i](2),
  216. m_fovNormal[i](1));
  217. // one target position in each plane
  218. m_pointTargetTvec.push_back(m_fovCenter[i]);
  219. cv::Vec3d rvec = cv::Vec3d(0,0,1).cross(m_fovNormal[i]);
  220. rvec = cv::normalize(rvec);
  221. rvec *= acos(m_fovNormal[i](2));
  222. m_pointTargetRvec.push_back(rvec);
  223. }
  224. // calibration target
  225. size_t num = 2*m_pointTargetNum + 1;
  226. m_pointTarget.resize(num*num);
  227. std::vector<cv::Point3d>::iterator itTarget = m_pointTarget.begin();
  228. for (int iY = -m_pointTargetNum; iY <= m_pointTargetNum; ++iY)
  229. {
  230. for (int iX = -m_pointTargetNum; iX <= m_pointTargetNum; ++iX, ++itTarget)
  231. {
  232. *itTarget = cv::Point3d(iX, iY, 0) * m_pointTargetDist;
  233. }
  234. }
  235. // oblique target positions
  236. // approximate distance to the near and far plane
  237. double dist = std::max(
  238. std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[1])),
  239. std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[2])));
  240. // maximal angle such that target border "reaches" near and far plane
  241. double maxAngle = atan2(dist, m_pointTargetNum*m_pointTargetDist);
  242. std::vector<double> angle;
  243. angle.push_back(-maxAngle);
  244. angle.push_back(maxAngle);
  245. cv::Matx33d baseMatrix;
  246. cv::Rodrigues(m_pointTargetRvec.front(), baseMatrix);
  247. for (std::vector<double>::const_iterator itAngle = angle.begin(); itAngle != angle.end(); ++itAngle)
  248. {
  249. cv::Matx33d rmat;
  250. for (int i = 0; i < 2; ++i)
  251. {
  252. cv::Vec3d rvec(0,0,0);
  253. rvec(i) = *itAngle;
  254. cv::Rodrigues(rvec, rmat);
  255. rmat = baseMatrix*rmat;
  256. cv::Rodrigues(rmat, rvec);
  257. m_pointTargetTvec.push_back(m_fovCenter.front());
  258. m_pointTargetRvec.push_back(rvec);
  259. }
  260. }
  261. // camera matrix
  262. double cx = .5 * (m_imageSize.width - 1);
  263. double cy = .5 * (m_imageSize.height - 1);
  264. double f = m_imageDistance/m_pixelSize;
  265. m_cameraMatrix = cv::Matx33d(
  266. f,0,cx,
  267. 0,f,cy,
  268. 0,0,1);
  269. // distortion coefficients
  270. m_distortionCoeff = cv::Vec<double, NUM_DIST_COEFF_TILT>::all(0);
  271. // tauX
  272. m_distortionCoeff(12) = -m_toRadian*m_imageTiltDegree;
  273. }
  274. void cameraCalibrationTiltTest::numericalDerivative(
  275. cv::Mat& jac,
  276. double eps,
  277. const std::vector<cv::Point3d>& obj,
  278. const cv::Vec3d& rvec,
  279. const cv::Vec3d& tvec,
  280. const cv::Matx33d& camera,
  281. const cv::Vec<double, NUM_DIST_COEFF_TILT>& distor)
  282. {
  283. cv::Vec3d r(rvec);
  284. cv::Vec3d t(tvec);
  285. cv::Matx33d cm(camera);
  286. cv::Vec<double, NUM_DIST_COEFF_TILT> dc(distor);
  287. double* param[10+NUM_DIST_COEFF_TILT] = {
  288. &r(0), &r(1), &r(2),
  289. &t(0), &t(1), &t(2),
  290. &cm(0,0), &cm(1,1), &cm(0,2), &cm(1,2),
  291. &dc(0), &dc(1), &dc(2), &dc(3), &dc(4), &dc(5), &dc(6),
  292. &dc(7), &dc(8), &dc(9), &dc(10), &dc(11), &dc(12), &dc(13)};
  293. std::vector<cv::Point2d> pix0, pix1;
  294. double invEps = .5/eps;
  295. for (int col = 0; col < 10+NUM_DIST_COEFF_TILT; ++col)
  296. {
  297. double save = *(param[col]);
  298. *(param[col]) = save + eps;
  299. cv::projectPoints(obj, r, t, cm, dc, pix0);
  300. *(param[col]) = save - eps;
  301. cv::projectPoints(obj, r, t, cm, dc, pix1);
  302. *(param[col]) = save;
  303. std::vector<cv::Point2d>::const_iterator it0 = pix0.begin();
  304. std::vector<cv::Point2d>::const_iterator it1 = pix1.begin();
  305. int row = 0;
  306. for (;it0 != pix0.end(); ++it0, ++it1)
  307. {
  308. cv::Point2d d = invEps*(*it0 - *it1);
  309. jac.at<double>(row, col) = d.x;
  310. ++row;
  311. jac.at<double>(row, col) = d.y;
  312. ++row;
  313. }
  314. }
  315. }
  316. void cameraCalibrationTiltTest::removeInvalidPoints(
  317. std::vector<cv::Point2d>& imagePoints,
  318. std::vector<cv::Point3d>& objectPoints)
  319. {
  320. // remove object and imgage points out of range
  321. std::vector<cv::Point2d>::iterator itImg = imagePoints.begin();
  322. std::vector<cv::Point3d>::iterator itObj = objectPoints.begin();
  323. while (itImg != imagePoints.end())
  324. {
  325. bool ok =
  326. itImg->x >= 0 &&
  327. itImg->x <= m_imageSize.width - 1.0 &&
  328. itImg->y >= 0 &&
  329. itImg->y <= m_imageSize.height - 1.0;
  330. if (ok)
  331. {
  332. ++itImg;
  333. ++itObj;
  334. }
  335. else
  336. {
  337. itImg = imagePoints.erase(itImg);
  338. itObj = objectPoints.erase(itObj);
  339. }
  340. }
  341. }
  342. void cameraCalibrationTiltTest::addNoiseRemoveInvalidPoints(
  343. std::vector<cv::Point2f>& imagePoints,
  344. std::vector<cv::Point3f>& objectPoints,
  345. std::vector<cv::Point2f>& noisyImagePoints,
  346. double halfWidthNoise)
  347. {
  348. std::vector<cv::Point2f>::iterator itImg = imagePoints.begin();
  349. std::vector<cv::Point3f>::iterator itObj = objectPoints.begin();
  350. noisyImagePoints.clear();
  351. noisyImagePoints.reserve(imagePoints.size());
  352. while (itImg != imagePoints.end())
  353. {
  354. cv::Point2f pix = *itImg + cv::Point2f(
  355. (float)m_rng.uniform(-halfWidthNoise, halfWidthNoise),
  356. (float)m_rng.uniform(-halfWidthNoise, halfWidthNoise));
  357. bool ok =
  358. pix.x >= 0 &&
  359. pix.x <= m_imageSize.width - 1.0 &&
  360. pix.y >= 0 &&
  361. pix.y <= m_imageSize.height - 1.0;
  362. if (ok)
  363. {
  364. noisyImagePoints.push_back(pix);
  365. ++itImg;
  366. ++itObj;
  367. }
  368. else
  369. {
  370. itImg = imagePoints.erase(itImg);
  371. itObj = objectPoints.erase(itObj);
  372. }
  373. }
  374. }
  375. TEST_F(cameraCalibrationTiltTest, projectPoints)
  376. {
  377. std::vector<cv::Point2d> imagePoints;
  378. std::vector<cv::Point3d> objectPoints = m_pointTarget;
  379. cv::Vec3d rvec = m_pointTargetRvec.front();
  380. cv::Vec3d tvec = m_pointTargetTvec.front();
  381. cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
  382. .1, .1, // k1 k2
  383. .01, .01, // p1 p2
  384. .001, .001, .001, .001, // k3 k4 k5 k6
  385. .001, .001, .001, .001, // s1 s2 s3 s4
  386. .01, .01); // tauX tauY
  387. for (size_t numTest = 0; numTest < 10; ++numTest)
  388. {
  389. // create random distortion coefficients
  390. cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
  391. randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
  392. // projection
  393. cv::projectPoints(
  394. objectPoints,
  395. rvec,
  396. tvec,
  397. m_cameraMatrix,
  398. distortionCoeff,
  399. imagePoints);
  400. // remove object and imgage points out of range
  401. removeInvalidPoints(imagePoints, objectPoints);
  402. int numPoints = (int)imagePoints.size();
  403. int numParams = 10 + distortionCoeff.rows;
  404. cv::Mat jacobian(2*numPoints, numParams, CV_64FC1);
  405. // projection and jacobian
  406. cv::projectPoints(
  407. objectPoints,
  408. rvec,
  409. tvec,
  410. m_cameraMatrix,
  411. distortionCoeff,
  412. imagePoints,
  413. jacobian);
  414. // numerical derivatives
  415. cv::Mat numericJacobian(2*numPoints, numParams, CV_64FC1);
  416. double eps = 1e-7;
  417. numericalDerivative(
  418. numericJacobian,
  419. eps,
  420. objectPoints,
  421. rvec,
  422. tvec,
  423. m_cameraMatrix,
  424. distortionCoeff);
  425. #if 0
  426. for (size_t row = 0; row < 2; ++row)
  427. {
  428. std::cout << "------ Row = " << row << " ------\n";
  429. for (size_t i = 0; i < 10+NUM_DIST_COEFF_TILT; ++i)
  430. {
  431. std::cout << i
  432. << " jac = " << jacobian.at<double>(row,i)
  433. << " num = " << numericJacobian.at<double>(row,i)
  434. << " rel. diff = " << abs(numericJacobian.at<double>(row,i) - jacobian.at<double>(row,i))/abs(numericJacobian.at<double>(row,i))
  435. << "\n";
  436. }
  437. }
  438. #endif
  439. // relative difference for large values (rvec and tvec)
  440. cv::Mat check = abs(jacobian(cv::Range::all(), cv::Range(0,6)) - numericJacobian(cv::Range::all(), cv::Range(0,6)))/
  441. (1 + abs(jacobian(cv::Range::all(), cv::Range(0,6))));
  442. double minVal, maxVal;
  443. cv::minMaxIdx(check, &minVal, &maxVal);
  444. EXPECT_LE(maxVal, .01);
  445. // absolute difference for distortion and camera matrix
  446. EXPECT_MAT_NEAR(jacobian(cv::Range::all(), cv::Range(6,numParams)), numericJacobian(cv::Range::all(), cv::Range(6,numParams)), 1e-5);
  447. }
  448. }
  449. TEST_F(cameraCalibrationTiltTest, undistortPoints)
  450. {
  451. cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
  452. .2, .1, // k1 k2
  453. .01, .01, // p1 p2
  454. .01, .01, .01, .01, // k3 k4 k5 k6
  455. .001, .001, .001, .001, // s1 s2 s3 s4
  456. .001, .001); // tauX tauY
  457. double step = 99;
  458. double toleranceBackProjection = 1e-5;
  459. for (size_t numTest = 0; numTest < 10; ++numTest)
  460. {
  461. cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
  462. randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
  463. // distorted points
  464. std::vector<cv::Point2d> distorted;
  465. for (double x = 0; x <= m_imageSize.width-1; x += step)
  466. for (double y = 0; y <= m_imageSize.height-1; y += step)
  467. distorted.push_back(cv::Point2d(x,y));
  468. std::vector<cv::Point2d> normalizedUndistorted;
  469. // undistort
  470. cv::undistortPoints(distorted,
  471. normalizedUndistorted,
  472. m_cameraMatrix,
  473. distortionCoeff);
  474. // copy normalized points to 3D
  475. std::vector<cv::Point3d> objectPoints;
  476. for (std::vector<cv::Point2d>::const_iterator itPnt = normalizedUndistorted.begin();
  477. itPnt != normalizedUndistorted.end(); ++itPnt)
  478. objectPoints.push_back(cv::Point3d(itPnt->x, itPnt->y, 1));
  479. // project
  480. std::vector<cv::Point2d> imagePoints(objectPoints.size());
  481. cv::projectPoints(objectPoints,
  482. cv::Vec3d(0,0,0),
  483. cv::Vec3d(0,0,0),
  484. m_cameraMatrix,
  485. distortionCoeff,
  486. imagePoints);
  487. EXPECT_MAT_NEAR(distorted, imagePoints, toleranceBackProjection);
  488. }
  489. }
  490. template <typename INPUT, typename ESTIMATE>
  491. void show(const std::string& name, const INPUT in, const ESTIMATE est)
  492. {
  493. std::cout << name << " = " << est << " (init = " << in
  494. << ", diff = " << est-in << ")\n";
  495. }
  496. template <typename INPUT>
  497. void showVec(const std::string& name, const INPUT& in, const cv::Mat& est)
  498. {
  499. for (size_t i = 0; i < in.channels; ++i)
  500. {
  501. std::stringstream ss;
  502. ss << name << "[" << i << "]";
  503. show(ss.str(), in(i), est.at<double>(i));
  504. }
  505. }
  506. /**
  507. For given camera matrix and distortion coefficients
  508. - project point target in different positions onto the sensor
  509. - add pixel noise
  510. - estimate camera model with noisy measurements
  511. - compare result with initial model parameter
  512. Parameter are differently affected by the noise
  513. */
  514. TEST_F(cameraCalibrationTiltTest, calibrateCamera)
  515. {
  516. cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
  517. .2, .1, // k1 k2
  518. .01, .01, // p1 p2
  519. 0, 0, 0, 0, // k3 k4 k5 k6
  520. .001, .001, .001, .001, // s1 s2 s3 s4
  521. .001, .001); // tauX tauY
  522. double pixelNoiseHalfWidth = .5;
  523. std::vector<cv::Point3f> pointTarget;
  524. pointTarget.reserve(m_pointTarget.size());
  525. for (std::vector<cv::Point3d>::const_iterator it = m_pointTarget.begin(); it != m_pointTarget.end(); ++it)
  526. pointTarget.push_back(cv::Point3f(
  527. (float)(it->x),
  528. (float)(it->y),
  529. (float)(it->z)));
  530. for (size_t numTest = 0; numTest < 5; ++numTest)
  531. {
  532. // create random distortion coefficients
  533. cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
  534. randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
  535. // container for calibration data
  536. std::vector<std::vector<cv::Point3f> > viewsObjectPoints;
  537. std::vector<std::vector<cv::Point2f> > viewsImagePoints;
  538. std::vector<std::vector<cv::Point2f> > viewsNoisyImagePoints;
  539. // simulate calibration data with projectPoints
  540. std::vector<cv::Vec3d>::const_iterator itRvec = m_pointTargetRvec.begin();
  541. std::vector<cv::Vec3d>::const_iterator itTvec = m_pointTargetTvec.begin();
  542. // loop over different views
  543. for (;itRvec != m_pointTargetRvec.end(); ++ itRvec, ++itTvec)
  544. {
  545. std::vector<cv::Point3f> objectPoints(pointTarget);
  546. std::vector<cv::Point2f> imagePoints;
  547. std::vector<cv::Point2f> noisyImagePoints;
  548. // project calibration target to sensor
  549. cv::projectPoints(
  550. objectPoints,
  551. *itRvec,
  552. *itTvec,
  553. m_cameraMatrix,
  554. distortionCoeff,
  555. imagePoints);
  556. // remove invisible points
  557. addNoiseRemoveInvalidPoints(
  558. imagePoints,
  559. objectPoints,
  560. noisyImagePoints,
  561. pixelNoiseHalfWidth);
  562. // add data for view
  563. viewsNoisyImagePoints.push_back(noisyImagePoints);
  564. viewsImagePoints.push_back(imagePoints);
  565. viewsObjectPoints.push_back(objectPoints);
  566. }
  567. // Output
  568. std::vector<cv::Mat> outRvecs, outTvecs;
  569. cv::Mat outCameraMatrix(3, 3, CV_64F, cv::Scalar::all(1)), outDistCoeff;
  570. // Stopping criteria
  571. cv::TermCriteria stop(
  572. cv::TermCriteria::COUNT+cv::TermCriteria::EPS,
  573. 50000,
  574. 1e-14);
  575. // model choice
  576. int flag =
  577. cv::CALIB_FIX_ASPECT_RATIO |
  578. // cv::CALIB_RATIONAL_MODEL |
  579. cv::CALIB_FIX_K3 |
  580. // cv::CALIB_FIX_K6 |
  581. cv::CALIB_THIN_PRISM_MODEL |
  582. cv::CALIB_TILTED_MODEL;
  583. // estimate
  584. double backProjErr = cv::calibrateCamera(
  585. viewsObjectPoints,
  586. viewsNoisyImagePoints,
  587. m_imageSize,
  588. outCameraMatrix,
  589. outDistCoeff,
  590. outRvecs,
  591. outTvecs,
  592. flag,
  593. stop);
  594. EXPECT_LE(backProjErr, pixelNoiseHalfWidth);
  595. #if 0
  596. std::cout << "------ estimate ------\n";
  597. std::cout << "back projection error = " << backProjErr << "\n";
  598. std::cout << "points per view = {" << viewsObjectPoints.front().size();
  599. for (size_t i = 1; i < viewsObjectPoints.size(); ++i)
  600. std::cout << ", " << viewsObjectPoints[i].size();
  601. std::cout << "}\n";
  602. show("fx", m_cameraMatrix(0,0), outCameraMatrix.at<double>(0,0));
  603. show("fy", m_cameraMatrix(1,1), outCameraMatrix.at<double>(1,1));
  604. show("cx", m_cameraMatrix(0,2), outCameraMatrix.at<double>(0,2));
  605. show("cy", m_cameraMatrix(1,2), outCameraMatrix.at<double>(1,2));
  606. showVec("distor", distortionCoeff, outDistCoeff);
  607. #endif
  608. if (pixelNoiseHalfWidth > 0)
  609. {
  610. double tolRvec = pixelNoiseHalfWidth;
  611. double tolTvec = m_objectDistance * tolRvec;
  612. // back projection error
  613. for (size_t i = 0; i < viewsNoisyImagePoints.size(); ++i)
  614. {
  615. double dRvec = cv::norm(m_pointTargetRvec[i],
  616. cv::Vec3d(outRvecs[i].at<double>(0), outRvecs[i].at<double>(1), outRvecs[i].at<double>(2))
  617. );
  618. EXPECT_LE(dRvec, tolRvec);
  619. double dTvec = cv::norm(m_pointTargetTvec[i],
  620. cv::Vec3d(outTvecs[i].at<double>(0), outTvecs[i].at<double>(1), outTvecs[i].at<double>(2))
  621. );
  622. EXPECT_LE(dTvec, tolTvec);
  623. std::vector<cv::Point2f> backProjection;
  624. cv::projectPoints(
  625. viewsObjectPoints[i],
  626. outRvecs[i],
  627. outTvecs[i],
  628. outCameraMatrix,
  629. outDistCoeff,
  630. backProjection);
  631. EXPECT_MAT_NEAR(backProjection, viewsNoisyImagePoints[i], 1.5*pixelNoiseHalfWidth);
  632. EXPECT_MAT_NEAR(backProjection, viewsImagePoints[i], 1.5*pixelNoiseHalfWidth);
  633. }
  634. }
  635. pixelNoiseHalfWidth *= .25;
  636. }
  637. }
  638. }} // namespace