最近在做毕业设计,基于Zynq的实时视频去雾系统,用了暗通道先验算法。但是发现流水线设计不好,处理1080p视频时总是丢帧。求问各位大佬,暗通道先验中的最小值滤波和导向滤波怎么划分流水线阶段?行缓冲深度怎么设?有没有现成的优化经验分享?
2026年,FPGA工程师用Verilog实现实时视频去雾时,暗通道先验算法在Zynq上怎么优化流水线才能不丢帧?
提问
回答 3

我前两年也做过类似的毕设,当时卡在丢帧问题上很久。你的核心瓶颈其实不在算法本身,而在你如何处理帧间依赖。暗通道先验里最吃流水线的是最小值滤波和导向滤波。最小值滤波你千万别想着一次算完整个窗口,得拆成水平方向和垂直方向两级流水,每一级用行缓冲存中间结果。水平方向用移位寄存器做滑动窗口,垂直方向用BRAM行缓冲存若干行,深度设成窗口大小就行。导向滤波更关键,它的均值计算如果等整帧数据到齐再算,必然丢帧。你得用滑动窗口+系数复用的思路:把导向滤波拆成两步,第一步在行扫描过程中用累加器实时算局部均值,第二步等系数准备好后立即输出。这样流水线延迟只有几十行,不会超过两行缓冲的深度。还有双缓冲乒乓操作一定要做,帧级等待是不能忍的。你现在的行缓冲设了多少?用的是Zynq哪款芯片?BRAM资源够的话可以把深度设成窗口大小加2行,防止行尾数据冲突。

毕设做这个方向挺典型的,我说说我的优化顺序。第一步,先确认你的暗通道计算和透射率估计是不是都在帧级流水线上做的,如果是,改成行级。第二步,最小值滤波用两级流水:水平方向用移位寄存器,垂直方向用行缓冲,深度设成15(如果窗口是15×15)。第三步,导向滤波的均值计算不要用乘累加然后等整帧,用滑动窗口+累加器更新,每来一个像素就更新一次局部和,这样流水线延迟只有一行。最后,双缓冲乒乓操作一定要加在帧缓存和算法模块之间,不然一帧还没处理完下一帧就来了,必然丢帧。你用的是Vivado HLS还是手写RTL?如果是HLS,注意把流水线指令打在循环上,并且用dataflow模式让三个阶段并行跑。追问一下,你现在丢帧是出现在场景突变时还是全程都丢?前者可能是透射率估计的边界处理有问题,后者多半是行缓冲深度不对。

你这个问题其实可以换个角度想:流水线优化的本质不是让每个模块跑得最快,而是让数据流的节拍匹配。很多同学一上来就盯着最小值滤波的窗口大小和行缓冲深度,结果导向滤波那边又用了整帧均值,中间断流,帧就丢了。我个人感觉最实用的做法是先画一张数据流的时间图,把每一行从进入FPGA到输出去雾结果的时间轴标出来,看看哪里有空泡、哪里在等数据。暗通道先导的流水线可以按行扫描来切,分成三级:第一级是暗通道计算加最小值滤波的水平部分,第二级是垂直部分加透射率粗估计,第三级是导向滤波加最终恢复。每一级之间用FIFO或双缓冲乒乓接,深度不需要整帧,两到三行就够了,因为行缓存只需要存窗口半径那么多行。导向滤波的均值计算没必要等整帧,用滑动窗口累加器,每来一个像素就更新局部和,这样延迟只有一行。你用的Zynq具体是哪款?如果是7020,BRAM资源比较紧,行缓冲深度设成窗口大小加两行比较稳妥;如果是7045,可以稍微放宽。另外丢帧还有一个容易被忽略的原因:你用的摄像头输入时钟和算法处理时钟是不是同一个PLL出来的?跨时钟域没处理好,哪怕流水线再顺也会偶尔丢一帧。可以先查查这个。你现在的丢帧是固定每几秒丢一次,还是场景一变化就丢?前者可能是帧同步信号没对齐,后者才是流水线深度不够。
发表回答
登录后可在本页底部提交回答
