OpenCV-Python系列之相机校准实践
上个教程已经谈到,为了校准摄像头,我们至少需要10种测试模式。现在我们使用示例图片:
用同一相机从不同的位置,不同的角度,拍摄标定板的多张照片(10-20张最佳),将照片放到文件夹中:
设想一张棋盘的图像,需要用于校准摄像头最重要的输入数据是3D真实世界点的集合以及图像中这些点的相应2D坐标。2D图像点我们得能够轻易从图像中找出来。(这些图像点是棋盘中两个黑色方块相互接触的位置)
那现实世界空间的3D点怎么来呢?这些图像是从一个静止的摄像头上取下来的,并且棋盘被放置成了不同的位置和方向。所以我们得知道(X,Y,Z)值。但是为了简单,我们可以说棋盘在XY平面保持静止(所以 Z总是等于0),并且摄像头也相应的移动了。这样的考虑方式帮助我们可以只算出XY值。现在对于X,Y值,我们可以简单的传入点,比如(0,0), (1,0), (2,0), ... 用于表示点的位置。在这种情况下,我们得到的结果将是棋盘方格的放缩后的大小。但如果我们知道方格尺寸(比方说 30 mm),我们可以传入这样的值(0,0), (30,0), (60,0), ... . 于是我们得到的结果就是mm为单位的。(当前情况下,我们不知道方格的尺寸,因为我们没有取那些图像,所以我们按照方格尺寸放缩的模式传参)。
3D 点被称为是object points 对象点,而2D 图像上的点被称为 image points 图象点。
实现步骤:
1、为了找到棋盘格模板,可使用OpenCV中的函数cv2.findChessboardCorners()。需要告诉程序标明模板是何规格,在这里我们使用的是13x11的棋盘格,含有12x10的内部角点。这个函数如果检测到模板,会返回对应的角点,并返回true。当然不一定所有的图像都能找到需要的模板,所以我们可以使用多幅图像进行定标。
2、找到角点后,我们可以使用cv2.cornerSubPix()可以得到更为准确的角点像素坐标。我们也可以使用cv2.drawChessboardCorners()将角点绘制到图像上显示。
3、通过前面的步骤,得到了用于标定的三维点和与其对应的图像上的二维点对。我们使用cv2.calibrateCamera()进行标定,这个函数会返回标定结果、相机的内参数矩阵、畸变系数、旋转矩阵和平移向量。
4、通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。通过之前计算的内参数矩阵、畸变系数、旋转矩阵和平移向量,使用cv2.projectPoints()计算三维点到二维图像的投影,然后计算反投影得到的点与图像上检测到的点的误差,最后计算一个对于所有标定图像的平均误差,这个值就是反投影误差。
5、使用cv2.undistort()方法去除畸变
相关函数的参数解释在这里不再详细说明。
来看代码:
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 * 4, 3), np.float32) objp[:, :2] = np.mgrid[0:6, 0:4].T.reshape(-1, 2) # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y obj_points = [] # 存储3D点 img_points = [] # 存储2D点 images = glob.glob("D:/Python/ComputerView/test1/*.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, 4), 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(10) 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("-----------------------------------------------------") img = cv2.imread(images[2]) h, w = img.shape[:2] newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))#显示更大范围的图片(正常重映射之后会删掉一部分图像) print (newcameramtx) dst = cv2.undistort(img,mtx,dist,None,newcameramtx) x,y,w,h = roi dst1 = dst[y:y+h,x:x+w] cv2.imwrite('D:/Python/ComputerView/test1/calibresult3.jpg', dst1) print ("dst的大小为:", dst1.shape)
结果:
角点检测结果:
内参数矩阵:
畸变系数dist:
旋转向量:
平移向量:
畸变后内参矩阵:
最终畸变矫正后的图片: