计算机视觉:相机模型与参数标定

一、针孔照相机模型

针孔照相机模型(有时称为射影照相机模型)是计算机视觉中广泛使用的照相机模型。对于大多数应用来说,针孔照相机模型简单,并且具有足够的精确度。这个名字来源于一种类似暗箱机的照相机。该照相机从一个小孔采集射到暗箱内部的光线。在针孔照相机模型中,在光线投影到图像平面之前,从唯一一个点经过,也就是照相机中心 C。图 4-1 为从照相机中心前画出图像平面的图解。事实上,在真实的照相机中,图像平面位于照相机中心之后,但是照相机的模型和图 4-1 的模型是一样的。
在这里插入图片描述
由图像坐标轴和三维坐标系中的 x 轴和 y 轴对齐平行的假设,我们可以得出针孔照相机的投影性质。照相机的光学坐标轴和 z 轴一致,该投影几何可以简化成相似三角形。在投影之前通过旋转和平移变换,对该坐标系加入三维点,会出现完整的投影变换。在针孔照相机中,三维点 X 投影为图像点 x(两个点都是用齐次坐标表示的),如下所示:
λx = PX(4.1)
这里,3×4 的矩阵 P 为照相机矩阵(或投影矩阵)。注意,在齐次坐标系中,三维点 X 的坐标由 4 个元素组成,X=[X, Y, Z, W]。这里的标量 λ 是三维点的逆深度。如果我们打算在齐次坐标中将最后一个数值归一化为 1,那么就会使用到它。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.像主点偏移

主点是光轴和相机成像平面的交点,之前考虑的是理想成像情况,图像坐标系和相机坐标系原点重合,因此不存在坐标系偏移。但是在实际情况中,图像坐标系往往在图片的左上角,光轴过图像中心,因此图像坐标系和相机坐标系不重合。两个坐标系之间存在一个平移运动(如图)。
在这里插入图片描述
在这里插入图片描述
考虑主点偏移后,图像坐标和3D在相机坐标系坐标的关系为:
在这里插入图片描述

主点偏移后,透视投影模型的的齐次坐标表达为:
在这里插入图片描述

2.内参矩阵

内参矩阵是将3D相机坐标变换到2D齐次图像坐标。透视投影的一个理想模型就是针孔相机。
在这里插入图片描述
在这里插入图片描述

fx,fy表示焦距。焦距就是真空与图像平面(投影屏幕)的距离,类似于人眼和视网膜,焦距的度量是针对像素的。针孔相机的fx,fy有相同的值。上图中红线部分就是焦距。

3.畸变现象

畸变是指拍摄出来的照片四周出现了卷翘或膨胀的现象,例如,使用广角镜头拍摄时,最容易出现画面呈桶形膨胀的桶形失真现象。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.畸变校正

单应性矩阵,是由两部分构成的:内参矩阵和外参矩阵,在OpenCV的3D重建中,对摄像机的内参外参有讲解:
**外参:**摄像机的旋转平移属于外参,用于描述相机在静态场景下相机的运动,或者在相机固定时,运动物体的刚性运动。因此,在图像拼接或者三维重建中,就需要使用外参来求几幅图像之间的相对运动,从而将其注册到同一个坐标系下面来。
**内参:**下面给出了内参矩阵,需要注意的是,真实的镜头还会有径向和切向畸变,而这些畸变是属于相机的内参的。
摄像机内参矩阵。

其中,fx,fy为焦距,一般情况下,二者相等,x0、y0为主点坐标(相对于成像平面),s为坐标轴倾斜参数,理想情况下为0。
摄像机外参矩阵:包括旋转矩阵和平移矩阵,旋转矩阵和平移矩阵共同描述了如何把点从世界坐标系转换到摄像机坐标系。
旋转矩阵:描述了世界坐标系的坐标轴相对于摄像机坐标轴的方向。
平移矩阵:描述了在摄像机坐标系下,空间原点的位置。

二、照相机标定

1.标定原理

摄像机标定(Camera calibration)简单来说是从世界坐标系换到图像坐标系的过程,也就是求最终的投影矩阵 P 的过程。

一般来说,标定的过程分为两个部分:
第一步是从世界坐标系转换为相机坐标系,这一步是三维点到三维点的转换,包括 R,t (相机外参)等参数;
第二部是从相机坐标系转为图像坐标系,这一步是三维点到二维点的转换,包括 K(相机内参)等参数;

世界坐标系转换为相机坐标系:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
相机坐标系转换为图像坐标系:
在这里插入图片描述
其中:

  • C 点表示camera centre,即相机的中心点,也是相机坐标系的中心点;
  • Z 轴表示principal axis,即相机的主轴;
  • p 点所在的平面表示image plane,即相机的像平面,也就是图片坐标系所在的二维平面;
  • p 点表示principal point,即主点,主轴与像平面相交的点;
  • C 点到 p点的距离,也就是右边图中的f ff 表示focal length,即相机的焦距;
  • 像平面上的 x和 y 坐标轴是与相机坐标系上的 X和 Y 坐标轴互相平行的;
  • 相机坐标系是以 X, Y , Z 三个轴组成的且原点在 C CC 点,度量值为米(m);
  • 像平面坐标系是以 x xx,y yy(小写)两个轴组成的且原点在 p pp 点,度量值为米(m);
  • 图像坐标系一般指图片相对坐标系,在这里可以认为和像平面坐标系在一个平面上,不过原点是在图片的角上,而且度量值为像素的个数(pixel);

通过对上面坐标系的介绍,可以得到以下的转换公式:
在这里插入图片描述
加上偏移量:
在这里插入图片描述
如上图所示,其中主点 p pp 是像平面坐标系的原点,但在图像坐标系中的位置为(px,py),在这里,图形坐标系的原点是图片的左下角,所以可以得到:
在这里插入图片描述
把这个换成矩阵计算:
在这里插入图片描述
在这里插入图片描述
最后,可以得到矩阵K,就是相机内参:
在这里插入图片描述
投影矩阵P(在这里可以认为旋转矩阵 R 为单位矩阵 I,平移矩阵 t 都为0):
在这里插入图片描述
最终的转换式:
从上面两个转换的过程,我们可以得到从世界坐标轴转换到图像的过程可以把投影矩阵P表示为:
在这里插入图片描述
在这里插入图片描述
在这里,K 一般称为相机内参(intrinsic parameters),描述了相机的内部参数,包括焦距 f 、主点 p 的位置、以及像素与真实环境的大小比例等,这个是固有属性,是提供好的;R 和 t 称为相机外参(extrinsic parameters),R 在这里是旋转矩阵,可以转换为三维的旋转向量,分别表示绕x ,y ,z 三个轴的旋转角度,t 目前就是一个平移向量,分别表示在x ,y ,z 三个方向上的平移量。

通过相机标记我们需要得到以下的参数:

  • 摄像机的内参和外参矩阵,内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像。
  • 每一幅标定图像的选择和平移矩阵。

2.实验流程

相机标定的流程可以简单的表述为:

  • 准备图片。这里要注意,拍摄不同角度的图片时只需要对焦一次就好了,如果反复对焦,得到的每张图片的焦距就会一直变化。
  • 对每张图片提取角点信息。
  • 对每张图片,进一步提取亚像素角点信息。
  • 相机标定。
  • 畸变矫正。

实验数据准备:
准备20张左右的黑白棋盘格标定图片。
可以给图片进行一定的缩小,加快运算速度。
拍摄过程中,如果方格纸是横着摆的就一直横着摆着拍,因为横纵角点个数是要提前设置好的。

实验步骤:
1)准备一个张正友标定法的棋盘格,棋盘格大小已知,用相机对其进行不同角度的拍摄,得到一组图像;

2)对图像中的特征点如标定板角点进行检测,得到标定板角点的像素坐标值,根据已知的棋盘格大小和世界坐标系原点,计算得到标定板角点的物理坐标值;

3)求解内参矩阵与外参矩阵。
根据物理坐标值和像素坐标值的关系,求出 H 矩阵,进而构造 V 矩阵,求解 B 矩阵,利用B矩阵求解相机内参矩阵 A ,最后求解每张图片对应的相机外参矩阵 :
在这里插入图片描述

4)求解畸变参数。
构造 D 矩阵,计算径向畸变参数;

5)利用L-M(Levenberg-Marquardt)算法对上述参数进行优化

标定:

ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None);

其中:
obj_points = [] # 存储3D点
img_points = [] # 存储2D点

三、代码实现(python+opencv)

1.数据准备

  • 手机型号:红米K20Pro
  • 准备10~20张各个角度棋盘格图片:
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/da93bd74ef7e4443aa04f6dce474add9.png

2.代码实现

import cv2
import numpy as np
import glob

# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#棋盘格模板规格
w = 9
h = 9
# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
objp = np.zeros((w*h,3), np.float32)
objp[:, :2] = np.mgrid[0:w,0:h].T.reshape(-1, 2)
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = [] # 在世界坐标系中的三维点
imgpoints = [] # 在图像平面的二维点

images = glob.glob('images/camerea/*.jpg')
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 找到棋盘格角点
    ret, corners = cv2.findChessboardCorners(gray, (w, h), None)
    # 如果找到足够点对,将其存储起来
    if ret == True:
        cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        objpoints.append(objp)
        imgpoints.append(corners)
        # 将角点在图像上显示
        cv2.drawChessboardCorners(img, (w,h), corners, ret)
        cv2.imshow('findCorners', img)
        cv2.waitKey(1)
cv2.destroyAllWindows()
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], 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)    # 平移向量  # 外参数
# 去畸变
img2 = cv2.imread('images/camerea/5.jpg')
h, w = img2.shape[:2]
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 0, (w, h)) # 自由比例参数
dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
# 根据前面ROI区域裁剪图片
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.jpg', dst)

# 反投影误差
total_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
    total_error += error
print(("total error: "), total_error/len(objpoints))

3.标定结果

ret: 4.394683405977059
内参数矩阵mtx:
 [[3.04026495e+03 0.00000000e+00 1.00582112e+03]
 [0.00000000e+00 3.06724113e+03 1.84187962e+03]
 [0.00000000e+00 0.00000000e+00 1.00000000e+00]]
畸变系数dist:
 [[ 0.1886868  -1.28335205 -0.03039867 -0.01408399  1.23207105]]
旋转向量rvecs:
 (array([[-0.62541049],
       [ 0.03757851],
       [ 0.03363172]]), array([[-0.49134236],
       [ 0.46131222],
       [ 1.47950141]]), array([[-0.33401901],
       [ 0.18937699],
       [ 0.10108383]]), array([[-0.73541629],
       [ 0.17123902],
       [ 0.1131898 ]]), array([[-0.078195  ],
       [-0.04737046],
       [ 0.00441623]]), array([[-0.0413743 ],
       [-0.05055354],
       [ 0.0013402 ]]), array([[-0.09104257],
       [ 0.06598451],
       [ 0.07026327]]), array([[-0.02369878],
       [ 0.0177626 ],
       [-0.004309  ]]), array([[-0.04565214],
       [-0.0346831 ],
       [-0.01685584]]), array([[-0.03797529],
       [ 0.03933211],
       [ 0.84075042]]), array([[-0.0623513 ],
       [ 0.03704442],
       [ 0.53046414]]), array([[-0.04967806],
       [ 0.02656794],
       [ 0.86136283]]), array([[-0.05078251],
       [ 0.04350796],
       [ 0.46372503]]))
平移向量tvecs:
 (array([[-5.76483394],
       [-5.59425897],
       [28.06129709]]), array([[ 2.15315313],
       [-5.08760426],
       [29.37687568]]), array([[ 1.16719442],
       [-9.7140158 ],
       [18.22928894]]), array([[ 2.49065458],
       [-9.78458582],
       [23.70042512]]), array([[-4.96942674],
       [-2.87605716],
       [20.81731809]]), array([[-4.18474341],
       [-3.42998663],
       [20.12393487]]), array([[-4.30814304],
       [-2.56427041],
       [16.96971532]]), array([[-4.64090087],
       [-3.41084605],
       [27.40839687]]), array([[-4.74237678],
       [-3.32990083],
       [20.72290355]]), array([[-0.60515262],
       [-7.11571211],
       [26.78100432]]), array([[-2.87759829],
       [-6.03695933],
       [26.955581  ]]), array([[-0.87636625],
       [-5.79114751],
       [28.57031037]]), array([[-3.02281071],
       [-6.52443868],
       [24.93057195]]))
total error:  0.3697801222902063

最后得到的内参矩阵:
在这里插入图片描述

4.分析

可以通过反投影误差来评估结果的好坏,越接近0,说明结果越理想。

通过之前计算的内参数矩阵、畸变系数、旋转矩阵和平移向量,使用cv2.projectPoints()计算三维点到二维图像的投影,然后计算反投影得到的点与图像上检测到的点的误差,最后计算一个对于所有标定图像的平均误差即反投影误差。

误差分析:棋盘打印出来有些不平整,可能是打印的纸张没有放正,导致有些地方翘着,效果不是很好,误差值有些大了,把纸张贴平整应该会好很多。而且拍照的角度变化不是太大,可以试着把拍照的角度更加差异些,结果会更明显。 另外光线问题、棋盘格的材质也会影响标定结果。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laker 23

要秃啦,支持一下嘛~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值