Skip to content
Snippets Groups Projects
calibrate.cpp 19.9 KiB
Newer Older
Nicolas Pope's avatar
Nicolas Pope committed
/*
 * Copyright 2019 Nicolas Pope
 */

#include <glog/logging.h>
#include <ftl/config.h>
#include <ftl/configuration.hpp>

#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
#include <cstdio>

#include "calibrate.hpp"

#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>

using ftl::Calibrate;
using cv::FileStorage;
using cv::CALIB_FIX_PRINCIPAL_POINT;
using cv::CALIB_ZERO_TANGENT_DIST;
using cv::CALIB_FIX_ASPECT_RATIO;
using cv::CALIB_FIX_K1;
using cv::CALIB_FIX_K2;
using cv::CALIB_FIX_K3;
using cv::CALIB_FIX_K4;
using cv::CALIB_FIX_K5;
using cv::CALIB_ZERO_DISPARITY;
using cv::CALIB_USE_INTRINSIC_GUESS;
using cv::CALIB_SAME_FOCAL_LENGTH;
using cv::CALIB_RATIONAL_MODEL;
using cv::CALIB_CB_ADAPTIVE_THRESH;
using cv::CALIB_CB_NORMALIZE_IMAGE;
using cv::CALIB_CB_FAST_CHECK;
using cv::CALIB_USE_LU;
using cv::NORM_L2;
using cv::INTER_LINEAR;
using cv::COLOR_BGR2GRAY;
using cv::FileNode;
using cv::FileNodeIterator;
using cv::Mat;
using cv::Size;
using cv::Point2f;
using cv::Point3f;
using cv::Matx33d;
using cv::Scalar;
using cv::Rect;
using cv::TermCriteria;
using cv::waitKey;
using std::string;
using std::vector;

void Calibrate::Settings::write(FileStorage& fs) const {
    fs << "{"
		<< "BoardSize_Width"  << boardSize.width
		<< "BoardSize_Height" << boardSize.height
		<< "Square_Size"         << squareSize
		<< "Calibrate_Pattern" << patternToUse
		<< "Calibrate_NrOfFrameToUse" << nrFrames
		<< "Calibrate_FixAspectRatio" << aspectRatio
		<< "Calibrate_AssumeZeroTangentialDistortion" << calibZeroTangentDist
		<< "Calibrate_FixPrincipalPointAtTheCenter" << calibFixPrincipalPoint
		<< "Write_DetectedFeaturePoints" << writePoints
		<< "Write_extrinsicParameters"   << writeExtrinsics
		<< "Write_gridPoints" << writeGrid
		<< "Input_FlipAroundHorizontalAxis" << flipVertical
		<< "Input_Delay" << delay
		<< "}";
}

void Calibrate::Settings::read(const nlohmann::json& node) {
    boardSize.width = node["board_size"][0];
    boardSize.height = node["board_size"][1];
    squareSize = node["square_size"];
    nrFrames = node["num_frames"];
    aspectRatio = node["fix_aspect_ratio"];
    calibZeroTangentDist = node["assume_zero_tangential_distortion"];
    calibFixPrincipalPoint = node["fix_principal_point_at_center"];
    useFisheye =  node["use_fisheye_model"];
    flipVertical = node["flip_vertical"];
    delay = node["frame_delay"];
    fixK1 = node["fix_k1"];
    fixK2 = node["fix_k2"];
    fixK3 = node["fix_k3"];
    fixK4 = node["fix_k4"];
    fixK5 = node["fix_k5"];

    validate();
}

void Calibrate::Settings::validate() {
    goodInput = true;
    if (boardSize.width <= 0 || boardSize.height <= 0) {
        LOG(ERROR) << "Invalid Board size: " << boardSize.width << " " <<
        		boardSize.height;
        goodInput = false;
    }

    if (squareSize <= 10e-6) {
        LOG(ERROR) << "Invalid square size " << squareSize;
        goodInput = false;
    }

    if (nrFrames <= 0) {
        LOG(ERROR) << "Invalid number of frames " << nrFrames;
        goodInput = false;
    }

    flag = 0;
    if (calibFixPrincipalPoint) flag |= CALIB_FIX_PRINCIPAL_POINT;
    if (calibZeroTangentDist)   flag |= CALIB_ZERO_TANGENT_DIST;
    if (aspectRatio)            flag |= CALIB_FIX_ASPECT_RATIO;
    if (fixK1)                  flag |= CALIB_FIX_K1;
    if (fixK2)                  flag |= CALIB_FIX_K2;
    if (fixK3)                  flag |= CALIB_FIX_K3;
    if (fixK4)                  flag |= CALIB_FIX_K4;
    if (fixK5)                  flag |= CALIB_FIX_K5;

    if (useFisheye) {
        // the fisheye model has its own enum, so overwrite the flags
        flag = cv::fisheye::CALIB_FIX_SKEW |
        		cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
        if (fixK1)                   flag |= cv::fisheye::CALIB_FIX_K1;
        if (fixK2)                   flag |= cv::fisheye::CALIB_FIX_K2;
        if (fixK3)                   flag |= cv::fisheye::CALIB_FIX_K3;
        if (fixK4)                   flag |= cv::fisheye::CALIB_FIX_K4;
        if (calibFixPrincipalPoint)
        	flag |= cv::fisheye::CALIB_FIX_PRINCIPAL_POINT;
    }

    atImageList = 0;
}

Mat Calibrate::_nextImage(size_t cam) {
    Mat result;
    if (cam == 0) {
    	local_->left(result);
    } else if (cam == 1 && local_->isStereo()) {
    	local_->right(result);
    }
    return result;
}

bool Calibrate::Settings::readStringList(const string& filename,
		vector<string>& l) {
    l.clear();
    FileStorage fs(filename, FileStorage::READ);
    if ( !fs.isOpened() )
        return false;
    FileNode n = fs.getFirstTopLevelNode();
    if ( n.type() != FileNode::SEQ )
        return false;
    FileNodeIterator it = n.begin(), it_end = n.end();
    for ( ; it != it_end; ++it )
        l.push_back((string)*it);
    return true;
}

bool Calibrate::Settings::isListOfImages(const string& filename) {
    string s(filename);
    // Look for file extension
    if ( s.find(".xml") == string::npos && s.find(".yaml") == string::npos &&
    		s.find(".yml") == string::npos )
        return false;
    else
        return true;
}

enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };

bool runCalibration(const Calibrate::Settings& s, Size imageSize,
		Mat&  cameraMatrix, Mat& distCoeffs,
		vector<vector<Point2f> > imagePoints, float grid_width,
		bool release_object);


Calibrate::Calibrate(ftl::LocalSource *s, nlohmann::json &config) : local_(s) {
    /*FileStorage fs(cal, FileStorage::READ); // Read the settings
    if (!fs.isOpened())
    {
        LOG(ERROR) << "Could not open the configuration file: \"" << cal << "\"";
        return;
    }*/
    // fs["Settings"] >> settings_;
    settings_.read(config);
    // fs.release();

    if (!settings_.goodInput) {
        LOG(ERROR) << "Invalid input detected. Application stopping.";
        return;
    }

    map1_.resize(2);
    map2_.resize(2);

    calibrated_ = _loadCalibration();

    if (calibrated_) {
    	LOG(INFO) << "Calibration loaded from file";
    }
}

bool Calibrate::_loadCalibration() {
	// Capture one frame to get size;
	Mat l, r;
	local_->get(l, r);
	Size img_size = l.size();
	float scale = 1.0f;

    Rect roi1, roi2;
    FileStorage fs;

    // reading intrinsic parameters
    auto ifile = ftl::locateFile("intrinsics.yml");
    if (ifile) {
		fs.open((*ifile).c_str(), FileStorage::READ);
		if (!fs.isOpened()) {
		    LOG(WARNING) << "Could not open intrinsics file";
		    return false;
		}
		
		LOG(INFO) << "Intrinsics from: " << *ifile;
    } else {
    	LOG(WARNING) << "Calibration intrinsics file not found";
		return false;
    }

    Mat M1, D1, M2, D2;
    fs["M1"] >> M1;
    fs["D1"] >> D1;
    fs["M2"] >> M2;
    fs["D2"] >> D2;

    M1 *= scale;
    M2 *= scale;

	auto efile = ftl::locateFile("extrinsics.yml");
	if (efile) {
		fs.open((*efile).c_str(), FileStorage::READ);
		if (!fs.isOpened()) {
		    LOG(WARNING) << "Could not open extrinsics file";
		    return false;
		}
		
		LOG(INFO) << "Extrinsics from: " << *efile;
    } else {
    	LOG(WARNING) << "Calibration extrinsics file not found";
		return false;
    }

    Mat R, T, R1, P1, R2, P2;
    fs["R"] >> R;
    fs["T"] >> T;
    fs["R1"] >> R1;
    fs["R2"] >> R2;
    fs["P1"] >> P1;
    fs["P2"] >> P2;
    fs["Q"] >> Q_;

    /*stereoRectify(M1, D1, M2, D2, img_size, R, T, R1, R2, P1, P2, Q_,
    		CALIB_ZERO_DISPARITY, -1, img_size, &roi1, &roi2);*/
Nicolas Pope's avatar
Nicolas Pope committed

    Mat map11, map12, map21, map22;
    initUndistortRectifyMap(M1, D1, R1, P1, img_size, CV_16SC2,
    		map1_[0], map2_[0]);
    initUndistortRectifyMap(M2, D2, R2, P2, img_size, CV_16SC2,
    		map1_[1], map2_[1]);
Nicolas Pope's avatar
Nicolas Pope committed

	// Re-distort
	Mat P1_cam = (cv::Mat_<double>(3,3) << P1.at<double>(0, 0), P1.at<double>(0, 1) , P1.at<double>(0, 2),
		P1.at<double>(1, 0), P1.at<double>(1, 1), P1.at<double>(1, 2),
		P1.at<double>(2, 0), P1.at<double>(2, 1), P1.at<double>(2, 2));
	Mat M1_trans = (cv::Mat_<double>(3, 4) << M1.at<double>(0, 0), M1.at<double>(0, 1), M1.at<double>(0, 2), -P1.at<double>(0, 3),
		M1.at<double>(1, 0), M1.at<double>(1, 1), M1.at<double>(1, 2), -P1.at<double>(1, 3),
		M1.at<double>(2, 0), M1.at<double>(2, 1), M1.at<double>(2, 2), -P1.at<double>(2, 3));
	initUndistortRectifyMap(P1_cam, Mat(), R1.t(), P1,
Nicolas Pope's avatar
Nicolas Pope committed
			img_size, CV_16SC2, imap1_, imap2_);
	r1_ = P1;
Nicolas Pope's avatar
Nicolas Pope committed
    return true;
}

bool Calibrate::recalibrate() {
	vector<vector<Point2f>> imagePoints[2];
    Mat cameraMatrix[2], distCoeffs[2];
    Size imageSize[2];

	bool r = _recalibrate(imagePoints, cameraMatrix, distCoeffs, imageSize);

	if (r) calibrated_ = true;

	if (r && local_->isStereo()) {
		int nimages = static_cast<int>(imagePoints[0].size());
		auto squareSize = settings_.squareSize;
		vector<vector<Point3f>> objectPoints;
		objectPoints.resize(nimages);

		for (auto i = 0; i < nimages; i++) {
		    for (auto j = 0; j < settings_.boardSize.height; j++)
		        for (auto  k = 0; k < settings_.boardSize.width; k++)
		            objectPoints[i].push_back(Point3f(k*squareSize,
		            		j*squareSize, 0));
		}

		Mat R, T, E, F;

		LOG(INFO) << "Running stereo calibration...";

		double rms = stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1],
                cameraMatrix[0], distCoeffs[0],
                cameraMatrix[1], distCoeffs[1],
                imageSize[0], R, T, E, F,
                CALIB_FIX_ASPECT_RATIO +
                CALIB_ZERO_TANGENT_DIST +
                CALIB_USE_INTRINSIC_GUESS +
                CALIB_SAME_FOCAL_LENGTH +
                CALIB_RATIONAL_MODEL +
                CALIB_FIX_K3 + CALIB_FIX_K4 + CALIB_FIX_K5,
                TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 100, 1e-5));
		LOG(INFO) << "... done with RMS error=" << rms;

		// save intrinsic parameters
		FileStorage fs(FTL_LOCAL_CONFIG_ROOT "/intrinsics.yml", FileStorage::WRITE);
		if (fs.isOpened()) {
		    fs << "M1" << cameraMatrix[0] << "D1" << distCoeffs[0] <<
		        "M2" << cameraMatrix[1] << "D2" << distCoeffs[1];
		    fs.release();
		} else {
		    LOG(ERROR) << "Error: can not save the intrinsic parameters";
		}

		Mat R1, R2, P1, P2;
		Rect validRoi[2];

		stereoRectify(cameraMatrix[0], distCoeffs[0],
		              cameraMatrix[1], distCoeffs[1],
		              imageSize[0], R, T, R1, R2, P1, P2, Q_,
		              CALIB_ZERO_DISPARITY, 1, imageSize[0],
		              &validRoi[0], &validRoi[1]);

		fs.open(FTL_LOCAL_CONFIG_ROOT "/extrinsics.yml", FileStorage::WRITE);
		if (fs.isOpened()) {
		    fs << "R" << R << "T" << T << "R1" << R1 << "R2" << R2 << "P1" <<
		    		P1 << "P2" << P2 << "Q" << Q_;
		    fs.release();
		} else {
		    LOG(ERROR) << "Error: can not save the extrinsic parameters";
		}

		// Precompute maps for cv::remap()
    	initUndistortRectifyMap(cameraMatrix[0], distCoeffs[0], R1, P1,
    			imageSize[0], CV_16SC2, map1_[0], map2_[0]);
    	initUndistortRectifyMap(cameraMatrix[1], distCoeffs[1], R2, P2,
    			imageSize[0], CV_16SC2, map1_[1], map2_[1]);

	} else {
		int cam = 0;
		if (settings_.useFisheye) {
		    Mat newCamMat;
		    cv::fisheye::estimateNewCameraMatrixForUndistortRectify(
		    		cameraMatrix[cam], distCoeffs[cam], imageSize[cam],
		            Matx33d::eye(), newCamMat, 1);
		    cv::fisheye::initUndistortRectifyMap(
		    		cameraMatrix[cam], distCoeffs[cam], Matx33d::eye(),
		    		newCamMat, imageSize[cam],
		            CV_16SC2, map1_[cam], map2_[cam]);
		} else {
		    initUndistortRectifyMap(
		        cameraMatrix[cam], distCoeffs[cam], Mat(),
		        getOptimalNewCameraMatrix(cameraMatrix[cam], distCoeffs[cam],
		        		imageSize[cam], 1, imageSize[cam], 0),
		        imageSize[cam], CV_16SC2, map1_[cam], map2_[cam]);
		}
	}

	return r;
}

bool Calibrate::_recalibrate(vector<vector<Point2f>> *imagePoints,
		Mat *cameraMatrix, Mat *distCoeffs, Size *imageSize) {
	int winSize = 11;  // parser.get<int>("winSize");

	float grid_width = settings_.squareSize * (settings_.boardSize.width - 1);
	bool release_object = false;
	double prevTimestamp = 0.0;
	const Scalar RED(0, 0, 255), GREEN(0, 255, 0);

#if CV_VERSION_MAJOR >= 4
	int chessBoardFlags = CALIB_CB_NORMALIZE_IMAGE;
#else
	int chessBoardFlags = CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE;

	if (!settings_.useFisheye) {
		// fast check erroneously fails with high distortions like fisheye
		chessBoardFlags |= CALIB_CB_FAST_CHECK;
	}
#endif
	for (;;) {
		Mat view[2];
		local_->get(view[0], view[1]);
		LOG(INFO) << "Grabbing calibration image...";

		if (view[0].empty() || (local_->isStereo() && view[1].empty()) ||
				imagePoints[0].size() >= (size_t)settings_.nrFrames) {
			settings_.outputFileName = FTL_LOCAL_CONFIG_ROOT "/cam0.xml";
			bool r = runCalibration(settings_, imageSize[0],
							cameraMatrix[0], distCoeffs[0], imagePoints[0],
							grid_width, release_object);

			if (local_->isStereo()) {
				settings_.outputFileName = FTL_LOCAL_CONFIG_ROOT "/cam1.xml";
				r &= runCalibration(settings_, imageSize[1],
								cameraMatrix[1], distCoeffs[1], imagePoints[1],
								grid_width, release_object);
			}

			if (!r && view[0].empty()) {
				LOG(ERROR) << "Not enough frames to calibrate";
				return false;
			} else if (r) {
				LOG(INFO) << "Calibration successful";
				break;
			}
		}
		imageSize[0] = view[0].size();  // Format input image.
		imageSize[1] = view[1].size();
		// if( settings_.flipVertical )    flip( view[cam], view[cam], 0 );
		vector<Point2f> pointBuf[2];
		bool found1, found2;
#if CV_VERSION_MAJOR >= 4
		LOG(INFO) << "Using findChessboardCornersSB()";
		found1 = findChessboardCornersSB(view[0], settings_.boardSize,
				pointBuf[0], chessBoardFlags);
		found2 = !local_->isStereo() || findChessboardCornersSB(view[1],
			settings_.boardSize, pointBuf[1], chessBoardFlags);
#else
		LOG(INFO) << "Using findChessboardCorners()";
Nicolas Pope's avatar
Nicolas Pope committed
		found1 = findChessboardCorners(view[0], settings_.boardSize,
				pointBuf[0], chessBoardFlags);
		found2 = !local_->isStereo() || findChessboardCorners(view[1],
				settings_.boardSize, pointBuf[1], chessBoardFlags);
#endif
		if (found1 && found2 &&
				local_->getTimestamp()-prevTimestamp > settings_.delay) {
Nicolas Pope's avatar
Nicolas Pope committed
			prevTimestamp = local_->getTimestamp();
#if CV_VERSION_MAJOR >=4
			// findChessboardCornersSB() does not require subpixel step.
#else
			// improve the found corners' coordinate accuracy for chessboard.
			Mat viewGray;
			cvtColor(view[0], viewGray, COLOR_BGR2GRAY);
			cornerSubPix(viewGray, pointBuf[0], Size(winSize, winSize),
				Size(-1, -1), TermCriteria(
					TermCriteria::EPS+TermCriteria::COUNT, 30, 0.0001));
			
			if (local_->isStereo()) {
				cvtColor(view[1], viewGray, COLOR_BGR2GRAY);
				cornerSubPix(viewGray, pointBuf[1], Size(winSize, winSize),
					Size(-1, -1), TermCriteria(
						TermCriteria::EPS+TermCriteria::COUNT, 30, 0.0001));
			}
#endif
			imagePoints[0].push_back(pointBuf[0]);
			if (local_->isStereo()) { imagePoints[1].push_back(pointBuf[1]); }
			// Draw the corners.
			
			drawChessboardCorners(view[0], settings_.boardSize,
					Mat(pointBuf[0]), found1);
			if (local_->isStereo())
				drawChessboardCorners(view[1],settings_.boardSize, Mat(pointBuf[1]), found2);
		} else {
			LOG(WARNING) << "No calibration pattern found";
		}

		imshow("Left", view[0]);
		if (local_->isStereo()) imshow("Right", view[1]);
		char key = static_cast<char>(waitKey(50));

		if (key  == 27)
			break;
	}
Nicolas Pope's avatar
Nicolas Pope committed

	return true;
}

bool Calibrate::undistort(cv::Mat &l, cv::Mat &r) {
	Mat l_tmp, r_tmp;
	local_->get(l_tmp, r_tmp);
Nicolas Pope's avatar
Nicolas Pope committed
	if (!calibrated_) return false;
	if (l_tmp.empty() || r_tmp.empty()) {
		LOG(ERROR) << "l/r empty in undistort()!!!";
		return false;
	}
	remap(l_tmp, l, map1_[0], map2_[0], INTER_LINEAR);
	if (local_->isStereo()) remap(r_tmp, r, map1_[1], map2_[1], INTER_LINEAR);
Nicolas Pope's avatar
Nicolas Pope committed
	return true;
}

Nicolas Pope's avatar
Nicolas Pope committed
void Calibrate::distort(cv::Mat &rgb, cv::Mat &depth) {
	remap(rgb, rgb, imap1_, imap2_, INTER_LINEAR);
	remap(depth, depth, imap1_, imap2_, INTER_LINEAR);
}

Nicolas Pope's avatar
Nicolas Pope committed
bool Calibrate::rectified(cv::Mat &l, cv::Mat &r) {
	return undistort(l, r);
}

bool Calibrate::isCalibrated() {
	return calibrated_;
}

//! [compute_errors]
static double computeReprojectionErrors(
		const vector<vector<Point3f> >& objectPoints,
		const vector<vector<Point2f> >& imagePoints,
		const vector<Mat>& rvecs, const vector<Mat>& tvecs,
		const Mat& cameraMatrix , const Mat& distCoeffs,
		vector<float>& perViewErrors, bool fisheye) {
	vector<Point2f> imagePoints2;
	size_t totalPoints = 0;
	double totalErr = 0;
	perViewErrors.resize(objectPoints.size());

	for (size_t i = 0; i < objectPoints.size(); ++i) {
		if (fisheye) {
			cv::fisheye::projectPoints(objectPoints[i], imagePoints2, rvecs[i],
					tvecs[i], cameraMatrix, distCoeffs);
		} else {
			projectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix,
					distCoeffs, imagePoints2);
		}
		double err = norm(imagePoints[i], imagePoints2, NORM_L2);

		size_t n = objectPoints[i].size();
		perViewErrors[i] = static_cast<float>(std::sqrt(err*err/n));
		totalErr        += err*err;
		totalPoints     += n;
	}
Nicolas Pope's avatar
Nicolas Pope committed
}
//! [compute_errors]
//! [board_corners]
static void calcBoardCornerPositions(Size boardSize, float squareSize,
		vector<Point3f>& corners, Calibrate::Settings::Pattern patternType) {
	corners.clear();

	switch (patternType) {
	case Calibrate::Settings::CHESSBOARD:
	case Calibrate::Settings::CIRCLES_GRID:
		for (int i = 0; i < boardSize.height; ++i)
			for (int j = 0; j < boardSize.width; ++j)
				corners.push_back(Point3f(j*squareSize, i*squareSize, 0));
		break;

	case Calibrate::Settings::ASYMMETRIC_CIRCLES_GRID:
		for (int i = 0; i < boardSize.height; i++)
			for (int j = 0; j < boardSize.width; j++)
				corners.push_back(Point3f((2*j + i % 2)*squareSize,
						i*squareSize, 0));
		break;
	default:
		break;
	}
Nicolas Pope's avatar
Nicolas Pope committed
}
//! [board_corners]
static bool _runCalibration(const Calibrate::Settings& s, Size& imageSize,
		Mat& cameraMatrix, Mat& distCoeffs,
		vector<vector<Point2f> > imagePoints, vector<Mat>& rvecs,
		vector<Mat>& tvecs, vector<float>& reprojErrs,  double& totalAvgErr,
		vector<Point3f>& newObjPoints, float grid_width, bool release_object) {
	cameraMatrix = Mat::eye(3, 3, CV_64F);
	if (s.flag & CALIB_FIX_ASPECT_RATIO)
		cameraMatrix.at<double>(0, 0) = s.aspectRatio;
	if (s.useFisheye) {
		distCoeffs = Mat::zeros(4, 1, CV_64F);
	} else {
		distCoeffs = Mat::zeros(8, 1, CV_64F);
	}
	vector<vector<Point3f> > objectPoints(1);
	calcBoardCornerPositions(s.boardSize, s.squareSize, objectPoints[0],
			Calibrate::Settings::CHESSBOARD);
	objectPoints[0][s.boardSize.width - 1].x = objectPoints[0][0].x +
			grid_width;
	newObjPoints = objectPoints[0];
	objectPoints.resize(imagePoints.size(), objectPoints[0]);
	// Find intrinsic and extrinsic camera parameters
	double rms;
	if (s.useFisheye) {
		Mat _rvecs, _tvecs;
		rms = cv::fisheye::calibrate(objectPoints, imagePoints, imageSize,
				cameraMatrix, distCoeffs, _rvecs, _tvecs, s.flag);
		rvecs.reserve(_rvecs.rows);
		tvecs.reserve(_tvecs.rows);
		for (int i = 0; i < static_cast<int>(objectPoints.size()); i++) {
			rvecs.push_back(_rvecs.row(i));
			tvecs.push_back(_tvecs.row(i));
		}
	} else {
		rms = calibrateCamera(objectPoints, imagePoints, imageSize,
								cameraMatrix, distCoeffs, rvecs, tvecs,
								s.flag | CALIB_USE_LU);
	}

	LOG(INFO) << "Re-projection error reported by calibrateCamera: "<< rms;

	bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);

	objectPoints.clear();
	objectPoints.resize(imagePoints.size(), newObjPoints);
	totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, rvecs,
			tvecs, cameraMatrix, distCoeffs, reprojErrs, s.useFisheye);

	return ok;
Nicolas Pope's avatar
Nicolas Pope committed
}

//! [run_and_save]
bool runCalibration(const Calibrate::Settings& s, Size imageSize,
		Mat& cameraMatrix, Mat& distCoeffs,
		vector<vector<Point2f> > imagePoints, float grid_width,
		bool release_object) {
	vector<Mat> rvecs, tvecs;
	vector<float> reprojErrs;
	double totalAvgErr = 0;
	vector<Point3f> newObjPoints;

	bool ok = _runCalibration(s, imageSize, cameraMatrix, distCoeffs,
			imagePoints, rvecs, tvecs, reprojErrs, totalAvgErr, newObjPoints,
			grid_width, release_object);
	LOG(INFO) << (ok ? "Calibration succeeded" : "Calibration failed")
		 << ". avg re projection error = " << totalAvgErr;

	return ok;