4、多曝光图像对齐模块设计

多曝光图像对齐,说白了就是解决「手抖」的问题。你拍三张不同曝光的照片,手稍微一晃,画面就错位了。HDR合成前,必须先把它们对齐到像素级。

我刚开始做这个模块时,觉得软件上跑个特征点匹配挺简单的。但到了RTL里,一切都变了——面积、延迟、流水线,每个都是硬骨头。今天咱们就一块块啃下来。

4.1 基于特征点的运动估计

运动估计的核心思路:找到两幅图像之间的偏移量。我习惯用「特征点+块匹配」的组合方案。纯特征点法硬件开销大,纯块匹配法对旋转敏感。两者结合,效果最好。

关键流程:

  1. 特征点提取——用Harris角点检测,找到图像中纹理丰富的点
  2. 特征点匹配——在参考帧和当前帧之间找对应点对
  3. 运动模型拟合——用RANSAC剔除误匹配,算出全局平移/仿射参数
  4. 块匹配精调——在粗估计基础上,做局部亚像素对齐

我在项目中遇到过一个问题:特征点提取在暗部区域几乎失效。后来加了自适应阈值,根据局部亮度动态调整检测灵敏度,效果立竿见影。

4.2 块匹配算法硬件化

块匹配,说白了就是拿一个模板块在搜索窗口里滑动,找最相似的位置。软件上直接两层for循环搞定,硬件上可不能这么干。

硬件化的核心挑战:

  • 数据复用——每个像素要被读很多次,必须设计行缓存
  • 并行度——多个SAD/SSD计算单元同时跑
  • 搜索策略——全搜索太慢,三步搜索/菱形搜索更实用

我的经验:全搜索虽然简单,但搜索范围一大,面积就爆炸。我一般用三步搜索(TSS),硬件开销只有全搜索的1/5,精度损失在1%以内。

你想想看,一个16×16的块,搜索范围±8,全搜索要算289个位置。三步搜索只需要25个位置。差距就在这里。

4.3 SAD/SSD计算单元设计

SAD(差值和)和SSD(平方差值和)是块匹配的核心算子。SAD硬件开销小,SSD对大偏差更敏感。我一般用SAD做粗匹配,SSD做精匹配。

计算单元的结构:

// SAD计算单元伪代码
module sad_unit #(
    parameter BLOCK_SIZE = 16
)(
    input  [7:0] ref_data [BLOCK_SIZE-1:0],
    input  [7:0] cur_data [BLOCK_SIZE-1:0],
    output [15:0] sad_value
);
    // 并行减法+值+累加
    // 16个像素同时计算,一拍出结果
    genvar i;
    generate
        for (i = 0; i < BLOCK_SIZE; i = i + 1) begin
            assign abs_diff[i] = (ref_data[i] > cur_data[i]) ?
                                 (ref_data[i] - cur_data[i]) :
                                 (cur_data[i] - ref_data[i]);
        end
    endgenerate
    // 加法树累加
    adder_tree #(.WIDTH(8), .NUM(BLOCK_SIZE)) u_adder (
        .din (abs_diff),
        .dout(sad_value)
    );
endmodule

注意:加法树要用流水线结构,否则关键路径会很长。16输入的加法树,至少分4级流水,每级加4个。

SSD计算单元类似,只是把值换成乘法器。乘法器面积大,我建议用查表法近似平方运算,精度损失可接受。

4.4 流水线架构与延迟分析

整个对齐模块的流水线,我分成5级:

流水线级 功能 延迟(时钟周期)
Stage 1 特征点检测(Harris角点) 64
Stage 2 特征点匹配 32
Stage 3 运动模型拟合(RANSAC) 128
Stage 4 块匹配粗搜索(三步搜索) 256
Stage 5 块匹配精搜索(亚像素) 64

总延迟约544个时钟周期。对于1080p图像,一帧约207万个像素,这个延迟完全可以接受。

我曾经踩过一个坑:RANSAC的迭代次数没控制好,导致Stage 3的延迟抖动很大。后来改成固定迭代次数(32次),虽然精度略降,但延迟完全可预测了。

流水线设计要点:

  • 各级之间用FIFO缓冲,防止反压
  • 关键路径(Stage 3)用双缓冲,当前帧处理时下一帧数据提前加载
  • 亚像素插值用双线性,不要用双三次,硬件开销差4倍

4.5 整体架构图

下面这张图是我设计的对齐模块整体架构。你可以看到数据流从特征点检测开始,经过匹配、拟合,再到块匹配精调,最后输出运动矢量。

多曝光图像对齐模块架构 参考帧 当前帧 特征点检测 特征点匹配 RANSAC拟合 三步搜索粗匹配 亚像素精匹配 运动矢量输出 流水线级说明 Stage 1: 特征点检测 (64 clk) Stage 2: 特征点匹配 (32 clk) Stage 3: RANSAC拟合 (128 clk) Stage 4: 三步搜索粗匹配 (256 clk) Stage 5: 亚像素精匹配 (64 clk)

设计技巧:Stage 3的RANSAC是瓶颈。我建议用「提前终止」策略——如果当前模型的内点比例超过95%,直接跳出迭代。平均延迟能降40%。

4.6 避坑指南

做这个模块时,我踩过不少坑。分享几个典型的:

  • 数据位宽选择——SAD累加器位宽要留够。16×16块,8位像素,最大SAD是16×16×255=65280,至少16位。我吃过一次溢出的亏,仿真没问题,上板就花屏。
  • 亚像素插值——双线性插值用移位代替除法。除以2用右移1位,除以4用右移2位。别用除法器,面积大还慢。
  • 边界处理——搜索窗口超出图像边界时,用镜像填充。不要补0,补0会导致边缘匹配错误。
  • 时钟域同步——如果特征点检测和块匹配在不同时钟域,一定要用异步FIFO。我见过有人直接用寄存器打两拍,结果亚稳态导致整个系统崩溃。

嗯,这块内容确实不少。但只要你把流水线架构搭好,每个模块的延迟算清楚,对齐模块其实不难。关键是多仿真、多上板验证。纸上谈兵和实际跑起来,完全是两码事。