备赛2026年FPGA大赛,我们组选了实时视频去雾方向,用Zynq做暗通道先验算法加速。现在能跑到720P 30fps,但目标是要实现1080P 60fps。透射率计算那块的流水线怎么设计才能减少延迟?还有导向滤波用行缓存还是块RAM更省资源?求有经验的大佬指点具体优化步骤,最好能分享下实际测试数据。
2026年,FPGA大赛做实时视频去雾,暗通道先验算法在PL端怎么优化才能跑到1080P 60fps?
提问
回答 6

其实暗通道先验上1080P60的关键瓶颈,往往不在算法本身,而在你从720P30翻倍到1080P60时,数据吞吐和行缓存深度带来的时序压力。透射率计算那块的流水线,建议你把最小值滤波和导向滤波的窗口分开处理:最小值滤波用3×3或5×5的滑动窗口,直接在行缓存上做,不要等整帧图像存完再算,否则延迟会随分辨率线性增长。具体做法是开两行或三行LineBuffer,每来一行新数据就更新当前窗口的暗通道值,这样透射率计算可以和像素流入同步,延迟压在一个窗口宽度以内。导向滤波的话,如果是做快速版本,用行缓存更划算,因为块RAM你一个1080P的帧存就要吃掉将近3Mbit,Zynq的BRAM总共才几兆,留给其他模块就紧了。行缓存只存几行数据,配合片内寄存器做均值滤波,虽然控制逻辑复杂点,但资源省很多。实际测试时,注意把时钟频率拉到150MHz以上,用AXI4-Stream的接口做乒乓操作,把读帧和写帧解耦。另外,暗通道先验在天空区域容易过增强,你们可以考虑加一个自适应阈值,不然评审可能会挑这个点。你们目前用的是哪个系列的Zynq?7010还是7020?BRAM和DSP切片数量不同,优化策略会有差异。

我个人觉得,你们先别急着调算法,把720P30的时序报告翻出来,看看最长的路径在哪。一般这种卡住都是因为最小值滤波的窗口比较器链太长。试试把3×3窗口换成2D的分离式滤波,先做水平再做垂直,这样每级比较器位宽减半,组合逻辑深度能压下去。导向滤波用行缓存就行,BRAM留给帧缓存做乒乓。1080P60的像素时钟大约148.5MHz,只要时序收敛,流水线多拍不是问题。你们现在透射率计算的延迟具体是多少拍?

说实话,你们现在720P30能跑通,说明算法模块的基本结构是没问题的。但直接翻倍到1080P60,像素吞吐量是原来的4.5倍左右(1920108060 / 128072030 ≈ 4.5),光靠简单流水线重排是不够的。核心瓶颈其实在透射率计算里的最小值滤波——很多人习惯用3×3窗口做全并行比较,但1080P下每个像素的窗口覆盖了9个像素,如果每个时钟都要算出当前窗口的最小值,比较器树会非常深,直接拖垮时序。我建议你们把最小值滤波拆成两步:先做水平方向的1×3滑动最小值,这个只需要一个3输入比较器,延迟1拍;然后对水平结果做垂直方向的3×1滑动最小值,再延迟1拍。这样整个3×3窗口的最小值只需要两级比较,组合逻辑深度从原来的log2(9)≈4级降到2级,时序压力小很多。代价是行缓存要多存一行中间结果,但行缓存用分布式RAM就能搞定,不占BRAM。导向滤波那边,如果你们用的是快速导向滤波(均值滤波+boxfilter),强烈建议用行缓存方案。原因很简单:1080P一帧的行缓存深度是1920像素,按8bit算,一行只要15Kbit左右,开4到6行也就90Kbit,用BRAM开一个真双口就能搞定,剩下的BRAM可以全给帧缓存做乒乓操作。如果用块RAM直接存整帧,一个1080P的Y分量就要将近2Mbit,Zync的BRAM总共才几兆,其他模块就没法用了。另外注意透射率计算和导向滤波之间的接口——如果你们透射率输出是逐像素的,导向滤波也得跟着逐像素流式处理,中间不要插帧存,否则延迟会多出一帧。最后问一下,你们现在的透射率计算流水线具体是多少拍?有没有试过把暗通道先验的窗口从15×15缩小到9×9?这对画质影响不大,但能大幅减少行缓存深度和比较器数量。

别光盯着流水线,先看看你们时钟域怎么切的。720P30用74.25MHz晶振就能跑,但1080P60的像素时钟是148.5MHz,翻了一倍。很多组在720P下时序收敛的代码,换到1080P直接崩,不是因为逻辑变复杂了,而是寄存器到寄存器的路径没留够余量。建议你们先把综合后的时序报告打开,看最差路径的slack是正还是负。如果关键路径在透射率计算模块,试试把比较器链拆成两级流水,多拍一拍结果。导向滤波用行缓存就行,BRAM留给帧缓存,不然资源不够。

看你们现在720P 30fps能跑通,说明基础实现是稳的。但1080P 60fps的像素时钟从74.25MHz跳到了148.5MHz,时序收敛是第一道坎。我建议先别急着改算法结构,而是把Xilinx的Vivado时序报告打开,看最差路径的slack。如果关键路径在透射率计算里的最小值滤波,那大概率是3×3窗口的全并行比较器树太深了——每来一个像素都要从9个数里找最小值,组合逻辑延迟会随窗口尺寸增大而飙升。一个常见的骚操作是把3×3窗口分解成水平1×3和垂直3×1的串联比较,这样每级只需要一个3输入比较器,深度从log2(9)≈4降到2,时序压力小很多。代价是行缓存要多存一行中间结果,但行缓存用分布式RAM或者SLRIM就能搞定,不占BRAM。导向滤波那边,如果你们做的是快速版本(均值滤波+一次引导),行缓存就够了,BRAM留给帧缓存做乒乓,因为1080P一帧要将近3Mbit,Zynq的BRAM总量也就几兆,全塞帧存其他模块就没法做了。另外,注意透射率计算的延迟不要超过一行的时间,否则流水线会断。你们现在最小值滤波具体是几级比较器?

转向1080P 60fps时,很多组都会忽略一个关键点:暗通道先验算法里的透射率计算和导向滤波,本质上是两个不同量级的资源消耗。透射率计算的核心是最小值滤波,它只依赖局部窗口数据,用行缓存就能实现流式处理——每来一行新像素就更新当前窗口的暗通道值,延迟严格控制在窗口宽度以内(比如3行)。但导向滤波如果做完整的局部均值滤波和协方差计算,需要同时访问多个窗口内的数据,这时候行缓存会变得非常深:对于5×5窗口,你得存5行数据才能算出每个像素的均值。1080P下每行1920像素,5行就是9600个像素,如果用行缓存,这9600个像素要存到寄存器或分布式RAM里,逻辑资源会爆炸。所以我个人的建议是:导向滤波模块用BRAM来存行缓存数据,因为BRAM是专用存储,不会占用你宝贵的LUT和FF。暗通道先验里的最小值滤波则完全不用BRAM,用分布式RAM或移位寄存器实现3行缓存就够了,这样BRAM全部留给导向滤波和帧缓存。另外,你们透射率计算和导向滤波之间要做数据对齐——透射率计算每输出一个有效值,导向滤波要能同步消费,否则流水线会出现气泡。一个实用的做法是在透射率计算模块的末尾插入一个FIFO,深度取一行像素的一半,用来缓冲两个模块之间的速率抖动。你们现在是用AXI-Stream还是自定义的valid-ready握手来连这两个模块?这个细节如果没处理好,哪怕时序收敛了,帧率也上不去。
发表回答
登录后可在本页底部提交回答
