From 7c5084e385f1d7046a21a89cd6751c28d9785ba9 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Wed, 25 Mar 2015 16:11:53 +0100 Subject: [PATCH] - update documentation * correct the part about fixed aspect * improve formatting * add snippet markers to source * replace inline code by @snippet - do not run calibration twice when using a imageList - make output consistent in itself (CamelCase vs no_camel_case) as well as with old camera calibrate sample - respect write extrinsic/ points flags - set the aspectRatio value when specified - fix writing of calibration settings. also update grammar. - fix intendation and remove some size_t -> int casts by using size_t --- .../camera_calibration.markdown | 272 +++--------------- .../camera_calibration/camera_calibration.cpp | 234 ++++++++------- 2 files changed, 162 insertions(+), 344 deletions(-) diff --git a/doc/tutorials/calib3d/camera_calibration/camera_calibration.markdown b/doc/tutorials/calib3d/camera_calibration/camera_calibration.markdown index 0b23643964..a7bd1f0597 100644 --- a/doc/tutorials/calib3d/camera_calibration/camera_calibration.markdown +++ b/doc/tutorials/calib3d/camera_calibration/camera_calibration.markdown @@ -30,7 +30,7 @@ y_{corrected} = y + [ p_1(r^2+ 2y^2)+ 2p_2xy]\f] So we have five distortion parameters which in OpenCV are presented as one row matrix with 5 columns: -\f[Distortion_{coefficients}=(k_1 \hspace{10pt} k_2 \hspace{10pt} p_1 \hspace{10pt} p_2 \hspace{10pt} k_3)\f] +\f[distortion\_coefficients=(k_1 \hspace{10pt} k_2 \hspace{10pt} p_1 \hspace{10pt} p_2 \hspace{10pt} k_3)\f] Now for the unit conversion we use the following formula: @@ -96,83 +96,30 @@ on how to do this you can find in the @ref tutorial_file_input_output_with_xml_y Explanation ----------- --# **Read the settings.** - @code{.cpp} - Settings s; - const string inputSettingsFile = argc > 1 ? argv[1] : "default.xml"; - FileStorage fs(inputSettingsFile, FileStorage::READ); // Read the settings - if (!fs.isOpened()) - { - cout << "Could not open the configuration file: \"" << inputSettingsFile << "\"" << endl; - return -1; - } - fs["Settings"] >> s; - fs.release(); // close Settings file - - if (!s.goodInput) - { - cout << "Invalid input detected. Application stopping. " << endl; - return -1; - } - @endcode +-# **Read the settings** + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp file_read + For this I've used simple OpenCV class input operation. After reading the file I've an additional post-processing function that checks validity of the input. Only if all inputs are good then *goodInput* variable will be true. --# **Get next input, if it fails or we have enough of them - calibrate**. After this we have a big +-# **Get next input, if it fails or we have enough of them - calibrate** + + After this we have a big loop where we do the following operations: get the next image from the image list, camera or video file. If this fails or we have enough images then we run the calibration process. In case of image we step out of the loop and otherwise the remaining frames will be undistorted (if the option is set) via changing from *DETECTION* mode to the *CALIBRATED* one. - @code{.cpp} - for(int i = 0;;++i) - { - Mat view; - bool blinkOutput = false; - - view = s.nextImage(); - - //----- If no more image, or got enough, then stop calibration and show result ------------- - if( mode == CAPTURING && imagePoints.size() >= (unsigned)s.nrFrames ) - { - if( runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints)) - mode = CALIBRATED; - else - mode = DETECTION; - } - if(view.empty()) // If no more images then run calibration, save and stop loop. - { - if( imagePoints.size() > 0 ) - runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints); - break; - imageSize = view.size(); // Format input image. - if( s.flipVertical ) flip( view, view, 0 ); - } - @endcode + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp get_input For some cameras we may need to flip the input image. Here we do this too. --# **Find the pattern in the current input**. The formation of the equations I mentioned above aims +-# **Find the pattern in the current input** + + The formation of the equations I mentioned above aims to finding major patterns in the input: in case of the chessboard this are corners of the squares and for the circles, well, the circles themselves. The position of these will form the result which will be written into the *pointBuf* vector. - @code{.cpp} - vector pointBuf; - - bool found; - switch( s.calibrationPattern ) // Find feature points on the input format - { - case Settings::CHESSBOARD: - found = findChessboardCorners( view, s.boardSize, pointBuf, - CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | 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; - } - @endcode + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp find_pattern Depending on the type of the input pattern you use either the @ref cv::findChessboardCorners or the @ref cv::findCirclesGrid function. For both of them you pass the current image and the size of the board and you'll get the positions of the patterns. Furthermore, they return a boolean @@ -188,109 +135,27 @@ Explanation *imagePoints* vector to collect all of the equations into a single container. Finally, for visualization feedback purposes we will draw the found points on the input image using @ref cv::findChessboardCorners function. - @code{.cpp} - if ( found) // If done with success, - { - // improve the found corners' coordinate accuracy for chessboard - if( s.calibrationPattern == Settings::CHESSBOARD) - { - Mat viewGray; - cvtColor(view, viewGray, COLOR_BGR2GRAY); - cornerSubPix( viewGray, pointBuf, Size(11,11), - Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::MAX_ITER, 30, 0.1 )); - } - - if( mode == CAPTURING && // For camera only take new samples after delay time - (!s.inputCapture.isOpened() || clock() - prevTimestamp > s.delay*1e-3*CLOCKS_PER_SEC) ) - { - imagePoints.push_back(pointBuf); - prevTimestamp = clock(); - blinkOutput = s.inputCapture.isOpened(); - } - - // Draw the corners. - drawChessboardCorners( view, s.boardSize, Mat(pointBuf), found ); - } - @endcode --# **Show state and result to the user, plus command line control of the application**. This part - shows text output on the image. - @code{.cpp} - //----------------------------- Output Text ------------------------------------------------ - 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); - @endcode + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp pattern_found +-# **Show state and result to the user, plus command line control of the application** + + This part shows text output on the image. + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp output_text If we ran calibration and got camera's matrix with the distortion coefficients we may want to correct the image using @ref cv::undistort function: - @code{.cpp} - //------------------------- Video capture output undistorted ------------------------------ - if( mode == CALIBRATED && s.showUndistorsed ) - { - Mat temp = view.clone(); - undistort(temp, view, cameraMatrix, distCoeffs); - } - //------------------------------ Show image and check for input commands ------------------- - imshow("Image View", view); - @endcode - Then we wait for an input key and if this is *u* we toggle the distortion removal, if it is *g* - we start again the detection process, and finally for the *ESC* key we quit the application: - @code{.cpp} - char key = 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(); - } - @endcode --# **Show the distortion removal for the images too**. When you work with an image list it is not + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp output_undistorted + Then we show the image and wait for an input key and if this is *u* we toggle the distortion removal, + if it is *g* we start again the detection process, and finally for the *ESC* key we quit the application: + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp await_input +-# **Show the distortion removal for the images too** + + When you work with an image list it is not possible to remove the distortion inside the loop. Therefore, you must do this after the loop. Taking advantage of this now I'll expand the @ref cv::undistort function, which is in fact first calls @ref cv::initUndistortRectifyMap to find transformation matrices and then performs transformation using @ref cv::remap function. Because, after successful calibration map calculation needs to be done only once, by using this expanded form you may speed up your application: - @code{.cpp} - 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 View", rview); - char c = waitKey(); - if( c == ESC_KEY || c == 'q' || c == 'Q' ) - break; - } - } - @endcode + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp show_results The calibration and save ------------------------ @@ -304,24 +169,7 @@ Therefore in the first function we just split up these two processes. Because we of the calibration variables we'll create these variables here and pass on both of them to the calibration and saving function. Again, I'll not show the saving part as that has little in common with the calibration. Explore the source file in order to find out how and what: -@code{.cpp} -bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs,vector > imagePoints ) -{ - vector rvecs, tvecs; - vector 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 ) // save only if the calibration was done with success - saveCameraParams( s, imageSize, cameraMatrix, distCoeffs, rvecs ,tvecs, reprojErrs, - imagePoints, totalAvgErr); - return ok; -} -@endcode +@snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp run_and_save We do the calibration with the help of the @ref cv::calibrateCamera function. It has the following parameters: @@ -331,29 +179,7 @@ parameters: present. Because, we use a single pattern for all the input images we can calculate this just once and multiply it for all the other input views. We calculate the corner points with the *calcBoardCornerPositions* function as: - @code{.cpp} - void calcBoardCornerPositions(Size boardSize, float squareSize, vector& corners, - Settings::Pattern patternType /*= Settings::CHESSBOARD*/) - { - 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; - } - } - @endcode + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp board_corners And then multiply it as: @code{.cpp} vector > objectPoints(1); @@ -365,12 +191,8 @@ parameters: circle pattern). We have already collected this from @ref cv::findChessboardCorners or @ref cv::findCirclesGrid function. We just need to pass it on. - The size of the image acquired from the camera, video file or the images. -- The camera matrix. If we used the fixed aspect ratio option we need to set the \f$f_x\f$ to zero: - @code{.cpp} - cameraMatrix = Mat::eye(3, 3, CV_64F); - if( s.flag & CALIB_FIX_ASPECT_RATIO ) - cameraMatrix.at(0,0) = 1.0; - @endcode +- The camera matrix. If we used the fixed aspect ratio option we need to set \f$f_x\f$: + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp fixed_aspect - The distortion coefficient matrix. Initialize with zero. @code{.cpp} distCoeffs = Mat::zeros(8, 1, CV_64F); @@ -393,33 +215,7 @@ double rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, calculate the absolute norm between what we got with our transformation and the corner/circle finding algorithm. To find the average error we calculate the arithmetical mean of the errors calculated for all the calibration images. - @code{.cpp} - double computeReprojectionErrors( const vector >& objectPoints, - const vector >& imagePoints, - const vector& rvecs, const vector& tvecs, - const Mat& cameraMatrix , const Mat& distCoeffs, - vector& perViewErrors) - { - vector 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, // project - distCoeffs, imagePoints2); - err = norm(Mat(imagePoints[i]), Mat(imagePoints2), NORM_L2); // difference - - int n = (int)objectPoints[i].size(); - perViewErrors[i] = (float) std::sqrt(err*err/n); // save for this view - totalErr += err*err; // sum it up - totalPoints += n; - } - - return std::sqrt(totalErr/totalPoints); // calculate the arithmetical mean - } - @endcode + @snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp compute_errors Results ------- @@ -461,20 +257,20 @@ the input. Here's, how a detected pattern should look: In both cases in the specified output XML/YAML file you'll find the camera and distortion coefficients matrices: @code{.xml} - + 3 3
d
6.5746697944293521e+002 0. 3.1950000000000000e+002 0. - 6.5746697944293521e+002 2.3950000000000000e+002 0. 0. 1.
- + 6.5746697944293521e+002 2.3950000000000000e+002 0. 0. 1.
+ 5 1
d
-4.1802327176423804e-001 5.0715244063187526e-001 0. 0. - -5.7843597214487474e-001
+ -5.7843597214487474e-001 @endcode Add these values as constants to your program, call the @ref cv::initUndistortRectifyMap and the @ref cv::remap function to remove distortion and enjoy distortion free inputs for cheap and low diff --git a/samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp b/samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp index 34e2504c6e..8059a4aec5 100644 --- a/samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp +++ b/samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp @@ -34,7 +34,8 @@ public: void write(FileStorage& fs) const //Write serialization for this class { - fs << "{" << "BoardSize_Width" << boardSize.width + fs << "{" + << "BoardSize_Width" << boardSize.width << "BoardSize_Height" << boardSize.height << "Square_Size" << squareSize << "Calibrate_Pattern" << patternToUse @@ -43,8 +44,8 @@ public: << "Calibrate_AssumeZeroTangentialDistortion" << calibZeroTangentDist << "Calibrate_FixPrincipalPointAtTheCenter" << calibFixPrincipalPoint - << "Write_DetectedFeaturePoints" << bwritePoints - << "Write_extrinsicParameters" << bwriteExtrinsics + << "Write_DetectedFeaturePoints" << writePoints + << "Write_extrinsicParameters" << writeExtrinsics << "Write_outputFileName" << outputFileName << "Show_UndistortedImage" << showUndistorsed @@ -62,8 +63,8 @@ public: node["Square_Size"] >> squareSize; node["Calibrate_NrOfFrameToUse"] >> nrFrames; node["Calibrate_FixAspectRatio"] >> aspectRatio; - node["Write_DetectedFeaturePoints"] >> bwritePoints; - node["Write_extrinsicParameters"] >> bwriteExtrinsics; + node["Write_DetectedFeaturePoints"] >> writePoints; + node["Write_extrinsicParameters"] >> writeExtrinsics; node["Write_outputFileName"] >> outputFileName; node["Calibrate_AssumeZeroTangentialDistortion"] >> calibZeroTangentDist; node["Calibrate_FixPrincipalPointAtTheCenter"] >> calibFixPrincipalPoint; @@ -71,9 +72,9 @@ public: node["Show_UndistortedImage"] >> showUndistorsed; node["Input"] >> input; node["Input_Delay"] >> delay; - interprate(); + validate(); } - void interprate() + void validate() { goodInput = true; if (boardSize.width <= 0 || boardSize.height <= 0) @@ -105,10 +106,10 @@ public: else { if (readStringList(input, imageList)) - { - inputType = IMAGE_LIST; - nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size(); - } + { + inputType = IMAGE_LIST; + nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size(); + } else inputType = VIDEO_FILE; } @@ -121,7 +122,7 @@ public: } if (inputType == INVALID) { - cerr << " Inexistent input: " << input; + cerr << " Input does not exist: " << input; goodInput = false; } @@ -136,10 +137,10 @@ public: 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; - goodInput = false; - } + { + cerr << " Camera calibration mode does not exist: " << patternToUse << endl; + goodInput = false; + } atImageList = 0; } @@ -152,7 +153,7 @@ public: inputCapture >> view0; view0.copyTo(result); } - else if( atImageList < (int)imageList.size() ) + else if( atImageList < imageList.size() ) result = imread(imageList[atImageList++], IMREAD_COLOR); return result; @@ -173,26 +174,24 @@ public: return true; } public: - Size boardSize; // The size of the board -> Number of items by width and height - Pattern calibrationPattern;// One of the Chessboard, circles, or asymmetric circle pattern - float squareSize; // The size of a square in your defined unit (point, millimeter,etc). - int nrFrames; // The number of frames to use from the input for calibration - float aspectRatio; // The aspect ratio - int delay; // In case of a video input - bool bwritePoints; // Write detected feature points - bool bwriteExtrinsics; // Write extrinsic parameters - bool calibZeroTangentDist; // Assume zero tangential distortion - bool calibFixPrincipalPoint;// Fix the principal point at the center - bool flipVertical; // Flip the captured images around the horizontal axis - string outputFileName; // The name of the file where to write - bool showUndistorsed; // Show undistorted images after calibration - string input; // The input -> - - + Size boardSize; // The size of the board -> Number of items by width and height + Pattern calibrationPattern; // One of the Chessboard, circles, or asymmetric circle pattern + float squareSize; // The size of a square in your defined unit (point, millimeter,etc). + int nrFrames; // The number of frames to use from the input for calibration + float aspectRatio; // The aspect ratio + int delay; // In case of a video input + bool writePoints; // Write detected feature points + bool writeExtrinsics; // Write extrinsic parameters + bool calibZeroTangentDist; // Assume zero tangential distortion + bool calibFixPrincipalPoint; // Fix the principal point at the center + bool flipVertical; // Flip the captured images around the horizontal axis + string outputFileName; // The name of the file where to write + bool showUndistorsed; // Show undistorted images after calibration + string input; // The input -> int cameraID; vector imageList; - int atImageList; + size_t atImageList; VideoCapture inputCapture; InputType inputType; bool goodInput; @@ -204,7 +203,7 @@ private: }; -static void read(const FileNode& node, Settings& x, const Settings& default_value = Settings()) +static inline void read(const FileNode& node, Settings& x, const Settings& default_value = Settings()) { if(node.empty()) x = default_value; @@ -212,6 +211,11 @@ static void read(const FileNode& node, Settings& x, const Settings& default_valu x.read(node); } +static inline void write(FileStorage& fs, const String&, const Settings& s ) +{ + s.write(fs); +} + enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 }; bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs, @@ -220,6 +224,8 @@ bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& int main(int argc, char* argv[]) { help(); + + //! [file_read] Settings s; const string inputSettingsFile = argc > 1 ? argv[1] : "default.xml"; FileStorage fs(inputSettingsFile, FileStorage::READ); // Read the settings @@ -230,6 +236,10 @@ int main(int argc, char* argv[]) } fs["Settings"] >> s; fs.release(); // close Settings file + //! [file_read] + + //FileStorage fout("settings.yml", FileStorage::WRITE); // write config as YAML + //fout << "Settings" << s; if (!s.goodInput) { @@ -245,32 +255,35 @@ int main(int argc, char* argv[]) const Scalar RED(0,0,255), GREEN(0,255,0); const char ESC_KEY = 27; - for(int i = 0;;++i) + //! [get_input] + for(;;) { - Mat view; - bool blinkOutput = false; + Mat view; + bool blinkOutput = false; - view = s.nextImage(); + view = s.nextImage(); - //----- If no more image, or got enough, then stop calibration and show result ------------- - if( mode == CAPTURING && imagePoints.size() >= (unsigned)s.nrFrames ) - { + //----- If no more image, or got enough, then stop calibration and show result ------------- + if( mode == CAPTURING && imagePoints.size() >= (size_t)s.nrFrames ) + { if( runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints)) mode = CALIBRATED; else mode = DETECTION; - } - if(view.empty()) // If no more images then run calibration, save and stop loop. - { - if( imagePoints.size() > 0 ) + } + if(view.empty()) // If there are no more images stop the loop + { + // if calibration threshold was not reached yet, calibrate now + if( mode != CALIBRATED && !imagePoints.empty() ) runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints); break; - } - + } + //! [get_input] imageSize = view.size(); // Format input image. if( s.flipVertical ) flip( view, view, 0 ); + //! [find_pattern] vector pointBuf; bool found; @@ -290,7 +303,8 @@ int main(int argc, char* argv[]) found = false; break; } - + //! [find_pattern] + //! [pattern_found] if ( found) // If done with success, { // improve the found corners' coordinate accuracy for chessboard @@ -313,8 +327,9 @@ int main(int argc, char* argv[]) // Draw the corners. drawChessboardCorners( view, s.boardSize, Mat(pointBuf), found ); } - + //! [pattern_found] //----------------------------- Output Text ------------------------------------------------ + //! [output_text] string msg = (mode == CAPTURING) ? "100/100" : mode == CALIBRATED ? "Calibrated" : "Press 'g' to start"; int baseLine = 0; @@ -333,15 +348,17 @@ int main(int argc, char* argv[]) if( blinkOutput ) bitwise_not(view, view); - + //! [output_text] //------------------------- Video capture output undistorted ------------------------------ + //! [output_undistorted] if( mode == CALIBRATED && s.showUndistorsed ) { Mat temp = view.clone(); undistort(temp, view, cameraMatrix, distCoeffs); } - + //! [output_undistorted] //------------------------------ Show image and check for input commands ------------------- + //! [await_input] imshow("Image View", view); char key = (char)waitKey(s.inputCapture.isOpened() ? 50 : s.delay); @@ -356,9 +373,11 @@ int main(int argc, char* argv[]) mode = CAPTURING; imagePoints.clear(); } + //! [await_input] } // -----------------------Show the undistorted image for the image list ------------------------ + //! [show_results] if( s.inputType == Settings::IMAGE_LIST && s.showUndistorsed ) { Mat view, rview, map1, map2; @@ -366,7 +385,7 @@ int main(int argc, char* argv[]) getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0), imageSize, CV_16SC2, map1, map2); - for(int i = 0; i < (int)s.imageList.size(); i++ ) + for(size_t i = 0; i < s.imageList.size(); i++ ) { view = imread(s.imageList[i], 1); if(view.empty()) @@ -378,11 +397,12 @@ int main(int argc, char* argv[]) break; } } - + //! [show_results] return 0; } +//! [compute_errors] static double computeReprojectionErrors( const vector >& objectPoints, const vector >& imagePoints, const vector& rvecs, const vector& tvecs, @@ -390,17 +410,16 @@ static double computeReprojectionErrors( const vector >& objectP vector& perViewErrors) { vector imagePoints2; - int i, totalPoints = 0; + size_t totalPoints = 0; double totalErr = 0, err; perViewErrors.resize(objectPoints.size()); - for( i = 0; i < (int)objectPoints.size(); ++i ) + for(size_t i = 0; i < objectPoints.size(); ++i ) { - projectPoints( Mat(objectPoints[i]), rvecs[i], tvecs[i], cameraMatrix, - distCoeffs, imagePoints2); - err = norm(Mat(imagePoints[i]), Mat(imagePoints2), NORM_L2); + projectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2); + err = norm(imagePoints[i], imagePoints2, NORM_L2); - int n = (int)objectPoints[i].size(); + size_t n = objectPoints[i].size(); perViewErrors[i] = (float) std::sqrt(err*err/n); totalErr += err*err; totalPoints += n; @@ -408,7 +427,8 @@ static double computeReprojectionErrors( const vector >& objectP return std::sqrt(totalErr/totalPoints); } - +//! [compute_errors] +//! [board_corners] static void calcBoardCornerPositions(Size boardSize, float squareSize, vector& corners, Settings::Pattern patternType /*= Settings::CHESSBOARD*/) { @@ -420,28 +440,28 @@ static void calcBoardCornerPositions(Size boardSize, float squareSize, vector > imagePoints, vector& rvecs, vector& tvecs, vector& reprojErrs, double& totalAvgErr) { - + //! [fixed_aspect] cameraMatrix = Mat::eye(3, 3, CV_64F); if( s.flag & CALIB_FIX_ASPECT_RATIO ) - cameraMatrix.at(0,0) = 1.0; - + cameraMatrix.at(0,0) = s.aspectRatio; + //! [fixed_aspect] distCoeffs = Mat::zeros(8, 1, CV_64F); vector > objectPoints(1); @@ -475,49 +495,48 @@ static void saveCameraParams( Settings& s, Size& imageSize, Mat& cameraMatrix, M time( &tm ); struct tm *t2 = localtime( &tm ); char buf[1024]; - strftime( buf, sizeof(buf)-1, "%c", t2 ); + strftime( buf, sizeof(buf), "%c", t2 ); - fs << "calibration_Time" << buf; + 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; + fs << "nr_of_frames" << (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 & CALIB_FIX_ASPECT_RATIO ) - fs << "FixAspectRatio" << s.aspectRatio; + fs << "fix_aspect_ratio" << s.aspectRatio; - if( s.flag ) + if (s.flag) { - sprintf( buf, "flags: %s%s%s%s", - s.flag & CALIB_USE_INTRINSIC_GUESS ? " +use_intrinsic_guess" : "", - s.flag & CALIB_FIX_ASPECT_RATIO ? " +fix_aspectRatio" : "", - s.flag & CALIB_FIX_PRINCIPAL_POINT ? " +fix_principal_point" : "", - s.flag & CALIB_ZERO_TANGENT_DIST ? " +zero_tangent_dist" : "" ); - //cvWriteComment( *fs, buf, 0 ); - + sprintf(buf, "flags: %s%s%s%s", + s.flag & CALIB_USE_INTRINSIC_GUESS ? " +use_intrinsic_guess" : "", + s.flag & CALIB_FIX_ASPECT_RATIO ? " +fix_aspect_ratio" : "", + s.flag & CALIB_FIX_PRINCIPAL_POINT ? " +fix_principal_point" : "", + s.flag & CALIB_ZERO_TANGENT_DIST ? " +zero_tangent_dist" : ""); + cvWriteComment(*fs, buf, 0); } - fs << "flagValue" << s.flag; + fs << "flags" << s.flag; - fs << "Camera_Matrix" << cameraMatrix; - fs << "Distortion_Coefficients" << distCoeffs; + fs << "camera_matrix" << cameraMatrix; + fs << "distortion_coefficients" << distCoeffs; - fs << "Avg_Reprojection_Error" << totalAvgErr; - if( !reprojErrs.empty() ) - fs << "Per_View_Reprojection_Errors" << Mat(reprojErrs); + fs << "avg_reprojection_error" << totalAvgErr; + if (s.writeExtrinsics && !reprojErrs.empty()) + fs << "per_view_reprojection_errors" << Mat(reprojErrs); - if( !rvecs.empty() && !tvecs.empty() ) + if(s.writeExtrinsics && !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++ ) + for( size_t i = 0; i < rvecs.size(); i++ ) { - Mat r = bigmat(Range(i, i+1), Range(0,3)); - Mat t = bigmat(Range(i, i+1), Range(3,6)); + Mat r = bigmat(Range(int(i), int(i+1)), Range(0,3)); + Mat t = bigmat(Range(int(i), int(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); @@ -526,35 +545,38 @@ static void saveCameraParams( Settings& s, Size& imageSize, Mat& cameraMatrix, M t = tvecs[i].t(); } //cvWriteComment( *fs, "a set of 6-tuples (rotation vector + translation vector) for each view", 0 ); - fs << "Extrinsic_Parameters" << bigmat; + fs << "extrinsic_parameters" << bigmat; } - if( !imagePoints.empty() ) + if(s.writePoints && !imagePoints.empty() ) { Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2); - for( int i = 0; i < (int)imagePoints.size(); i++ ) + for( size_t i = 0; i < imagePoints.size(); i++ ) { - Mat r = imagePtMat.row(i).reshape(2, imagePtMat.cols); + Mat r = imagePtMat.row(int(i)).reshape(2, imagePtMat.cols); Mat imgpti(imagePoints[i]); imgpti.copyTo(r); } - fs << "Image_points" << imagePtMat; + fs << "image_points" << imagePtMat; } } -bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs,vector > imagePoints ) +//! [run_and_save] +bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs, + vector > imagePoints) { vector rvecs, tvecs; vector reprojErrs; double totalAvgErr = 0; - bool ok = runCalibration(s,imageSize, cameraMatrix, distCoeffs, imagePoints, rvecs, tvecs, - reprojErrs, totalAvgErr); + bool ok = runCalibration(s, imageSize, cameraMatrix, distCoeffs, imagePoints, rvecs, tvecs, reprojErrs, + totalAvgErr); cout << (ok ? "Calibration succeeded" : "Calibration failed") - << ". avg re projection error = " << totalAvgErr ; + << ". avg re projection error = " << totalAvgErr << endl; - if( ok ) - saveCameraParams( s, imageSize, cameraMatrix, distCoeffs, rvecs ,tvecs, reprojErrs, - imagePoints, totalAvgErr); + if (ok) + saveCameraParams(s, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, reprojErrs, imagePoints, + totalAvgErr); return ok; } +//! [run_and_save]