【surround-view-system-introduction学习笔记】day 1: 项目介绍和目录结构
·

车辆全景环视项目解析

一、项目概述
本项目是一个基于 Python + OpenCV + PyQt5 实现的车辆全景环视系统(Surround View System)。该系统通过四个鱼眼摄像头采集车辆四周图像,经过畸变校正、透视投影、图像拼接等处理步骤,最终生成一幅完整的俯视鸟瞰图,为驾驶员提供360度无死角的视野。
系统特性
- 实时处理:支持四路摄像头实时采集和拼接
- 鱼眼校正:基于 OpenCV 鱼眼相机模型进行畸变校正
- 自适应亮度:自动平衡四路图像的亮度差异
- 色彩校正:白平衡处理确保色彩一致性
- 平滑拼接:基于距离的渐变权重融合重叠区域
- 多平台支持:支持 USB 摄像头和 CSI 摄像头(通过 GStreamer)
二、目录结构
surround-view-system-introduction-master/
├── doc/ # 文档目录
│ ├── img/ # 文档配图
│ └── en.md # 英文文档
├── images/ # 测试图像
│ ├── front.png # 前视图像
│ ├── back.png # 后视图像
│ ├── left.png # 左视图像
│ ├── right.png # 右视图像
│ └── car.png # 车辆图标
├── surround_view/ # 核心模块
│ ├── __init__.py # 模块初始化
│ ├── base_thread.py # 线程基类
│ ├── birdview.py # 鸟瞰图拼接
│ ├── capture_thread.py # 图像采集线程
│ ├── fisheye_camera.py # 鱼眼相机模型
│ ├── imagebuffer.py # 线程安全缓冲区
│ ├── param_settings.py # 参数配置
│ ├── process_thread.py # 图像处理线程
│ ├── simple_gui.py # GUI工具
│ ├── structures.py # 数据结构
│ └── utils.py # 工具函数
├── yaml/ # 相机参数文件
│ ├── front.yaml # 前视相机参数
│ ├── back.yaml # 后视相机参数
│ ├── left.yaml # 左视相机参数
│ └── right.yaml # 右视相机参数
├── run_calibrate_camera.py # 相机标定脚本
├── run_get_projection_maps.py # 投影标定脚本
├── run_get_weight_matrices.py # 权重计算脚本
├── run_live_demo.py # 实时演示脚本
└── test_cameras.py # 相机测试脚本
目录功能说明
| 目录 | 功能 | 重要性 |
|---|---|---|
surround_view/ |
核心模块,包含所有类和工具函数 | 核心 |
yaml/ |
存储相机内参、畸变系数、投影矩阵 | 必需 |
images/ |
测试用图像和车辆图标 | 可选 |
doc/ |
项目文档和分析报告 | 参考 |
三、核心功能实现
3.1 相机标定
功能:获取鱼眼相机的内参矩阵和畸变系数
技术原理:
- 使用棋盘格标定板
- 基于 OpenCV 的
cv2.fisheye.calibrate()实现 - 支持普通相机和鱼眼相机两种模式
标定流程:
- 采集多张棋盘格图像
- 检测角点并亚像素精化
- 执行标定计算
- 保存参数到 yaml 文件
# 标定核心代码
ret, mtx, dist, rvecs, tvecs = cv2.fisheye.calibrate(
objpoints, imgpoints, (W, H), K, D,
rvecs, tvecs, calibration_flags,
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-6)
)
3.2 畸变校正
功能:校正鱼眼镜头的径向畸变
技术原理:
- 使用 OpenCV 的
cv2.fisheye.initUndistortRectifyMap()预计算映射表 - 实时校正使用
cv2.remap()进行快速映射
关键参数:
scale_xy:校正后图像的缩放比例shift_xy:校正后图像的平移量
3.3 透视投影
功能:将校正后的图像投影到俯视视角
技术原理:
- 使用单应性变换(Homography)
- 通过手动选择四个特征点计算投影矩阵
- 使用
cv2.getPerspectiveTransform()获取单应矩阵
投影流程:
原始图像 → 畸变校正 → 透视投影 → 方向翻转 → 鸟瞰图区域
3.4 图像拼接
功能:将四路投影图像合成为完整鸟瞰图
鸟瞰图区域划分:
┌───────────────────┐
│ FL │ F │ FR │ ← 前视相机
├──────┼─────┼──────┤
│ L │ C │ R │ ← 左/右视相机
├──────┼─────┼──────┤
│ BL │ B │ BR │ ← 后视相机
└──────┴─────┴──────┘
- 单相机区域(F、B、L、R):直接复制对应相机的中心区域
- 融合区域(FL、FR、BL、BR):使用渐变权重融合相邻相机的重叠部分
- 车辆区域(C):覆盖车辆图标
3.5 亮度平衡
功能:调整四路图像的亮度使其一致
算法原理:
- 在四个重叠区域计算相邻相机的亮度比率
- 求解全局亮度调整系数(几何平均)
- 对各通道应用非线性调整
调整公式:
全局亮度系数: t = (a × b × c × d)^0.25
各相机调整系数: x = t / (d/a)^0.5
非线性调整函数:
def tune(x):
if x >= 1:
return x * np.exp((1 - x) * 0.5)
else:
return x * np.exp((1 - x) * 0.8)
3.6 权重计算
功能:计算重叠区域的渐变权重矩阵
算法原理:
- 获取两幅图像的重叠区域掩码
- 提取各自的非重叠部分边界
- 对重叠区域每个像素计算到两个边界的距离
- 根据距离计算权重:
G = dB² / (dA² + dB²)
权重特性:
- 靠近图像 A 边界的像素,A 的权重趋近于 1
- 靠近图像 B 边界的像素,B 的权重趋近于 1
- 中间区域平滑过渡
四、代码架构
4.1 线程架构
系统采用多线程架构实现实时处理:
采集层 (CaptureThread × 4)
↓
同步层 (MultiBufferManager)
↓
处理层 (CameraProcessingThread × 4)
↓
同步层 (ProjectedImageBuffer)
↓
拼接层 (BirdView)
↓
输出层 (显示/保存)
| 线程类型 | 数量 | 职责 |
|---|---|---|
| CaptureThread | 4 | 从摄像头采集图像帧 |
| CameraProcessingThread | 4 | 畸变校正、投影、翻转 |
| BirdView | 1 | 图像拼接、亮度平衡、白平衡 |
4.2 核心类关系
BaseThread (线程基类)
├── CaptureThread (采集线程)
├── CameraProcessingThread (处理线程)
└── BirdView (拼接线程)
FisheyeCameraModel (相机模型)
└── CameraProcessingThread (使用)
Buffer (缓冲区)
├── MultiBufferManager (管理采集缓冲区)
└── ProjectedImageBuffer (管理投影缓冲区)
4.3 关键类职责
| 类名 | 文件 | 职责 |
|---|---|---|
FisheyeCameraModel |
fisheye_camera.py | 相机参数管理、畸变校正、投影变换 |
BirdView |
birdview.py | 图像拼接、亮度平衡、白平衡 |
CaptureThread |
capture_thread.py | 摄像头采集、帧同步 |
CameraProcessingThread |
process_thread.py | 单路图像预处理 |
MultiBufferManager |
imagebuffer.py | 采集线程同步 |
ProjectedImageBuffer |
birdview.py | 处理线程同步 |
Buffer |
imagebuffer.py | 线程安全队列 |
五、算法详解
5.1 鱼眼相机模型
畸变模型:
x' = x(1 + k1*r² + k2*r⁴ + k3*r⁶ + k4*r⁸)
y' = y(1 + k1*r² + k2*r⁴ + k3*r⁶ + k4*r⁸)
其中 r² = x² + y²,k1~k4 为畸变系数。
校正步骤:
- 预计算映射表:
cv2.fisheye.initUndistortRectifyMap() - 实时校正:
cv2.remap()
5.2 透视投影
单应变换:
[u, v, w] = H × [x, y, 1]
x' = u/w
y' = v/w
其中 H 是 3×3 的单应矩阵,通过四个对应点计算得到。
5.3 图像融合
渐变融合公式:
result = imA × G + imB × (1 - G)
其中 G 是权重矩阵(0 ≤ G ≤ 1)。
权重计算步骤:
# 获取重叠区域
overlap_mask = get_overlap_region_mask(imA, imB)
# 获取边界多边形
polyA = get_outmost_polygon_boundary(imA_diff)
polyB = get_outmost_polygon_boundary(imB_diff)
# 逐像素计算权重
for y, x in indices:
distToA = cv2.pointPolygonTest(polyA, (x, y), True)
distToB = cv2.pointPolygonTest(polyB, (x, y), True)
G[y, x] = distToB**2 / (distToA**2 + distToB**2)
5.4 白平衡
算法原理:
# 分离通道
B, G, R = cv2.split(image)
# 计算均值
m1 = np.mean(B)
m2 = np.mean(G)
m3 = np.mean(R)
# 计算校正系数
K = (m1 + m2 + m3) / 3
c1 = K / m1
c2 = K / m2
c3 = K / m3
# 调整亮度
B = adjust_luminance(B, c1)
G = adjust_luminance(G, c2)
R = adjust_luminance(R, c3)
# 合并通道
result = cv2.merge((B, G, R))
六、运行流程
6.1 离线标定流程
Step 1: 相机内参标定
└── python run_calibrate_camera.py -i 0 -grid 9x6 -fisheye
↓
输出: yaml/camera_params.yaml
Step 2: 投影矩阵标定(需执行4次,对应四个相机)
└── python run_get_projection_maps.py -camera front -scale 0.7 0.8 -shift -150 -100
↓
输出: yaml/front.yaml (追加投影参数)
Step 3: 权重矩阵计算
└── python run_get_weight_matrices.py
↓
输出: weights.png, masks.png, result.png
6.2 实时运行流程
python run_live_demo.py
↓
1. 加载相机模型 (4个)
2. 创建采集线程 (4个)
3. 创建处理线程 (4个)
4. 创建拼接线程 (1个)
5. 启动所有线程
6. 主循环显示结果

七、技术亮点
7.1 线程同步机制
采用 Barrier 同步模式确保四路相机帧同步:
def sync(self, device_id):
self.mutex.lock()
if device_id in self.sync_devices:
self.arrived += 1
if self.arrived == len(self.sync_devices):
# 最后一个到达,唤醒所有线程
self.buffer.add(self.current_frames)
self.wc.wakeAll()
else:
# 等待其他线程
self.wc.wait(self.mutex)
self.arrived -= 1
self.mutex.unlock()
7.2 预计算优化
- 畸变校正映射表:提前计算,避免实时计算开销
- 权重矩阵:离线计算并保存,实时运行时直接加载
7.3 自适应亮度平衡
自动检测并调整四路图像的亮度差异,无需手动参数调整。
八、应用场景
- 车载环视系统:为驾驶员提供360度全景视野
- 自动驾驶:作为环境感知的视觉输入
- 机器人导航:为移动机器人提供周围环境的俯视图
- 安防监控:多个摄像头的全景拼接
九、总结
本项目实现了一个完整的车辆全景环视系统,涵盖了从相机标定到实时拼接的完整流程。核心技术包括:
- 鱼眼畸变校正:基于 OpenCV 鱼眼相机模型
- 透视投影变换:手动标定获取单应矩阵
- 多线程架构:实现高效的实时处理
- 自适应亮度平衡:自动调整图像亮度
- 渐变权重融合:平滑拼接重叠区域

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)