正在备赛2026年FPGA大赛,选了实时视频去雾的项目,用Zynq做加速器。暗通道先验算法里,透射率计算那一步在PL侧实现时,发现流水线跑不动,延迟太大,帧率上不去。试过用并行计算和流水线分割,但资源占用也飙升了。有没有大佬指点一下,怎么优化透射率计算的流水线结构?比如从行缓冲或者数据复用角度,或者用更高效的近似算法?
2026年FPGA大赛,用Zynq做实时视频去雾加速器,暗通道先验的透射率计算太慢怎么优化流水线?
提问
回答 5

看到你在2026年FPGA大赛里卡在暗通道先验的透射率计算上,这确实是很多做视频去雾的同学会碰到的瓶颈。我从数据流重构的角度给一个可能的方向:不要把透射率当成一个独立的计算阶段,而是把它融合进暗通道求取的过程里。一般做法是先算暗通道图,再基于暗通道去算透射率,但这样就需要两轮行缓冲或者两轮对DDR的读写,延迟自然大。你可以考虑在计算每个像素的局部最小值时,同步维护一个透射率的中间结果。具体来说,暗通道需要的是局部窗口内的最小RGB值,而透射率公式里用到的是这个最小值除以大气光值再取反,本质上就是一次查表或一次减法加移位。如果你用滑动窗口做暗通道求取,那在每一行数据流过时,窗口每一次滑动都能直接输出一个透射率值,不需要额外的帧缓存。代价是窗口的尺寸决定了你要用多少行缓冲,这个资源占用是逃不掉的,但至少省掉了跨帧的延迟。另外,如果资源已经撑不住了,可以考虑把窗口从15×15缩小到7×7或9×9,去雾效果在大部分场景下肉眼差别不大,但行缓冲资源能省一半以上。还有一个小技巧:透射率公式里的除法可以用查找表加近似乘法替代,大气光值在视频场景下一般变化很慢,可以隔几帧更新一次,这样查找表不用频繁重载。你现在的设计是用双端口BRAM做行缓冲还是用FIFO?窗口大小和行数是多少?这个信息能帮大家更准地给建议。

这个问题其实可以拆成两个层面看:一是算法本身的计算密度,二是你当前的流水线架构是否匹配Zynq的PL侧资源特性。先说第一个层面,暗通道先验里透射率计算的核心是局部最小值滤波,这一步如果用传统的全搜索滑动窗口,每个像素需要遍历窗口内所有像素,即使你做了流水线分割,也容易因为窗口过大导致逻辑级数太长,时序收敛困难。常见的优化思路是把最小值滤波转换成一种类似于图像腐蚀的形态学操作,用两个一维的排序滤波器级联来实现二维效果。具体做法是先在水平方向做一维最小值滤波,再用行缓冲做垂直方向的一维最小值滤波。这样每个像素只需要处理两个一维序列,计算量从O(wh)降到O(w+h),而且流水线深度可控,非常适合FPGA实现。但要注意,这种分解方式在窗口不是正方形时会有边界效应,不过对视频去雾来说影响很小。第二个层面,你提到资源占用飙升,那就要检查一下你的行缓冲是不是用了过多的BRAM。Zynq的BRAM是宝贵的,但你可以用分布式RAM或者利用DDR的带宽来换BRAM。如果帧率要求不是特别极端,比如30fps左右,可以考虑把行缓冲做到DDR里,用AXI HP口做高带宽读写,PL侧只保留几行缓存做乒乓操作。这样做虽然会引入DDR访问延迟,但配合流水线深度缓冲,实际吞吐量并不差,而且能释放大量BRAM给其他模块。另外,透射率计算后的导向滤波或软抠图步骤,如果你们赛题不强制要求,可以考虑直接用最小值滤波后的透射率图做简单修正,比如加上一个固定下限值0.1,效果在大多数户外场景下已经够用。最后给一个备赛策略上的建议:在优化流水线之前,先用HLS或者纯RTL仿真跑一个带时序约束的版本,找到真正的关键路径是在计算单元内部还是在行缓冲的读写控制逻辑上。很多选手花大量时间优化计算单元,结果发现瓶颈是AXI Stream的握手信号阻塞。你可以先用Vivado的时序报告定位一下,再针对性改。你们目前目标帧率是多少?使用的窗口大小和大气光值更新策略定了吗?这两个参数直接决定了优化方向。

个人感觉你遇到的瓶颈很可能不是透射率公式本身,而是窗口滤波的硬件映射方式。暗通道先验里透射率依赖局部最小值滤波,如果直接用二维滑动窗口,每个时钟周期要比较窗口内所有像素,逻辑级数会随窗口边长线性增长,时序自然崩。常见的做法是把二维分解成两个一维:先做水平方向的一维最小值滤波,再用行缓冲缓存若干行,做垂直方向的一维最小值滤波。这样每个流水线级只处理一个方向,逻辑深度可控,而且行缓冲的深度等于窗口高度减一,资源占用是确定性的。但有个隐藏坑:这种分解在窗口不是正方形时会有边界效应,不过视频去雾一般用15×15以内的正方形窗口,影响很小。另外你可以考虑把透射率计算和暗通道求取合并,在滑动窗口输出局部最小RGB值的同时,直接做一个减法加移位得到透射率,省掉一轮行缓冲。代价是窗口尺寸变大时,行缓冲的BRAM占用会线性增长,你得在帧率和资源之间取舍。还有一个容易被忽略的点:大气光值的估计方式也会影响透射率计算的实时性。如果你的设计里大气光是从暗通道图里扫描全局最大值得到的,那这个扫描过程会引入跨帧延迟。备赛时间紧的话,可以改成固定大气光值或者用帧间平滑估计,牺牲一点精度换流水线连续性。你目前用的窗口尺寸和行缓冲深度是多少?

这个问题其实有两个层次:算法层面的计算密度和架构层面的数据流匹配。先说算法,暗通道先验的核心是局部最小值滤波,传统做法是用全搜索滑动窗口,每个像素需要遍历窗口内所有元素,即使你做了流水线分割,也会因为窗口过大导致逻辑级数太长,时序收敛困难。更常见的工程化做法是把二维最小值滤波转换成类似图像腐蚀的形态学操作,用两个一维排序滤波器级联实现二维效果。具体来说,先在水平方向做一维最小值滤波,每个时钟周期只比较窗口宽度内的像素,然后用行缓冲缓存若干行,再做垂直方向的一维滤波。这样每个像素的处理复杂度从O(wh)降到O(w+h),而且流水线深度只取决于一维窗口长度,可控性大幅提升。但要注意,这种分解方式在窗口不是正方形时会有边界失真,不过视频去雾对边界精度要求不高,可以接受。再往深一层,你可以考虑把透射率计算融合进暗通道求取过程中。常规流程是先算暗通道图,再基于暗通道图算透射率,这需要两轮行缓冲或者两轮对DDR的读写。如果你在滑动窗口计算局部最小值的同时,同步维护一个透射率中间结果,即每输出一个局部最小值,立即用查表或减法移位得到透射率,这样就能省掉一轮行缓冲,延迟直接减半。代价是行缓冲的BRAM占用依然存在,但至少不需要额外帧缓存。还有一个容易被忽略的点:大气光值的估计方式。如果你的设计里大气光是从暗通道图里扫描全局最大值得到的,这个扫描过程会引入跨帧依赖,破坏流水线的连续性。备赛时间紧的话,可以改用固定大气光值或者帧间滑动平均估计,牺牲一点精度换取流水线无停顿。另外,如果你的Zynq芯片上有足够多的DSP切片,可以考虑把最小值滤波中的比较器替换成排序网络,用并行比较树来压缩逻辑级数,但这会大幅增加LUT占用,适合资源充裕的场景。你目前用的窗口尺寸是多少?行缓冲是用BRAM还是分布式RAM实现的?如果BRAM紧张,可以试试把行缓冲切分成多个小尺寸的FIFO,用分布式RAM实现浅层缓冲,但要注意时序收敛问题。

如果你已经在行缓冲和二维分解上下了功夫,但帧率还是卡住,建议检查一下大气光值的获取方式。很多参赛方案把大气光值当成一个全局常数,在暗通道求完后单独从帧缓存里统计,这本身就会引入一帧的延迟,而且统计过程如果放在PL侧做排序或直方图,资源开销不小。一个取巧的做法是:在暗通道求取的流水线末端,加一个简单的比较器树,只保留当前帧已处理像素中的前0.1%最大值,同时维护一个滑动窗口内的局部最大值,两者结合估算大气光值。这样大气光值在每帧开始时可以快速更新,不需要额外帧缓存。代价是精度略降,但视频去雾对大气光的微小偏差不敏感,视觉效果几乎不变。另外,透射率公式里那个减法加除法,完全可以用查表替代,把0到255的像素值映射成预计算的透射率,省掉一个除法器。你当前用的窗口尺寸和行缓冲深度具体是多少?如果窗口大于15×15,可以考虑缩小到11×11,对去雾效果影响很小,但资源能降一大截。
发表回答
登录后可在本页底部提交回答
