文章目录
- 一、cv库函数
- 1.找棋盘角点findChessboardCorners
- 2.获得角点cornerSubPix
- 3.绘制内角点drawChessboardCorners
- 4.相机标定calibrateCamera
- 5.对标定结果进行评价projectPoints
- 6.矫正图像undistort
- 二、程序
- 参考
一、cv库函数
1.找棋盘角点findChessboardCorners
bool cv::findChessboardCorners(
InputArray image,
Size patternSize,
OutputArray corners,
int flags = CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE
)
参数:
- image:传入拍摄的棋盘图,必须是8位的灰度或者彩色图像。
- patternSize:棋盘图内的内角点的行列数,相当于格子的Size(行-1,列-1)
- corners:用于存储内角点的坐标位置
一般用元素是Point2f的向量来表示:vector<Point2f> corners
- flage:用于定义棋盘图上内角点查找的不同处理方式,有默认值。
返回值:
- 返回非零值:如果找到所有角点并按特定顺序(每行从左到右,逐行)放置。
- 返回0:如果函数无法找到所有角点或重新排序。
2.获得角点cornerSubPix
亚像素级角点检测:cornerSubPix()
3.绘制内角点drawChessboardCorners
void cv::drawChessboardCorners(
InputOutputArray image,
Size patternSize,
InputArray corners,
bool patternWasFound
)
参数:
- image:8位灰度或者彩色图像;
- patternSize:每张标定棋盘上内角点的行列数;
- corners:
findChessboardCorners()
输出的内角点的坐标位置 - patternWasFound:用来指示定义的棋盘内角点是否被完整的探测到。
findChessboardCorners()
的返回值
true表示别完整的探测到,函数会用直线依次连接所有的内角点,作为一个整体,false表示有未被探测到的内角点,这时候函数会以(红色)圆圈标记处检测到的内角点
4.相机标定calibrateCamera
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)
)
参数:
- objectPoints:为世界坐标系中的三维点。在使用时,应该输入一个三维坐标点的向量的向量,即
vector<vector<Point3f>> object_points
。需要依据棋盘上单个黑白矩阵的大小,计算出(初始化)每一个内角点的世界坐标。 - imagePoints:为每一个内角点对应的图像坐标点。
vector<vector<Point2f>> imagePoints
形式的变量; - imageSize:为图像的像素尺寸大小,在计算相机的内参和畸变矩阵时需要使用到该参数;
- cameraMatrix:为相机的内参矩阵。。如
Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));
- distCoeffs:为畸变矩阵。输入一个
Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))
即可; - rvecs:为旋转向量;应该输入一个Mat类型的vector,即
vector<Mat>rvecs;
- tvecs:为位移向量,和rvecs一样,应该为
vector<Mat> tvecs;
- flags:为标定时所采用的算法。有如下几个参数:
-
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个畸变参数。
- criteria:是最优迭代终止条件设定。
在使用该函数进行标定运算之前,需要对棋盘上每一个内角点的空间坐标系的位置坐标进行初始化,标定的结果是生成相机的内参矩阵cameraMatrix、相机的5个畸变系数distCoeffs,另外每张图像都会生成属于自己的平移向量和旋转向量。
5.对标定结果进行评价projectPoints
void cv::projectPoints(
InputArray objectPoints,
InputArray rvec,
InputArray tvec,
InputArray cameraMatrix,
InputArray distCoeffs,
OutputArray imagePoints,
OutputArray jacobian = noArray(),
double aspectRatio = 0
)
参数:
- objectPoints:为相机坐标系中的三维点坐标。
vector<Point3f>
,即calibrateCamera()
的每个objectPoints[i]
- rvec:旋转向量。即
calibrateCamera()
的每个rvecs[i]
- tvec:位移向量。即
calibrateCamera()
的每个tvecs[i]
- cameraMatrix:为求得的相机的内参数矩阵。即
calibrateCamera()
的cameraMatrix
- distCoeffs:为求得的相机的畸变矩阵;即
calibrateCamera()
的distCoeffs
- imagePoints:保存重新计算得到的投影点,为每一个内角点对应的图像上的坐标点;
vector<Point2f> imagePoints;
- jacobian:是雅可比行列式;
- aspectRatio:是跟相机传感器的感光单元有关的可选参数,如果设置为非0,则函数默认感光单元的dx/dy是固定的,会依此对雅可比矩阵进行调整;
功能:
将3D点投影到图像平面
6.矫正图像undistort
void cv::undistort(
InputArray src,
OutputArray dst,
InputArray cameraMatrix,
InputArray distCoeffs,
InputArray newCameraMatrix = noArray()
)
- src:输入(失真)图像。
- dst:输出(校正)的图像,其大小和类型与src相同。
- cameraMatrix:相机矩阵A
- distCoeffs:相机畸变矩阵
- newCameraMatrix:相机矩阵。默认情况下,它与cameraMatrix相同,但是您还可以使用其他矩阵来缩放和移动图像。
二、程序
/*
@brief 你可能需要修改一些信息
@param image_count 图像数量,这个看你自己想拍多少张,建议10
@param patternsize 标定板上每行、列的角点数。这个得看你自己的标定板的实际数目
@brief 操作流程
采集棋盘图时:按y表示确认采集此图像作为棋盘图,如果通过判定,则会显示棋盘图上角点的连线。
如果不通过,会继续让你采样。按q退出程序。按其他键无意义。
*/
#include <opencv2/opencv.hpp>
#include <iostream>
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实现以及标定流程&&标定结果评价&&图像矫正流程解析(附标定程序和棋盘图)