相机标定注意事项
听了上海交大的博士的相机标定直播,列举了以下注意事项
- 拍摄15-20张图片进行标定;
- 使用标准的标定板减小误差,最好不要直接打印一张A4的棋盘格就去标定,由于张氏标定法假设z=0,A4纸粘在纸板上无法保证z=0,从标定处便引入了误差,圆形标定板的精度比棋盘格高;
- 拍摄图片中棋盘格的位置最好在中心和边缘都有分布;
- 标定精度可用重投影误差计算,还可以从2D到3D投影特定点,比较点之间的距离。
使用边长为25mm的12*9的标准棋盘格标定相机,使用标定好的参数将两对匹配点投影到3D控件,计算点与点之间的距离,并与实际的3D点的距离比较,从而得到精度。
以下程序选取棋盘格的距离最远的两个角点。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
///双目相机,y方向存在误差
Mat cameraMatrixL = (Mat_<float>(3, 3) << 2324.328, 0, 338.492,
0, 2325.87799, 268.76668, 0, 0, 1);
Mat distCoeffL = (Mat_<float>(5, 1) << -0.603, 1.3469, 0.0, 0.0, 0.00000);
Mat cameraMatrixR = (Mat_<float>(3, 3) << 2324.518, 0, 303.5,
0, 2324.6, 274.7977, 0, 0, 1);
Mat distCoeffR = (Mat_<float>(5, 1) << -0.6034, 1.6096, 0.0,
0.0, 0.00000);
//左右目之间的R,t可通过stereoCalibrate()或matlab工具箱calib求得
Mat T = (Mat_<float>(3, 1) << 0.2799, 23.91316, 7.2362); //T平移向量
Mat R = (Mat_<float>(3, 3) << 1.0, 0.00064, 0.0024,
-0.00064, 1.0, 0.0013, -0.0024,-0.0013, 1.0);
void reconstruct(Mat& K1, Mat& K2, Mat& R, Mat& T, vector<Point2f>& p1, vector<Point2f>& p2, Mat& structure)
{
//两个相机的投影矩阵[R T],triangulatePoints只支持float型
//K1,K2是相机的内参矩阵
Mat proj1(3, 4, CV_32FC1);
Mat proj2(3, 4, CV_32FC1);
proj1(Range(0, 3), Range(0, 3)) = Mat::eye(3, 3, CV_32FC1);
proj1.col(3) = Mat::zeros(3, 1, CV_32FC1);
R.convertTo(proj2(Range(0, 3), Range(0, 3)), CV_32FC1);
T.convertTo(proj2.col(3), CV_32FC1);
Mat fK1,fK2;
K1.convertTo(fK1, CV_32FC1);
K2.convertTo(fK2, CV_32FC1);
proj1 = fK1 * proj1;
proj2 = fK2 * proj2;
//三角化重建
triangulatePoints(proj1, proj2, p1, p2, structure);
}
int main()
{
char left_path[100], right_path[100];
sprintf_s(left_path, "images/left/left.txt");
sprintf_s(right_path, "images/right/right.txt");
ifstream finL(left_path);
ifstream finR(right_path);
string left_img, right_img;
float num= 0;
float total_dis=0;
while (getline(finL,left_img)&& getline(finR, right_img))
{
left_img = "images/left/"+left_img;
right_img = "images/right/" + right_img;
Mat f1 = imread(left_img);
Mat f2 = imread(right_img); //左右图读入
Mat undistorf1, undistorf2;
undistort(f1, undistorf1, cameraMatrixL, distCoeffL);
undistort(f2, undistorf2, cameraMatrixR, distCoeffR);//图片去畸变
vector<Point2f> p1, p2;
vector<Point2f> p3, p4;
//vector<Point2f> undistorp3, undistorp4;
findChessboardCorners(undistorf1, Size(11, 8), p1);
findChessboardCorners(undistorf2, Size(11, 8), p2);
p3.push_back(p1[0]);
p3.push_back(p1[p1.size() - 1]);
p4.push_back(p2[0]);
p4.push_back(p2[p2.size() - 1]);
Mat structure;
reconstruct(cameraMatrixL, cameraMatrixR, R, T, p3, p4, structure);
int i = 0;
vector<float> calculat;
for (i = 0; i < structure.cols; i++)
{
Mat_<float> col = structure.col(i);
col /= col(3);
// cout << "x:" << col(0) << endl << "y:" << col(1) << endl << "z:" << col(2) << endl;
calculat.push_back(col(0));
calculat.push_back(col(1));
calculat.push_back(col(2));
}
float cal_dis = sqrtf(powf(calculat[0] - calculat[3], 2) + powf(calculat[1] - calculat[4], 2) +
powf(calculat[2] - calculat[5], 2));
calculat.clear();
cout << "The calculated distance is " << cal_dis << "mm" << endl;
//cout << "The real distance is " << 305.164 << "mm" << endl;
total_dis=total_dis+cal_dis;
num++;
}
float mean_dis = total_dis / num;
cout << "The real distance is " << 305.164 << "mm" << endl;
cout << "The mean calculated distance is " <<mean_dis << "mm" << endl;
return 0;
}
我使用的11*8的棋盘格,格子的长度为25mm,2D投影到3D的距离(格子的边长)计算如下
计算距离最远的角点的距离