1.图像畸变
相机成像可以分为四个步骤:刚体变换、透视投影、畸变校正和数字化图像。
相机的图像有时候会出现畸变严重现象,畸变指真实成像点与理想成像点间的偏移,产生原因是镜头工艺的不完美,从而导致了不规则的折射。
修正图像需要相机两种参数:
相机的内部参数。例如镜头的焦距,光学中心和径向畸变系数。外部参数:这是指摄像机相对于某些世界坐标系的方向(旋转和平移)
通过校准可以改善畸变,图像不失真,接近真实图像,另外,还可以确定相机的自然单位(像素)与实际单位之间的关系(例如毫米),这样标定后就知道图像内物体的大小尺寸。
2.相机校准的流程步骤
步骤1:采集棋盘格图像(10张以上),并预处理步骤2:找出棋盘格角点坐标步骤3:进一步提取亚像素角点信息步骤4:计算出相机内参数矩阵和畸变系数步骤5:畸变图像校准步骤1:采集棋盘格图像(10张以上),并预处理
对于采集图像,方法是手里拿着A4纸打印的棋盘格,对着相机变换不同的方角度,采集十张以上;或者棋盘格放到桌上,拿着相机从不同角度一通拍摄。
为了演示这里用手机拍了十五张:
每张照片是3968x2976(宽x高),先进行下预处理把尺寸压缩四倍:
#include "stdafx.h"#include<opencv2/opencv.hpp>#include<iostream>using namespace cv;using namespace std;Mat image;int main(){vector<String> images_path;//创建容器存放读取图像路径string image_path = "D:/work_c++/image/chessboardimage/*.jpg";//待处理图路径glob(image_path, images_path);//读取指定文件夹下图像char saveimage[100];for (int i = 0; i < images_path.size(); i++){image = imread(images_path[i]);resize(image, image, Size(992, 744));//调整大小sprintf_s(saveimage, "D:/work_c++/image/temp/%d.jpg", i + 1);//数据格式化输出到字符串,图像存储路径imwrite(saveimage, image);//保存}return 0;}
结果:
步骤2:找出棋盘格角点坐标
这里棋盘格每行每列的内角数是(5,7)
主要函数:
findChessboardCorners函数
//! finds checkerboard pattern of the specified size in the image CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, OutputArray corners, int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE );/*第一个参数Image,传入拍摄的棋盘图Mat图像,必须是8位的灰度或者彩色图像;第二个参数patternSize,每个棋盘图上内角点的行列数,一般情况下,行列数不要相同,便于后续标定程序识别标定板的方向;第三个参数corners,用于存储检测到的内角点图像坐标位置,一般用元素是Point2f的向量来表示:vector<Point2f> image_points_buf;第四个参数flage:用于定义棋盘图上内角点查找的不同处理方式,有默认值。*/
步骤3:进一步提取亚像素角点信息
主要函数:
cornerSubPix函数
//! adjusts the corner locations with sub-pixel accuracy to maximize the certain cornerness criteriaCV_EXPORTS_W void cornerSubPix( InputArray image, InputOutputArray corners,Size winSize, Size zeroZone,TermCriteria criteria );/*第一个参数image,输入的Mat矩阵,最好是8位灰度图像,检测效率更高;第二个参数corners,初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector<Point2f/Point2d> iamgePointsBuf;第三个参数winSize,大小为搜索窗口的一半;第四个参数zeroZone,死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。它是用来避免自相关矩阵出现某些可能的奇异性。当值为(-1,-1)时表示没有死区;第五个参数criteria,定义求角点的迭代过程的终止条件,可以为迭代次数和角点精度两者的组合;*/
步骤4:计算出相机内参数矩阵和畸变系数
主要函数:
calibrateCamera函数
//! finds intrinsic and extrinsic camera parameters from several fews of a known calibration pattern. CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, Size imageSize, CV_OUT InputOutputArray cameraMatrix, CV_OUT 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,为每一个内角点对应的图像坐标点。和objectPoints一样,应该输入vector<vector<Point2f>> image_points_seq形式的变量;第三个参数imageSize,为图像的像素尺寸大小,在计算相机的内参和畸变矩阵时需要使用到该参数;第四个参数cameraMatrix为相机的内参矩阵。输入一个Mat 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:畸变图像校准
主要函数:
undistort函数
//! corrects lens distortion for the given camera matrix and distortion coefficients CV_EXPORTS_W void undistort( InputArray src, OutputArray dst, InputArray cameraMatrix, InputArray distCoeffs, InputArray newCameraMatrix=noArray() ); /*第一个参数src,输入参数,代表畸变的原始图像;第二个参数dst,矫正后的输出图像,跟输入图像具有相同的类型和大小;第三个参数cameraMatrix为之前求得的相机的内参矩阵;第四个参数distCoeffs为之前求得的相机畸变矩阵;第五个参数newCameraMatrix,默认跟cameraMatrix保持一致;方法一相比方法二执行效率更高一些,推荐使用*/
3.代码实现
环境:
OpenCV3.4.1vs
// CheckerBoardDemo.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include<opencv2/opencv.hpp>#include<iostream>using namespace cv;using namespace std;Mat image,img_gray;int BOARDSIZE[2]{5,7 };//棋盘格每行每列角点个数int main(){vector<vector<Point3f>> objpoints_img;//保存棋盘格上角点的三维坐标vector<Point3f> obj_world_pts;//三维世界坐标vector<vector<Point2f>> images_points;//保存所有角点vector<Point2f> img_corner_points;//保存每张图检测到的角点vector<String> images_path;//创建容器存放读取图像路径string image_path = "D:/work_c++/image/temp/*.jpg";//待处理图路径glob(image_path, images_path);//读取指定文件夹下图像//转世界坐标系for (int i = 0; i < BOARDSIZE[1]; i++){for (int j = 0; j < BOARDSIZE[0]; j++){obj_world_pts.push_back(Point3f(j, i, 0));}}for (int i = 0; i < images_path.size(); i++){image = imread(images_path[i]);cvtColor(image, img_gray, COLOR_BGR2GRAY);//检测角点bool found_success = findChessboardCorners(img_gray, Size(BOARDSIZE[0], BOARDSIZE[1]), img_corner_points,CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE);//显示角点if (found_success){//迭代终止条件TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 30, 0.001);//进一步提取亚像素角点cornerSubPix(img_gray, img_corner_points, Size(11, 11),Size(-1, -1), criteria);//绘制角点drawChessboardCorners(image, Size(BOARDSIZE[0], BOARDSIZE[1]),img_corner_points,found_success);objpoints_img.push_back(obj_world_pts);//从世界坐标系到相机坐标系images_points.push_back(img_corner_points);}char *output = "image";imshow(output, image);waitKey(200);}/*计算内参和畸变系数等*/Mat cameraMatrix, distCoeffs, R, T;//内参矩阵,畸变系数,旋转量,偏移量calibrateCamera(objpoints_img, images_points, img_gray.size(), cameraMatrix, distCoeffs, R, T);cout << "cameraMatrix:" << endl;cout << cameraMatrix << endl;cout << "*****************************" << endl;cout << "distCoeffs:" << endl;cout << distCoeffs << endl;cout << "*****************************" << endl;cout << "Rotation vector:" << endl;cout << R << endl;cout << "*****************************" << endl;cout << "Translation vector:" << endl;cout << T << endl;/*畸变图像校准*/Mat src, dst;src = imread("D:/work_c++/image/test/test.jpg");undistort(src, dst, cameraMatrix, distCoeffs);char *dst_output = "image_dst";imshow(dst_output, dst);waitKey(100);imwrite("D:/work_c++/image/test/result.jpg", dst);destroyAllWindows();//销毁显示窗口system("pause");return 0;}
4. 结果
4.1 检测角点效果
4.2 内参,畸变系数结果
4.3 畸变图校准测试结果
测试原图:
测试结果图:
注:用手机拍的棋盘格图,在打印棋盘格时,勾选了适应A4纸张,没按实际大小打印,导致棋盘格不是方正的格子,一边3.5cm,一边3.7cm,没有重新制作打印,仅用来测试一下。
另外,这里以一个方格为单位,没有考虑单位长度,若要计算实际的参数,需要乘单位长度。
如程序中改变:
//转世界坐标系realsize = Size(x,y);//测得实际真实尺寸for (int i = 0; i < BOARDSIZE[1]; i++){for (int j = 0; j < BOARDSIZE[0]; j++){obj_world_pts.push_back(Point3f(j*realsie.with, i*realsize.height, 0));}}//与计算内参矩阵无关,与计算外参旋转、平移量有关
目前工业相机基本上畸变都非常小,厂家已经做了很多畸变影响消除,用户不用特地去做畸变矫正,一般是便宜的网络摄像头畸变特别大,需要做畸变矫正处理。
Reference:
/articles/62472
.cn/opencvdoc/2.3.2/html/doc/tutorials/calib3d/camera_calibration_square_chess/camera_calibration_square_chess.html
/Crystal_YS/article/details/86582414