123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 |
- /*M///////////////////////////////////////////////////////////////////////////////////////
- //
- // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
- //
- // By downloading, copying, installing or using the software you agree to this license.
- // If you do not agree to this license, do not download, install,
- // copy or use the software.
- //
- //
- // License Agreement
- // For Open Source Computer Vision Library
- //
- // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
- // Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
- // Third party copyrights are property of their respective owners.
- //
- // Redistribution and use in source and binary forms, with or without modification,
- // are permitted provided that the following conditions are met:
- //
- // * Redistribution's of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- //
- // * Redistribution's in binary form must reproduce the above copyright notice,
- // this list of conditions and the following disclaimer in the documentation
- // and/or other materials provided with the distribution.
- //
- // * The name of the copyright holders may not be used to endorse or promote products
- // derived from this software without specific prior written permission.
- //
- // This software is provided by the copyright holders and contributors "as is" and
- // any express or implied warranties, including, but not limited to, the implied
- // warranties of merchantability and fitness for a particular purpose are disclaimed.
- // In no event shall the Intel Corporation or contributors be liable for any direct,
- // indirect, incidental, special, exemplary, or consequential damages
- // (including, but not limited to, procurement of substitute goods or services;
- // loss of use, data, or profits; or business interruption) however caused
- // and on any theory of liability, whether in contract, strict liability,
- // or tort (including negligence or otherwise) arising in any way out of
- // the use of this software, even if advised of the possibility of such damage.
- //
- //M*/
- #include "test_precomp.hpp"
- #include "opencv2/ts/cuda_test.hpp" // EXPECT_MAT_NEAR
- namespace opencv_test { namespace {
- #define NUM_DIST_COEFF_TILT 14
- /**
- Some conventions:
- - the first camera determines the world coordinate system
- - y points down, hence top means minimal y value (negative) and
- bottom means maximal y value (positive)
- - the field of view plane is tilted around x such that it
- intersects the xy-plane in a line with a large (positive)
- y-value
- - image sensor and object are both modelled in the halfspace
- z > 0
- **/
- class cameraCalibrationTiltTest : public ::testing::Test {
- protected:
- cameraCalibrationTiltTest()
- : m_toRadian(acos(-1.0)/180.0)
- , m_toDegree(180.0/acos(-1.0))
- {}
- virtual void SetUp();
- protected:
- static const cv::Size m_imageSize;
- static const double m_pixelSize;
- static const double m_circleConfusionPixel;
- static const double m_lensFocalLength;
- static const double m_lensFNumber;
- static const double m_objectDistance;
- static const double m_planeTiltDegree;
- static const double m_pointTargetDist;
- static const int m_pointTargetNum;
- /** image distance corresponding to working distance */
- double m_imageDistance;
- /** image tilt angle corresponding to the tilt of the object plane */
- double m_imageTiltDegree;
- /** center of the field of view, near and far plane */
- std::vector<cv::Vec3d> m_fovCenter;
- /** normal of the field of view, near and far plane */
- std::vector<cv::Vec3d> m_fovNormal;
- /** points on a plane calibration target */
- std::vector<cv::Point3d> m_pointTarget;
- /** rotations for the calibration target */
- std::vector<cv::Vec3d> m_pointTargetRvec;
- /** translations for the calibration target */
- std::vector<cv::Vec3d> m_pointTargetTvec;
- /** camera matrix */
- cv::Matx33d m_cameraMatrix;
- /** distortion coefficients */
- cv::Vec<double, NUM_DIST_COEFF_TILT> m_distortionCoeff;
- /** random generator */
- cv::RNG m_rng;
- /** degree to radian conversion factor */
- const double m_toRadian;
- /** radian to degree conversion factor */
- const double m_toDegree;
- /**
- computes for a given distance of an image or object point
- the distance of the corresponding object or image point
- */
- double opticalMap(double dist) {
- return m_lensFocalLength*dist/(dist - m_lensFocalLength);
- }
- /** magnification of the optical map */
- double magnification(double dist) {
- return m_lensFocalLength/(dist - m_lensFocalLength);
- }
- /**
- Changes given distortion coefficients randomly by adding
- a uniformly distributed random variable in [-max max]
- \param coeff input
- \param max limits for the random variables
- */
- void randomDistortionCoeff(
- cv::Vec<double, NUM_DIST_COEFF_TILT>& coeff,
- const cv::Vec<double, NUM_DIST_COEFF_TILT>& max)
- {
- for (int i = 0; i < coeff.rows; ++i)
- coeff(i) += m_rng.uniform(-max(i), max(i));
- }
- /** numerical jacobian */
- void numericalDerivative(
- cv::Mat& jac,
- double eps,
- const std::vector<cv::Point3d>& obj,
- const cv::Vec3d& rvec,
- const cv::Vec3d& tvec,
- const cv::Matx33d& camera,
- const cv::Vec<double, NUM_DIST_COEFF_TILT>& distor);
- /** remove points with projection outside the sensor array */
- void removeInvalidPoints(
- std::vector<cv::Point2d>& imagePoints,
- std::vector<cv::Point3d>& objectPoints);
- /** add uniform distribute noise in [-halfWidthNoise, halfWidthNoise]
- to the image points and remove out of range points */
- void addNoiseRemoveInvalidPoints(
- std::vector<cv::Point2f>& imagePoints,
- std::vector<cv::Point3f>& objectPoints,
- std::vector<cv::Point2f>& noisyImagePoints,
- double halfWidthNoise);
- };
- /** Number of Pixel of the sensor */
- const cv::Size cameraCalibrationTiltTest::m_imageSize(1600, 1200);
- /** Size of a pixel in mm */
- const double cameraCalibrationTiltTest::m_pixelSize(.005);
- /** Diameter of the circle of confusion */
- const double cameraCalibrationTiltTest::m_circleConfusionPixel(3);
- /** Focal length of the lens */
- const double cameraCalibrationTiltTest::m_lensFocalLength(16.4);
- /** F-Number */
- const double cameraCalibrationTiltTest::m_lensFNumber(8);
- /** Working distance */
- const double cameraCalibrationTiltTest::m_objectDistance(200);
- /** Angle between optical axis and object plane normal */
- const double cameraCalibrationTiltTest::m_planeTiltDegree(55);
- /** the calibration target are points on a square grid with this side length */
- const double cameraCalibrationTiltTest::m_pointTargetDist(5);
- /** the calibration target has (2*n + 1) x (2*n + 1) points */
- const int cameraCalibrationTiltTest::m_pointTargetNum(15);
- void cameraCalibrationTiltTest::SetUp()
- {
- m_imageDistance = opticalMap(m_objectDistance);
- m_imageTiltDegree = m_toDegree * atan2(
- m_imageDistance * tan(m_toRadian * m_planeTiltDegree),
- m_objectDistance);
- // half sensor height
- double tmp = .5 * (m_imageSize.height - 1) * m_pixelSize
- * cos(m_toRadian * m_imageTiltDegree);
- // y-Value of tilted sensor
- double yImage[2] = {tmp, -tmp};
- // change in z because of the tilt
- tmp *= sin(m_toRadian * m_imageTiltDegree);
- // z-values of the sensor lower and upper corner
- double zImage[2] = {
- m_imageDistance + tmp,
- m_imageDistance - tmp};
- // circle of confusion
- double circleConfusion = m_circleConfusionPixel*m_pixelSize;
- // aperture of the lense
- double aperture = m_lensFocalLength/m_lensFNumber;
- // near and far factor on the image side
- double nearFarFactorImage[2] = {
- aperture/(aperture - circleConfusion),
- aperture/(aperture + circleConfusion)};
- // on the object side - points that determine the field of
- // view
- std::vector<cv::Vec3d> fovBottomTop(6);
- std::vector<cv::Vec3d>::iterator itFov = fovBottomTop.begin();
- for (size_t iBottomTop = 0; iBottomTop < 2; ++iBottomTop)
- {
- // mapping sensor to field of view
- *itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]);
- *itFov *= magnification((*itFov)(2));
- ++itFov;
- for (size_t iNearFar = 0; iNearFar < 2; ++iNearFar, ++itFov)
- {
- // scaling to the near and far distance on the
- // image side
- *itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]) *
- nearFarFactorImage[iNearFar];
- // scaling to the object side
- *itFov *= magnification((*itFov)(2));
- }
- }
- m_fovCenter.resize(3);
- m_fovNormal.resize(3);
- for (size_t i = 0; i < 3; ++i)
- {
- m_fovCenter[i] = .5*(fovBottomTop[i] + fovBottomTop[i+3]);
- m_fovNormal[i] = fovBottomTop[i+3] - fovBottomTop[i];
- m_fovNormal[i] = cv::normalize(m_fovNormal[i]);
- m_fovNormal[i] = cv::Vec3d(
- m_fovNormal[i](0),
- -m_fovNormal[i](2),
- m_fovNormal[i](1));
- // one target position in each plane
- m_pointTargetTvec.push_back(m_fovCenter[i]);
- cv::Vec3d rvec = cv::Vec3d(0,0,1).cross(m_fovNormal[i]);
- rvec = cv::normalize(rvec);
- rvec *= acos(m_fovNormal[i](2));
- m_pointTargetRvec.push_back(rvec);
- }
- // calibration target
- size_t num = 2*m_pointTargetNum + 1;
- m_pointTarget.resize(num*num);
- std::vector<cv::Point3d>::iterator itTarget = m_pointTarget.begin();
- for (int iY = -m_pointTargetNum; iY <= m_pointTargetNum; ++iY)
- {
- for (int iX = -m_pointTargetNum; iX <= m_pointTargetNum; ++iX, ++itTarget)
- {
- *itTarget = cv::Point3d(iX, iY, 0) * m_pointTargetDist;
- }
- }
- // oblique target positions
- // approximate distance to the near and far plane
- double dist = std::max(
- std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[1])),
- std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[2])));
- // maximal angle such that target border "reaches" near and far plane
- double maxAngle = atan2(dist, m_pointTargetNum*m_pointTargetDist);
- std::vector<double> angle;
- angle.push_back(-maxAngle);
- angle.push_back(maxAngle);
- cv::Matx33d baseMatrix;
- cv::Rodrigues(m_pointTargetRvec.front(), baseMatrix);
- for (std::vector<double>::const_iterator itAngle = angle.begin(); itAngle != angle.end(); ++itAngle)
- {
- cv::Matx33d rmat;
- for (int i = 0; i < 2; ++i)
- {
- cv::Vec3d rvec(0,0,0);
- rvec(i) = *itAngle;
- cv::Rodrigues(rvec, rmat);
- rmat = baseMatrix*rmat;
- cv::Rodrigues(rmat, rvec);
- m_pointTargetTvec.push_back(m_fovCenter.front());
- m_pointTargetRvec.push_back(rvec);
- }
- }
- // camera matrix
- double cx = .5 * (m_imageSize.width - 1);
- double cy = .5 * (m_imageSize.height - 1);
- double f = m_imageDistance/m_pixelSize;
- m_cameraMatrix = cv::Matx33d(
- f,0,cx,
- 0,f,cy,
- 0,0,1);
- // distortion coefficients
- m_distortionCoeff = cv::Vec<double, NUM_DIST_COEFF_TILT>::all(0);
- // tauX
- m_distortionCoeff(12) = -m_toRadian*m_imageTiltDegree;
- }
- void cameraCalibrationTiltTest::numericalDerivative(
- cv::Mat& jac,
- double eps,
- const std::vector<cv::Point3d>& obj,
- const cv::Vec3d& rvec,
- const cv::Vec3d& tvec,
- const cv::Matx33d& camera,
- const cv::Vec<double, NUM_DIST_COEFF_TILT>& distor)
- {
- cv::Vec3d r(rvec);
- cv::Vec3d t(tvec);
- cv::Matx33d cm(camera);
- cv::Vec<double, NUM_DIST_COEFF_TILT> dc(distor);
- double* param[10+NUM_DIST_COEFF_TILT] = {
- &r(0), &r(1), &r(2),
- &t(0), &t(1), &t(2),
- &cm(0,0), &cm(1,1), &cm(0,2), &cm(1,2),
- &dc(0), &dc(1), &dc(2), &dc(3), &dc(4), &dc(5), &dc(6),
- &dc(7), &dc(8), &dc(9), &dc(10), &dc(11), &dc(12), &dc(13)};
- std::vector<cv::Point2d> pix0, pix1;
- double invEps = .5/eps;
- for (int col = 0; col < 10+NUM_DIST_COEFF_TILT; ++col)
- {
- double save = *(param[col]);
- *(param[col]) = save + eps;
- cv::projectPoints(obj, r, t, cm, dc, pix0);
- *(param[col]) = save - eps;
- cv::projectPoints(obj, r, t, cm, dc, pix1);
- *(param[col]) = save;
- std::vector<cv::Point2d>::const_iterator it0 = pix0.begin();
- std::vector<cv::Point2d>::const_iterator it1 = pix1.begin();
- int row = 0;
- for (;it0 != pix0.end(); ++it0, ++it1)
- {
- cv::Point2d d = invEps*(*it0 - *it1);
- jac.at<double>(row, col) = d.x;
- ++row;
- jac.at<double>(row, col) = d.y;
- ++row;
- }
- }
- }
- void cameraCalibrationTiltTest::removeInvalidPoints(
- std::vector<cv::Point2d>& imagePoints,
- std::vector<cv::Point3d>& objectPoints)
- {
- // remove object and imgage points out of range
- std::vector<cv::Point2d>::iterator itImg = imagePoints.begin();
- std::vector<cv::Point3d>::iterator itObj = objectPoints.begin();
- while (itImg != imagePoints.end())
- {
- bool ok =
- itImg->x >= 0 &&
- itImg->x <= m_imageSize.width - 1.0 &&
- itImg->y >= 0 &&
- itImg->y <= m_imageSize.height - 1.0;
- if (ok)
- {
- ++itImg;
- ++itObj;
- }
- else
- {
- itImg = imagePoints.erase(itImg);
- itObj = objectPoints.erase(itObj);
- }
- }
- }
- void cameraCalibrationTiltTest::addNoiseRemoveInvalidPoints(
- std::vector<cv::Point2f>& imagePoints,
- std::vector<cv::Point3f>& objectPoints,
- std::vector<cv::Point2f>& noisyImagePoints,
- double halfWidthNoise)
- {
- std::vector<cv::Point2f>::iterator itImg = imagePoints.begin();
- std::vector<cv::Point3f>::iterator itObj = objectPoints.begin();
- noisyImagePoints.clear();
- noisyImagePoints.reserve(imagePoints.size());
- while (itImg != imagePoints.end())
- {
- cv::Point2f pix = *itImg + cv::Point2f(
- (float)m_rng.uniform(-halfWidthNoise, halfWidthNoise),
- (float)m_rng.uniform(-halfWidthNoise, halfWidthNoise));
- bool ok =
- pix.x >= 0 &&
- pix.x <= m_imageSize.width - 1.0 &&
- pix.y >= 0 &&
- pix.y <= m_imageSize.height - 1.0;
- if (ok)
- {
- noisyImagePoints.push_back(pix);
- ++itImg;
- ++itObj;
- }
- else
- {
- itImg = imagePoints.erase(itImg);
- itObj = objectPoints.erase(itObj);
- }
- }
- }
- TEST_F(cameraCalibrationTiltTest, projectPoints)
- {
- std::vector<cv::Point2d> imagePoints;
- std::vector<cv::Point3d> objectPoints = m_pointTarget;
- cv::Vec3d rvec = m_pointTargetRvec.front();
- cv::Vec3d tvec = m_pointTargetTvec.front();
- cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
- .1, .1, // k1 k2
- .01, .01, // p1 p2
- .001, .001, .001, .001, // k3 k4 k5 k6
- .001, .001, .001, .001, // s1 s2 s3 s4
- .01, .01); // tauX tauY
- for (size_t numTest = 0; numTest < 10; ++numTest)
- {
- // create random distortion coefficients
- cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
- randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
- // projection
- cv::projectPoints(
- objectPoints,
- rvec,
- tvec,
- m_cameraMatrix,
- distortionCoeff,
- imagePoints);
- // remove object and imgage points out of range
- removeInvalidPoints(imagePoints, objectPoints);
- int numPoints = (int)imagePoints.size();
- int numParams = 10 + distortionCoeff.rows;
- cv::Mat jacobian(2*numPoints, numParams, CV_64FC1);
- // projection and jacobian
- cv::projectPoints(
- objectPoints,
- rvec,
- tvec,
- m_cameraMatrix,
- distortionCoeff,
- imagePoints,
- jacobian);
- // numerical derivatives
- cv::Mat numericJacobian(2*numPoints, numParams, CV_64FC1);
- double eps = 1e-7;
- numericalDerivative(
- numericJacobian,
- eps,
- objectPoints,
- rvec,
- tvec,
- m_cameraMatrix,
- distortionCoeff);
- #if 0
- for (size_t row = 0; row < 2; ++row)
- {
- std::cout << "------ Row = " << row << " ------\n";
- for (size_t i = 0; i < 10+NUM_DIST_COEFF_TILT; ++i)
- {
- std::cout << i
- << " jac = " << jacobian.at<double>(row,i)
- << " num = " << numericJacobian.at<double>(row,i)
- << " rel. diff = " << abs(numericJacobian.at<double>(row,i) - jacobian.at<double>(row,i))/abs(numericJacobian.at<double>(row,i))
- << "\n";
- }
- }
- #endif
- // relative difference for large values (rvec and tvec)
- cv::Mat check = abs(jacobian(cv::Range::all(), cv::Range(0,6)) - numericJacobian(cv::Range::all(), cv::Range(0,6)))/
- (1 + abs(jacobian(cv::Range::all(), cv::Range(0,6))));
- double minVal, maxVal;
- cv::minMaxIdx(check, &minVal, &maxVal);
- EXPECT_LE(maxVal, .01);
- // absolute difference for distortion and camera matrix
- EXPECT_MAT_NEAR(jacobian(cv::Range::all(), cv::Range(6,numParams)), numericJacobian(cv::Range::all(), cv::Range(6,numParams)), 1e-5);
- }
- }
- TEST_F(cameraCalibrationTiltTest, undistortPoints)
- {
- cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
- .2, .1, // k1 k2
- .01, .01, // p1 p2
- .01, .01, .01, .01, // k3 k4 k5 k6
- .001, .001, .001, .001, // s1 s2 s3 s4
- .001, .001); // tauX tauY
- double step = 99;
- double toleranceBackProjection = 1e-5;
- for (size_t numTest = 0; numTest < 10; ++numTest)
- {
- cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
- randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
- // distorted points
- std::vector<cv::Point2d> distorted;
- for (double x = 0; x <= m_imageSize.width-1; x += step)
- for (double y = 0; y <= m_imageSize.height-1; y += step)
- distorted.push_back(cv::Point2d(x,y));
- std::vector<cv::Point2d> normalizedUndistorted;
- // undistort
- cv::undistortPoints(distorted,
- normalizedUndistorted,
- m_cameraMatrix,
- distortionCoeff);
- // copy normalized points to 3D
- std::vector<cv::Point3d> objectPoints;
- for (std::vector<cv::Point2d>::const_iterator itPnt = normalizedUndistorted.begin();
- itPnt != normalizedUndistorted.end(); ++itPnt)
- objectPoints.push_back(cv::Point3d(itPnt->x, itPnt->y, 1));
- // project
- std::vector<cv::Point2d> imagePoints(objectPoints.size());
- cv::projectPoints(objectPoints,
- cv::Vec3d(0,0,0),
- cv::Vec3d(0,0,0),
- m_cameraMatrix,
- distortionCoeff,
- imagePoints);
- EXPECT_MAT_NEAR(distorted, imagePoints, toleranceBackProjection);
- }
- }
- template <typename INPUT, typename ESTIMATE>
- void show(const std::string& name, const INPUT in, const ESTIMATE est)
- {
- std::cout << name << " = " << est << " (init = " << in
- << ", diff = " << est-in << ")\n";
- }
- template <typename INPUT>
- void showVec(const std::string& name, const INPUT& in, const cv::Mat& est)
- {
- for (size_t i = 0; i < in.channels; ++i)
- {
- std::stringstream ss;
- ss << name << "[" << i << "]";
- show(ss.str(), in(i), est.at<double>(i));
- }
- }
- /**
- For given camera matrix and distortion coefficients
- - project point target in different positions onto the sensor
- - add pixel noise
- - estimate camera model with noisy measurements
- - compare result with initial model parameter
- Parameter are differently affected by the noise
- */
- TEST_F(cameraCalibrationTiltTest, calibrateCamera)
- {
- cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
- .2, .1, // k1 k2
- .01, .01, // p1 p2
- 0, 0, 0, 0, // k3 k4 k5 k6
- .001, .001, .001, .001, // s1 s2 s3 s4
- .001, .001); // tauX tauY
- double pixelNoiseHalfWidth = .5;
- std::vector<cv::Point3f> pointTarget;
- pointTarget.reserve(m_pointTarget.size());
- for (std::vector<cv::Point3d>::const_iterator it = m_pointTarget.begin(); it != m_pointTarget.end(); ++it)
- pointTarget.push_back(cv::Point3f(
- (float)(it->x),
- (float)(it->y),
- (float)(it->z)));
- for (size_t numTest = 0; numTest < 5; ++numTest)
- {
- // create random distortion coefficients
- cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
- randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
- // container for calibration data
- std::vector<std::vector<cv::Point3f> > viewsObjectPoints;
- std::vector<std::vector<cv::Point2f> > viewsImagePoints;
- std::vector<std::vector<cv::Point2f> > viewsNoisyImagePoints;
- // simulate calibration data with projectPoints
- std::vector<cv::Vec3d>::const_iterator itRvec = m_pointTargetRvec.begin();
- std::vector<cv::Vec3d>::const_iterator itTvec = m_pointTargetTvec.begin();
- // loop over different views
- for (;itRvec != m_pointTargetRvec.end(); ++ itRvec, ++itTvec)
- {
- std::vector<cv::Point3f> objectPoints(pointTarget);
- std::vector<cv::Point2f> imagePoints;
- std::vector<cv::Point2f> noisyImagePoints;
- // project calibration target to sensor
- cv::projectPoints(
- objectPoints,
- *itRvec,
- *itTvec,
- m_cameraMatrix,
- distortionCoeff,
- imagePoints);
- // remove invisible points
- addNoiseRemoveInvalidPoints(
- imagePoints,
- objectPoints,
- noisyImagePoints,
- pixelNoiseHalfWidth);
- // add data for view
- viewsNoisyImagePoints.push_back(noisyImagePoints);
- viewsImagePoints.push_back(imagePoints);
- viewsObjectPoints.push_back(objectPoints);
- }
- // Output
- std::vector<cv::Mat> outRvecs, outTvecs;
- cv::Mat outCameraMatrix(3, 3, CV_64F, cv::Scalar::all(1)), outDistCoeff;
- // Stopping criteria
- cv::TermCriteria stop(
- cv::TermCriteria::COUNT+cv::TermCriteria::EPS,
- 50000,
- 1e-14);
- // model choice
- int flag =
- cv::CALIB_FIX_ASPECT_RATIO |
- // cv::CALIB_RATIONAL_MODEL |
- cv::CALIB_FIX_K3 |
- // cv::CALIB_FIX_K6 |
- cv::CALIB_THIN_PRISM_MODEL |
- cv::CALIB_TILTED_MODEL;
- // estimate
- double backProjErr = cv::calibrateCamera(
- viewsObjectPoints,
- viewsNoisyImagePoints,
- m_imageSize,
- outCameraMatrix,
- outDistCoeff,
- outRvecs,
- outTvecs,
- flag,
- stop);
- EXPECT_LE(backProjErr, pixelNoiseHalfWidth);
- #if 0
- std::cout << "------ estimate ------\n";
- std::cout << "back projection error = " << backProjErr << "\n";
- std::cout << "points per view = {" << viewsObjectPoints.front().size();
- for (size_t i = 1; i < viewsObjectPoints.size(); ++i)
- std::cout << ", " << viewsObjectPoints[i].size();
- std::cout << "}\n";
- show("fx", m_cameraMatrix(0,0), outCameraMatrix.at<double>(0,0));
- show("fy", m_cameraMatrix(1,1), outCameraMatrix.at<double>(1,1));
- show("cx", m_cameraMatrix(0,2), outCameraMatrix.at<double>(0,2));
- show("cy", m_cameraMatrix(1,2), outCameraMatrix.at<double>(1,2));
- showVec("distor", distortionCoeff, outDistCoeff);
- #endif
- if (pixelNoiseHalfWidth > 0)
- {
- double tolRvec = pixelNoiseHalfWidth;
- double tolTvec = m_objectDistance * tolRvec;
- // back projection error
- for (size_t i = 0; i < viewsNoisyImagePoints.size(); ++i)
- {
- double dRvec = cv::norm(m_pointTargetRvec[i],
- cv::Vec3d(outRvecs[i].at<double>(0), outRvecs[i].at<double>(1), outRvecs[i].at<double>(2))
- );
- EXPECT_LE(dRvec, tolRvec);
- double dTvec = cv::norm(m_pointTargetTvec[i],
- cv::Vec3d(outTvecs[i].at<double>(0), outTvecs[i].at<double>(1), outTvecs[i].at<double>(2))
- );
- EXPECT_LE(dTvec, tolTvec);
- std::vector<cv::Point2f> backProjection;
- cv::projectPoints(
- viewsObjectPoints[i],
- outRvecs[i],
- outTvecs[i],
- outCameraMatrix,
- outDistCoeff,
- backProjection);
- EXPECT_MAT_NEAR(backProjection, viewsNoisyImagePoints[i], 1.5*pixelNoiseHalfWidth);
- EXPECT_MAT_NEAR(backProjection, viewsImagePoints[i], 1.5*pixelNoiseHalfWidth);
- }
- }
- pixelNoiseHalfWidth *= .25;
- }
- }
- }} // namespace
|