- 一、针孔照相机模型
- 1.1 针孔相机
- 1.2 坐标转换
- 1.3 像主点偏移
- 1.4 畸变现象
- 1.5 畸变矫正
- 1.6 两类参数
- 二、相机参数标定
- 2.1 实验数据
- 2.2 代码实现
- 2.3 结果
一、针孔照相机模型 1.1 针孔相机
针孔照相机模型(有时称为射影照相机模型)是计算机视觉中广泛使用的照相机模型。对于大多数应用来说,针孔照相机模型简单,并且具有足够的精确度。
针孔相机模型就是把相机简化成小孔成像,在这种模型下,物体的空间坐标和图像坐标之间是线性的关系,因此对相机参数的求解就归结到求解线性方程组上。而相机标定就是确定相机的内部参数和外部参数。
1.2 坐标转换相机将三维世界中的坐标点(单位:米)映射到二维图像平面(单位:像素)
- C C C 点表示相机的中心点,也是相机坐标系的中心点
- Z Z Z 轴表示相机的主轴
- p p p 点所在的平面表示相机的像平面,也就是图片坐标系所在的二维平面
- p p p 点表示主点,主轴与像平面相交的点
- C C C 点到 p p p 点的距离,也就是上图中的 f f f ,即相机的焦距
- 像平面上的 x x x 和 y y y 坐标轴是与相机坐标系上的 X X X 和 Y Y Y 坐标轴互相平行的
- 相机坐标系是以 X X X, Y Y Y, Z Z Z(大写)三个轴组成的且原点在 C C C 点,度量值为米(m)
- 像平面坐标系是以 x x x, y y y(小写)两个轴组成的且原点在 p p p 点,度量值为米(m)
- 图像坐标系一般指图片相对坐标系,在这里可以认为和像平面坐标系在一个平面上,不过原点是在图片的角上,而且度量值为像素的个数(pixel)
其中得到转换公式:
x
=
f
X
Z
,
y
=
f
Y
Z
x = frac{fX}{Z} , y = frac{fY}{Z}
x=ZfX,y=ZfY
可以表示为矩阵计算为:
(
u
v
1
)
∼
(
f
X
f
Y
Z
)
=
[
f
0
0
0
f
0
0
0
1
]
[
1
0
0
0
0
1
0
0
0
0
1
0
]
(
X
Y
Z
1
)
begin{pmatrix} u\ v\ 1\ end{pmatrix} sim begin{pmatrix} fX\ fY\ Z\ end{pmatrix} = begin{bmatrix} f&0&0\ 0&f&0\ 0&0&1\ end{bmatrix} begin{bmatrix} 1&0&0&0\ 0&1&0&0\ 0&0&1&0\ end{bmatrix} begin{pmatrix} X\ Y\ Z\ 1\ end{pmatrix}
⎝⎛uv1⎠⎞∼⎝⎛fXfYZ⎠⎞=⎣⎡f000f0001⎦⎤⎣⎡100010001000⎦⎤⎝⎜⎜⎛XYZ1⎠⎟⎟⎞
由于各种原因,生产原因,个人使用原因等等,相机光心要想都处在原点可能性较小,会发生一定的偏移。如下图,
u
0
u_0
u0 和
v
0
v_0
v0 分别是 x、y方向上的偏移量。相机标定就是确定相机的内部参数和外部参数。这里K表示内参矩阵,接下来我们还要知道外参矩阵。
相机的成像过程实质上是坐标系的转换。但是由于透镜制造精度以及组装工艺的偏差会引入畸变,导致原始图像的失真。镜头的畸变分为径向畸变和切向畸变两类。
- 图像径向畸变:径向畸变是沿着透镜半径方向分布的畸变,产生原因是光线在原理透镜中心的地方比靠近中心的地方更加弯曲,这种畸变在普通廉价的镜头中表现更加明显,径向畸变主要包括桶形畸变和枕形畸变两种。
- 切向畸变:切向畸变是由于透镜本身与相机传感器平面(成像平面)或图像平面不平行而产生的,这种情况多是由于透镜被粘贴到镜头模组上的安装偏差导致。
- 相机内部参数/内方位元素: 焦距、像主点坐标、畸变参数
- 相机外部参数/外方位元素: 旋转、平移
一般情况下,刚体从世界坐标系转换到相机坐标系的过程,可以通过旋转和平移来得到。因此相机的外部参数就包括了旋转、平移矩阵。
import cv2
import numpy as np
import glob
np.set_printoptions(suppress=True)
# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 棋盘格模板规格
w = 11 # 内角点个数,内角点是和其他格子连着的点
h = 7
# 世界坐标系中的棋盘格点,例如(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('./image/*.jpg')
i = 0
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 死区 求角点的迭代终止条件
i += 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.imwrite('h' + str(i) + '.jpg', img)
cv2.waitKey(10)
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(("内参矩阵:n"), mtx) # 内参数矩阵
print(("畸变参数:n"), dist) # 畸变系数 distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print(("旋转向量(外参数):n"), rvecs) # 旋转向量 # 外参数
print(("平移向量(外参数):n"), tvecs) # 平移向量 # 外参数
# 去畸变
img2 = cv2.imread('./image/2.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))
2.3 结果
角点检测结果:
输出参数结果:
原图像
矫正后图像



