一、源头论文

论文: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,需通过以下步骤投影到图像平面:

  1. 转换到相机坐标系
    通过相机外参(旋转矩阵 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

  2. 归一化到单位球面
    计算点 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=PcPc=[xs,ys,zs]T,Pc=Xc2+Yc2+Zc2


2. 球面到图像平面的映射(KB模型核心)

KB模型的关键是通过角度 θ \theta θ(点与光轴的夹角)的非线性函数 r ( θ ) r(\theta) r(θ) 定义投影:

  1. 计算角度 θ \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(PcZc)

  2. 多项式畸变模型
    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项。

  3. 归一化平面到图像平面
    将单位球面上的点 [ 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]Tr(θ)=[un,vn]T
    (注:方向由 [ x s , y s ] [x_s, y_s] [xs,ys] 决定,距离由 r ( θ ) r(\theta) r(θ) 决定)

  4. 添加内参变换
    通过相机内参(焦距 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

步骤计算:

  1. 归一化到单位球面:
    ∥ 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

  2. 计算 θ \theta θ
    θ = arccos ⁡ ( 0.915 ) ≈ 0.414  radians \theta = \arccos(0.915) \approx 0.414 \text{ radians} θ=arccos(0.915)0.414 radians

  3. 计算 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.00.4140.1(0.414)3+0.01(0.414)50.407

  4. 归一化平面坐标:
    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]T0.407[0.183,0.274]T

  5. 像素坐标:
    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=5000.183+320411.5v=5000.274+240377.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

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐