计算机视觉——相机标定
计算机视觉——相机标定
-
1.相机标定
-
- 1.1原理
- 1.2相机的内参矩阵
-
2.张正友相机标定法简介
-
3.标定流程
-
4.实现相机标定
-
- 4.1标定板注意事项:
- 4.2 实现代码
- 4.3 实现结果
-
5.总结
1.相机标定
1.1原理
我们拍摄的物体都处于三维世界坐标系中,而相机拍摄时镜头看到的是三维相机坐标系,成像时三维相机坐标系向二维图像坐标系转换。不同的镜头成像时的转换矩阵不同,同时可能引入失真,标定的作用是近似地估算出转换矩阵和失真系数。为了估算,需要知道若干点的三维世界坐标系中的坐标和二维图像坐标系中的坐标,也就是拍摄棋盘的意义。通过相机标定,获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像。
1.2相机的内参矩阵
设P=(X,Y,Z)为场景中的一点,在针孔相机模型中,其要经过以下几个变换,最终变为二维图像上的像点p=(μ,ν):
将P从世界坐标系通过刚体变换(旋转和平移)变换到相机坐标系,这个变换过程使用的是相机间的相对位置,也就是相机的外参数。
从相机坐标系,通过透视投影变换到相机的成像平面上的像点p=(x,y)。
将像点p从成像坐标系,通过缩放和平移变换到像素坐标系上点p=(μ,ν)。
相机将场景中的三维点变换为图像中的二维点,也就是各个坐标系变换的组合,可将上面的变换过程整理为矩阵相乘的形式:

将矩阵K称为相机的内参数,

其中,α,β表示图像上单位距离上像素的个数,则fx=αf,fy=βf将相机的焦距f变换为在x,y方向上像素度量表示。
另外,为了不失一般性,可以在相机的内参矩阵上添加一个畸变参数γ,该参数用来表示像素坐标系两个坐标轴的畸变大小。则内参矩阵K变为

2.张正友相机标定法简介
张正友相机标定法是张正友教授1998年提出的单平面棋盘格的相机标定方法。传统标定法的标定板是需要三维的,需要非常精确,这很难制作,而张正友教授提出的方法介于传统标定法和自标定法之间,但克服了传统标定法需要的高精度标定物的缺点,而仅需使用一个打印出来的棋盘格就可以。同时也相对于自标定而言,提高了精度,便于操作。因此张氏标定法被广泛应用于计算机视觉方面。
1.计算外参
2.计算内参
3.最大似然估计
4.径向畸变估计
该方法将位于棋盘格上的点的Z坐标都设为0,即
再利用homographic(共线方程)进行单应性变换,得到棋盘平面Π和图像平面π的单应矩阵H。再根据上式可得出将棋盘格所在的平面映射到相机的成像平面的方程 p=Hp 其中p为棋盘格所成像的像点坐标,P棋盘格角点在世界坐标系的坐标。
将上面两个等式进行整合,则可以得到单应矩阵H和相机矩阵(包含内参和外参)的相等:

对于张正友棋盘标定法的详解可以参考:张正友标定算法原理详解.
3.标定流程
1.打印棋盘格
2.针对棋盘格拍摄照片
3.在图片中检测特征点(Harris角点)
4.根据角点位置信息及图像中的坐标,求解Homographic矩阵
5.利用解析解估算方法计算出5个内部参数,以及6个外部参数
6.根据极大似然估计策略,设计优化目标并实现参数的refinement
4.实现相机标定
4.1标定板注意事项:
将打印出的纸固定放到一个平板上,使用同一相机从不同的位置,不同的角度,拍摄标定板的多张照片(10-20张最佳),将照片放到文件夹中:
本次实验用了9*6为大小的棋盘格。并用荣耀10拍摄了12张不同位置、不同角度的图像。

需要注意标定板的大小是标定板在水平和竖直方向上内角点的个数。内角点指的是,标定板上不挨着边界的角点(如图我的实验标定板大小为6×4)。
4.2 实现代码
import cv2
import numpy as np
import glob
# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#棋盘格模板规格
w = 9 #内角点个数,内角点是和其他格子连着的点,w是长度,h是宽度,w内角点个数,内角点是连接两个格子以上的
h = 6
# 世界坐标系中的棋盘格点,例如(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('picture/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 找到棋盘格角点
# 棋盘图像(8位灰度或彩色图像) 棋盘尺寸 存放角点的位置
ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
# 如果找到足够点对,将其存储起来
if ret == True:
# 角点精确检测
# 输入图像 角点初始坐标 搜索窗口为2*winsize+1 死区 求角点的迭代终止条件
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(1000)
cv2.destroyAllWindows()
#标定、去畸变
# 输入:世界坐标系里的位置 像素坐标 图像的像素尺寸大小 3*3矩阵,相机内参数矩阵 畸变矩阵
# 输出:标定结果 相机的内参数矩阵 畸变系数 旋转矩阵 平移向量
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# mtx:内参数矩阵
# dist:畸变系数
# rvecs:旋转向量 (外参数)
# tvecs :平移向量 (外参数)
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('picture/5.jpg')
h,w = img2.shape[:2]
# 我们已经得到了相机内参和畸变系数,在将图像去畸变之前,
# 我们还可以使用cv.getOptimalNewCameraMatrix()优化内参数和畸变系数,
# 通过设定自由自由比例因子alpha。当alpha设为0的时候,
# 将会返回一个剪裁过的将去畸变后不想要的像素去掉的内参数和畸变系数;
# 当alpha设为1的时候,将会返回一个包含额外黑色像素点的内参数和畸变系数,并返回一个ROI用于将其剪裁掉
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)
# 反投影误差
# 通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。
# 通过之前计算的内参数矩阵、畸变系数、旋转矩阵和平移向量,使用cv2.projectPoints()计算三维点到二维图像的投影,
# 然后计算反投影得到的点与图像上检测到的点的误差,最后计算一个对于所有标定图像的平均误差,这个值就是反投影误差。
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))
4.3 实现结果
1.角点检测结果(图片太多,展示部分差异较大的图片检测结果)



2.标定结果:
手机型号:荣耀10
ret: 0.4815201056807158
内参数矩阵:

畸变系数dist:

旋转向量:

平移向量:

对5.jpg畸变矫正后的图片:

误差:

5.总结
(1)一般情况下,需要把打印的棋盘纸张平整的放在板子上,拍出来的照片和显示图像及物理点和世界点存在的误差才会很理想;如果测试的图像反投影误差很大是因为棋盘纸张很扭曲,拍出来的图片会有曲线,矩阵之间的转换就存在很大误差,所以一定要注意棋盘格纸张拍照时要放平整。
(2)通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。通过之前计算的内参数矩阵、畸变系数、旋转矩阵和平移向量,使用cv2.projectPoints()计算三维点到二维图像的投影,然后计算反投影得到的点与图像上检测到的点的误差,最后计算一个对于所有标定图像的平均误差,这个值就是反投影误差。
