6
#include <opencv2/core.hpp>
7
#include <opencv2/core/utility.hpp>
8
#include <opencv2/imgproc.hpp>
9
#include <opencv2/calib3d.hpp>
10
#include <opencv2/imgcodecs.hpp>
11
#include <opencv2/videoio.hpp>
12
#include <opencv2/highgui.hpp>
14
#ifndef _CRT_SECURE_NO_WARNINGS
15
# define _CRT_SECURE_NO_WARNINGS
23
cout << "This is a camera calibration sample." << endl
24
<< "Usage: calibration configurationFile" << endl
25
<< "Near the sample file you'll find the configuration file, which has detailed help of "
26
"how to edit it. It may be any OpenCV supported file format XML/YAML." << endl;
31
Settings() : goodInput(false) {}
32
enum Pattern { NOT_EXISTING, CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };
33
enum InputType { INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST };
35
void write(FileStorage& fs) const //Write serialization for this class
38
<< "BoardSize_Width" << boardSize.width
39
<< "BoardSize_Height" << boardSize.height
40
<< "Square_Size" << squareSize
41
<< "Calibrate_Pattern" << patternToUse
42
<< "Calibrate_NrOfFrameToUse" << nrFrames
43
<< "Calibrate_FixAspectRatio" << aspectRatio
44
<< "Calibrate_AssumeZeroTangentialDistortion" << calibZeroTangentDist
45
<< "Calibrate_FixPrincipalPointAtTheCenter" << calibFixPrincipalPoint
47
<< "Write_DetectedFeaturePoints" << writePoints
48
<< "Write_extrinsicParameters" << writeExtrinsics
49
<< "Write_outputFileName" << outputFileName
51
<< "Show_UndistortedImage" << showUndistorsed
53
<< "Input_FlipAroundHorizontalAxis" << flipVertical
54
<< "Input_Delay" << delay
58
void read(const FileNode& node) //Read serialization for this class
60
node["BoardSize_Width" ] >> boardSize.width;
61
node["BoardSize_Height"] >> boardSize.height;
62
node["Calibrate_Pattern"] >> patternToUse;
63
node["Square_Size"] >> squareSize;
64
node["Calibrate_NrOfFrameToUse"] >> nrFrames;
65
node["Calibrate_FixAspectRatio"] >> aspectRatio;
66
node["Write_DetectedFeaturePoints"] >> writePoints;
67
node["Write_extrinsicParameters"] >> writeExtrinsics;
68
node["Write_outputFileName"] >> outputFileName;
69
node["Calibrate_AssumeZeroTangentialDistortion"] >> calibZeroTangentDist;
70
node["Calibrate_FixPrincipalPointAtTheCenter"] >> calibFixPrincipalPoint;
71
node["Calibrate_UseFisheyeModel"] >> useFisheye;
72
node["Input_FlipAroundHorizontalAxis"] >> flipVertical;
73
node["Show_UndistortedImage"] >> showUndistorsed;
74
node["Input"] >> input;
75
node["Input_Delay"] >> delay;
81
if (boardSize.width <= 0 || boardSize.height <= 0)
83
cerr << "Invalid Board size: " << boardSize.width << " " << boardSize.height << endl;
86
if (squareSize <= 10e-6)
88
cerr << "Invalid square size " << squareSize << endl;
93
cerr << "Invalid number of frames " << nrFrames << endl;
97
if (input.empty()) // Check for valid input
101
if (input[0] >= '0' && input[0] <= '9')
103
stringstream ss(input);
109
if (readStringList(input, imageList))
111
inputType = IMAGE_LIST;
112
nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size();
115
inputType = VIDEO_FILE;
117
if (inputType == CAMERA)
118
inputCapture.open(cameraID);
119
if (inputType == VIDEO_FILE)
120
inputCapture.open(input);
121
if (inputType != IMAGE_LIST && !inputCapture.isOpened())
124
if (inputType == INVALID)
126
cerr << " Input does not exist: " << input;
130
flag = CALIB_FIX_K4 | CALIB_FIX_K5;
131
if(calibFixPrincipalPoint) flag |= CALIB_FIX_PRINCIPAL_POINT;
132
if(calibZeroTangentDist) flag |= CALIB_ZERO_TANGENT_DIST;
133
if(aspectRatio) flag |= CALIB_FIX_ASPECT_RATIO;
136
// the fisheye model has its own enum, so overwrite the flags
137
flag = fisheye::CALIB_FIX_SKEW | fisheye::CALIB_RECOMPUTE_EXTRINSIC |
138
// fisheye::CALIB_FIX_K1 |
139
fisheye::CALIB_FIX_K2 | fisheye::CALIB_FIX_K3 | fisheye::CALIB_FIX_K4;
142
calibrationPattern = NOT_EXISTING;
143
if (!patternToUse.compare("CHESSBOARD")) calibrationPattern = CHESSBOARD;
144
if (!patternToUse.compare("CIRCLES_GRID")) calibrationPattern = CIRCLES_GRID;
145
if (!patternToUse.compare("ASYMMETRIC_CIRCLES_GRID")) calibrationPattern = ASYMMETRIC_CIRCLES_GRID;
146
if (calibrationPattern == NOT_EXISTING)
148
cerr << " Camera calibration mode does not exist: " << patternToUse << endl;
157
if( inputCapture.isOpened() )
160
inputCapture >> view0;
161
view0.copyTo(result);
163
else if( atImageList < imageList.size() )
164
result = imread(imageList[atImageList++], IMREAD_COLOR);
169
static bool readStringList( const string& filename, vector<string>& l )
172
FileStorage fs(filename, FileStorage::READ);
175
FileNode n = fs.getFirstTopLevelNode();
176
if( n.type() != FileNode::SEQ )
178
FileNodeIterator it = n.begin(), it_end = n.end();
179
for( ; it != it_end; ++it )
180
l.push_back((string)*it);
184
Size boardSize; // The size of the board -> Number of items by width and height
185
Pattern calibrationPattern; // One of the Chessboard, circles, or asymmetric circle pattern
186
float squareSize; // The size of a square in your defined unit (point, millimeter,etc).
187
int nrFrames; // The number of frames to use from the input for calibration
188
float aspectRatio; // The aspect ratio
189
int delay; // In case of a video input
190
bool writePoints; // Write detected feature points
191
bool writeExtrinsics; // Write extrinsic parameters
192
bool calibZeroTangentDist; // Assume zero tangential distortion
193
bool calibFixPrincipalPoint; // Fix the principal point at the center
194
bool flipVertical; // Flip the captured images around the horizontal axis
195
string outputFileName; // The name of the file where to write
196
bool showUndistorsed; // Show undistorted images after calibration
197
string input; // The input ->
198
bool useFisheye; // use fisheye camera model for calibration
201
vector<string> imageList;
203
VideoCapture inputCapture;
214
static inline void read(const FileNode& node, Settings& x, const Settings& default_value = Settings())
222
static inline void write(FileStorage& fs, const String&, const Settings& s )
227
enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };
229
bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs,
230
vector<vector<Point2f> > imagePoints );
232
int main(int argc, char* argv[])
238
const string inputSettingsFile = argc > 1 ? argv[1] : "default.xml";
239
FileStorage fs(inputSettingsFile, FileStorage::READ); // Read the settings
242
cout << "Could not open the configuration file: \"" << inputSettingsFile << "\"" << endl;
246
fs.release(); // close Settings file
249
//FileStorage fout("settings.yml", FileStorage::WRITE); // write config as YAML
250
//fout << "Settings" << s;
254
cout << "Invalid input detected. Application stopping. " << endl;
258
vector<vector<Point2f> > imagePoints;
259
Mat cameraMatrix, distCoeffs;
261
int mode = s.inputType == Settings::IMAGE_LIST ? CAPTURING : DETECTION;
262
clock_t prevTimestamp = 0;
263
const Scalar RED(0,0,255), GREEN(0,255,0);
264
const char ESC_KEY = 27;
270
bool blinkOutput = false;
272
view = s.nextImage();
274
//----- If no more image, or got enough, then stop calibration and show result -------------
275
if( mode == CAPTURING && imagePoints.size() >= (size_t)s.nrFrames )
277
if( runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints))
282
if(view.empty()) // If there are no more images stop the loop
284
// if calibration threshold was not reached yet, calibrate now
285
if( mode != CALIBRATED && !imagePoints.empty() )
286
runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints);
291
imageSize = view.size(); // Format input image.
292
if( s.flipVertical ) flip( view, view, 0 );
295
vector<Point2f> pointBuf;
299
int chessBoardFlags = CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE;
302
// fast check erroneously fails with high distortions like fisheye
303
chessBoardFlags |= CALIB_CB_FAST_CHECK;
306
switch( s.calibrationPattern ) // Find feature points on the input format
308
case Settings::CHESSBOARD:
309
found = findChessboardCorners( view, s.boardSize, pointBuf, chessBoardFlags);
311
case Settings::CIRCLES_GRID:
312
found = findCirclesGrid( view, s.boardSize, pointBuf );
314
case Settings::ASYMMETRIC_CIRCLES_GRID:
315
found = findCirclesGrid( view, s.boardSize, pointBuf, CALIB_CB_ASYMMETRIC_GRID );
323
if ( found) // If done with success,
325
// improve the found corners' coordinate accuracy for chessboard
326
if( s.calibrationPattern == Settings::CHESSBOARD)
329
cvtColor(view, viewGray, COLOR_BGR2GRAY);
330
cornerSubPix( viewGray, pointBuf, Size(11,11),
331
Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.1 ));
334
if( mode == CAPTURING && // For camera only take new samples after delay time
335
(!s.inputCapture.isOpened() || clock() - prevTimestamp > s.delay*1e-3*CLOCKS_PER_SEC) )
337
imagePoints.push_back(pointBuf);
338
prevTimestamp = clock();
339
blinkOutput = s.inputCapture.isOpened();
343
drawChessboardCorners( view, s.boardSize, Mat(pointBuf), found );
346
//----------------------------- Output Text ------------------------------------------------
348
string msg = (mode == CAPTURING) ? "100/100" :
349
mode == CALIBRATED ? "Calibrated" : "Press 'g' to start";
351
Size textSize = getTextSize(msg, 1, 1, 1, &baseLine);
352
Point textOrigin(view.cols - 2*textSize.width - 10, view.rows - 2*baseLine - 10);
354
if( mode == CAPTURING )
356
if(s.showUndistorsed)
357
msg = format( "%d/%d Undist", (int)imagePoints.size(), s.nrFrames );
359
msg = format( "%d/%d", (int)imagePoints.size(), s.nrFrames );
362
putText( view, msg, textOrigin, 1, 1, mode == CALIBRATED ? GREEN : RED);
365
bitwise_not(view, view);
367
//------------------------- Video capture output undistorted ------------------------------
368
//! [output_undistorted]
369
if( mode == CALIBRATED && s.showUndistorsed )
371
Mat temp = view.clone();
372
undistort(temp, view, cameraMatrix, distCoeffs);
374
//! [output_undistorted]
375
//------------------------------ Show image and check for input commands -------------------
377
imshow("Image View", view);
378
char key = (char)waitKey(s.inputCapture.isOpened() ? 50 : s.delay);
383
if( key == 'u' && mode == CALIBRATED )
384
s.showUndistorsed = !s.showUndistorsed;
386
if( s.inputCapture.isOpened() && key == 'g' )
394
// -----------------------Show the undistorted image for the image list ------------------------
396
if( s.inputType == Settings::IMAGE_LIST && s.showUndistorsed )
398
Mat view, rview, map1, map2;
403
fisheye::estimateNewCameraMatrixForUndistortRectify(cameraMatrix, distCoeffs, imageSize,
404
Matx33d::eye(), newCamMat, 1);
405
fisheye::initUndistortRectifyMap(cameraMatrix, distCoeffs, Matx33d::eye(), newCamMat, imageSize,
406
CV_16SC2, map1, map2);
410
initUndistortRectifyMap(
411
cameraMatrix, distCoeffs, Mat(),
412
getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0), imageSize,
413
CV_16SC2, map1, map2);
416
for(size_t i = 0; i < s.imageList.size(); i++ )
418
view = imread(s.imageList[i], 1);
421
remap(view, rview, map1, map2, INTER_LINEAR);
422
imshow("Image View", rview);
423
char c = (char)waitKey();
424
if( c == ESC_KEY || c == 'q' || c == 'Q' )
434
static double computeReprojectionErrors( const vector<vector<Point3f> >& objectPoints,
435
const vector<vector<Point2f> >& imagePoints,
436
const vector<Mat>& rvecs, const vector<Mat>& tvecs,
437
const Mat& cameraMatrix , const Mat& distCoeffs,
438
vector<float>& perViewErrors, bool fisheye)
440
vector<Point2f> imagePoints2;
441
size_t totalPoints = 0;
442
double totalErr = 0, err;
443
perViewErrors.resize(objectPoints.size());
445
for(size_t i = 0; i < objectPoints.size(); ++i )
449
fisheye::projectPoints(objectPoints[i], imagePoints2, rvecs[i], tvecs[i], cameraMatrix,
454
projectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);
456
err = norm(imagePoints[i], imagePoints2, NORM_L2);
458
size_t n = objectPoints[i].size();
459
perViewErrors[i] = (float) std::sqrt(err*err/n);
464
return std::sqrt(totalErr/totalPoints);
468
static void calcBoardCornerPositions(Size boardSize, float squareSize, vector<Point3f>& corners,
469
Settings::Pattern patternType /*= Settings::CHESSBOARD*/)
475
case Settings::CHESSBOARD:
476
case Settings::CIRCLES_GRID:
477
for( int i = 0; i < boardSize.height; ++i )
478
for( int j = 0; j < boardSize.width; ++j )
479
corners.push_back(Point3f(j*squareSize, i*squareSize, 0));
482
case Settings::ASYMMETRIC_CIRCLES_GRID:
483
for( int i = 0; i < boardSize.height; i++ )
484
for( int j = 0; j < boardSize.width; j++ )
485
corners.push_back(Point3f((2*j + i % 2)*squareSize, i*squareSize, 0));
492
static bool runCalibration( Settings& s, Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs,
493
vector<vector<Point2f> > imagePoints, vector<Mat>& rvecs, vector<Mat>& tvecs,
494
vector<float>& reprojErrs, double& totalAvgErr)
497
cameraMatrix = Mat::eye(3, 3, CV_64F);
498
if( s.flag & CALIB_FIX_ASPECT_RATIO )
499
cameraMatrix.at<double>(0,0) = s.aspectRatio;
502
distCoeffs = Mat::zeros(4, 1, CV_64F);
504
distCoeffs = Mat::zeros(8, 1, CV_64F);
507
vector<vector<Point3f> > objectPoints(1);
508
calcBoardCornerPositions(s.boardSize, s.squareSize, objectPoints[0], s.calibrationPattern);
510
objectPoints.resize(imagePoints.size(),objectPoints[0]);
512
//Find intrinsic and extrinsic camera parameters
517
rms = fisheye::calibrate(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, _rvecs,
520
rvecs.reserve(_rvecs.rows);
521
tvecs.reserve(_tvecs.rows);
522
for(int i = 0; i < int(objectPoints.size()); i++){
523
rvecs.push_back(_rvecs.row(i));
524
tvecs.push_back(_tvecs.row(i));
527
rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs,
531
cout << "Re-projection error reported by calibrateCamera: "<< rms << endl;
533
bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);
535
totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs, cameraMatrix,
536
distCoeffs, reprojErrs, s.useFisheye);
541
// Print camera parameters to the output file
542
static void saveCameraParams( Settings& s, Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs,
543
const vector<Mat>& rvecs, const vector<Mat>& tvecs,
544
const vector<float>& reprojErrs, const vector<vector<Point2f> >& imagePoints,
547
FileStorage fs( s.outputFileName, FileStorage::WRITE );
551
struct tm *t2 = localtime( &tm );
553
strftime( buf, sizeof(buf), "%c", t2 );
555
fs << "calibration_time" << buf;
557
if( !rvecs.empty() || !reprojErrs.empty() )
558
fs << "nr_of_frames" << (int)std::max(rvecs.size(), reprojErrs.size());
559
fs << "image_width" << imageSize.width;
560
fs << "image_height" << imageSize.height;
561
fs << "board_width" << s.boardSize.width;
562
fs << "board_height" << s.boardSize.height;
563
fs << "square_size" << s.squareSize;
565
if( s.flag & CALIB_FIX_ASPECT_RATIO )
566
fs << "fix_aspect_ratio" << s.aspectRatio;
572
sprintf(buf, "flags:%s%s%s%s%s%s",
573
s.flag & fisheye::CALIB_FIX_SKEW ? " +fix_skew" : "",
574
s.flag & fisheye::CALIB_FIX_K1 ? " +fix_k1" : "",
575
s.flag & fisheye::CALIB_FIX_K2 ? " +fix_k2" : "",
576
s.flag & fisheye::CALIB_FIX_K3 ? " +fix_k3" : "",
577
s.flag & fisheye::CALIB_FIX_K4 ? " +fix_k4" : "",
578
s.flag & fisheye::CALIB_RECOMPUTE_EXTRINSIC ? " +recompute_extrinsic" : "");
582
sprintf(buf, "flags:%s%s%s%s",
583
s.flag & CALIB_USE_INTRINSIC_GUESS ? " +use_intrinsic_guess" : "",
584
s.flag & CALIB_FIX_ASPECT_RATIO ? " +fix_aspectRatio" : "",
585
s.flag & CALIB_FIX_PRINCIPAL_POINT ? " +fix_principal_point" : "",
586
s.flag & CALIB_ZERO_TANGENT_DIST ? " +zero_tangent_dist" : "");
588
cvWriteComment(*fs, buf, 0);
591
fs << "flags" << s.flag;
593
fs << "fisheye_model" << s.useFisheye;
595
fs << "camera_matrix" << cameraMatrix;
596
fs << "distortion_coefficients" << distCoeffs;
598
fs << "avg_reprojection_error" << totalAvgErr;
599
if (s.writeExtrinsics && !reprojErrs.empty())
600
fs << "per_view_reprojection_errors" << Mat(reprojErrs);
602
if(s.writeExtrinsics && !rvecs.empty() && !tvecs.empty() )
604
CV_Assert(rvecs[0].type() == tvecs[0].type());
605
Mat bigmat((int)rvecs.size(), 6, rvecs[0].type());
606
for( size_t i = 0; i < rvecs.size(); i++ )
608
Mat r = bigmat(Range(int(i), int(i+1)), Range(0,3));
609
Mat t = bigmat(Range(int(i), int(i+1)), Range(3,6));
611
CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1);
612
CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1);
613
//*.t() is MatExpr (not Mat) so we can use assignment operator
617
//cvWriteComment( *fs, "a set of 6-tuples (rotation vector + translation vector) for each view", 0 );
618
fs << "extrinsic_parameters" << bigmat;
621
if(s.writePoints && !imagePoints.empty() )
623
Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2);
624
for( size_t i = 0; i < imagePoints.size(); i++ )
626
Mat r = imagePtMat.row(int(i)).reshape(2, imagePtMat.cols);
627
Mat imgpti(imagePoints[i]);
630
fs << "image_points" << imagePtMat;
635
bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs,
636
vector<vector<Point2f> > imagePoints)
638
vector<Mat> rvecs, tvecs;
639
vector<float> reprojErrs;
640
double totalAvgErr = 0;
642
bool ok = runCalibration(s, imageSize, cameraMatrix, distCoeffs, imagePoints, rvecs, tvecs, reprojErrs,
644
cout << (ok ? "Calibration succeeded" : "Calibration failed")
645
<< ". avg re projection error = " << totalAvgErr << endl;
648
saveCameraParams(s, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, reprojErrs, imagePoints,