赞
踩
在标定之前我要想一想为什么要标定,什么相机需要标定,标定的输入是啥,标定的输出是啥
标定的目的:为了求出相机的内参和外参,内参和外参就可以对之后相机拍出来的照片进行矫正,得到畸变很小的图片。
标定的输入:用相机拍出来一系列的棋盘格图片。
标定的输出:相机的内参和外参。
1,采集一系列棋盘格图;
2,对每一张图,提取其角点信息;
3,对每一张图,提取其亚像素角点信息;
4,对相机进行标定
5,查看标定结果,并对结果进行评价
6,利用标定结果对棋盘图进行矫正。
标定图片需要标定板在不同位置、不同角度、不同姿态下拍摄,最少需要三张,以10-20张为宜。
- CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, OutputArray corners,
- int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE );
image:传进来的棋盘格图
patternSize:一张图中角点的行列数,比方说棋盘格子大小是12*9,那么角点就有11*9个。vector<Point2f>
为了进一步的提高精度,需要在初步提取的角点位置上进一步提取亚像素信息,降低标定的误差
- CV_EXPORTS_W void cornerSubPix( InputArray image, InputOutputArray corners,
- Size winSize, Size zeroZone,
- TermCriteria criteria );
image:标定的棋盘图。
corners:一系列的初始的角点坐标,同时也作为亚像素坐标的输出,一般需要float型,或者double型。
winSize:搜索的半径。
zeroZone:死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。它是用来避免自相关矩阵出现某些可能的奇异性。当值为(-1,-1)是表示没有死区。
criteria:定义求角点的终止条件,可以是迭代次数和角点精度的组合。
CV_EXPORTS bool find4QuadCornerSubpix( InputArray img, InputOutputArray corners, Size region_size );
image:标定的棋盘图。
corners:一系列的初始的角点坐标,同时也作为亚像素坐标的输出,一般需要float型,或者double型。
region_size:搜索窗口的半径。
画图展示一下效果:
- CV_EXPORTS_W void drawChessboardCorners( InputOutputArray image, Size patternSize,
- InputArray corners, bool patternWasFound );
获得一系列的图的角点信息之后就可以对相机进行标定了,然后计算相机的内参和外参。
- CV_EXPORTS_W double 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>> obj;
imagePoints:每一张图角点对用的坐标点 ;vector<vector<Point2f>> allCorners;
imageSize:图片的像素尺寸大小
cameraMatrix:相机的内参矩阵;Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));
distCoeffs:畸变矩阵(外参);Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))
rvecs:旋转向量;vector<Mat>rvecs;
tvecs:位移向量;vector<Mat>tvecs;
flag:标定所采取的方法;
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:迭代的终止条件;
- CV_EXPORTS_W void projectPoints( InputArray objectPoints,
- InputArray rvec, InputArray tvec,
- InputArray cameraMatrix, InputArray distCoeffs,
- OutputArray imagePoints,
- OutputArray jacobian = noArray(),
- double aspectRatio = 0 );
利用相机的内外参,对一幅图中的三维点重新投影计算,得到新的投影点
objectPoints:相机坐标系中的三维坐标
rvec, tvec:旋转矩阵和位移矩阵
cameraMatrix, distCoeffs:相机的内参矩阵和畸变矩阵
imagePoints:每个点对应图像中的坐标点
jacobian是雅可比行列式;
aspectRatio是跟相机传感器的感光单元有关的可选参数,如果设置为非0,则函数默认感光单元的dx/dy是固定的,会依此对雅可比矩阵进行调整;
方法一:使用initUndistortRectifyMap和remap两个函数配合实现。
initUndistortRectifyMap用来计算畸变映射,remap把求得的映射应用到图像上。
initUndistortRectifyMap的函数原型:
方法二:使用undistort函数实现
- CV_EXPORTS_W void undistort( InputArray src, OutputArray dst,
- InputArray cameraMatrix,
- InputArray distCoeffs,
- InputArray newCameraMatrix = noArray() );
src:棋盘格图
dst:矫正好的棋盘格图
cameraMatrix:相机内参
distCoeffs:相机外参
srcMat和dstMat:
- void CameraCalibrationZhang()
- {
- int imageHeight, imageWidth;
- vector<String> leftStrings, rightStrings;
- vector<Mat> leftMats, rightMats;
-
- glob("C:\\Users\\Ring\\Desktop\\0420\\left\\", leftStrings);
- glob("C:\\Users\\Ring\\Desktop\\0420\\right\\", rightStrings);
-
- for (int i = 0; i < leftStrings.size(); i++)
- {
- leftMats.push_back(imread(leftStrings[i], 0));
- rightMats.push_back(imread(rightStrings[i], 0));
- }
-
- vector<vector<Point2f>> allCorners; allCorners.clear();
- int boardWidth = 11, boardHeight = 8;
- float squaresize = 25; //棋盘格一格的实际大小是25mm
-
- for (int i = 0; i < leftMats.size(); i++)
- {
- Mat src = leftMats[i];
- //1,设置一些参数,比方说:棋盘格总共右多少个角点,棋盘格的真实大小啊,等等
-
- //2,寻找角点
- vector<Point2f> corners;
- bool isFind = findChessboardCorners(src, Size(boardWidth, boardHeight), corners);
-
- //3,寻找亚像素角点信息
- cornerSubPix(src, corners, Size(5, 5), Size(-1, -1), TermCriteria(1 + 2, 20, 0.1));
- //find4QuadCornerSubpix(src, corners, Size(5, 5)); //第二种方法
- allCorners.push_back(corners);
-
- /*Mat rgbMat;
- cvtColor(src, rgbMat, CV_GRAY2RGB);
- Mat showMat = rgbMat.clone();
- drawChessboardCorners(showMat, Size(boardWidth, boardHeight), corners, isFind);*/
-
- cout << "";
- }
-
- //4,相机标定
- vector<vector<Point3f>> obj;
- vector<Point3f> imgpoint;
- for (int rowIndex = 0; rowIndex < boardHeight; rowIndex++)
- {
- for (int colIndex = 0; colIndex < boardWidth; colIndex++)
- {
- imgpoint.push_back(Point3f(rowIndex * squaresize, colIndex * squaresize, 0));
- }
- }
- for (int imgIndex = 0; imgIndex < leftMats.size(); imgIndex++)
- {
- obj.push_back(imgpoint);
- }
- Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));
- Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));
- vector<Mat> rvecs, tvecs;
- calibrateCamera(obj, allCorners, leftMats[0].size(), cameraMatrix, distCoeffs, rvecs, tvecs);
-
- //5,对标定结果进行评价
- vector<Point2f> image_points2; /* 保存重新计算得到的投影点 */
- double total_err = 0.0; /* 所有图像的平均误差的总和 */
- double err = 0.0; /* 每幅图像的平均误差 */
- for (int i = 0; i<leftMats.size(); i++)
- {
- vector<Point3f> tempPointSet = obj[i];
- /* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
- projectPoints(tempPointSet, rvecs[i], tvecs[i], cameraMatrix, distCoeffs, image_points2);
- /* 计算新的投影点和旧的投影点之间的误差*/
- vector<Point2f> tempImagePoint = allCorners[i];
- Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);
- Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2);
- for (int j = 0; j < tempImagePoint.size(); j++)
- {
- image_points2Mat.at<Vec2f>(0, j) = Vec2f(image_points2[j].x, image_points2[j].y);
- tempImagePointMat.at<Vec2f>(0, j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);
- }
- double err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
- total_err += err /= 88;
- std::cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
- cout<< "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
- }
-
-
- //6,查看标定的结果,并对棋盘图进行矫正
- Mat srcMat = leftMats[0], dstMat;
- undistort(srcMat, dstMat, cameraMatrix, distCoeffs);
-
- cout << "";
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。