本文高度致敬张正友的文章《A Flexible New Technique forCamera Calibration》,同时介绍如何在 OpenCV 中实现其方法。虽然看不懂原理,不过东拼西凑姑且能弄出个能用的纯过程代码,放在这里供大伙看个乐。
摄像机参数
内参矩阵 intrinsic matrix
1 | fx s x0 |
其中 fx、fy 为焦距,x0、y0 为主点坐标(相对于成像平面),s 为坐标轴倾斜参数,理想情况下为 0。
外参 Extrinsics
相机外参分为旋转矩阵和平移矩阵,旋转矩阵和平移矩阵共同描述了如何把点从世界坐标系转换到摄像机坐标系。
- 旋转矩阵:描述了世界坐标系的坐标轴相对于摄像机坐标轴的方向;
- 平移矩阵:描述了在摄像机坐标系下,空间原点的位置。
畸变参数 distortion parameters
共 5 个值,k1、k2、k3 为径向畸变系数,p1、p2 为切向畸变系数。
标定板
为了处理摄像头的畸变,需要求出内参与畸变参数,本文使用 OpenCV 进行图像处理。标定板 (Calibration Target) 可以帮助我们得到这些参数,相对的,其精度要求非常之高,推荐打印在实体纸张上以确保精度。
由于缺打印机,只好用显示器来代替纸制标定板。因此需要得到显示器的点距来换算像素与现实世界的度量衡。这之后的步骤就非常简单了,创建一个图像矩阵往里面填像素,按照国际象棋盘图案的规则,并保留外边框。
代码实现已上传至repo。
提取角点
该过程使用了 cv::findChessboardCorners()->bool
函数,其一个重载的参数表为:
cv::InputArray image
:检测源图像,需要是 8 位彩色或灰度图片;cv::Size patternSize
:图像中角点的行列个数,需要和图像中表示的完全一致,否则函数可能返回 false;cv::OutputArray corners
:传入一个vector<Point2f>
即可,引用返回角点位置列表;int flag
:其他或运算选项。
接着接着需要通过采样亚像素对提取的角点进行精确化 cv::find4QuadCornerSubpix()
并将角点画在图像上 cv::drawChessboardCorners
。
相机标定
用到的函数是 cv::calibrateCamera()
,参数表为:
cv::InputArrayOfArrays objectPoints
:世界空间标定板上角点的三维坐标,长度为图像个数;cv::InputArrayOfArrays imagePoints
:图像上角点的二维坐标,长度为图像个数;cv::Size imageSize
:图像大小cv::InputOutputArray cameraMatrix
:传出相机内参,大小为 3x3;cv::InputOutputArray distCoeffs
:传出摄像机的 5 个畸变系数;OutputArrayOfArrays rvecs
,OutputArrayOfArrays tvecs
:相机外参,分别为平移、旋转矩阵。
世界空间标定板角点坐标可以通过下列算法实现:
1 | vector<vector<Point3f>> object_points; |
图像矫正
直接使用 cv::undistort()
即可,参数表为:
cv:InputArray src
,cv::OutputArray dst
:源图像与输出目标cv::InputArray cameraMatrix
:相机内参cv::InputArray distCoeffs
:相机畸变参数
只需上文求得的参数直接填入即可,推荐将这些参数写入文件以便下次读取。
EOF.