三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

049、3D降噪的联合调优——空域+时域+频域联合降噪的工程实现——从3DNR的帧缓存管理到运动补偿的精度要求——低照度视频的实战调参

049、3D降噪的联合调优——空域+时域+频域联合降噪的工程实现——从3DNR的帧缓存管理到运动补偿的精度要求——低照度视频的实战调参

049、3D降噪的联合调优——空域+时域+频域联合降噪的工程实现——从3DNR的帧缓存管理到运动补偿的精度要求——低照度视频的实战调参

昨晚在实验室盯着一块IMX585的RAW图盯到凌晨两点,客户报了个case——楼道监控在0.1lux下噪点像雪花一样,3DNR开了跟没开似的,反而把运动的人拖出了鬼影。我盯着示波器上那根跳动的Y通道曲线,突然意识到问题不在降噪算法本身,而在我们根本没搞清楚空域、时域、频域这三兄弟在低照度下到底谁该让谁。

先说说那个让人抓狂的现场。楼道里只有一盏声控灯,人走过去灯亮,人走了灯灭。灯亮的时候画面还行,灯一灭,整个画面就像撒了一把盐。我们当时用的方案是典型的“先空域后时域”串联结构——2DNR在前,3DNR在后,最后再挂一个频域的小维纳滤波。理论上这个架构没毛病,但实际跑起来,2DNR把细节磨平了,3DNR拿到的输入已经是“糊了的底片”,运动补偿的块匹配在低纹理区域直接失效,频域滤波又把这个错误进一步放大。最后输出的画面,静态区域噪点压住了,但动态区域全是撕裂感。

这个case让我重新审视了3DNR的帧缓存管理。很多工程师觉得帧缓存不就是开三块DDR存三帧嘛,但低照度下,帧缓存的管理直接决定了运动补偿的精度。我们当时用的是双帧缓存,一帧参考一帧当前,运动矢量搜索范围是±8像素。在0.1lux下,噪点的幅度已经超过了真实物体的边缘梯度,块匹配算法会把噪点当成特征点来追踪,结果就是运动矢量全是乱的。后来我把帧缓存改成三帧,中间帧做双向运动估计,搜索范围缩到±4像素,但增加了子像素精度——用1/4像素插值。这个改动让运动矢量的准确率提升了将近40%,但代价是DDR带宽涨了30%,而且子像素插值本身会引入轻微的模糊。

这里踩过一个坑——三帧缓存不是简单地把三帧按时间顺序排开,而是要做一个环形缓冲,并且要区分“参考帧”和“去噪帧”。参考帧是原始RAW,去噪帧是经过空域预滤波后的结果。如果你直接用去噪帧做运动估计,那等于把2DNR的误差也带进了时域,鬼影会加倍。正确做法是:运动估计用原始帧,运动补偿用去噪帧,最后融合的时候再根据运动矢量的置信度做自适应权重。这个“双轨制”设计,是我在调试车载夜视系统时总结出来的,搬到安防场景同样适用。

再说运动补偿的精度要求。低照度下,运动补偿的精度要求不是“像素级”,而是“亚像素级+置信度门限”。我们当时犯的错误是,对所有的运动矢量一视同仁,不管置信度高低都做同样的时域滤波强度。结果就是,低置信度的区域(比如纯色墙壁、黑暗角落)被强行做了时域叠加,噪点反而被“积累”了。后来我加了一个置信度图,用SAD(绝对差值和)和纹理梯度联合计算,置信度低于阈值的区域,时域滤波强度直接降到接近零,完全靠空域撑着。这个改动让暗部区域的噪点反而比之前更干净了,因为空域滤波在低纹理区域的表现其实比时域好。

频域这块,我们用的是DCT域的维纳滤波,但一开始挂在最后面,效果很差。后来我把频域滤波挪到了空域和时域之间,并且只对高频系数做衰减,低频系数保持原样。为什么?因为空域滤波已经去掉了大部分高斯噪声,时域滤波去掉了时间维度的随机噪声,剩下的频域噪声主要是“残留的块效应”和“运动补偿的振铃”。这时候再用维纳滤波,只需要针对高频段做轻量级的收缩,强度系数控制在0.3到0.5之间,千万别超过0.7,否则画面会像塑料一样假。

实战调参的时候,我习惯用“三档递进”策略。第一档,先关掉时域,只调空域和频域,把静态场景的噪点压到可接受范围,记录下空域的强度系数和频域的截止频率。第二档,打开时域,但把时域强度设成空域的一半,重点观察运动区域的鬼影和拖尾。如果鬼影明显,优先调运动矢量的置信度门限,而不是降时域强度——因为降强度是治标,调门限是治本。第三档,把三者的强度系数做成一个联动曲线,随着ISO的升高,空域强度线性增加,时域强度先增后减(因为高ISO下运动估计更容易出错),频域强度保持恒定。这个曲线不是拍脑袋定的,是从几十组实验数据里拟合出来的。

还有一个容易被忽略的细节——帧率。低照度下,很多方案会把帧率降到15fps甚至10fps来延长曝光,但这对3DNR是致命的。帧率越低,相邻帧之间的时间间隔越大,运动物体的位移就越大,运动补偿的搜索范围根本跟不上。我做过一个对比实验,同样0.1lux下,30fps的3DNR效果比15fps好一个档次,因为运动矢量更小,置信度更高。所以如果条件允许,宁可提高增益把帧率保住,也不要为了降噪而降帧率。这个反直觉的结论,让不少同事一开始都以为我疯了。

最后说说那个楼道case的结局。我们把帧缓存改成三帧环形缓冲,运动估计用原始帧、补偿用去噪帧,加了一个基于SAD和纹理梯度的置信度图,频域滤波挪到中间位置,强度系数调到0.4,帧率从15fps拉回30fps,增益提高了3dB。最终画面,静态区域的噪点比之前降了大概6dB,运动区域几乎没有鬼影,暗部细节保留得比预期好。客户很满意,但我心里清楚,这只是一次针对特定场景的调优,换一个场景,比如雨夜的车载摄像头,参数又得重新来一遍。

经验这东西,说白了就是踩坑踩出来的。3DNR的联合调优,核心不是算法多先进,而是你要清楚每个模块在什么条件下会“帮倒忙”。空域在低纹理区域会磨皮,时域在运动区域会拖影,频域在边缘会振铃——你要做的,就是让它们在各自擅长的区域干活,在彼此冲突的区域学会“闭嘴”。这个“闭嘴”的机制,就是置信度门限和自适应权重。别指望一套参数打天下,每个场景都得单独调,但调参的思路是通用的——先分模块验证,再联合调试,最后做联动曲线。

如果你也在调3DNR,我建议你从帧缓存入手,先确认你的运动估计是不是在“用脏数据算运动”。这个坑最隐蔽,也最致命。

← 返回列表