计算机视觉(四)相机标定
摄像机几何

① 世界坐标系与相机坐标系
② 相机坐标系与图像物理坐标系
该系统能够实现三维坐标系向二维坐标系的转换,并基于几何投影模型建立两个坐标系之间的关联
③ 图像物理坐标系与图像像素坐标系
首先通过一个具象化的例子来阐述两个不同坐标系统的区别性特征。其中物理坐标系统被定义为一种连续性概念其计量基准是以毫米单位来衡量类似于电影院内某观众所处的具体空间位置(3.4,5.9);而像素坐标系统则被定位为一种离散性概念其计量基准限定于整数坐标的值类似于观众在电影院内的座位安排属于第几排第几列的位置信息。值得注意的是这两种不同的测量基准系统其零点设定也存在显著差异其中物理坐标系统的零点被设定在摄像机光轴与图像平面物空间坐标的交点位置即所谓的主点;而像素坐标的零点则固定在其所代表的数字图像矩阵的第一行第一列位置上。
整体的变换为:

R为旋转变换矩阵,T为平移变换矩阵,K为内参数矩阵
内参数矩阵有五个自由度

a等于焦距乘以与x方向相关联的像素缩放因子;b等于焦距乘以与y方向相关联的像素缩放因子;o表示在非正交的像素坐标系中旋转的角度;cx和cy分别代表在x轴和y轴方向上的偏移量
标定板的选择
对于棋盘标定板而言,在完成初步检测后能够实现对角点位置的精确测定。由于几何学中鞍点本质上具有无限小的特性,在透视变换或镜头失真过程中这种特性使其保持无偏性。然而,在OpenCV框架中整个棋盘必须在所有图像中可见才能被检测到 这往往会导致仅从图像边缘提取信息变得具有挑战性 这些区域往往能成为可靠的信息来源 因为它们能够适当地约束镜头失真模型
圆形标定板:通过使用圆周上的所有像素进行定位计算,能够实现高精度的圆确定位,并且有效降低图像中的噪声干扰。然而,在相机成像过程中,则会将原本完美的圆形物体表现为椭圆形。
张正友标定
相机标定过程是基于像素坐标系与世界坐标系之间的关系,并遵循一系列限制条件,以求得相机内外参数及变形系数的过程
但是传统标定法所使用的标定板具有三维特性,并且需要高度精确才能完成制作过程,在实际操作中存在较大的困难性与挑战性;张正友教授提出的方法作为传统标定法与自标定法的折中方案,在继承传统标定法优势的同时去除了对高精度标定物的需求这一局限性;相较于自标定方法而言,在精度上有了显著提升,并且操作流程更加简便直观;因此这种方法被广泛应用于计算机视觉领域中

使用opencv的代码实现
import cv2
import numpy as np
import glob
# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#棋盘格模板规格
w = 14 #内角点个数,内角点是和其他格子连着的点
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 = ['/Users/apple/Desktop/相机标定/7.jpg','/Users/apple/Desktop/相机标定/8.jpg','/Users/apple/Desktop/相机标定/9.jpg','/Users/apple/Desktop/相机标定/10.jpg'] # 标定所用图像
for fname in images:
img = cv2.imread(fname)
#print(img)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 找到棋盘格角点
# 棋盘图像(8位灰度或彩色图像) 棋盘尺寸 存放角点的位置
ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
# 如果找到足够点对,将其存储起来
print(ret)
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()
print(objpoints, imgpoints)
#标定、去畸变
# 输入:世界坐标系里的位置 像素坐标 图像的像素尺寸大小 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('/Users/apple/Desktop/相机标定/1.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))
由于OpenCV中的findChessboardCorners函数对于图像质量有较高的敏感度,在实际应用中往往会导致检测结果不准确或出现空值/布尔值false的情况。

经过一段时间的学习和研究,在OpenCV 4.0中发现findChessboardCornersSB函数的鲁棒性明显增强,并且可能有效解决了当前遇到的问题
但是这里直接使用matlib来做


最后导出相机参数

