bool cv::findChessboardCorners(
InputArray image,
Size patternSize,
OutputArray corners,
int flags = CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE
)
参数:
vector corners
返回值:
亚像素级角点检测:cornerSubPix()
void cv::drawChessboardCorners(
InputOutputArray image,
Size patternSize,
InputArray corners,
bool patternWasFound
)
参数:
image:8位灰度或者彩色图像;
patternSize:每张标定棋盘上内角点的行列数;
corners:findChessboardCorners()
输出的内角点的坐标位置
patternWasFound:用来指示定义的棋盘内角点是否被完整的探测到。findChessboardCorners()
的返回值
true表示别完整的探测到,函数会用直线依次连接所有的内角点,作为一个整体,false表示有未被探测到的内角点,这时候函数会以(红色)圆圈标记处检测到的内角点
double cv::calibrateCamera(
InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints,
Size imageSize,
InputOutputArray cameraMatrix,
InputOutputArray distCoeffs,
OutputArrayOfArrays rvecs,
OutputArrayOfArrays tvecs,
int flags = 0,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON)
)
参数:
vector> object_points
。需要依据棋盘上单个黑白矩阵的大小,计算出(初始化)每一个内角点的世界坐标。vector> imagePoints
形式的变量;Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));
Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))
即可;vectorrvecs;
vector tvecs;
CV_CALIB_USE_INTRINSIC_GUESS
:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,u0,v0的估计值。否则的话,将初始化(u0,v0)图像的中心点,使用最小二乘估算出fx,fy。CV_CALIB_FIX_PRINCIPAL_POINT
:在进行优化时会固定光轴点。当CV_CALIB_USE_INTRINSIC_GUESS
参数被设置,光轴点将保持在中心或者某个输入的值。CV_CALIB_FIX_ASPECT_RATIO
:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当CV_CALIB_USE_INTRINSIC_GUESS
没有被设置,fx和fy将会被忽略。只有fx/fy的比值在计算中会被用到。CV_CALIB_ZERO_TANGENT_DIST
:设定切向畸变参数(p1,p2)为零。CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6
:对应的径向畸变在优化中保持不变。CV_CALIB_RATIONAL_MODEL
:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它5个畸变参数。在使用该函数进行标定运算之前,需要对棋盘上每一个内角点的空间坐标系的位置坐标进行初始化,标定的结果是生成相机的内参矩阵cameraMatrix、相机的5个畸变系数distCoeffs,另外每张图像都会生成属于自己的平移向量和旋转向量。
void cv::projectPoints(
InputArray objectPoints,
InputArray rvec,
InputArray tvec,
InputArray cameraMatrix,
InputArray distCoeffs,
OutputArray imagePoints,
OutputArray jacobian = noArray(),
double aspectRatio = 0
)
参数:
vector
,即calibrateCamera()
的每个objectPoints[i]
calibrateCamera()
的每个rvecs[i]
calibrateCamera()
的每个tvecs[i]
calibrateCamera()
的cameraMatrix
calibrateCamera()
的distCoeffs
vector imagePoints;
功能:
将3D点投影到图像平面
void cv::undistort(
InputArray src,
OutputArray dst,
InputArray cameraMatrix,
InputArray distCoeffs,
InputArray newCameraMatrix = noArray()
)
/*
@brief 你可能需要修改一些信息
@param image_count 图像数量,这个看你自己想拍多少张,建议10
@param patternsize 标定板上每行、列的角点数。这个得看你自己的标定板的实际数目
@brief 操作流程
采集棋盘图时:按y表示确认采集此图像作为棋盘图,如果通过判定,则会显示棋盘图上角点的连线。
如果不通过,会继续让你采样。按q退出程序。按其他键无意义。
*/
#include
#include
using namespace cv;
using namespace std;
int main()
{
int image_count = 10; /* 图像数量 */
Size image_size; /* 图像的尺寸 */
Size patternsize = Size(6, 9); /* 标定板上每行、列的角点数 */
vector<vector<Point2f>> corner_all; /* 保存检测到的所有角点 */
VideoCapture capture;
capture.open(1);
// 原图像
Mat imageInput;
for (int i = 0; i < image_count;)
{
capture >> imageInput;
if (imageInput.empty())
{
cout << "empty image!!!\n";
return 0;
}
imshow("src", imageInput);
// 只是为了获得键盘而已
switch (waitKey(10))
{
case 'q':
return 0;
break;
case 'y':
break;
default:
continue;
break;
}
i++;
// 用于观察检验输出
printf("第%d张图片\t", i);
if (i == 1) //读入第一张图片时获取图像宽高信息
{
// 列宽,行高
image_size.width = imageInput.cols;
image_size.height = imageInput.rows;
cout << "image_size.width = " << image_size.width << endl;
cout << "image_size.height = " << image_size.height << endl;
}
Mat gray;
cvtColor(imageInput, gray, CV_RGB2GRAY);
vector<Point2f> corner_single; /* 缓存每幅图像上检测到的角点 */
/* 提取角点 */
bool patternfound = findChessboardCorners(gray, patternsize, corner_single);
// 如果找到了
if (patternfound)
{
/* 亚像素精确化 */
TermCriteria criteria = TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 40, 0.001);
cornerSubPix(gray, corner_single, Size(5, 5), Size(-1, -1), criteria);
corner_all.push_back(corner_single); //保存亚像素角点
/* 在图像上显示角点位置 */
drawChessboardCorners(gray, patternsize, corner_single, patternfound); //用于在图片中标记角点
imshow("drawChessboardCorners", gray); //显示图片
}
else
{
cout << "can not find chessboard corners!\n"; //找不到角点
i--;
continue;
}
cout << "\n******************\n";
}
int total = corner_all.size();
cout << "已收集的棋盘图个数:" << total << endl;
/***************************** 以下是摄像机标定 ************************************************/
cout << "\n[开始标定]>>>>>>\n";
Size square_size = Size(10, 10); /* 实际测量得到的标定板上每个棋盘格的大小 */
/* calibrateCamera的参数 */
vector<vector<Point3f>> object_points; /* 保存标定板上角点的三维坐标 */
// imagePoints
// imageSize上面已经获得
Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 摄像机内参数矩阵 */
Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */
vector<Mat> rvecs; /* 每幅图像的旋转向量 */
vector<Mat> tvecs; /* 每幅图像的平移向量 */
/* 初始化标定板上角点的三维坐标 */
for (int count = 0; count < image_count; count++)
{
vector<Point3f> single_object_points;
// 每列
for (int i = 0; i < patternsize.height; i++)
{
// 每行
for (int j = 0; j < patternsize.width; j++)
{
Point3f realPoint;
/* 假设标定板放在世界坐标系中z=0的平面上 */
realPoint.x = i * square_size.width;
realPoint.y = j * square_size.height;
realPoint.z = 0;
single_object_points.push_back(realPoint);
}
}
object_points.push_back(single_object_points);
}
calibrateCamera(object_points, corner_all, image_size, cameraMatrix, distCoeffs, rvecs, tvecs);
cout << "[标定完成]\n";
/*********************************** 对标定结果进行评价 **********************************************/
cout << "\n[开始评价标定结果]>>>>>>\n";
//每张图片上总的角点数
int cornerNum = patternsize.width * patternsize.height;
double err_single = 0.0; /* 每幅图像的误差 */
double err_total = 0.0; /* 所有图像的误差的总和 */
double err_mean = 0.0; /* 所有图像的平均误差 */
vector<Point2f> imagePoints2; /* 保存重新计算得到的投影点 */
for (int i = 0; i < image_count; i++)
{
/* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
projectPoints(object_points[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);
/* 计算新的投影点和旧的投影点之间的误差*/
// 每张图像的角点
vector<Point2f> corner_single = corner_all[i];
// 声明原结果的角点矩阵
Mat mat_corner_single = Mat(1, corner_single.size(), CV_32FC2);
// 声明评测结果的角点矩阵
Mat mat_imagePoints2 = Mat(1, imagePoints2.size(), CV_32FC2);
// 每个角点
for (int j = 0; j < corner_single.size(); j++)
{
// 给原结果的角点矩阵赋值
mat_corner_single.at<Vec2f>(0, j) = Vec2f(corner_single[j].x, corner_single[j].y);
// 给评测结果的角点矩阵赋值
mat_imagePoints2.at<Vec2f>(0, j) = Vec2f(imagePoints2[j].x, imagePoints2[j].y);
}
// 取2范数
err_single = norm(mat_imagePoints2, mat_corner_single, NORM_L2);
err_total += err_single;
}
// 平均误差
err_mean = err_total / cornerNum;
cout << "[总体平均误差]:" << err_mean << "像素" << endl;
cout << "[评价完成!]" << endl;
cout << "cameraMatrix:\n"
<< cameraMatrix << endl;
cout << "distCoeffs:\n"
<< distCoeffs << endl;
/************************ 显示定标结果 ******************************/
cout << "[矫正图像]>>>>>>" << endl;
Mat result;
undistort(imageInput, result, cameraMatrix, distCoeffs);
imshow("result", result);
waitKey();
return 0;
}
cameraMatrix:
[413.2283193644378, 0, 336.8666037520817;
0, 414.4252596235103, 225.1160821905549;
0, 0, 1]
distCoeffs:
[0.0707390966157103, -0.271389263833054, -0.002877120698026549, 0.0020488715864277, 0.2587209157266493]
张正友相机标定Opencv实现以及标定流程&&标定结果评价&&图像矫正流程解析(附标定程序和棋盘图)