鱼眼相机模型

1. 模型概述

鱼眼相机通过特殊镜头获得超大视场角,水平或对角视场角可接近或超过 180 ∘ 180^\circ 180∘。普通针孔模型采用透视投影,当视角接近 180 ∘ 180^\circ 180∘ 时,像平面坐标会趋于无穷,因此不能直接准确描述鱼眼镜头。

鱼眼模型通常把入射光线与光轴之间的夹角映射为图像半径:

ρ = f   g ( θ ) \rho=f\,g(\theta) ρ=fg(θ)

其中:

  • θ \theta θ:空间光线与相机光轴之间的夹角;
  • ρ \rho ρ:投影点到主点的图像半径;
  • f f f:尺度或等效焦距;
  • g ( θ ) g(\theta) g(θ):鱼眼投影函数。

2. 坐标系统

2.1 世界坐标系

空间点表示为:

P w = X w Y w Z w T \mathbf{P}_w= \begin{bmatrix} X_w & Y_w & Z_w \end{bmatrix}^{T} Pw=XwYwZwT

2.2 相机坐标系

通过外参将世界坐标转换到相机坐标:

P c = R P w + t \mathbf{P}_c=\mathbf{R}\mathbf{P}_w+\mathbf{t} Pc=RPw+t

其中:

  • R ∈ S O ( 3 ) \mathbf{R}\in SO(3) R∈SO(3):旋转矩阵;
  • t ∈ R 3 \mathbf{t}\in\mathbb{R}^{3} t∈R3:平移向量;
  • P c = X c , Y c , Z c T \mathbf{P}_c=X_c,Y_c,Z_c^T Pc=Xc,Yc,ZcT。

2.3 像素坐标系

像素点表示为:

p = u v T \mathbf{p}= \begin{bmatrix} u & v \end{bmatrix}^{T} p=uvT

通常以图像左上角为原点, u u u 向右, v v v 向下。


3. OpenCV 鱼眼投影模型

3.1 归一化透视坐标

对于相机坐标系中的三维点:

a = X c Z c , b = Y c Z c a=\frac{X_c}{Z_c},\qquad b=\frac{Y_c}{Z_c} a=ZcXc,b=ZcYc

定义:

r = a 2 + b 2 r=\sqrt{a^2+b^2} r=a2+b2

光线夹角为:

θ = arctan ⁡ ( r ) \theta=\arctan(r) θ=arctan(r)

3.2 Kannala--Brandt 角度多项式

OpenCV cv::fisheye 使用四个畸变系数描述角度映射:

θ d = θ ( 1 + k 1 θ 2 + k 2 θ 4 + k 3 θ 6 + k 4 θ 8 ) \theta_d= \theta \left( 1+k_1\theta^2+k_2\theta^4+k_3\theta^6+k_4\theta^8 \right) θd=θ(1+k1θ2+k2θ4+k3θ6+k4θ8)

畸变后的归一化坐标为:

x d = θ d r a , y d = θ d r b x_d=\frac{\theta_d}{r}a,\qquad y_d=\frac{\theta_d}{r}b xd=rθda,yd=rθdb

当 r r r 接近 0 时,应使用极限值处理,避免除零。

3.3 映射到像素坐标

相机内参矩阵为:

K = f x α f x c x 0 f y c y 0 0 1 \mathbf{K}= \begin{bmatrix} f_x & \alpha f_x & c_x\\ 0 & f_y & c_y\\ 0 & 0 & 1 \end{bmatrix} K= fx00αfxfy0cxcy1

最终像素坐标:

u = f x ( x d + α y d ) + c x u=f_x(x_d+\alpha y_d)+c_x u=fx(xd+αyd)+cx

v = f y y d + c y v=f_y y_d+c_y v=fyyd+cy

其中:

  • f x , f y f_x,f_y fx,fy:横向和纵向焦距,单位为像素;
  • c x , c y c_x,c_y cx,cy:主点坐标;
  • α \alpha α:倾斜系数,工程中通常固定为 0;
  • k 1 , k 2 , k 3 , k 4 k_1,k_2,k_3,k_4 k1,k2,k3,k4:鱼眼畸变系数。

完整转换链路:

text 复制代码
世界三维点 Pw
   ↓ 外参 R、t
相机三维点 Pc
   ↓ 归一化
透视坐标 a、b
   ↓ 计算入射角 θ
鱼眼角度映射 θd
   ↓ 半径缩放
畸变归一化坐标 xd、yd
   ↓ 内参 K
像素坐标 u、v

4. 常见鱼眼投影类型

投影模型 半径映射 特点 常见用途
等距投影 ρ = f θ \rho=f\theta ρ=fθ 角度与图像半径成正比 全景、机器人、运动相机
等立体角投影 ρ = 2 f sin ⁡ ( θ / 2 ) \rho=2f\sin(\theta/2) ρ=2fsin(θ/2) 保持单位面积对应的立体角关系 测光、天空成像
体视投影 ρ = 2 f tan ⁡ ( θ / 2 ) \rho=2f\tan(\theta/2) ρ=2ftan(θ/2) 保角性较好,边缘拉伸明显 视觉测量、特殊全景
正射投影 ρ = f sin ⁡ θ \rho=f\sin\theta ρ=fsinθ 边缘压缩明显,投影范围有限 监控、半球视场
多项式模型 ρ = f   g ( θ ) \rho=f\,g(\theta) ρ=fg(θ) 能拟合真实镜头,精度较高 工业标定、OpenCV 鱼眼模型

真实镜头通常不严格满足单一理想投影函数,因此工程中常用多项式模型或统一相机模型拟合。


5. 鱼眼模型与针孔模型对比

对比项 针孔相机 鱼眼相机
基本映射 r = f tan ⁡ θ r=f\tan\theta r=ftanθ r = f   g ( θ ) r=f\,g(\theta) r=fg(θ)
典型视场角 通常小于 120 ∘ 120^\circ 120∘ 可接近或超过 180 ∘ 180^\circ 180∘
图像边缘 透视拉伸 弯曲和径向压缩明显
直线性质 三维直线通常投影为二维直线 非过光心直线常投影为曲线
常用标定模型 Brown--Conrady Kannala--Brandt、统一相机模型
典型场景 测量、普通检测、双目 环视、全景、机器人导航、监控

6. 逆投影:像素到空间射线

逆投影用于把像素恢复为相机坐标系中的一条空间射线。

6.1 像素归一化

忽略倾斜系数时:

x d = u − c x f x , y d = v − c y f y x_d=\frac{u-c_x}{f_x},\qquad y_d=\frac{v-c_y}{f_y} xd=fxu−cx,yd=fyv−cy

r d = x d 2 + y d 2 r_d=\sqrt{x_d^2+y_d^2} rd=xd2+yd2

6.2 求解入射角

根据鱼眼模型求解:

r d = θ ( 1 + k 1 θ 2 + k 2 θ 4 + k 3 θ 6 + k 4 θ 8 ) r_d= \theta \left( 1+k_1\theta^2+k_2\theta^4+k_3\theta^6+k_4\theta^8 \right) rd=θ(1+k1θ2+k2θ4+k3θ6+k4θ8)

该非线性方程通常使用牛顿迭代求解 θ \theta θ。

6.3 恢复单位射线

令:

ϕ = atan2 ⁡ ( y d , x d ) \phi=\operatorname{atan2}(y_d,x_d) ϕ=atan2(yd,xd)

则单位方向向量为:

d = sin ⁡ θ cos ⁡ ϕ sin ⁡ θ sin ⁡ ϕ cos ⁡ θ \mathbf{d}= \begin{bmatrix} \sin\theta\cos\phi\\ \sin\theta\sin\phi\\ \cos\theta \end{bmatrix} d= sinθcosϕsinθsinϕcosθ

单个像素只能恢复一条空间射线。必须结合双目视差、深度网络、激光雷达、地面平面或已知尺寸,才能恢复唯一三维点。


7. 标定参数

鱼眼相机标定通常输出:

text 复制代码
图像尺寸:
  width, height

内参:
  fx, fy, cx, cy, alpha

畸变:
  k1, k2, k3, k4

每张标定图外参:
  rvec, tvec

质量指标:
  RMS 重投影误差
  每幅图重投影误差
  边缘区域误差
  参数稳定性

推荐 YAML 格式:

yaml 复制代码
camera_model: fisheye
image_width: 1920
image_height: 1080

camera_matrix:
  rows: 3
  cols: 3
  data: [fx, 0.0, cx,
         0.0, fy, cy,
         0.0, 0.0, 1.0]

distortion_model: equidistant
distortion_coefficients:
  rows: 1
  cols: 4
  data: [k1, k2, k3, k4]

8. 鱼眼标定流程

  1. 准备棋盘格、圆点阵列或 ChArUco 标定板;
  2. 从图像中心、边缘和四角采集不同姿态;
  3. 保证标定板覆盖尽可能大的视场;
  4. 检测角点并进行亚像素优化;
  5. 初始化内参矩阵和四个鱼眼畸变参数;
  6. 最小化所有图像的重投影误差;
  7. 剔除模糊、遮挡或误差过大的图像;
  8. 重新标定并检查参数稳定性;
  9. 生成去畸变或立体校正映射;
  10. 使用真实场景直线和边缘区域验证。

采集建议

  • 标定板应覆盖图像中心、边缘和四角;
  • 不要只采集正对相机的图像;
  • 包含俯仰、偏航、滚转和不同距离;
  • 固定焦距、分辨率、裁剪模式和 ISP 配置;
  • 关闭动态防抖、数字变焦和动态裁剪;
  • 避免运动模糊、强反光、过曝和角点遮挡;
  • 标定板尺寸必须准确,并保持平整。

9. 去畸变与视场控制

去畸变不是简单地恢复"原始场景",而是将鱼眼图像重新投影到目标相机模型。

输出效果受新相机矩阵和输出尺寸影响:

  • 新焦距变大:中心区域放大,保留视场减小;
  • 新焦距变小:保留更多视场,但黑边增多;
  • 输出透视图:适合检测和测量,但边缘拉伸明显;
  • 输出柱面图:适合水平全景;
  • 输出球面图:适合全景拼接和环视;
  • 输出多个透视视图:适合机器人环视检测和 BEV 感知。

OpenCV 常用处理链:

cpp 复制代码
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(
    K, D, image_size, R, K_new,
    balance, new_size, fov_scale);

cv::fisheye::initUndistortRectifyMap(
    K, D, R, K_new, new_size,
    CV_16SC2, map1, map2);

cv::remap(
    src, dst, map1, map2,
    cv::INTER_LINEAR,
    cv::BORDER_CONSTANT);

其中:

  • balance 控制裁剪与视场保留的平衡;
  • fov_scale 调整输出视场;
  • map1map2 可预计算,适合视频实时处理。

10. OpenCV C++ 标定接口

cpp 复制代码
#include <opencv2/calib3d.hpp>
#include <opencv2/imgproc.hpp>

std::vector<std::vector<cv::Point3f>> object_points;
std::vector<std::vector<cv::Point2f>> image_points;

cv::Mat K = cv::Mat::eye(3, 3, CV_64F);
cv::Mat D = cv::Mat::zeros(4, 1, CV_64F);
std::vector<cv::Vec3d> rvecs;
std::vector<cv::Vec3d> tvecs;

int flags =
    cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC |
    cv::fisheye::CALIB_CHECK_COND |
    cv::fisheye::CALIB_FIX_SKEW;

cv::TermCriteria criteria(
    cv::TermCriteria::COUNT + cv::TermCriteria::EPS,
    100,
    1e-8);

double rms = cv::fisheye::calibrate(
    object_points,
    image_points,
    image_size,
    K,
    D,
    rvecs,
    tvecs,
    flags,
    criteria);

11. 重投影误差

对于第 i i i 个标定点,观测像素为 p i \mathbf{p}_i pi,模型投影为 p ^ i \hat{\mathbf{p}}_i p^i:

e i = ∥ p i − p ^ i ∥ 2 e_i= \left\| \mathbf{p}_i-\hat{\mathbf{p}}_i \right\|_2 ei=∥pi−p^i∥2

均方根误差:

E R M S = 1 N ∑ i = 1 N e i 2 E_{\mathrm{RMS}}= \sqrt{ \frac{1}{N} \sum_{i=1}^{N}e_i^2 } ERMS=N1i=1∑Nei2

不能只检查全局 RMS,还应统计:

  • 图像中心区域误差;
  • 图像边缘和四角误差;
  • 每张标定图平均误差;
  • 最大误差和 P95 误差;
  • 不同距离、姿态下的误差;
  • 去畸变后直线的弯曲残差。

12. 工程注意事项

12.1 分辨率变化

纯缩放图像时,可按比例调整:

f x ′ = s x f x , f y ′ = s y f y , c x ′ = s x c x , c y ′ = s y c y f_x'=s_xf_x,\quad f_y'=s_yf_y,\quad c_x'=s_xc_x,\quad c_y'=s_yc_y fx′=sxfx,fy′=syfy,cx′=sxcx,cy′=sycy

鱼眼畸变系数通常保持不变。

若分辨率变化来自传感器裁剪、binning、数字防抖、不同读出区域或不同 ISP 模式,则主点和有效成像关系可能改变,应重新进行投影验证,必要时重新标定。

12.2 模型不要混用

  • 普通相机模型使用 cv::calibrateCamera
  • OpenCV 鱼眼模型使用 cv::fisheye::calibrate
  • 两套模型的畸变参数定义不同;
  • 不应把普通模型的 k 1 , k 2 , p 1 , p 2 , k 3 k_1,k_2,p_1,p_2,k_3 k1,k2,p1,p2,k3 直接传入鱼眼接口;
  • ROS 中常把 OpenCV 鱼眼模型标记为 equidistant

12.3 边缘误差

鱼眼图像边缘区域对参数更敏感。若标定板只出现在中心区域,即使全局 RMS 较小,边缘投影仍可能严重偏差。

12.4 检测与分割

将整幅鱼眼图强行展开为透视图,会改变边缘目标的尺寸和形状。常见方案包括:

  • 直接使用原始鱼眼图训练模型;
  • 将鱼眼图切分为多个透视视图;
  • 使用柱面或球面特征;
  • 在原始鱼眼坐标中检测,再反投影为空间射线;
  • 为 BEV 感知预计算像素到射线查找表。

13. 机器人融合感知应用

13.1 环视感知

多颗鱼眼相机可覆盖机器人周围近 360 ∘ 360^\circ 360∘ 区域,用于:

  • 近场障碍物检测;
  • 环视拼接;
  • 可行驶区域分割;
  • 泊车和低速导航;
  • 视觉里程计与定位;
  • BEV 特征构建。

13.2 相机---激光雷达融合

text 复制代码
激光雷达三维点
   ↓ 外参变换
鱼眼相机坐标系三维点
   ↓ 鱼眼投影模型
鱼眼像素坐标
   ↓ 图像边界与遮挡检查
语义、颜色或特征关联

不能使用普通针孔投影直接代替鱼眼投影,否则边缘区域会产生明显投影偏差。

13.3 鱼眼双目

鱼眼双目需要:

  1. 左右相机分别完成鱼眼内参标定;
  2. 求解左右相机外参;
  3. 使用鱼眼立体校正;
  4. 生成适合立体匹配的目标投影图;
  5. 在校正图上计算视差;
  6. 使用校正模型恢复三维点。

OpenCV 常用接口:

cpp 复制代码
cv::fisheye::stereoCalibrate(...);
cv::fisheye::stereoRectify(...);

14. 推荐模块接口

输入结构

cpp 复制代码
struct FisheyeCameraModelInput {
    cv::Mat image;
    cv::Mat K;
    cv::Mat D;
    cv::Mat R;
    cv::Mat t;
    cv::Size image_size;
};

输出结构

cpp 复制代码
struct FisheyeProjectionResult {
    cv::Point2f pixel;
    cv::Vec3d camera_ray;
    bool inside_image;
    bool valid_depth;
};

软件模块划分

text 复制代码
FisheyeCalibration
├── PatternDetector
├── CornerRefiner
├── CalibrationSolver
├── ReprojectionEvaluator
└── CalibrationSerializer

FisheyeCameraModel
├── ProjectPoint
├── UnprojectPixel
├── DistortPoint
├── UndistortPoint
├── BuildRemapTable
└── ValidateProjection

FisheyeFusion
├── LidarToCamera
├── CameraToPixel
├── OcclusionCheck
├── SemanticAssociation
└── ProjectionVisualizer

15. 总结

鱼眼相机模型的核心不是在针孔模型后简单增加普通径向畸变,而是建立:

空间光线角度 → 鱼眼图像半径 → 像素坐标 \text{空间光线角度} \rightarrow \text{鱼眼图像半径} \rightarrow \text{像素坐标} 空间光线角度→鱼眼图像半径→像素坐标

完整工程链路应包含:

  1. 准确的鱼眼投影和逆投影模型;
  2. 内参、畸变和外参标定;
  3. 像素到空间射线的转换;
  4. 可控视场的去畸变和重映射;
  5. 中心与边缘分区误差验证;
  6. 分辨率和传感器模式一致性检查;
  7. 与激光雷达、IMU、双目和 BEV 模块的坐标统一。

参考资料

  1. OpenCV Documentation --- Fisheye camera model

    https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html

  2. Juho Kannala, Sami S. Brandt, A Generic Camera Model and Calibration Method for Conventional, Wide-Angle, and Fish-Eye Lenses, IEEE TPAMI, 2006.

相关推荐
学术小李2 天前
图形学中的世界、相机、Clip(裁剪)、NDC等坐标系
数码相机
2601_957418802 天前
如何开发一个相机照片传输App?从设备连接到图片上传完整流程解析
数码相机
浪里镖客5 天前
Python调用海康MV-CU013-AUOM(USB3.0连接)工业相机录制视频
数码相机
QYR-分析5 天前
手持声学相机行业增速稳健 国产突围开启产业新格局
数码相机
gaosushexiangji6 天前
基于高速3D-DIC的风力叶片旋转工况下应变测量与振动模态分析
数码相机
格林威7 天前
工业相机Chunk功能全解析:图像嵌入时间戳、编码器元数据(附堡盟C#代码)
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机
XMAIPC_Robot7 天前
RK3588四路1080P工业MIPI相机视觉网关,同时支持RK3588+FPGA的24路相机同步方案
人工智能·数码相机
吐了啊取名字太难8 天前
美颜系统AI修图本地跑并支持Mac、win、安卓、iOS不卡顿
android·人工智能·windows·数码相机·mac·ai编程
科技圈快迅8 天前
科美锐(Komery)新手入门数码相机产品梳理:百元到千元,不同价位实际能买到什么
数码相机