在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立摄像机成像的几何模型,这些几何模型参数就是摄像机参数。在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定。简单来说是从世界坐标系换到图像坐标系的过程,也就是求最终的投影矩阵P PP的过程。
无论是在图像测量或者机器视觉应用中,摄像机参数的标定都是非常关键的环节,其标定结果的精度及算法的稳定性直接影响摄像机工作产生结果的准确性。因此,做好摄像机标定是做好后续工作的前提,是提高标定精度是科研工作的重点所在。其标定的目的就是为了相机内参、外参、畸变参数。
点击这个
1.打印一张棋盘格A4纸张(黑白间距已知),并贴在一个平板上
2.针对棋盘格拍摄若干张图片(一般10-20张)
3.在图片中检测特征点(Harris特征)
4.利用解析解估算方法计算出5个内部参数,以及6个外部参数
5.根据极大似然估计策略,设计优化目标并实现参数的refinement
#include
#include
#include
using namespace std;
using namespace cv;
// 定义棋盘格的尺寸
int CHECKERBOARD[2]{ 7, 10 };
int main()
{
// 创建矢量以存储每个棋盘图像的三维点矢量
std::vector<std::vector<cv::Point3f>> objpoints;
// 创建矢量以存储每个棋盘图像的二维点矢量
std::vector<std::vector<cv::Point2f>> imgpoints;
// 为三维点定义世界坐标系
std::vector<cv::Point3f> objp;
for (int i{ 0 }; i < CHECKERBOARD[1]; i++)
{
for (int j{ 0 }; j < CHECKERBOARD[0]; j++)
{
objp.push_back(cv::Point3f(j, i, 0));
}
}
// 提取存储在给定目录中的单个图像的路径
std::vector<cv::String> images;
// 包含棋盘图像的文件夹的路径
std::string path = "path/*.jpg";
// 使用glob函数读取所有图像的路径
cv::glob(path, images);
cv::Mat frame, gray;
// 存储检测到的棋盘转角像素坐标的矢量
std::vector<cv::Point2f> corner_pts;
bool success;
// 循环读取图像
for (int i{ 0 }; i < images.size(); i++)
{
frame = cv::imread(images[i]);
if (frame.empty())
{
continue;
}
if (i == 40)
{
int b = 1;
}
cout << "the current image is " << i << "th" << endl;
cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
// 寻找角点
// 如果在图像中找到所需数量的角,则success = true
success = cv::findChessboardCorners(gray, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), corner_pts, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
// 如果检测到所需数量的角点,我们将细化像素坐标并将其显示在棋盘图像上
if (success)
{
cv::TermCriteria criteria(TermCriteria::EPS | TermCriteria::Type::MAX_ITER, 30, 0.001);
// 为给定的二维点细化像素坐标
cv::cornerSubPix(gray, corner_pts, cv::Size(11, 11), cv::Size(-1, -1), criteria);
// 在棋盘上显示检测到的角点
cv::drawChessboardCorners(frame, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), corner_pts, success);
objpoints.push_back(objp);
imgpoints.push_back(corner_pts);
}
}
cv::destroyAllWindows();
cv::Mat cameraMatrix, distCoeffs, R, T;
// 通过传递已知3D点(objpoints)的值和检测到的角点(imgpoints)的相应像素坐标来执行相机校准
cv::calibrateCamera(objpoints, imgpoints, cv::Size(gray.rows, gray.cols), cameraMatrix, distCoeffs, R, T);
// 内参矩阵
std::cout << "cameraMatrix : " << cameraMatrix << std::endl;
// 透镜畸变系数
std::cout << "distCoeffs : " << distCoeffs << std::endl;
// rvecs
std::cout << "Rotation vector : " << R << std::endl;
// tvecs
std::cout << "Translation vector : " << T << std::endl;
// 当摄像头打开时,实时矫正图片,并实时输出;
VideoCapture capture(0);
Mat original, undistorted;
while (capture.isOpened()) {
capture >> original;
undistort(original, undistorted, cameraMatrix, distCoeffs);
imshow("original", original);
waitKey(30);
imshow("undistorted", undistorted);
waitKey(30);
}
return 0;
}
改代码运行后会在终端输出相机的标定参数,类似这样
distCoeffs : [-0.02162196561014432, 0.8156897591817076, -0.001041489039745999, 0.000329734346922623, -2.529048558218271]
Rotation vector : [-0.1880605257327365, -0.2035463454382497, 1.038866670000656;
0.04224265322813118, 0.08349053828673256, 1.550907048595723;
-0.2061275988483871, -0.1742486587611411, 1.011835020287875;
0.2623006057691435, 0.4775103694892211, 1.521999357286531;
0.6064817803573732, 0.2414109519198919, 1.948244857258349;
-0.347427168181143, -0.1816443548548454, 1.547298425742006;
0.09186035217206993, 0.308971243124817, 1.562331860854399;
-0.05181363895492256, -0.06405363662161791, 1.582678011268307;
-0.2306223725118385, 0.02160813919516534, 2.038739462639227;
-0.5079519220900065, -0.073243258860196, 2.080923745898526;
0.04052695460924716, -0.08137734788052249, 1.305163108477118;
-0.4259233498436448, -0.03016341908357208, 2.06657425430407;
-0.5042329250694289, -0.5528681020057626, 1.02525142255519;
0.1193994365666385, 0.2242340324018716, 1.39497483227831;
0.04636447639111788, -0.07487819904814215, 1.301228496118263;
-0.5334457723318971, -0.2663337760417988, 1.324305366856931;
-0.1020148476066573, 0.06785705759031695, 1.433732427287213]
Translation vector : [2.204329770126416, -5.0865762934912, 21.87311681025048;
13.30056907239103, -4.806340710535522, 35.00480925806997;
4.036146737361956, -4.179831854618391, 24.37791170937228;
11.84530710572466, -3.138108080702295, 26.40692626130893;
10.57072914796873, -2.867475617171669, 25.93161776312742;
2.543994713361965, -2.82170198182411, 18.93014235660454;
3.220875841132327, -2.960152247928921, 15.79165655660373;
4.11392633810845, -2.829104126378235, 17.01365704495468;
6.110693669886816, -0.296264818266518, 23.6632133243999;
5.345922298897213, 0.6130777039816416, 26.27387226974149;
3.655359807490066, -8.365828322631375, 33.98924195002526;
12.45601141838911, 2.73064399604324, 34.32793704089273;
1.095852022591996, -4.367037288693907, 23.86825671830289;
12.04850766951271, -2.739166147282706, 32.55538594582906;
3.660506604988873, -8.444138346403973, 33.95029125182767;
-0.03963749073575707, -3.30466985124385, 26.07659707846161;
12.28508099238953, -3.740062306628959, 28.60341870633826]
还会调用摄像头,同时输出两个画面,一个是未经标定的图像,另一个是已经矫正过的图像
标定需要黑白棋盘,可以在这个网页生成
生成棋盘