计算机视觉——相机参数标定
1、相机标定法原理
在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何关系位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些模型参数就是相机参数。在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数(内参,外参,畸变参数)的过程就称之为相机标定。
一般来说,标定的过程分为两个部分:
第一步:从世界坐标系转换为相机坐标系,这一步是三维点到三维点的转换,包括 R,t (相机外参)等参数;
第二步:从相机坐标系转为图像坐标系,这一步是三维点到二维点的转换,包括 K(相机内参)等参数;
针孔相机成像原理其实就是利用投影将真实的三维世界坐标转换到二维的相机坐标上去,其模型示意图如下图所示:
从示意图中可以看出,在世界坐标中的一条直线上的点在相机上只呈现出了一个点,其中发生了非常大的变化,同时也损失很多重要的信息,这也成了我们3D重建、目标检测与识别领域的重点和难点。
2、相机标定的意义
- 在机器视觉领域,相机的标定是一个关键的环节,它决定了机器视觉系统能否有效的定位,能否有效的计算目标物。
- 相机的标定基本上可以分为两种:
第一种是相机的自标定;是相机拍摄周围物体,通过数字图像处理的方法和相关的几何计算得到相机参数,但是这种方法标定的结果误差较大,不适合于高精度应用场合。
第二种是依赖于标定参照物的标定方法。是通过标定参照物,由相机成像,并通过数字图像处理的方法,以及后期的空间算术运算计算相机的内参和外参。这种方法标定的精度高,适用于对精度要求高的应用场合。
3、实验要求
1.制作棋盘格(每个格子的大小可测量);
2.根据棋盘格,采集10-20张图片,提取角点;
3.解算出内外参数,内参截图放在博客中,外部参数最好能可视化。
4、实验内容
4.1 数据集
- 该数据集是通过将8*8棋盘格投影到电脑屏幕上,然后用VIVO手机拍摄的20张图片 ,格子大小为1.8cm*1.8cm。
4.2 代码实现
# coding=utf-8
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((7 * 7, 3), np.float32)
objp[:, :2] = np.mgrid[0:7, 0:7].T.reshape(-1, 2) # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
obj_points = [] # 存储3D点
img_points = [] # 存储2D点
images = glob.glob("C:/Users/Administrator/Desktop/棋盘格/IMG_20200407_163457/*.jpg")
i=0
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
size = gray.shape[::-1]
ret, corners = cv2.findChessboardCorners(gray, (7, 7), None)
#print(corners)
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, (7, 7), corners, ret) # 记住,OpenCV的绘制函数一般无返回值
i+=1
cv2.imwrite('C:/Users/Administrator/Desktop/棋盘格/IMG_20200407_163457/'+str(i)+'.jpg', img)
cv2.waitKey(1500)
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:", mtx) # 内参数矩阵
print("dist:", dist) # 畸变系数 distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print("rvecs:", rvecs) # 旋转向量 # 外参数
print("tvecs:", tvecs ) # 平移向量 # 外参数
print("-----------------------------------------------------")
img = cv2.imread(images[2])
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))#显示更大范围的图片(正常重映射之后会删掉一部分图像)
print (newcameramtx)
print("------------------使用undistort函数-------------------")
dst = cv2.undistort(img,mtx,dist,None,newcameramtx)
x,y,w,h = roi
dst1 = dst[y:y+h,x:x+w]
cv2.imwrite('calibresult3.jpg', dst1)
print ("方法一:dst的大小为:", dst1.shape)
4、实验结果
4.1 内参矩阵mtx
4.2 畸变系数dist
4.3 旋转向量rvecs
4.4 平移向量tvecs
4.5 相机内置参数矩阵
5、分析与总结
- 由于拍摄角度的不同,所以每一张图片的外参也不相同。
- 反投影误差可以反映结果的好坏,反投影误差越接近0,表明结果越好。
- 内参矩阵只关心相机坐标和图像坐标之间的关系,与相机的绝对尺寸无关。投影面的位置与其尺寸相关。