获取相机内参,畸变参数,从而进行相机校准,为手眼标定做准备。
相机将三维世界中的坐标点映射到二维图像平面的过程能够抽象成用一个几何模型进行描述。这个模型有很多种,其中最简单的称为针孔模型。
现实世界中源于某个物体的光线穿过小孔,会在摄像机成像平面上形成一幅倒立的图像。
从世界坐标系到相机坐标系,涉及到旋转和平移。R为旋转矩阵,T为偏移向量。因此只要知道R和T就知道两坐标系之间的联系。推到过程可看知乎相关文章
备注:左边的矩阵增加1个维度可以更加方便的表示,可以参考博客。最终是一个4x4的矩阵与一个4x1的矩阵相乘。
从相机坐标系到图像坐标系,属于透视投影关系,满足三角形的相似定理。小孔成像是中心对称关系,实像是倒立的,而我们在显示器中看到的图像却是正立的(显示器显示的图像其实就是对称的虚像)。因此用对称的虚像进行计算更加方便。
我们可以把小孔成像的像平面放在物体同侧,结果如下:
在已知相机坐标(Xc,Yc,Zc),并且知道焦距f的时候,可以得到图像坐标
注意:realsense是定焦设备,之所以可以在一定范围内都清楚,是因为有一定的景深
像素坐标系和图像坐标系都在成像平面上,只是各自的原点和度量单位不一样。图像坐标系的单位是mm,属于物理单位,而像素坐标系的单位是pixel,我们平常描述一个像素点都是几行几列。
dx和dy表示每一列和每一行分别代表多少mm。
以上转换关系是理想针孔模型的,由于通过针孔的光线少,摄像机曝光太慢,在实际使用中均采用透镜,可以使图像生成迅速,但代价是引入了畸变。径向畸变包括桶形畸变和枕形畸变两种。以下分别是枕形和桶形畸变示意图:
径向畸变的转换公式:
切向畸变是由于透镜本身与相机传感器平面(像平面)或图像平面不平行而产生的,这种情况多是由于透镜被粘贴到镜头模组上的安装偏差导致。切向畸变公式:
将径向畸变和切向畸变放在一起考虑,可以得到下式:
所以对于镜头畸变一共有 5 个参数 k1, k2, k3, p1, p2 需要校准。opencv 输出的即便参数顺序是 k1, k2, p1, p2, k3 因为 k3 没那么重要。
进行摄像机标定的目的:求出相机的内参矩阵,以及畸变参数[k1,k2,k3,p1,p2]。
相机标定的外参一般没有用
通过一组三维点(Xw,Yw,Zw)以及这些点对应的像素位置(u,v)来计算相机参数。
完整代码如下所示:
import cv2
import numpy as np
import glob
# 设置寻找亚像素角点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)
# 获取标定板角点的位置(内角点是不挨着边界的角点)
objp = np.zeros((6 * 9, 3), np.float32)
objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2) # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
obj_points = [] # 存储3D点
img_points = [] # 存储2D点
images = glob.glob("D:/company/py/pythonProject/image/*.jpg") # 标定图片的地址
for fname in images:
img = cv2.imread(fname)
cv2.imshow('img',img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
size = gray.shape[::-1]
ret, corners = cv2.findChessboardCorners(gray, (6, 9), None)
print(ret)
if ret:
obj_points.append(objp)
corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) # 在原角点的基础上寻找亚像素角点
#print(corners2)
if [corners2]:
img_points.append(corners2)
else:
img_points.append(corners)
cv2.drawChessboardCorners(img, (8, 6), corners, ret) # 记住,OpenCV的绘制函数一般无返回值
cv2.imshow('img', img)
cv2.waitKey(2000)
print(len(img_points))
cv2.destroyAllWindows()
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None)
print("ret:", ret)
print("mtx:\n", mtx) # 内参数矩阵
print("dist:\n", dist) # 畸变系数 distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print("rvecs:\n", rvecs) # 旋转向量 # 外参数
print("tvecs:\n", tvecs ) # 平移向量 # 外参数
print("-----------------------------------------------------")
看代码中的注释便可以知道具体的返回值的意义。
matlab的calibration toolbox 高度封装,能直观的看重投影误差等信息,因此更推荐用matlab进行相机标定。
radial coefficients:为径向畸变系数。在相机标定中,径向畸变去拟合的时候是用多项式去拟合的。系数越多,拟合能力越强,但是不是说越多越好。鼠标停留在上面的时候,有如下提示:
仅仅适用于广角相机,因此我这里只需要选择2coefficients即可。
skew偏斜系数:当x轴y轴不垂直的时候,需要这个。同理,当鼠标放上去的时候,提示大多数现代相机不存在这个问题,这里我不选择
tangential distortion切向畸变:这个一般也不勾选
可以剔除掉误差比较大的图:然后再次进行标定
对于50万像素以下的分辨率,重投影误差需要在0.1以下才比较好,因此我这里精度还不够。我用的是480x640<50w。目前基本在1.2左右。需要更多的拍摄技巧去完善
opencv和matlab获取的内参矩阵互为转置