OpenCV的Sample分析:相机标定(1)分析类Settings
此次分析的程序“camera_calibration.cpp”位于opencv-3.2.0/samples/cpp/tutorial_code/calib3d/camera_calibration 的文件夹中
在正式分析程序之前,先分析class Settings
class Settings { public: Settings() : goodInput(false) {} enum Pattern { NOT_EXISTING, CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID }; enum InputType { INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST }; //略去成员函数 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 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 -> bool useFisheye; // use fisheye camera model for calibration bool fixK1; // fix K1 distortion coefficient bool fixK2; // fix K2 distortion coefficient bool fixK3; // fix K3 distortion coefficient bool fixK4; // fix K4 distortion coefficient bool fixK5; // fix K5 distortion coefficient int cameraID; vector<string> imageList; size_t atImageList; VideoCapture inputCapture; InputType inputType; bool goodInput; int flag; private: string patternToUse; };
从中可以看见Settings的成员变量,关于标定板的尺寸大小,拍摄标定板的帧数,畸变参数等。注意enum变量Pattern和InputType变量的含义。
再分析Settings的成员函数
void write(FileStorage& fs) const //Write serialization for this class { 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_outputFileName" << outputFileName << "Show_UndistortedImage" << showUndistorsed << "Input_FlipAroundHorizontalAxis" << flipVertical << "Input_Delay" << delay << "Input" << input << "}"; } void read(const FileNode& node) //Read serialization for this class { node["BoardSize_Width" ] >> boardSize.width; node["BoardSize_Height"] >> boardSize.height; node["Calibrate_Pattern"] >> patternToUse; node["Square_Size"] >> squareSize; node["Calibrate_NrOfFrameToUse"] >> nrFrames; node["Calibrate_FixAspectRatio"] >> aspectRatio; node["Write_DetectedFeaturePoints"] >> writePoints; node["Write_extrinsicParameters"] >> writeExtrinsics; node["Write_outputFileName"] >> outputFileName; node["Calibrate_AssumeZeroTangentialDistortion"] >> calibZeroTangentDist; node["Calibrate_FixPrincipalPointAtTheCenter"] >> calibFixPrincipalPoint; node["Calibrate_UseFisheyeModel"] >> useFisheye; node["Input_FlipAroundHorizontalAxis"] >> flipVertical; node["Show_UndistortedImage"] >> showUndistorsed; node["Input"] >> input; node["Input_Delay"] >> delay; node["Fix_K1"] >> fixK1; node["Fix_K2"] >> fixK2; node["Fix_K3"] >> fixK3; node["Fix_K4"] >> fixK4; node["Fix_K5"] >> fixK5; validate(); }上述的两个函数一个用来写文件,另一个用来读文件。注意他们的写读格式。
在程序的初始阶段,我们需要读入标定板的基本参数,比如我们的标定板是6×9还是5×8,以便我们进行标定操作。于是,我们要首先读取文件“in_VID5.xml”,而它的一部分节选如下
<!-- Number of inner corners per a item row and column. (square, circle) --> <BoardSize_Width> 9</BoardSize_Width> <BoardSize_Height>6</BoardSize_Height> <!-- The size of a square in some user defined metric system (pixel, millimeter)--> <Square_Size>50</Square_Size> <!-- The type of input used for camera calibration. One of: CHESSBOARD CIRCLES_GRID ASYMMETRIC_CIRCLES_GRID --> <Calibrate_Pattern>"CHESSBOARD"</Calibrate_Pattern>
从这个文件可以看出,enum变量Pattern选定的是CHESSBOARD,选择的标定板是9×6,格子的数量是50。
在read()函数中,还有有一个函数需要说明,那就是validate()
void validate() { goodInput = true; if (boardSize.width <= 0 || boardSize.height <= 0) { cerr << "Invalid Board size: " << boardSize.width << " " << boardSize.height << endl; goodInput = false; } if (squareSize <= 10e-6) { cerr << "Invalid square size " << squareSize << endl; goodInput = false; } if (nrFrames <= 0) { cerr << "Invalid number of frames " << nrFrames << endl; goodInput = false; } if (input.empty()) // Check for valid input 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); if (inputType == VIDEO_FILE) inputCapture.open(input); if (inputType != IMAGE_LIST && !inputCapture.isOpened()) inputType = INVALID; } if (inputType == INVALID) { cerr << " Input does not exist: " << input; 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 = fisheye::CALIB_FIX_SKEW | fisheye::CALIB_RECOMPUTE_EXTRINSIC; if(fixK1) flag |= fisheye::CALIB_FIX_K1; if(fixK2) flag |= fisheye::CALIB_FIX_K2; if(fixK3) flag |= fisheye::CALIB_FIX_K3; if(fixK4) flag |= fisheye::CALIB_FIX_K4; } 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 << " Camera calibration mode does not exist: " << patternToUse << endl; goodInput = false; } atImageList = 0; }
validate()函数会对输入文件“in_VID5.xml”所设定的初始数值进行检验,如果检验合格,那么bool型参数goodinput赋值为True,否则是False。除此之外,要确定nrframe,以及inputType等等。
最后,在类Settings中还有两个成员函数,
Mat nextImage() { Mat result; if( inputCapture.isOpened() ) { Mat view0; inputCapture >> view0; view0.copyTo(result); } else if( atImageList < imageList.size() ) {
result = imread(imageList[atImageList++], IMREAD_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;
}
函数nestImage()的含义就是“下一幅图像”,输入的需要分视频流和图像帧处理。需要注意这条语
result = imread(imageList[atImageList++], IMREAD_COLOR)
在这条语句中,变量imageList是什么呢?先看一下它的定义,vector<string> imageList; 那么是那一条语句给imagelist赋值呢,在validate()函数可以找到,readStringList(input, imageList)。可以看到变量input被赋值于变量imageList。那么input是什么,可以在read()中找到,
node["Input"] >> input;
read()是读取文件“in_VID5.xml”,input的来源是
<Input>"images/CameraCalibration/VID5/VID5.xml"</Input>
即,input需要读入文件“VID5.xml”,
<opencv_storage><images> images/CameraCalibraation/VID5/xx1.jpg images/CameraCalibraation/VID5/xx2.jpg images/CameraCalibraation/VID5/xx3.jpg images/CameraCalibraation/VID5/xx4.jpg images/CameraCalibraation/VID5/xx5.jpg images/CameraCalibraation/VID5/xx6.jpg images/CameraCalibraation/VID5/xx7.jpg images/CameraCalibraation/VID5/xx8.jpg </images></opencv_storage>
所以,imageList储存的是标定图片的名称地址。
最后,我们分析一下函数readStringList(input, imageList)。
可以发现,input是const string类型变量,而imageList是vector<string>类型变量。input是一个包括所有图片地址的长长字符串,这个长长字符串被分割成一块一块的(用了getFirstTopLevelNode这个函数),并且被储存于imageList