【鱼眼镜头10】等距Equidistant模型的Kannala-Brandt模型【opencv的鱼眼标定使用的模型、kalibr中的 pinhole + equidistant 】
在之前的博客,说明了:为了从全向相机捕获的图像中提取有用的信息,我们需要知道光线如何从三维空间映射到二维图像平面。这就是投影模型的作用。一个准确的投影模型可以帮助我们更准确地估计场景中物体的位置、姿态和其他属性。常见的鱼眼相机基本成像模型主要有四种,它们分别是等距投影(最广泛)、等立体角投影、体视投影、正交投影。镜头的设计基本是按照上述四种投影模型而制作的,可看出鱼眼相机成像模型通用性较差。为了解
一、源头论文
论文:A Generic Camera Model and Calibration Method for Conventional, Wide-Angle, and Fish-Eye Lenses
二、工程化
Kannala Brandt 模型、 opencv中的fisheye::calibrate、kalibr中的 pinhole + equidistant 都是指该模型。
- opencv
- https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
- kalibr
- https://github.com/ethz-asl/kalibr/wiki/supported-models
三、介绍
本论文介绍了一种可以建模传统相机和鱼眼相机的通用的内参模型(radially symmetric projection model)
- 由于镜头的制造工艺等,并不是所有的鱼眼相机都是Radially Symmetric,因此作者又额外建模了径向畸变和切向畸变(14个参数),p9 + 14 = p23
- 但在各大开源工具对该模型的复现中,一般只用p9模型,并不用p23,可能是因为如今的镜头工艺比较成熟了。
四、先说:鱼眼镜头的四种投影模型
在之前的博客【鱼眼镜头1】鱼眼镜头的四种投影模型(指导镜头的设计),中央镜头综述,说明了投影模型的重要性:为了从全向相机捕获的图像中提取有用的信息,我们需要知道光线如何从三维空间映射到二维图像平面。这就是投影模型的作用。一个准确的投影模型可以帮助我们更准确地估计场景中物体的位置、姿态和其他属性。
常见的鱼眼相机基本成像模型主要有四种,它们分别是等距投影(最广泛)、等立体角投影、体视投影、正交投影。镜头的设计基本是按照上述四种投影模型而制作的。
Equidistant模型(等距投影模型)是一种常用的鱼眼相机投影模型。在这个模型中,图像半径rd
与入射角Θ
(光线与相机光轴的夹角)之间的关系是线性的,即rd = f * Θ
,其中f
是相机的焦距。这个模型假设在图像平面上,沿各个方向上的距离都是等比例缩放的。
对于实际的鱼眼镜头来说,由于制造和设计的限制,它们不可能完全精确地按照某个特定的投影模型来设计。因此,为了更准确地描述鱼眼相机的成像过程,需要使用更复杂的模型或近似方法。为了提高提高标定的准确性:Kannala提出了一种鱼眼相机的一般多项式近似模型。这个模型使用多项式来近似描述图像半径rd
与入射角Θ
之间的关系。这种方法的优点是可以更灵活地适应不同鱼眼相机的成像特性,提高标定的准确性。
总结:使用Equidistant模型可以简化鱼眼相机的成像过程,但实际的鱼眼镜头可能无法完全遵循这个模型。为了更准确地描述鱼眼相机的成像过程,可以使用更复杂的模型或近似方法,如Kannala的多项式近似模型。
五、投影过程:3D到2D投影
Kannala 的多项式近似模型:
-
Kannala 提出了一种更灵活的模型,用多项式来近似描述 r d rd rd 和 Θ \Theta Θ 的关系。
-
公式可以写成: r d = f ⋅ ( Θ + k 1 Θ 3 + k 2 Θ 5 + ⋯ ) rd = f \cdot (\Theta + k_1 \Theta^3 + k_2 \Theta^5 + \cdots) rd=f⋅(Θ+k1Θ3+k2Θ5+⋯),其中 k 1 , k 2 k_1, k_2 k1,k2 是多项式系数。
-
奇函数和泰勒级数展开:
- Θ d \Theta_d Θd 是 Θ \Theta Θ 的奇函数,也就是说 Θ d ( − Θ ) = − Θ d ( Θ ) \Theta_d(-\Theta) = -\Theta_d(\Theta) Θd(−Θ)=−Θd(Θ)。这是因为鱼眼相机的成像过程在光轴的两侧是对称的。
- 通过泰勒级数展开, Θ d \Theta_d Θd 可以表示成 Θ \Theta Θ 的奇次多项式: Θ d = Θ + k 1 Θ 3 + k 2 Θ 5 + ⋯ \Theta_d = \Theta + k_1 \Theta^3 + k_2 \Theta^5 + \cdots Θd=Θ+k1Θ3+k2Θ5+⋯。奇次多项式在原点附近是对称的,这与鱼眼相机的成像特性相符。
Kannala-Brandt(KB)模型是一种通用的鱼眼相机标定模型,适用于大视场角(FOV)镜头的几何畸变建模。其核心思想是将3D空间点通过非线性的角度-半径映射投影到2D图像平面。以下是一个具体的3D到2D投影过程示例及步骤说明:
1. 3D点定义
假设有一个世界坐标系下的3D点 P w = [ X w , Y w , Z w ] T \mathbf{P}_w = [X_w, Y_w, Z_w]^T Pw=[Xw,Yw,Zw]T,需通过以下步骤投影到图像平面:
-
转换到相机坐标系
通过相机外参(旋转矩阵 R \mathbf{R} R 和平移向量 t \mathbf{t} t)将 P w \mathbf{P}_w Pw 转换到相机坐标系:
P c = R P w + t = [ X c , Y c , Z c ] T \mathbf{P}_c = \mathbf{R} \mathbf{P}_w + \mathbf{t} = [X_c, Y_c, Z_c]^T Pc=RPw+t=[Xc,Yc,Zc]T -
归一化到单位球面
计算点 P c \mathbf{P}_c Pc 在单位球面上的投影:
P s = P c ∥ P c ∥ = [ x s , y s , z s ] T , ∥ P c ∥ = X c 2 + Y c 2 + Z c 2 \mathbf{P}_s = \frac{\mathbf{P}_c}{\|\mathbf{P}_c\|} = [x_s, y_s, z_s]^T, \quad \|\mathbf{P}_c\| = \sqrt{X_c^2 + Y_c^2 + Z_c^2} Ps=∥Pc∥Pc=[xs,ys,zs]T,∥Pc∥=Xc2+Yc2+Zc2
2. 球面到图像平面的映射(KB模型核心)
KB模型的关键是通过角度 θ \theta θ(点与光轴的夹角)的非线性函数 r ( θ ) r(\theta) r(θ) 定义投影:
-
计算角度 θ \theta θ
θ = arccos ( z s ) = arccos ( Z c ∥ P c ∥ ) \theta = \arccos(z_s) = \arccos\left(\frac{Z_c}{\|\mathbf{P}_c\|}\right) θ=arccos(zs)=arccos(∥Pc∥Zc) -
多项式畸变模型
KB模型使用多项式展开描述 r ( θ ) r(\theta) r(θ):
r ( θ ) = k 1 θ + k 2 θ 3 + k 3 θ 5 + k 4 θ 7 + … r(\theta) = k_1 \theta + k_2 \theta^3 + k_3 \theta^5 + k_4 \theta^7 + \dots r(θ)=k1θ+k2θ3+k3θ5+k4θ7+…
其中 k 1 , k 2 , … k_1, k_2, \dots k1,k2,… 为标定参数,通常取前4项。 -
归一化平面到图像平面
将单位球面上的点 [ x s , y s ] [x_s, y_s] [xs,ys] 映射到归一化图像平面:
p n = [ x s , y s ] T x s 2 + y s 2 ⋅ r ( θ ) = [ u n , v n ] T \mathbf{p}_n = \frac{[x_s, y_s]^T}{\sqrt{x_s^2 + y_s^2}} \cdot r(\theta) = [u_n, v_n]^T pn=xs2+ys2[xs,ys]T⋅r(θ)=[un,vn]T
(注:方向由 [ x s , y s ] [x_s, y_s] [xs,ys] 决定,距离由 r ( θ ) r(\theta) r(θ) 决定) -
添加内参变换
通过相机内参(焦距 f x , f y f_x, f_y fx,fy、主点 c x , c y c_x, c_y cx,cy、倾斜系数 s s s)得到最终像素坐标:
3. 具体数值示例
假设:
- 相机坐标系点 P c = [ 0.2 , 0.3 , 0.8 ] T \mathbf{P}_c = [0.2, 0.3, 0.8]^T Pc=[0.2,0.3,0.8]T
- KB参数: k 1 = 1.0 , k 2 = − 0.1 , k 3 = 0.01 k_1 = 1.0, k_2 = -0.1, k_3 = 0.01 k1=1.0,k2=−0.1,k3=0.01
- 内参: f x = f y = 500 f_x = f_y = 500 fx=fy=500, c x = 320 c_x = 320 cx=320, c y = 240 c_y = 240 cy=240, s = 0 s = 0 s=0
步骤计算:
-
归一化到单位球面:
∥ P c ∥ = 0. 2 2 + 0. 3 2 + 0. 8 2 = 0.874 \|\mathbf{P}_c\| = \sqrt{0.2^2 + 0.3^2 + 0.8^2} = 0.874 ∥Pc∥=0.22+0.32+0.82=0.874
P s = [ 0.229 , 0.343 , 0.915 ] T \mathbf{P}_s = [0.229, 0.343, 0.915]^T Ps=[0.229,0.343,0.915]T -
计算 θ \theta θ:
θ = arccos ( 0.915 ) ≈ 0.414 radians \theta = \arccos(0.915) \approx 0.414 \text{ radians} θ=arccos(0.915)≈0.414 radians -
计算 r ( θ ) r(\theta) r(θ):
r ( θ ) = 1.0 ⋅ 0.414 − 0.1 ⋅ ( 0.414 ) 3 + 0.01 ⋅ ( 0.414 ) 5 ≈ 0.407 r(\theta) = 1.0 \cdot 0.414 - 0.1 \cdot (0.414)^3 + 0.01 \cdot (0.414)^5 \approx 0.407 r(θ)=1.0⋅0.414−0.1⋅(0.414)3+0.01⋅(0.414)5≈0.407 -
归一化平面坐标:
p n = [ 0.229 , 0.343 ] T 0.22 9 2 + 0.34 3 2 ⋅ 0.407 ≈ [ 0.183 , 0.274 ] T \mathbf{p}_n = \frac{[0.229, 0.343]^T}{\sqrt{0.229^2 + 0.343^2}} \cdot 0.407 \approx [0.183, 0.274]^T pn=0.2292+0.3432[0.229,0.343]T⋅0.407≈[0.183,0.274]T -
像素坐标:
u = 500 ⋅ 0.183 + 320 ≈ 411.5 v = 500 ⋅ 0.274 + 240 ≈ 377.0 u = 500 \cdot 0.183 + 320 \approx 411.5 \\ v = 500 \cdot 0.274 + 240 \approx 377.0 u=500⋅0.183+320≈411.5v=500⋅0.274+240≈377.0
最终投影点: ( 411.5 , 377.0 ) (411.5, 377.0) (411.5,377.0)。
4. 关键点总结
- KB模型通过角度 θ \theta θ 的多项式函数建模非线性畸变,适用于鱼眼镜头。
- 投影过程包含:坐标系转换→单位球面归一化→角度计算→多项式畸变→内参变换。
- 实际应用中需通过标定获取 k i k_i ki 和内参。
class SVCCalibrationSDK:
def __init__(self, intrinsic_param, width, height):
self.image_size = np.array([height, width]) # rows, cols
self.world2cam = np.array(intrinsic_param["world2cam"]).reshape(-1, 1)
self.world2cam_len = np.array(intrinsic_param["world2cam_len"])
self.svc_rotation = np.array([[1., intrinsic_param["affine_e"]],
[intrinsic_param["affine_d"], intrinsic_param["affine_c"]]])
self.svc_translation = np.array(intrinsic_param["center"])
def cam_to_pixel(self, points):
num_points = len(points)
if num_points == 0:
return np.empty((0, 3))
else:
norm = np.sqrt(np.sum(points[:, 0:2] * points[:, 0:2], axis=1, keepdims=True))
theta = np.arctan(points[:, 2:3] * (-1 / norm))
poly_theta = np.power(theta, np.arange(self.world2cam_len))
rho = (poly_theta @ self.world2cam)
pixels = (points[:, 0:2] * (rho / norm)) @ self.svc_rotation + self.svc_translation
return pixels
2D到3D投影
参考
https://zhuanlan.zhihu.com/p/532501102
https://blog.csdn.net/j879159541/article/details/125400727
更多推荐
所有评论(0)