text: #include <iostream>
  #include <sstream>
  #include <time.h>
  #include <stdio.h>
  
  #include <opencv2\core\core.hpp>
  #include <opencv2\calib3d\calib3d.hpp>
  #include <opencv2\imgproc\imgproc.hpp>
  #include <opencv2\highgui\highgui.hpp>
  
  #ifndef _CRT_SECURE_NO_WARNINGS
  #define _CRT_SECURE_NO_WARNINGS
  #endif
  
  using namespace cv;
  using namespace std;
  
  class Settings {
  public:
  	Settings() : validInput(false) {}
  	enum Pattern { NOT_EXISTING, CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };
  	enum InputType { INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST };
  
  	void write(FileStorage& fs) const {
  		fs << "{" << "BoardSize_Width" << boardSize.width
  			<< "BoardSize_Height" << boardSize.height
  			<< "Square_Size" << squareSize
  			<< "Calibrate_Pattern" << patternToUse
  			<< "Calibrate_NrOfFramesToUse" << nrFrames
  			<< "Calibrate_FixAspectRatio" << aspectRatio
  			<< "Calibrate_AssumeZeroTangentialDistortion" << calibZeroTangentDist
  			<< "Calibrate_FixPrincipalPointAtTheCenter" << calibFixPrincipalPoint
  			<< "Write_DetectedFeaturePoints" << bwritePoints
  			<< "Write_extrinsicParameters" << bwriteExtrinsics
  			<< "Write_outputFileName" << outputFileName
  			<< "Show_UndistortedImage" << showUndistorsed
  			<< "Input_FlipAroundHorizontalAxis" << flipVertical
  			<< "Input_Delay" << delay
  			<< "Input" << input
  			<< "}";
  	}
  	void read(const FileNode& node) {
  		node["BoardSize_Width"] >> boardSize.width;
  		node["BoardSize_Height"] >> boardSize.height;
  		node["Calibrate_Pattern"] >> patternToUse;
  		node["Square_Size"] >> squareSize;
  		node["Calibrate_NrOfFramesToUse"] >> nrFrames;
  		node["Calibrate_FixAspectRatio"] >> aspectRatio;
  		node["Write_DetectedFeaturePoints"] >> bwritePoints;
  		node["Write_extrinsicParameters"] >> bwriteExtrinsics;
  		node["Write_outputFileName"] >> outputFileName;
  		node["Calibrate_AssumeZeroTangentialDistortion"] >> calibZeroTangentDist;
  		node["Calibrate_FixPrincipalPointAtTheCenter"] >> calibFixPrincipalPoint;
  		node["Input_FlipAroundHorizontalAxis"] >> flipVertical;
  		node["Show_UndistortedImage"] >> showUndistorsed;
  		node["Input"] >> input;
  		node["Input_Delay"] >> delay;
  		interprate();
  	}
  	void interprate() {
  		validInput = true;
  		if (boardSize.width <= 0 || boardSize.height <= 0) {
  			cerr << "Invalid Board size: " << boardSize.width << " " << boardSize.height << endl;
  			validInput = false;
  		}
  		if (squareSize <= 10e-6) {
  			cerr << "Invalid square size " << squareSize << endl;
  			validInput = false;
  		}
  		if (nrFrames <= 0) {
  			cerr << "Invalid number of frames " << nrFrames << endl;
  			validInput = false;
  		}
  		if (input.empty()) {
  			inputType = INVALID;
  		}
  		else {
  			if (input[0] >= '0' && input[0] <= '9') {
  				stringstream ss(input);
  				ss >> cameraID;
  				inputType = CAMERA;
  			}
  			else {
  				if (readStringList(input, imageList)) {
  					inputType = IMAGE_LIST;
  					nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size();
  				}
  				else {
  					inputType = VIDEO_FILE;
  				}
  			}
  			if (inputType == CAMERA) {
  				//inputCapture.open(cameraID);
  				inputCapture.open(0);
  			}
  			if (inputType == VIDEO_FILE) {
  				inputCapture.open(input);
  			}
  			if (inputType != IMAGE_LIST && !inputCapture.isOpened()) {
  				inputType = INVALID;
  			}
  		}
  		if (inputType == INVALID) {
  			cerr << "Inexistent input: " << input;
  			validInput = false;
  		}
  
  		flag = 0;
  
  		if (calibFixPrincipalPoint) {
  			flag |= CV_CALIB_FIX_PRINCIPAL_POINT;
  		}
  		if (calibZeroTangentDist) {
  			flag |= CV_CALIB_ZERO_TANGENT_DIST;
  		}
  		if (aspectRatio) {
  			flag |= CV_CALIB_FIX_ASPECT_RATIO;
  		}
  
  		calibrationPattern = NOT_EXISTING;
  
  		if (!patternToUse.compare("CHESSBOARD")) {
  			calibrationPattern = CHESSBOARD;
  		}
  		if (!patternToUse.compare("CIRCLES_GRID")) {
  			calibrationPattern = CIRCLES_GRID;
  		}
  		if (!patternToUse.compare("ASYMMETRIC_CIRCLES_GRID")) {
  			calibrationPattern = ASYMMETRIC_CIRCLES_GRID;
  		}
  		if (calibrationPattern == NOT_EXISTING) {
  			cerr << "Inexistent camera calibration mode: " << patternToUse << endl;
  			validInput = false;
  		}
  		atImageList = 0;
  	}
  	Mat nextImage() {
  		Mat result;
  		if (inputCapture.isOpened()) {
  			Mat view0;
  			inputCapture >> view0;
  			view0.copyTo(result);
  		}
  		else if (atImageList < (int)imageList.size()) {
  			result = imread(imageList[atImageList++], CV_LOAD_IMAGE_COLOR);
  		}
  
  		return result;
  	}
  	static bool 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;
  	}
  public:
  	Size boardSize;
  
  	Pattern calibrationPattern;
  
  	float squareSize;
  	float aspectRatio;
  
  	int nrFrames;
  	int delay;
  
  	bool bwritePoints;
  	bool bwriteExtrinsics;
  	bool calibZeroTangentDist;
  	bool calibFixPrincipalPoint;
  	bool flipVertical;
  	bool showUndistorsed;
  
  	string outputFileName;
  	string input;
  
  	int cameraID;
  	vector<string> imageList;
  	int atImageList;
  	VideoCapture inputCapture;
  	InputType inputType;
  	bool validInput;
  	int flag;
  
  private:
  	string patternToUse;
  };
  
  static void read(const FileNode& node, Settings& x, const Settings& default_value = Settings()) {
  	if (node.empty()) {
  		x = default_value;
  	}
  	else {
  		x.read(node);
  	}
  }
  
  enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };
  
  bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs, vector<vector<Point2f>> imagePoints);
  
  int start(int argc, char* argv[]) {
  	Settings s;
  	const string inputSettingsFile = argc > 1 ? argv[1] : "default.xml";
  	FileStorage fs(inputSettingsFile, FileStorage::READ);
  
  	if (!fs.isOpened()) {
  		cout << "Could not open the configuration file: \"" << inputSettingsFile << "\"" << endl;
  		return -1;
  	}
  	else {
  		cout << "File opened correctly." << endl;
  	}
  
  	fs["Settings"] >> s;
  	fs.release();
  
  	if (!s.validInput) {
  		cout << "Invalid input detected. Application stopping. " << endl;
  		return -1;
  	}
  	else {
  		cout << "Settings loaded correctly." << endl;
  	}
  
  	vector<vector<Point2f>> imagePoints;
  	Mat cameraMatrix, distCoeffs;
  	Size imageSize;
  	int mode = s.inputType == Settings::IMAGE_LIST ? CAPTURING : DETECTION;
  	clock_t prevTimestamp = 0;
  	const Scalar RED(0, 0, 255), GREEN(0, 255, 0);
  	const char ESC_KEY = 27;
  
  	for (int i = 0;; i++) {
  		Mat view;
  		bool blinkOutput = false;
  
  		view = s.nextImage();
  
  		if (mode == CAPTURING && imagePoints.size() >= (unsigned)s.nrFrames) {
  			if (runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints)) {
  				mode = CALIBRATED;
  			}
  			else {
  				mode = DETECTION;
  			}
  		}
  
  		if (view.empty()) {
  			if (imagePoints.size() > 0) {
  				runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints);
  			}
  			break;
  		}
  
  		imageSize = view.size();
  		if (s.flipVertical) {
  			flip(view, view, 0);
  		}
  
  		vector<Point2f> pointBuf;
  
  		bool found;
  
  		switch (s.calibrationPattern) {
  		case Settings::CHESSBOARD:
  			found = findChessboardCorners(view, s.boardSize, pointBuf, CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE);
  			break;
  		case Settings::CIRCLES_GRID:
  			found = findCirclesGrid(view, s.boardSize, pointBuf);
  			break;
  		case Settings::ASYMMETRIC_CIRCLES_GRID:
  			found = findCirclesGrid(view, s.boardSize, pointBuf, CALIB_CB_ASYMMETRIC_GRID);
  			break;
  		}
  
  		if (found) {
  			if (s.calibrationPattern == Settings::CHESSBOARD) {
  				Mat viewGray;
  				cvtColor(view, viewGray, COLOR_BGR2GRAY);
  				cornerSubPix(viewGray, pointBuf, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
  			}
  
  			if (mode == CAPTURING && (!s.inputCapture.isOpened() || clock() - prevTimestamp > s.delay * 1e-3 * CLOCKS_PER_SEC)) {
  				imagePoints.push_back(pointBuf);
  				prevTimestamp = clock();
  				blinkOutput = s.inputCapture.isOpened();
  
  				cout << "Image added." << endl;
  			}
  
  			drawChessboardCorners(view, s.boardSize, Mat(pointBuf), found);
  		}
  		string msg = (mode == CAPTURING) ? "100/100" : mode == CALIBRATED ? "Calibrated" : "Press 'g' to start";
  		int baseLine = 0;
  		Size textSize = getTextSize(msg, 1, 1, 1, &baseLine);
  		Point textOrigin(view.cols - 2 * textSize.width - 10, view.rows - 2 * baseLine - 10);
  
  		if (mode == CAPTURING) {
  			if (s.showUndistorsed) {
  				msg = format("%d/%d Undist", (int)imagePoints.size(), s.nrFrames);
  			}
  			else {
  				msg = format("%d/%d", (int)imagePoints.size(), s.nrFrames);
  			}
  		}
  
  		putText(view, msg, textOrigin, 1, 1, mode == CALIBRATED ? GREEN : RED);
  
  		if (blinkOutput) {
  			bitwise_not(view, view);
  		}
  		if (mode == CALIBRATED && s.showUndistorsed) {
  			Mat temp = view.clone();
  			undistort(temp, view, cameraMatrix, distCoeffs);
  		}
  
  		imshow("Calibration Mode", view);
  		char key = (char)waitKey(s.inputCapture.isOpened() ? 50 : s.delay);
  
  		if (key == ESC_KEY) {
  			break;
  		}
  		if (key == 'u' && mode == CALIBRATED) {
  			s.showUndistorsed = !s.showUndistorsed;
  		}
  		if (s.inputCapture.isOpened() && key == 'g') {
  			mode = CAPTURING;
  			imagePoints.clear();
  		}
  	}
  
  	if (s.inputType == Settings::IMAGE_LIST && s.showUndistorsed) {
  		Mat view, rview, map1, map2;
  		initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0), imageSize, CV_16SC2, map1, map2);
  
  		for (int i = 0; i < (int)s.imageList.size(); i++) {
  			view = imread(s.imageList[i], 1);
  			if (view.empty()) {
  				continue;
  			}
  			remap(view, rview, map1, map2, INTER_LINEAR);
  			imshow("Image Review", rview);
  			char c = (char)waitKey();
  
  			if (c == ESC_KEY || c == 'q' || c == 'Q') {
  				break;
  			}
  		}
  	}
  
  	return 0;
  }
  
  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) {
  	vector<Point2f> imagePoints2;
  	int i, totalPoints = 0;
  	double totalErr = 0, err;
  	perViewErrors.resize(objectPoints.size());
  
  	for (i = 0; i < (int)objectPoints.size(); i++) {
  		projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);
  		err = norm(Mat(imagePoints[i]), Mat(imagePoints2), CV_L2);
  
  		int n = (int)objectPoints[i].size();
  		perViewErrors[i] = (float)std::sqrt(err * err / n);
  		totalErr += err * err;
  		totalPoints += n;
  	}
  
  	return std::sqrt(totalErr / totalPoints);
  }
  
  static void calcBoardCornerPositions(Size boardSize, float squareSize, vector<Point3f>& corners, Settings::Pattern patternType) {
  	corners.clear();
  
  	switch (patternType) {
  	case Settings::CHESSBOARD:
  	case Settings::CIRCLES_GRID:
  		for (int i = 0; i < boardSize.height; ++i) {
  			for (int j = 0; j < boardSize.width; ++j) {
  				corners.push_back(Point3f(float(j * squareSize), float(i * squareSize), 0));
  			}
  		}
  		break;
  	case Settings::ASYMMETRIC_CIRCLES_GRID:
  		for (int i = 0; i < boardSize.height; i++) {
  			for (int j = 0; j < boardSize.width; j++) {
  				corners.push_back(Point3f(float((2 * j + i % 2) * squareSize), float(i * squareSize), 0));
  			}
  		}
  		break;
  	default:
  		break;
  	}
  }
  
  static bool runCalibration(Settings& s, Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs, vector<vector<Point2f>> imagePoints, vector<Mat>& rvecs, vector<Mat>& tvecs, vector<float>& reprojErrs, double& totalAvgErr) {
  	cameraMatrix = Mat::eye(3, 3, CV_64F);
  
  	if (s.flag & CV_CALIB_FIX_ASPECT_RATIO) {
  		cameraMatrix.at<double>(0, 0) = 1.0;
  	}
  
  	distCoeffs = Mat::zeros(8, 1, CV_64F);
  
  	vector<vector<Point3f>> objectPoints(1);
  	calcBoardCornerPositions(s.boardSize, s.squareSize, objectPoints[0], s.calibrationPattern);
  
  	objectPoints.resize(imagePoints.size(), objectPoints[0]);
  
  	double rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, s.flag | CV_CALIB_FIX_K4 | CV_CALIB_FIX_K5);
  
  	cout << "Re-projection error reported by calibratedCamera:" << rms << endl;
  
  	bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);
  
  	totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs, cameraMatrix, distCoeffs, reprojErrs);
  
  	return ok;
  }
  
  static void saveCameraParams(Settings& s, Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs, const vector<Mat>& rvecs, const vector<Mat>& tvecs, const vector<float>& reprojErrs, const vector<vector<Point2f>>& imagePoints, double totalAvgErr) {
  	FileStorage fs(s.outputFileName, FileStorage::WRITE);
  
  	time_t tm;
  	time(&tm);
  
  	struct tm t2;
  	localtime_s(&t2, &tm);
  
  	char buf[1024];
  	strftime(buf, sizeof(buf) - 1, "%c", &t2);
  
  	fs << "calibration_Time" << buf;
  
  	if (!rvecs.empty() || !reprojErrs.empty()) {
  		fs << "nrOfFrames" << (int)std::max(rvecs.size(), reprojErrs.size());
  	}
  	fs << "image_Width" << imageSize.width;
  	fs << "image_Height" << imageSize.height;
  	fs << "board_Width" << s.boardSize.width;
  	fs << "board_Height" << s.boardSize.height;
  	fs << "square_Size" << s.squareSize;
  
  	if (s.flag & CV_CALIB_FIX_ASPECT_RATIO) {
  		fs << "FixAspectRatio" << s.aspectRatio;
  	}
  
  	if (s.flag) {
  		sprintf_s(buf, "flags: %s%s%s%s",
  			s.flag & CV_CALIB_USE_INTRINSIC_GUESS ? " +use_instrinsic_guess" : "",
  			s.flag & CV_CALIB_FIX_ASPECT_RATIO ? " +fix_aspectRatio" : "",
  			s.flag & CV_CALIB_FIX_PRINCIPAL_POINT ? " +fix_principal_point" : "",
  			s.flag & CV_CALIB_ZERO_TANGENT_DIST ? " +zero_tangent_dist" : "");
  		cvWriteComment(*fs, buf, 0);
  	}
  
  	fs << "flagValue" << s.flag;
  	fs << "Camera_Matrix" << cameraMatrix;
  	fs << "Distortion_Coefficients" << distCoeffs;
  	fs << "Avg_Reprojection_Error" << totalAvgErr;
  
  	if (!reprojErrs.empty()) {
  		fs << "Per_View_Reprojection_Errors" << Mat(reprojErrs);
  	}
  
  	if (!rvecs.empty() && !tvecs.empty()) {
  		CV_Assert(rvecs[0].type() == tvecs[0].type());
  		Mat bigmat((int)rvecs.size(), 6, rvecs[0].type());
  
  		for (int i = 0; i < (int)rvecs.size(); i++) {
  			Mat r = bigmat(Range(i, i + 1), Range(0, 3));
  			Mat t = bigmat(Range(i, i + 1), Range(3, 6));
  
  			CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1);
  			CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1);
  
  			r = rvecs[i].t();
  			t = tvecs[i].t();
  		}
  
  		cvWriteComment(*fs, "a set of 6-tuples (rotation vector + translation vector) for each view", 0);
  		fs << "Extrinsic_Parameters" << bigmat;
  	}
  
  	if (!imagePoints.empty()) {
  		Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2);
  
  		for (int i = 0; i < (int)imagePoints.size(); i++) {
  			Mat r = imagePtMat.row(i).reshape(2, imagePtMat.cols);
  			Mat imgpti(imagePoints[i]);
  			imgpti.copyTo(r);
  		}
  
  		fs << "Image_points" << imagePtMat;
  	}
  }
  
  bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs, vector<vector<Point2f>> imagePoints) {
  	vector<Mat> rvecs, tvecs;
  	vector<float> reprojErrs;
  	double totalAvgErr = 0;
  
  	bool ok = runCalibration(s, imageSize, cameraMatrix, distCoeffs, imagePoints, rvecs, tvecs, reprojErrs, totalAvgErr);
  
  	cout << (ok ? "Calibration succeeded" : "Calibration failed") << ". avg re projection error = " << totalAvgErr;
  
  	if (ok) {
  		saveCameraParams(s, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, reprojErrs, imagePoints, totalAvgErr);
  	}
  
  	return ok;
  }