OpenCV的Sample分析:相机标定(4)
今天来分析runCalibration() 这个函数
bool ok = runCalibration(s, imageSize, cameraMatrix, distCoeffs, imagePoints, rvecs, tvecs, reprojErrs,
totalAvgErr);
这个函数的原型是这样的,
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)
明白这些变量的含义后,再来看程序,
//! [fixed_aspect]
cameraMatrix = Mat::eye(3, 3, CV_64F);
if( s.flag & CALIB_FIX_ASPECT_RATIO )
cameraMatrix.at<double>(0,0) = s.aspectRatio;
//! [fixed_aspect]
if (s.useFisheye) {
distCoeffs = Mat::zeros(4, 1, CV_64F);
} else {
distCoeffs = Mat::zeros(8, 1, CV_64F);
}
cameraMatrix被初始化为3×3的单位矩阵。flag CALIB_FIX_ASPECT_RATIO指焦距fu和fv的比值是否固定,如果是固定的,焦距比的信息可以从in_VID5.xml文件中读到。与此同时,畸变参数也初始化了。
接下来,
vector<vector<Point3f> > objectPoints(1);
calcBoardCornerPositions(s.boardSize, s.squareSize, objectPoints[0], s.calibrationPattern);
objectPoints.resize(imagePoints.size(),objectPoints[0]);
这段代码中,calcBoardCornerPositions()函数的意义是什么呢?
static void calcBoardCornerPositions(Size boardSize, float squareSize, vector<Point3f>& 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(j*squareSize, 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((2*j + i % 2)*squareSize, i*squareSize, 0));
break;
default:
break;
}
}
这个函数的作用是,根据棋盘的大小boardsize,以及棋盘中一个方格的大小squaresize,确定棋盘格上角点的三维坐标,把角点的三维坐标存放在vector<Point3f>型的corner变量中。
vector<vector<Point3f> > objectPoints(1) 这条语句设定了一个数组,数组的元素只有一个
也许会对这个赋值语句感到奇怪,这条语句我也不太理解,但是也比较重要(不好意思)
objectPoints.resize(imagePoints.size(),objectPoints[0]);
然后进行标定操作,
//Find intrinsic and extrinsic camera parameters
double rms;
if (s.useFisheye) {
Mat _rvecs, _tvecs;
rms = fisheye::calibrate(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, _rvecs,
_tvecs, s.flag);
rvecs.reserve(_rvecs.rows);
tvecs.reserve(_tvecs.rows);
for(int i = 0; i < 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);
}
cout << "Re-projection error reported by calibrateCamera: "<< rms << endl;
这里不太关心calibrateCamera()的具体实现,会调用就行。opencv document给出了这个函数的说明点击打开链接。
总之calibrateCamera()的结果是,求出相机的内参数矩阵camera Matrix,畸变系数distCoeffs,相机在每一帧的的旋转矩阵rvecs以及位移向量tvecs。
最后再分析这个函数的最后三行代码,checkRange比较好理解,就是检查标定结果有没有问题。opencv doc上是这样解释的,
The function cv::checkRange checks that every array element is neither NaN nor infinite. When minVal > -DBL_MAX and maxVal < DBL_MAX, the function also checks that each value is between minVal and maxVal. In case of multi-channel arrays, each channel is processed independently. If some values are out of range, position of the first outlier is stored in pos (when pos != NULL). Then, the function either returns false (when quiet=true) or throws an exception.
bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);
totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs, cameraMatrix,
distCoeffs, reprojErrs, s.useFisheye);
return ok;
再来看一下函数 computeReprojectionErrors()的作用。这个函数比较容易理解,就是计算每一帧的各个像素点的重投影误差,要注意projectPoints()函数的使用。
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, err;
perViewErrors.resize(objectPoints.size());
for(size_t i = 0; i < objectPoints.size(); ++i )
{
if (fisheye)
{
fisheye::projectPoints(objectPoints[i], imagePoints2, rvecs[i], tvecs[i], cameraMatrix,
distCoeffs);
}
else
{
projectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);
}
err = norm(imagePoints[i], imagePoints2, NORM_L2);
size_t n = objectPoints[i].size();
perViewErrors[i] = (float) std::sqrt(err*err/n);
totalErr += err*err;
totalPoints += n;
}
return std::sqrt(totalErr/totalPoints);
}