calibrateCamera函数的实际用法
????标定相机参数之前,要拍10张以上标定板图片并成功提取标定板中的角点之后才可以进行标定相机的内外参数
????代码如下
//计算标定数据
void QGuiCalib3d::btnCalcCalibData_clicked()
{
int ImgTotal = m_vcImagePointsSeq.size();
if (ImgTotal < 11)
{
QMessageBox msgBox;
msgBox.setText("图片数量不够12张");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setWindowIcon(QIcon(":/OpenCVTestSf/ico/opencv-logo.ico"));
msgBox.exec();
return;
}
m_boardSize.width = ui.lineEditBoardCol->text().toInt();
m_boardSize.height = ui.lineEditBoardRow->text().toInt();
m_squareSize.width = ui.lineEditSquareWidth->text().toInt();
m_squareSize.height = ui.lineEditSquareHeight->text().toInt();
//在指定的文件夹中写入到指定的文件
QString qsFilepath = QCoreApplication::applicationDirPath() + "/Calibration/";
QDir* pFolder = new QDir;
bool bExist = pFolder->exists(qsFilepath);//文件夹是否存在
if (!bExist)
pFolder->mkdir(qsFilepath);//创建文件夹
QDateTime* datatime = new QDateTime(QDateTime::currentDateTime());
QString strTime = datatime->toString("yyyy_MM_dd hh_mm_ss ");
string filepath = qsFilepath.toStdString();
string fileTime = strTime.toStdString();
string filexmlname = "CalibrationCalibData.yaml";
string fileName = filepath + fileTime + filexmlname;
FileStorage fwriteCalibData(fileName, FileStorage::WRITE);//以写入的模式打开文件
vector<vector<Point3f>> objectPoints;
int i, j, t;
for (t = 0; t < ImgTotal; t++)
{
vector<Point3f> tempPointSet;
for (i = 0; i < m_boardSize.height; i++)
{
for (j = 0; j < m_boardSize.width; j++)
{
Point3f realPoint;
// 假设标定板放在世界坐标系中z=0的平面上
realPoint.x = (float)i * m_squareSize.width;
realPoint.y = (float)j * m_squareSize.height;
realPoint.z = 0;
tempPointSet.push_back(realPoint);
}
}
objectPoints.push_back(tempPointSet);//每张标定板图的所有坐标
}
fwriteCalibData << "worldPoints"
<< "{"
<< "objectPoints" << objectPoints
<< "}";
vector<Mat> vcRotateMat;// 每幅图像的旋转向量
vector<Mat> vcMoveMat;// 每幅图像的平移向量
try
{
/*开始标定 返回值是重投影的总的均方根误差*/
double res = calibrateCamera(
objectPoints,//世界坐标系中的三维点
m_vcImagePointsSeq,//为每一个内角点对应的图像坐标点
m_imageSize,//为图像的像素尺寸大小,在计算相机的内参和畸变矩阵时需要使用到该参数
m_cameraMatrix,//为相机的内参矩阵
m_distCoeffs,//为畸变矩阵
vcRotateMat,//为旋转向量
vcMoveMat,//为位移向量
0);//为标定时所采用的算法
}
catch (cv::Exception& e)
{
const char* err_msg = e.what();
QMessageBox msgBox;
msgBox.setText(err_msg);
msgBox.setIcon(QMessageBox::Warning);
msgBox.setWindowIcon(QIcon(":/OpenCVTestSf/ico/opencv-logo.ico"));
msgBox.exec();
return;
}
// 每幅图像中角点的数量
vector<int> pointCounts;
for (i = 0; i < ImgTotal; i++)
{
// 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板
pointCounts.push_back(m_boardSize.width * m_boardSize.height);
fwriteCalibData << "pointCounts"
<< "{"
<< "num" << i
<< "count" << m_boardSize
<< "point" << objectPoints[i]
<< "RotateMat" << vcRotateMat[i]
<< "MoveMat" << vcMoveMat[i]
<< "}";
}
double totalErr = 0.0; // 所有图像的平均误差的总和
for (i = 0; i < ImgTotal; i++)
{
// 保存重新计算得到的投影点
vector<Point2f> newPoints;
//对空间的三维点进行重新投影计算,得到新的投影点
projectPoints(
objectPoints[i], //世界坐标系中的三维点
vcRotateMat[i], //旋转向量
vcMoveMat[i], //平移向量
m_cameraMatrix, //相机内参
m_distCoeffs, //相机外参
newPoints); //计算出的新的投影点
// 计算新的投影点和旧的投影点之间的误差
Mat tempImagePointMat = Mat(1, m_vcImagePointsSeq[i].size(), CV_32FC2);
Mat imagePoints2Mat = Mat(1, newPoints.size(), CV_32FC2);
for (int j = 0; j < (int)m_vcImagePointsSeq[i].size(); j++)
{
float fx = newPoints[j].x;
float fy = newPoints[j].y;
imagePoints2Mat.at<Vec2f>(0, j) = Vec2f(fx, fy);
float imgx = m_vcImagePointsSeq[i][j].x;
float imgy = m_vcImagePointsSeq[i][j].y;
tempImagePointMat.at<Vec2f>(0, j) = Vec2f(imgx, imgy);
}
double err = norm(imagePoints2Mat, tempImagePointMat, NORM_L2);// 每幅图像的平均误差
double err1 = err / pointCounts[i];
totalErr += err1;
fwriteCalibData << "imgpoints"
<< "{"
<< "imgnum" << i
<< "newx" << newPoints
<< "oldx" << m_vcImagePointsSeq[i]
<< "normerr" << err
<< "err" << err1
<< "totalerr" << totalErr
<< "}";
}
double rAverErr = totalErr / ImgTotal;
fwriteCalibData << "AverErr" << rAverErr;
QString szText1;
szText1.sprintf("标定平均误差 %.3f \n", rAverErr);
vector<double> vcCamera;
vcCamera.clear();
vector<double> vcCoeff;
vcCoeff.clear();
//获得的内参参数
QString szText2 = "内参参数:\n";
for (int i = 0; i < m_cameraMatrix.rows; i++)
{
for (int j = 0; j < m_cameraMatrix.cols; j++)
{
double value = m_cameraMatrix.at<double>(i, j);
QString temp = QString("%1 ").arg(value);
szText2.append(temp);
vcCamera.push_back(value);
}
szText2.append("\n");
}
//畸变系数
QString szText3 = "畸变系数:\n";
for (int i = 0; i < m_distCoeffs.rows; i++)
{
fwriteCalibData << "i" << i;
for (int j = 0; j < m_distCoeffs.cols; j++)
{
double value = m_distCoeffs.at<double>(i, j);
QString temp = QString("%1 ").arg(value);
szText3.append(temp);
vcCoeff.push_back(value);
}
szText3.append("\n");
}
//保存每幅图像的旋转矩阵
vector<Mat> rotationMatrix;
rotationMatrix.clear();
for (int i = 0; i < ImgTotal; i++)
{
Mat matric = Mat(3, 3, CV_32FC1, Scalar::all(0));
rotationMatrix.push_back(matric);
}
for (int i = 0; i < ImgTotal; i++)
{
// 将旋转向量转换为相对应的旋转矩阵
Rodrigues(vcRotateMat[i], rotationMatrix[i]);
}
fwriteCalibData << "cameraData"
<< "{"
<< "cameraMatrix" << m_cameraMatrix
<< "distCoeffs" << m_distCoeffs
<< "vcRotateMat" << vcRotateMat
<< "rotationMatrix" << rotationMatrix
<< "}";
fwriteCalibData.release();//关闭文件
QString readStr;
readStr.append(szText1);
readStr.append(szText2);
readStr.append(szText3);
ui.textBrowser->clear();
ui.textBrowser->setText(readStr);
}
我这里取的是不低于11张标定板图片的角点数据
欢迎关注公众号《Qt学视觉》?
?
?
|