相机畸变矫正原理及代码实现
相机畸变矫正原理及代码实现
相机畸变矫正是一项重要的视觉处理技术,广泛应用于测量、定位等领域。本文将从坐标系变换入手,介绍相机畸变矫正的基本原理,并给出使用OpenCV进行畸变矫正的具体实现方法。
坐标系简介
在视觉应用中,主要涉及四个坐标系:像素坐标系、图像坐标系、相机坐标系和世界坐标系。
1. 像素坐标系
像素坐标系是数字图像在计算机内部存储的形式。图像中任意一点的坐标可以表示为:
2. 图像坐标系
图像坐标系是将像素坐标系的中心平移到图像中心,可以方便地反映出物体的尺寸信息。坐标系如下图所示:
设图像坐标系的中心为原点,相机中感光器件的尺寸为已知,则两坐标系之间的关系可表示为:
将其写成矩阵形式:
将偏移项纳入乘积项,转化为齐次坐标形式:
3. 相机坐标系
相机坐标系的中心与图像坐标系的中心连线就是Z轴,x和y轴分别平行。并且两中心连线的距离就是焦距。一个物体从相机坐标系成像到图像坐标系的过程如图:
根据距离关系有:
即:
转化为齐次形式:
4. 世界坐标系
安装相机时,会分别绕相机坐标系的X、Y、Z轴做平移和旋转操作,最后得到世界坐标系。注意,此时三个轴与其他坐标系并不平行。
首先考虑平移操作:
其次考虑旋转操作,分别绕X、Y、Z轴旋转有旋转矩阵:
基本旋转矩阵:
基本矩阵:
故整个相机坐标系到世界坐标系的变换公式为:
其中:
相机的内参和外参
通过几个坐标系的转化,我们现在可以直接从像素坐标系变换到世界坐标系:
其中,u和v是像素坐标系中的坐标,XYZ是世界坐标系中的坐标,剩余的两个矩阵分别为:
RT01矩阵:相机外参,是相机相对于世界坐标系的旋转和平移变换关系。
4*3矩阵:相机内参,是相机的固有属性,含有焦距、像元尺寸等参数。
相机畸变矫正中,重要的一步就是获取相机的内参。
图像的畸变和矫正
图像的畸变主要有两种:径向畸变和切向畸变。
径向畸变
正中心位置的畸变最小,随着半径的增大,畸变增大。径向畸变可以分为枕形畸变和桶形畸变:
径向畸变矫正公式如下(泰勒级数展开式前3项):
其中是理想坐标,和是畸变后的像素点坐标,且:
切向畸变
在透镜与成像平面不平行时就会产生,类似于透视变换。
切向畸变的矫正公式如下:
两种畸变最后都归结到五个参数:;知道这五个参数后即可完成畸变的矫正。
畸变矫正代码实现
畸变矫正在OpenCV中已经做的很成熟了,只需要调用封装好的API就可以。接下来简要地说明一下流程:
- 完成标定板图像的采集(至少3张)
- 利用findChessboardCorners()函数检测标定板角点,并利用find4QuadCornerSubpix()函数完成亚像素级校准
- 利用calibrateCamera()函数进行相机标定,得到内参矩阵和畸变系数
1. 标定板图像采集
将标定板置于不同的角度进行拍摄,此时采集的图像为畸变图像。
2. 角点检测 & 亚像素级校准
主要用到了两个OpenCV内置函数:
1、角点检测函数
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:用于定义棋盘图上内角点查找的不同处理方式,有默认值。
2、提取亚像素角点信息
专门用来获取棋盘图上内角点的精确位置,降低相机标定偏差,还可以使用cornerSubPix函数
bool find4QuadCornerSubpix(InputArray img,
InputOutputArray corners,
Size region_size
);
// img:输入的Mat矩阵,最好是8位灰度图像,检测效率更高
// corners:初始的角点坐标向量,同时作为亚像素坐标位置的输出vector<Point2f> iamgePointsBuf;
// region_size:角点搜索窗口的尺寸
角点检测结果可以可视化出来:
3. 完成相机标定
3、相机标定
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:每一个内角点对应的图像坐标点,vector<vector<Point2f>> image_points_seq形式
// imageSize:图像的像素尺寸大小(列数=cols,行数=rows)(宽度=width,高度=height)
// cameraMatrix:相机的3*3内参矩阵,Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));
// distCoeffs:1*5畸变矩阵,Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))
// rvecs:旋转向量,输入一个Mat类型的vector,即vector<Mat>rvecs;
// tvecs:位移向量,和rvecs一样,应该为vector<Mat> tvecs;
// flags:标定时所采用的算法
// criteria:最优迭代终止条件设定
具体代码细节请参看:https://blog.csdn.net/piaoxuezhong/article/details/75268535
本文原文来自CSDN博客