单目相机标定(使用OpenCV)
关于相机标定的理论知识和使用Matlab软件进行相机标定请看这篇博文:单目相机标定(使用Matlab)
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
#include
#include
#include
using namespace cv;
using namespace std;
using namespace std::experimental::filesystem;
Size const cornerSize = Size(9, 7); //定义标定板每行、每列角点数
Size squareSize = Size(20, 20); //实际测量得到的标定板上每个棋盘格的尺寸,单位mm
struct calibrationCache
{
vector<Point2f> cornerPointBuffer; //缓存每幅图像上检测到的角点
vector<vector<Point2f>> cornerPointSequece; //保存检测到的所有角点
vector<vector<Point3f>> tempCornerPosition; //保存标定板上角点的三维坐标
int imageCount = 0; //采样图像数量
int cornerCount = 0; //检测到的角点数量
Size imageSize; //输入图像的像素尺寸
//----结果部分---------//
Mat intrinsicMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); //摄像机内参数矩阵
Mat distCoeffs = Mat(1, 12, CV_32FC1, Scalar::all(0)); //摄像机的畸变系数12个:k1,k2,p1,p2,k3
vector<Mat> tvecsMat; //每幅图像的旋转向量
vector<Mat> rvecsMat; //每幅图像的平移向量
};
//----函数声明---------//
void drawIMage(Mat img, vector<Point2f> cornerBuffer);
void outPutCornerInfo(calibrationCache& cache);
void calibrationCamera(calibrationCache& cache);
void caculateErrorAndSaveResult(calibrationCache& cache, const string& resultFileName);
void unDistortRectifyImage(calibrationCache& cache);
///显示图像
void drawIMage(Mat img, vector<Point2f> cornerBuffer)
{
drawChessboardCorners(img, cornerSize, cornerBuffer, false); //用于在图片中标记角点
imshow("Camera Calibration", img); //显示图片
waitKey(1000); //暂停1
}
void outPutCornerInfo(calibrationCache& cache)
{
int SampleImageCount = cache.cornerPointSequece.size();
cout << "采样图像数= " << SampleImageCount << endl;
if (SampleImageCount < 1)
exit(1);
for (int j = 0; j < cache.cornerPointSequece.size(); j++)
{
cout << endl;
cout << "第" << j + 1 << "张图片的角点数据: " << endl;
for (int i = 0; i < cache.cornerPointSequece[j].size(); i++)
{
cout << "(X:" << cache.cornerPointSequece[j][i].x << ",Y:" << cache.cornerPointSequece[j][i].y << ")"
<< " ";
if (0 == (i + 1) % 4) // 格式化输出,便于控制台查看
{
cout << endl;
}
}
cout << endl;
}
cout << endl;
}
//标定相机
void calibrationCamera(calibrationCache& cache)
{
int i, j, t;
for (t = 0; t < cache.imageCount; t++)
{
vector<Point3f>CornerPosition;
for (i = 0; i < cornerSize.height; i++)
{
for (j = 0; j < cornerSize.width; j++)
{
Point3f cornerPos;
cornerPos.x = i * squareSize.width;
cornerPos.y = j * squareSize.height;
cornerPos.z = 0; //假设标定板放在世界坐标系中z=0的平面上
CornerPosition.push_back(cornerPos);
}
}
cache.tempCornerPosition.push_back(CornerPosition);
}
calibrateCamera(cache.tempCornerPosition, cache.cornerPointSequece, cache.imageSize,
cache.intrinsicMatrix, cache.distCoeffs, cache.rvecsMat, cache.tvecsMat, CALIB_TILTED_MODEL | CALIB_RATIONAL_MODEL | CALIB_THIN_PRISM_MODEL);
cout << "标定完成!" << endl
<< endl;
}
//评估误差并保存结果
void caculateErrorAndSaveResult(calibrationCache& cache, const string& resultFileName)
{
int cornerCount = cornerSize.width * cornerSize.height;
double totalError = 0.0;
double sigleError = 0.0;
vector<Point2f> reprojectPoints;
ofstream fout(resultFileName);
cout << "每幅图像的标定误差:" << endl;
fout << "每幅图像的标定误差:\n";
for (int i = 0; i < cache.imageCount; i++)
{
vector<Point3f> tempPointSet = cache.tempCornerPosition[i];
projectPoints(tempPointSet, cache.rvecsMat[i], cache.tvecsMat[i], cache.intrinsicMatrix, cache.distCoeffs, reprojectPoints);
vector<Point2f> oldImagePoint = cache.cornerPointSequece[i];
Mat oldImagePointMatrix = Mat(1, oldImagePoint.size(), CV_32FC2);
Mat reprojectPointsMatrix = Mat(1, reprojectPoints.size(), CV_32FC2);
for (int j = 0; j < oldImagePoint.size(); j++)
{
reprojectPointsMatrix.at<Vec2f>(0, j) = Vec2f(reprojectPoints[j].x, reprojectPoints[j].y);
oldImagePointMatrix.at<Vec2f>(0, j) = Vec2f(oldImagePoint[j].x, oldImagePoint[j].y);
}
sigleError = norm(reprojectPointsMatrix, oldImagePointMatrix, NORM_L2);
totalError += (sigleError /= cornerCount);
cout << "第" << i + 1 << "幅图像的平均误差:" << sigleError << "像素" << endl;
fout << "第" << i + 1 << "幅图像的平均误差:" << sigleError << "像素" << endl;
}
cout << "总体平均误差:" << totalError / cache.imageCount << "像素" << endl;
fout << "总体平均误差:" << totalError / cache.imageCount << "像素" << endl
<< endl;
cout << "评价完成!" << endl
<< endl;
cout << "开始保存定标结果..." << endl;
Mat rotationMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); //保存每幅图像的旋转矩阵
fout << "相机内参数矩阵:" << endl;
cout << "相机内参数矩阵:" << endl;
fout << "【其中,焦距(0, 0):fx,(1, 1):fy; 像主点(0, 2):ux,(1, 2):uy】" << endl;
fout << cache.intrinsicMatrix << endl
<< endl;
cout << cache.intrinsicMatrix << endl
<< endl;
fout << "畸变系数:\n";
cout << "畸变系数:" << endl;
fout << "【其中,(0, 0):k1,(0, 1):k2,(0, 2):p1,(0, 3):p2,(0, 4):k3,(0, 5):k4,(0, 6):k5,(0, 7):k6,(0, 8):s1,(0, 9):s2,(0, 10):s3,(0, 11):s4】" << endl;
fout << cache.distCoeffs << endl
<< endl
<< endl;
cout << cache.distCoeffs << endl
<< endl
<< endl;
for (int i = 0; i < cache.imageCount; i++)
{
fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
cout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
fout << cache.rvecsMat[i] << endl;
cout << cache.rvecsMat[i] << endl;
Rodrigues(cache.rvecsMat[i], rotationMatrix);
fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
cout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
fout << rotationMatrix << endl;
cout << rotationMatrix << endl;
fout << "第" << i + 1 << "幅图像的平移向量:" << endl;
cout << "第" << i + 1 << "幅图像的平移向量:" << endl;
fout << cache.tvecsMat[i] << endl
<< endl;
cout << cache.tvecsMat[i] << endl
<< endl;
}
cout << "完成保存" << endl
<< endl;
fout << endl;
}
void LoadImages_txt(std::string filename, std::vector<std::string>& imageNames)
{
// step 1 : open file
std::ifstream file(filename);
if (!file) {
std::cout << "Wrong File !" << filename << " dose not exist!" << std::endl;
return;
}
// step 2 : load data
imageNames.clear();
while (!file.eof()) {
std::string imageName;
file >> imageName;
if (file.eof())
break;
imageNames.push_back(imageName);
std::cout << "Load Image, imageName: " << imageName << std::endl;
}
file.close();
return;
}
int main(int argc, char* argv[])
{
std::string dataFileName = "calibdata.txt"; //所有标定图片的存放路径
std::vector<std::string> imageNames;
LoadImages_txt(dataFileName, imageNames);
clock_t start_time, end_time;
start_time = clock();
cout << "开始读取图像..." << endl;
calibrationCache calibCache;
string imageName;
for (int i = 0; i < imageNames.size(); i++)
{
std::cout << imageNames[i] << std::endl;
Mat imageInput = imread(imageNames[i]);
if (0 == findChessboardCorners(imageInput, cornerSize, calibCache.cornerPointBuffer, cv::CALIB_CB_ADAPTIVE_THRESH))
{
cout << imageNames[i] << "图像有问题" << endl;
calibCache.imageCount--;
continue;
}
else
{
Mat grayImage;
if (calibCache.imageCount == 1)
{
calibCache.imageSize.width = imageInput.cols;
calibCache.imageSize.height = imageInput.rows;
}
cv::cvtColor(imageInput, grayImage, cv::COLOR_BGR2GRAY);
cornerSubPix(grayImage, calibCache.cornerPointBuffer, Size(5, 5), Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.01));
calibCache.cornerPointSequece.push_back(calibCache.cornerPointBuffer);
calibCache.imageCount++;
}
}
cout << "角点提取完成!" << endl << endl;
outPutCornerInfo(calibCache);
cout << "开始标定..." << endl;
calibrationCamera(calibCache);
cout << "开始评价标定结果..." << endl;
std::string result = "result.txt";
caculateErrorAndSaveResult(calibCache, result);
end_time = clock();
double Times = (double)(end_time - start_time) / CLOCKS_PER_SEC;
printf("%f seconds\n", Times);
return 0;
}