一、简述
在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。
在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定(或摄像机标定)
无论是在图像测量或者机器视觉应用中,相机参数的标定都是非常关键的环节,其标定结果的精度及算法的稳定性直接影响相机工作产生结果的准确性。因此,做好相机标定是做好后续工作的前提,提高标定精度是科研工作的重点所在。
二、原理
• 同步标定内部参数和外部参数,一般包括两种策略s:
- 光学标定: 利用已知的几何信息(如定长棋盘格)实 现参数求解。
- 自标定: 在静态场景中利用 structure from motion估算参数
标定参数线性回归
• 通过空间中已知坐标的(特征)点 (Xi,Yi,Zi),以及它 们在图像中的对应坐标 (ui,vi),直接估算 11 个待 求解的内部和外部参数。
借助单应性变换
优点: 所有的相机参数集中在一个矩阵中,便于求解 –通过矩阵可以直接描述世界坐标中的三维点,到二维 图像平面中点的映射关系。
缺点:无法直接得知具体的内参数和外参数 –求解出的11个未知量,比待标定参数(9个)更多。带 来了参数不独立/相关的问题 –对噪声/误差敏感
标定参数非线性优化
用概率的视角去看最小二乘问题
特征点投影方程为
本次实验主要是根据张正友相机标定法的原理实现
• 2D 图像点:
• 3D 空间点:
• 齐次坐标:
• 描述空间坐标到图像坐标的映射
• s: 世界坐标系到图像坐标系的尺度因子
• K: 相机内参矩阵
• (u0,v0): 像主点坐标
• α, β: 焦距与像素横纵比的融合
• γ: 径向畸变参数
不妨设棋盘格位于Z = 0
定义旋转矩阵R的第i列为 ri,
于是空间到图像的映射可改为:
• 其中H 是描述Homographic矩阵,可通过最小二乘 ,从角点世界坐标到图像坐标的关系求解
令 H 为 H = [h1 h2 h3]
• Homography 有 8 个自由度,
• 通过上述等式的矩阵运算,根据r1和r2正交,以及 归一化的约束可以得到如下等式:
而在Calibration方面
B 是对称阵,其未知量可表示为6D 向量 b
设H中的第i列为 hi
根据b的定义,可以推导出如下公式
可以推导出
• 如果有n组观察图像,则V 是 2n x 6 的矩阵
• 根据最小二乘定义,V b = 0 的解是 VTV 最小特 征值对应的特征向量。
• 因此, 可以直接估算出 b,后续可以通过b求解内参
• 当观测平面 n ≥ 3 时,可以得到b的唯一解
• 当 n = 2时, 一般可令畸变参数γ = 0
• 当 n = 1时, 仅能估算出α 与 β, 此时一般可假定像 主点坐标 u0 与 v0 为0
外部参数可通过Homography求解,由 H = [h1 h2 h3] = λK[r1 r2 t],可推出
一般而言,求解出的R = [r1 r2 r3] 不会满足正交与 归一的标准
在实际操作中,R 可以通过SVD分解实现规范化( 详见原文)
最终
给定 n 张棋盘格图像,每张图像有 m 个角点
最小化下述公式等同于极大似然估计
上述非线性优化问题可以利用Levenberg-Marquardt 算法求解
三、实验要求
1.制作棋盘格(每个格子的大小可测量)
2.根据棋盘格,采集10-20张图片,提取角点
3.解算出内外参数,内参截图,外部参数最好能可视化。
四、实验代码
# -*- 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((6 * 6, 3), np.float32)
objp[:, :2] = np.mgrid[0:6, 0:6].T.reshape(-1, 2) # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
obj_points = [] # 存储3D点
img_points = [] # 存储2D点
images = glob.glob("life2/*.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, (6, 6), 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, (6,6), corners, ret) # 记住,OpenCV的绘制函数一般无返回值
i+=1;
cv2.imwrite('conimg'+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:\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)
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)
五、实验结果与分析
我实验的数据集如下:
手机型号为小米6后置摄像头
实现相机的标定,运行结果如下:
内参数据如下:
mtx为内参数矩阵,dist为畸变系数:
rvecs为旋转向量:
tvecs为平移向量:
外参数据如下:
1.以相机为中心:
2.以棋盘格为中心:
六、总结
通过标定得到小米6的后置摄像头的内置参数矩阵为: