1. 项目背景与核心价值
在数字图像处理领域,白平衡(White Balance)是个绕不开的基础课题。我十年前第一次接触工业相机时,就曾被不同光源下颜色失真的问题困扰——日光灯下拍出的产品照片泛青,白炽灯环境下又偏黄,这种色偏会直接影响后续的图像分析精度。传统做法是靠软件算法后期校正,但实时性始终是个瓶颈。直到接触FPGA后,才发现用硬件实现白平衡不仅能突破速度限制,还能大幅降低系统功耗。
FPGA并行处理的特性特别适合做像素级操作。一个1080P的图像帧包含超过200万个像素点,CPU需要逐个处理,而FPGA可以同时对整行像素进行流水线操作。以Xilinx Artix-7系列为例,在100MHz时钟下就能实现60fps的全高清实时处理,延迟控制在毫秒级,这对工业检测、医疗内窥镜等场景至关重要。
2. 白平衡算法选型与优化
2.1 灰度世界算法硬件化改造
最经典的灰度世界算法(Gray World)假设整幅图像的平均反射光是无色的,即R、G、B三个通道均值应该相等。软件实现很简单:
python复制avg_r = np.mean(img[:,:,0])
avg_g = np.mean(img[:,:,1])
avg_b = np.mean(img[:,:,2])
scale_r = avg_g / avg_r
scale_b = avg_g / avg_b
img[:,:,0] = np.clip(img[:,:,0] * scale_r, 0, 255)
img[:,:,2] = np.clip(img[:,:,2] * scale_b, 0, 255)
但直接移植到FPGA会遇到三个挑战:
- 整帧统计需要存储所有像素,消耗大量Block RAM
- 除法运算在硬件中成本极高
- 浮点乘法会大幅增加DSP资源占用
我们的解决方案:
- 流水线统计:在像素输入时同步累加RGB分量,用32位累加器防止溢出
- 倒数查表法:预先计算1/avg_r和1/avg_b的定点数,用18x18 DSP做乘法
- 移位替代除法:当scale值可表示为2^n时,用移位寄存器代替乘法器
2.2 完美反射算法硬件加速
另一种完美反射算法(Perfect Reflection)假设图像中最亮区域就是白色参考点。FPGA实现时需要:
- 设计滑动窗口求局部最大值
- 实现RGB三通道的快速排序
- 对前5%的亮像素取均值
在Verilog中我们采用三级流水线结构:
verilog复制// 第一级:3x3窗口最大值滤波
always @(posedge clk) begin
window[0] <= {r_in, g_in, b_in};
for(int i=0; i<8; i++)
window[i+1] <= window[i];
max_val <= (window[4] > max_val) ? window[4] : max_val;
end
// 第二级:阈值比较与累加
always @(posedge clk) begin
if(window[4] > max_val * 0.95) begin
sum_r <= sum_r + window[4][23:16];
sum_g <= sum_g + window[4][15:8];
sum_b <= sum_b + window[4][7:0];
cnt <= cnt + 1;
end
end
// 第三级:计算缩放系数
always @(posedge clk) begin
avg_r <= sum_r / cnt;
avg_g <= sum_g / cnt;
avg_b <= sum_b / cnt;
end
3. FPGA架构设计与资源优化
3.1 并行流水线架构
采用典型的"三明治"结构:
code复制[摄像头接口] → [像素缓存] → [白平衡核心] → [伽马校正] → [DDR控制器]
↑ ↑
[统计模块] [系数寄存器]
关键时序约束:
tcl复制create_clock -period 10 [get_ports clk]
set_input_delay -clock clk 2 [get_ports {pixel_data*}]
set_output_delay -clock clk 3 [get_ports {corrected_data*}]
3.2 定点数精度优化
经过实测发现,缩放系数用Q4.12格式(4位整数+12位小数)既能保证精度又节省资源:
| 数据格式 | 最大误差 | LUT消耗 | DSP消耗 |
|---|---|---|---|
| Q2.14 | 0.1% | 243 | 8 |
| Q4.12 | 0.3% | 187 | 4 |
| Q8.8 | 1.2% | 156 | 2 |
经验提示:人眼对色差的感知阈值约5%,Q4.12格式完全够用
4. 实际效果与性能对比
4.1 主观质量评估
使用X-Rite ColorChecker测试卡,在不同色温光源下对比:
| 光源色温 | 原始图像 | 软件处理(ms) | FPGA处理(ms) | ΔE2000色差 |
|---|---|---|---|---|
| 2800K | 偏橙红 | 12.3 | 0.8 | 3.2 |
| 5000K | 轻微偏青 | 11.7 | 0.8 | 2.1 |
| 6500K | 基本正常 | 10.9 | 0.8 | 1.8 |
4.2 资源占用报告
在Xilinx xc7a100t器件上的实现结果:
| 模块 | LUT | FF | DSP | BRAM |
|---|---|---|---|---|
| 统计计算 | 423 | 587 | 2 | 0 |
| 系数计算 | 678 | 932 | 4 | 1 |
| 像素校正 | 1254 | 1567 | 8 | 0 |
| 总计 | 2355 | 3086 | 14 | 1 |
| 可用资源 | 63400 | 126800 | 240 | 135 |
5. 工程实践中的坑与经验
5.1 时序收敛难题
最初设计时遇到setup violation问题,关键路径出现在缩放乘法器。通过以下优化解决:
- 将一级18x18乘法拆分为两级9x9乘法
- 对系数寄存器插入pipeline
- 使用寄存器复制降低扇出
5.2 动态范围处理
当图像中存在过曝区域时,直接应用白平衡会导致颜色失真。我们的应对策略:
verilog复制// 检测过曝像素
wire over_exposed = (r_in > 250) && (g_in > 250) && (b_in > 250);
// 过曝像素保持原值
assign r_out = over_exposed ? r_in : (r_in * scale_r) >> 12;
assign g_out = over_exposed ? g_in : g_in;
assign b_out = over_exposed ? b_in : (b_in * scale_b) >> 12;
5.3 自动/手动模式切换
为适应不同场景,设计双模式控制:
- 自动模式:每帧更新白平衡系数
- 手动模式:固定使用预设系数(通过AXI-Lite配置)
实现时需要注意系数切换时的平滑过渡,避免画面闪烁:
verilog复制// 系数平滑过渡
reg [15:0] current_scale_r;
always @(posedge clk) begin
if(mode_auto)
current_scale_r <= (current_scale_r * 3 + new_scale_r) / 4;
else
current_scale_r <= manual_scale_r;
end
6. 扩展应用与优化方向
当前设计在Artix-7上功耗仅0.8W,未来可尝试:
- 结合自动曝光算法实现联合优化
- 增加色温估计输出(如生成DNG文件中的As Shot Neutral标签)
- 移植到UltraScale+器件利用AI引擎实现自适应白平衡
实测中发现一个有趣现象:当处理4K视频时,由于数据吞吐量剧增,DDR带宽会成为瓶颈。这时可以采用区域统计法——只对图像中心区域计算白平衡系数,在画质和性能间取得平衡。
