目录
- 一、相机模型
- 1、相机与图像
- 2、坐标系
- 1、世界坐标系
- 2、相机坐标系
- 3、图像物理坐标系
- 4、图像像素坐标系
- 3、相机成像
- 4、世界坐标系到摄像机坐标系
- 5、欧氏变换
- 6、齐次坐标
- 7、摄像机坐标系到图像物理坐标系
- 8、图像物理坐标系到图像像素坐标系
- 9、摄像机坐标系到图像像素坐标系
- 10、世界坐标系到图像像素坐标系
- 11、相机成像原理
- 二、镜头畸变
- 1、镜头畸变
- 2、径向畸变
- 3、径向畸变
- 4、切向畸变
- 5、畸变矫正
- 6、透视变换
- 7、代码实现矫正
一、相机模型
1、相机与图像
2、坐标系
针孔相机模型存在四个坐标系:世界坐标系、摄像机坐标系、图像物理坐标系和图像像素坐标系。
1、世界坐标系
是客观三维世界的绝对坐标系,也称客观坐标系。就是物体在真实世界中的坐标。世界坐标系是随着物体的大小和位置变化的,单位是长度单位。
2、相机坐标系
以相机的光心为坐标系的原点,以平行于图像的 x 和 y 方向为 x 轴和 y 轴,z 轴和光轴平行,x,y,z 互相垂直,单位是长度单位。
3、图像物理坐标系
以主光轴和图像平面交点为坐标原点,x’ 和 y’ 方向如图所示,单位是长度单位。
4、图像像素坐标系
以图像的顶点为坐标原点,u 和 v 方向平行于 x’ 和 y’ 方向,单位是以像素计。
假设:世界坐标系的坐标为 Pw(Xw,Yw,Zw)
对应的摄像机坐标系坐标为 Po(x,y,z)
对应的图像物理坐标系的坐标为 P’(x’,y’)
对应的图像像素坐标系的坐标为 p(u,v)
3、相机成像
4、世界坐标系到摄像机坐标系
这两个坐标系之间除了旋转矩阵 R,还存在平移矩阵 t。其关系可表示为:
[ X c Y c Z c 1 ] = [ R t 0 T 1 ] \begin{bmatrix} X_c \\ Y_c \\ Z_c \\ 1 \end{bmatrix}= \begin{bmatrix} R & t \\ 0^T & 1 \end{bmatrix} XcYcZc1 =[R0Tt1]
[ X Y Z 1 ] = L w [ X Y Z 1 ] \begin{bmatrix} X \\ Y \\ Z \\ 1 \end{bmatrix}=L_w \begin{bmatrix} X \\ Y \\ Z \\ 1 \end{bmatrix} XYZ1 =Lw XYZ1
5、欧氏变换
两部分组成:旋转和平移。
a
′
=
R
a
+
t
a' = Ra+t
a′=Ra+t
6、齐次坐标
多次连续的旋转和平移的情况下,假设我们将向量 a 进行了两次欧氏变换,旋转和平移分别为 R1, t1 和 R2,t2。
分别得到:
b
=
R
1
∗
a
+
t
1
,
c
=
R
2
∗
b
+
t
2
⇒
c
=
R
2
∗
(
R
1
∗
a
+
t
1
)
+
t
2
b = R_1*a + t_1,c = R2*b + t2 \Rightarrow c = R2*(R1*a + t1) + t2
b=R1∗a+t1,c=R2∗b+t2⇒c=R2∗(R1∗a+t1)+t2
[ a ′ 1 ] = [ R t 0 1 ] [ a 1 ] = T [ a 1 ] \begin{bmatrix} a' \\ 1 \end{bmatrix}= \begin{bmatrix} R & t \\ 0 & 1 \end{bmatrix} \begin{bmatrix} a\\ 1 \end{bmatrix}=T \begin{bmatrix} a\\ 1 \end{bmatrix} [a′1]=[R0t1][a1]=T[a1]
b ~ = T 1 a ~ , c ~ = T 2 b ~ ⇒ c ~ = T 2 T 1 a ~ \tilde{b} = T_1 \tilde{a},\tilde{c} = T_2 \tilde{b} \Rightarrow \tilde{c} = T_2T_1 \tilde{a} b~=T1a~,c~=T2b~⇒c~=T2T1a~
7、摄像机坐标系到图像物理坐标系
相似三角形:
{
X
′
=
f
X
c
Z
c
Y
′
=
f
Y
c
Z
c
\begin{cases} X' = f \frac{X_c}{Z_c} \\ Y' = f \frac{Y_c}{Z_c} \end{cases}
{X′=fZcXcY′=fZcYc
矩阵形式为:
Z
c
∗
[
x
y
1
]
=
[
f
0
0
0
f
0
0
0
1
]
∗
[
X
c
Y
c
Z
c
]
Z_c* \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}= \begin{bmatrix} f&0&0\\ 0&f&0\\ 0&0&1 \end{bmatrix}* \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix}
Zc∗
xy1
=
f000f0001
∗
XcYcZc
8、图像物理坐标系到图像像素坐标系
{ u = x d x + u 0 v = y d y + v 0 \begin{cases} u = \frac{x}{dx}+u_0 \\ v = \frac{y}{dy}+v_0 \end{cases} {u=dxx+u0v=dyy+v0
dx 和 dy 表示:x 方向和 y 方向的一个像素分别占多少个(可能是小数)长度单位。
图像物理坐标系到图像像素坐标系单位不同,需要统一。
齐次坐标下:
[
u
v
1
]
=
[
1
d
x
0
u
0
0
1
d
y
v
0
0
0
1
]
[
x
y
1
]
\begin{bmatrix} u \\ v \\ 1 \end{bmatrix}= \begin{bmatrix} \frac{1}{dx}&0&u_0\\ 0&\frac{1}{dy}&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}
uv1
=
dx1000dy10u0v01
xy1
9、摄像机坐标系到图像像素坐标系
Z c ∗ [ x y 1 ] = [ f 0 0 0 f 0 0 0 1 ] ∗ [ X c Y c Z c ] Z_c* \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}= \begin{bmatrix} f&0&0\\ 0&f&0\\ 0&0&1 \end{bmatrix}* \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} Zc∗ xy1 = f000f0001 ∗ XcYcZc
[ u v 1 ] = [ 1 d x 0 u 0 0 1 d y v 0 0 0 1 ] [ x y 1 ] \begin{bmatrix} u \\ v \\ 1 \end{bmatrix}= \begin{bmatrix} \frac{1}{dx}&0&u_0\\ 0&\frac{1}{dy}&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} uv1 = dx1000dy10u0v01 xy1
Z c [ u v 1 ] = [ f d x 0 u 0 0 f d y v 0 0 0 1 ] [ X c Y c Z c ] = [ f x 0 u 0 0 f y v 0 0 0 1 ] [ X c Y c Z c ] = K [ X c Y c Z c ] Z_c \begin{bmatrix} u \\ v \\ 1 \end{bmatrix}= \begin{bmatrix} \frac{f}{dx}&0&u_0\\ 0&\frac{f}{dy}&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix}= \begin{bmatrix} f_x&0&u_0\\ 0&f_y&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix}= K\begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} Zc uv1 = dxf000dyf0u0v01 XcYcZc = fx000fy0u0v01 XcYcZc =K XcYcZc
10、世界坐标系到图像像素坐标系
[ X c Y c Z c ] = R [ X w Y w Z w ] + t \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix}=R \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix}+t XcYcZc =R XwYwZw +t
Z c [ u v 1 ] = [ f d x 0 u 0 0 f d y v 0 0 0 1 ] [ X c Y c Z c ] = [ f x 0 u 0 0 f y v 0 0 0 1 ] [ X c Y c Z c ] = K [ X c Y c Z c ] Z_c \begin{bmatrix} u \\ v \\ 1 \end{bmatrix}= \begin{bmatrix} \frac{f}{dx}&0&u_0\\ 0&\frac{f}{dy}&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix}= \begin{bmatrix} f_x&0&u_0\\ 0&f_y&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix}= K\begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} Zc uv1 = dxf000dyf0u0v01 XcYcZc = fx000fy0u0v01 XcYcZc =K XcYcZc
Z c [ u v 1 ] = K ( R P w + t ) = K ( R [ X w Y w Z w ] + t ) Z_c \begin{bmatrix} u \\ v \\ 1 \end{bmatrix}=K(RP_w+t)= K \left( R \begin{bmatrix} X_w \\ Y_w \\ Z_w \end{bmatrix}+ t \right) Zc uv1 =K(RPw+t)=K R XwYwZw +t
Z c [ u v 1 ] = [ 1 d x 0 u 0 0 1 d y v 0 0 0 1 ] [ f 0 0 0 0 f 0 0 0 0 1 0 ] [ R t 0 T 1 ] [ X Y Z 1 ] Z_c \begin{bmatrix} u \\ v \\ 1 \end{bmatrix}= \begin{bmatrix} \frac{1}{dx}&0&u_0\\ 0&\frac{1}{dy}&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} f&0&0&0\\ 0&f&0&0\\ 0&0&1&0 \end{bmatrix} \begin{bmatrix} R&t\\0^T&1 \end{bmatrix} \begin{bmatrix} X\\Y\\Z\\1 \end{bmatrix} Zc uv1 = dx1000dy10u0v01 f000f0001000 [R0Tt1] XYZ1
11、相机成像原理
二、镜头畸变
1、镜头畸变
透镜由于制造精度以及组装工艺的偏差会引入畸变,导致原始图像的失真。
镜头的畸变分为径向畸变和切向畸变两类。
2、径向畸变
由透镜的形状引起的畸变称为径向畸变,透镜径向畸变后点位的偏移示意图。
3、径向畸变
枕形畸变
桶形畸变
4、切向畸变
切向畸变是由于透镜本身与相机传感器平面(成像平面)或图像平面不平行而产生的。
这种情况多是由于透镜被粘贴到镜头模组上的安装偏差导致。
5、畸变矫正
6、透视变换
透视变换是将图片投影到一个新的视平面,也称作投影映射。
我们常说的仿射变换是透视变换的一个特例。
透视变换的目的就是把现实中为直线的物体,在图片上可能呈现为斜线,通过透视变换转换成直线的变换。
仿射变换,又称为仿射映射,是指在几何中,图像进行从一个向量空间进行一次线性变换和一次平移,变换为到另一个向量空间的过程。
通用的变换公式为:
[
X
Y
Z
]
=
[
a
11
a
12
a
13
a
21
a
22
a
23
a
31
a
32
a
33
]
[
x
y
1
]
\begin{bmatrix} X \\ Y \\ Z \end{bmatrix}= \begin{bmatrix} a_{11}&a_{12}&a_{13} \\ a_{21}&a_{22}&a_{23} \\ a_{31}&a_{32}&a_{33} \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}
XYZ
=
a11a21a31a12a22a32a13a23a33
xy1
下式中的 X,Y 是原始图片坐标(上式的 x,y),对应得到变换后的图片坐标(X’,Y’,Z’)其中 Z’=1:
{
X
′
=
X
Z
Y
′
=
Y
Z
Z
′
=
Z
Z
\begin{cases} X' = \frac{X}{Z} \\ Y' = \frac{Y}{Z}\\ Z' = \frac{Z}{Z} \end{cases}
⎩
⎨
⎧X′=ZXY′=ZYZ′=ZZ
{ X ′ = a 11 x + a 12 y + a 13 a 31 x + a 32 y + a 33 Y ′ = a 21 x + a 22 y + a 23 a 31 x + a 32 y + a 33 Z ′ = 1 \begin{cases} X' = \frac{a_{11}x+a_{12}y+a_{13}}{a_{31}x+a_{32}y+a_{33}} \\ Y' = \frac{a_{21}x+a_{22}y+a_{23}}{a_{31}x+a_{32}y+a_{33}} \\ Z' = 1 \end{cases} ⎩ ⎨ ⎧X′=a31x+a32y+a33a11x+a12y+a13Y′=a31x+a32y+a33a21x+a22y+a23Z′=1
一般地,我们令 a33=1,展开上面公式,得到一个点的情况:
{
a
11
x
+
a
12
y
+
a
13
−
a
31
x
X
′
−
a
32
X
′
y
=
X
′
a
21
x
+
a
22
y
+
a
23
−
a
31
x
Y
′
−
a
32
y
Y
′
=
Y
′
\begin{cases} a_{11}x+a_{12}y+a_{13}-a_{31}xX'-a_{32}X'y=X'\\ a_{21}x+a_{22}y+a_{23}-a_{31}xY'-a_{32}yY'=Y'\\ \end{cases}
{a11x+a12y+a13−a31xX′−a32X′y=X′a21x+a22y+a23−a31xY′−a32yY′=Y′
源点四个坐标分别为 A:(x0,y0),(x1,y1),(x2,y2),(x3,y3)
目标点四个坐标分别为 B:(X’0,Y’0),(X’1,Y’1),(X’2,Y’2),(X’3,Y’3)
求 WarpMatrix 过程
import numpy as np # 导入 NumPy 库,用于数组操作
def WarpPerspectiveMatrix(src, dst):
assert src.shape[0] == dst.shape[0] and src.shape[0] >= 4 # 确保输入的源点和目标点数组行数相等,且不小于4
nums = src.shape[0] # 获取源点数组的行数
A = np.zeros((2 * nums, 8)) # 创建一个全零矩阵 A,用于构建线性方程组,A*warpMatrix=B
B = np.zeros((2 * nums, 1)) # 创建一个全零列向量 B,用于存储目标点的坐标
for i in range(0, nums):
A_i = src[i, :] # 获取源点数组的第i行
B_i = dst[i, :] # 获取目标点数组的第i行
# 构建线性方程组的两行,每行包含8个系数
A[2 * i, :] = [A_i[0], A_i[1], 1, 0, 0, 0, -A_i[0] * B_i[0], -A_i[1] * B_i[0]]
B[2 * i] = B_i[0] # 设置目标点坐标值
A[2 * i + 1, :] = [0, 0, 0, A_i[0], A_i[1], 1, -A_i[0] * B_i[1], -A_i[1] * B_i[1]]
B[2 * i + 1] = B_i[1] # 设置目标点坐标值
A = np.mat(A) # 将 A 转换为矩阵
# 用A.I求出A的逆矩阵,然后与B相乘,求出warpMatrix
warpMatrix = A.I * B # 求出a_11, a_12, a_13, a_21, a_22, a_23, a_31, a_32
warpMatrix = np.array(warpMatrix).T[0] # 将透视变换矩阵转换为数组,并进行转置
warpMatrix = np.insert(warpMatrix, warpMatrix.shape[0], values=1.0, axis=0) # 插入a_33 = 1
warpMatrix = warpMatrix.reshape((3, 3)) # 将数组重新形状为3x3的透视变换矩阵
return warpMatrix # 返回透视变换矩阵
if __name__ == '__main__':
print('warpMatrix')
# 源点和目标点的坐标
src = [[10.0, 457.0], [395.0, 291.0], [624.0, 291.0], [1000.0, 457.0]]
src = np.array(src)
dst = [[46.0, 920.0], [46.0, 100.0], [600.0, 100.0], [600.0, 920.0]]
dst = np.array(dst)
# 调用透视变换矩阵计算函数并打印结果
warpMatrix = WarpPerspectiveMatrix(src, dst)
print(warpMatrix)
# [[-5.01338334e-01 - 1.35357643e+00 5.82386716e+02]
# [-2.41793700e-16 - 4.84035391e+00 1.38781980e+03]
# [7.65500265e-20 - 4.14856327e-03
# 1.00000000e+00]]
7、代码实现矫正
import numpy as np # 导入 NumPy 库,用于数组操作
import cv2 # 导入 OpenCV 库,用于图像处理
img = cv2.imread('img/photo.jpg') # 读取图像文件
result3 = img.copy() # 复制图像,用于显示原始图像和处理后的图像的对比
'''
注意这里src和dst的输入并不是图像,而是图像对应的顶点坐标。
'''
src = np.float32([[207, 151], [517, 285], [17, 601], [343, 731]]) # 源图像中的四个顶点坐标
dst = np.float32([[0, 0], [337, 0], [0, 488], [337, 488]]) # 映射到目标图像中的四个顶点坐标
print(img.shape) # 打印图像的形状信息(高度、宽度、通道数)
m = cv2.getPerspectiveTransform(src, dst) # 获取透视变换矩阵
print("warpMatrix:")
print(m) # 打印透视变换矩阵
result = cv2.warpPerspective(result3, m, (337, 488)) # 应用透视变换到原始图像并获取处理后的图像
cv2.imshow("src", img) # 显示原始图像
cv2.imshow("result", result) # 显示处理后的图像
cv2.waitKey(0) # 等待用户按键退出
记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~