上一周在调一条串行图像编码链路,起因是传感器端噪声变大后,画面里出现了一整片横向拖出的杂点。负责链路的人第一反应是“去噪不够”,第二反应是把注意力集中在预测编码这一段:左向 DPCM 在强噪声下预测不准。这句话本身没错,但它不是“某个参数没调好”那么简单,而是它背后那条因果预测假设,早就被强噪声打破了。
这篇文章把左向 DPCM 从定义到失效的根因完整拆一遍,包括为什么左向预测在无噪声时能用、强噪声如何把预测器变成振荡器、量化器又怎样补了一刀,最后给几种还想保留低延迟实现时的补救思路。适合正在做图像压缩、串行传输、低带宽 IPC 通道,或者在课上写图像处理实验的人参考。读完你至少能判断:问题到底是噪声没滤干净,还是预测器结构本身就不适合这个场景。
1. 左向 DPCM 的默认逻辑:从光栅扫描到“只看左边”的因果选择
1.1 一阶左向预测是怎么工作的
先抛开晦涩的公式。把一张八位灰度图的扫描过程想成爬格子:解码器逐行输出像素,每个像素在收到差值之后,从它左侧已经重建的像素出发,加上一个修正量,还原出当前值。预测器用的参考值,是左侧像素的“重建值”,不是原始值,这是 DPCM 必须闭环的根本原因。当前像素和参考值的差叫残差,残差经量化、熵编码后送出去,解码端用完全相同的规则反推每个像素。
这个过程只依赖“当前位置左边那个像素”,不碰上一行,不碰右下角任何未来信息。硬件上需要缓存的数据量是一个像素点,延迟是一个像素周期,这对流水线结构非常友好。从 RAW 拜耳数据到行扫相机输出,大量场景都在用这种结构。它更学术的叫法是一阶固定预测,预测系数取 1,含义是“默认下一个像素和左边的重建像素一样,只把差异编码成残差”。
之所以非要选左边,是为了因果性。处理单元只能使用已经收到的信息,不能像块变换那样等完整行、完整块积齐再动手。实时监控、车载视觉、ISP 流水线这些场景里,输入是逐像素到达的,左向预测可以在像素时钟节拍内完成,不需要插入行缓冲。这个约束直接决定了左向 DPCM 的江湖地位:它不是效果最好的预测器,却是最简单、最容易落地的那个。
1.2 无噪声时它凭什么有效
左向 DPCM 能长期存在,依赖一条非常朴素的经验规律:自然图像里,相邻像素的灰度值大概率接近。皮肤表面、墙面、天空、路面的局部区域,灰度变化是缓慢的,像素之间更多是“量变”,很少突然“质变”。把当前值和左侧值做差,得到的残差分布会极度集中在零附近,少数边缘区域才出现大残差。这种残差分布具有很好的熵编码特性,总码率能压得很低。
这里真正起作用的是“空间冗余”。一个像素的值可以从邻居推断出一部分,预测器消掉这部分冗余,只传不可推断的新信息。左向版本用的是最简单的一阶线性外推:把前一个重建值直接当预测值。在平滑区,残差趋近于 0;在纹理区,残差变大,交给量化器和熵编码器去处理。
但这个模型隐含一个关键前提:信号是相对平滑的,噪声相对小。一旦噪声幅度上升到与局部灰度变化相当的量级,“相邻像素差不多”这句话就开始失效。遗憾的是,很多系统在无噪声测试卡上跑得很好,换到真实弱光或高增益场景就直接崩,原因就在于这个前提失效了。下面进入真正的讨论:强噪声到底动了 DPCM 的哪块奶酪。
2. 强噪声破坏了第一个前提:左参考点从“近邻”变成“噪声源”
2.1 左侧重建值本身携带噪声
DPCM 是闭环系统。上一像素的重建值不是原始像素值,而是原始值经过预测、量化、反量化之后恢复出来的值。强噪声场景下,这个重建值等于“真实信号 + 不可避免的预测误差 + 量化噪声”。预测器拿这个结果当参考,等于拿一个带误差的量去预测下一个带噪声的量。
我常用一个比喻:跟着一个左摇右晃的人下台阶。前面那个人每一步都偏离中线,你为了贴近他,也往同一个方向偏,结果你的路径不只是偏离,而是把前一步的偏转进一步放大。左向 DPCM 在强噪声下的行为就和这个相似。左侧像素如果刚好是一个噪声尖峰,预测器会把尖峰当作有效内容复制给下一个像素,然后下一个像素又把这份噪声继续传给下下个像素。噪声不再是孤立事件,而是被预测器主动搬运到旁边。
数学上看,输入序列可以写成 x[i]=s[i]+n[i],其中 s 是真实信号,n 是噪声。左向预测是 x̂[i]=x[i-1],残差就变成:
e[i]=x[i]-x[i-1]=(s[i]-s[i-1])+(n[i]-n[i-1])
大括号看起来简单,但信息量很大:残差里既有信号的真实变化,也有噪声的变化。问题在于,编码器根本区分不了这两者。在无噪声时候,信号残差占据主导,残差集中在零附近;强噪声时,信号变化项小,噪声差分项大,预测器的输出完全被噪声控制。
2.2 噪声被差分运算放大了一截
对不相关的随机噪声,n[i] 和 n[i-1] 独立同分布,差分后的期望能量是原来的两倍。假设噪声方差是 σ²,那么 (n[i]-n[i-1]) 的方差就是 2σ²。这个结论对 DPCM 非常致命:左向预测在理想平滑信号上不仅没有降低噪声的能量,反而把噪声高频分量放大了约 1.41 倍。
这个倍率看起来不吓人,但在熵编码里效应会被放大。残差符号集中、幅度更零散,熵编码器需要花更多比特去表达这些噪声主导的残差。原本 DPCM 的卖点是“残差集中在零附近、码率低”,强噪声把这个卖点彻底抹掉了。更直白地说,预测器把噪声当作信号细节保存下来,用宝贵的比特去编码一堆不应该被编码的内容。
加性高斯噪声还不是最糟糕的情形。如果系统里混有脉冲噪声、椒盐噪声、坏点,左侧像素可能会是一个极端值。一阶预测器会把这个极端值完整地带进残差,且随后的一整行都可能被拖累。残差分布从单峰集中型变成多峰、重尾、左右不对称的形态,任何按常规分布设计的量化器和熵编码器都会措手不及。
2.3 重尾分布对量化的连锁影响
你可以把强噪声下的残差分布理解成“正常的尖峰旁边多了一条肥大的尾巴”。量化器为了保证平滑区精度,步长通常不大,一旦残差落到尾部,量化器就进入饱和区。信号区域内本来应该细致量化的部分,被大量噪声残差挤占。等到编码码率受到限制时,量化器被迫做得更粗糙,细节信号也一并被抹掉。这个连锁反应的结果是:该压的噪声没压住,该保的信号反而丢了。所以在下一节里,我还得重点聊量化器失配的问题,它是左向 DPCM 崩坏的第二个扳机。
3. 第二层致命伤:闭环误差反馈和量化器失配
3.1 误差沿扫描方向单向累积
DPCM 比独立量化每个像素多了一根看不见的反馈线。编码端的重建值是反向地回到预测器的输入,解码端也做同样的事。如果某个像素重建值和原始值差了 δ,这个 δ 不会凭空消失,它会通过预测器进入下一个像素的预测过程。用公式看:xr[i]=xr[i-1]+dq[i],xr 是重建值,dq 是解码端恢复出来的残差。如果 dq 本身包含误差,重建值会像随机游走一样产生漂移。
这正是我在上一节提到的“噪声被搬运”的实质。在无噪声场景中,误差是小的、稀疏的,闭环影响可以忽略;在强噪声下,每个像素都引入不可忽略的残差量化误差,这些误差没有外部校正机制,只能沿着扫描方向一路累积。视觉表现就是横条纹、横向拖影,甚至出现一条不存在的灰度斜坡。
多提一句:这种单方向累积是左向 DPCM 特有的。如果预测器同时参考上方或左上像素,误差会在多个方向之间分散,视觉上不容易形成一条明显的线。而左向只有水平方向一条传播路径,任何误差都只能朝右走,于是横贯画面的伪迹就非常突出。很多人在现场看到这个现象,第一反应是“带宽不够”,但实际上是被误差累积方式骗了。
3.2 量化器按小方差残差设计,强噪声直接打穿
量化器通常假设残差幅度集中在某个小范围内,所以步长、死区、饱和点都是按“中低噪声”场景标定的。强噪声一旦把残差顶到量化器饱和区,编码器就进入斜率过载状态:当前像素的实际值已经超出可表示范围,量化器输出的只是一个被截断的近似值,解码端重建时大幅偏离真实值。
反方向也有问题。当噪声让残差出现高频小幅抖动时,量化器会把不必要的小噪声视为有效信号,给每个抖动都分配比特。简而言之,量化器在强噪声下面临的处境是:要么饱和、要么在噪声细节上浪费码率,几乎不存在一个“刚刚好”的运行点。 这也是为什么很多系统测试时信噪比刚够,上了噪声就把图像质量提高不上来的原因之一。
3.3 失效链路拼接:预测器、闭环和量化器相互放大
把前面三条线接起来看,左向 DPCM 的失效不是一个孤立环节坏了,而是三个问题叠加:
- 预测参考值不可靠:左边的重建像素被噪声污染,预测起点就歪了。
- 差分运算放大噪声:残差里的噪声分量被放大,熵编码的符号分布变差。
- 闭环误差单向累积:每次误差都向后传递,无法自我纠正。
- 量化器失配:码率有限时,该保的信号被挤掉,该滤的噪声被保留。
这四条合在一起,才构成“强噪声下预测不准”的完整解释。只改量化器不能解决参考值污染,只加去噪也不能解决误差累积,必须从整条链路层面去理解。到这一步已经能知道“为什么准不了”,但还可以进一步对比其他预测结构,看左向究竟输在哪里。
4. 拿“二维预测”和“上下文预测”对比,才看清左向的边界
4.1 一维预测缺少二维邻域的交叉验证
左向预测器只有一个信息来源。它无法回答一个基本问题:当前像素像不像噪声?如果旁边还有上方像素、左上像素可以参考,三个邻居能互相“投票”,单个异常值很容易被识别出来;但左向只有一位邻居,噪声像素和真实边缘像素在数值表现上毫无差别,预测器只能机械地说“你和左边一样”或者“你和左边差很多”。
举个直观例子:真实图像里有一条垂直边缘,当前像素位于边缘右侧,如果只用左边像素预测,会给出一个很大的残差,因为左右两侧灰度差很远。如果同时参考上方像素,就能判断这条垂直边缘是延续的结构,预测准确性会大幅提升。更关键的是,强噪声条件下,垂直边缘和噪声尖峰都可能产生大残差,一维预测器无法区分“这是边缘”还是“这是噪声”,二维邻域至少提供了交叉验证的可能。
二维预测需要行缓冲,延迟会从“一个像素”上升到“一行”,这对某些实时场景是负担,所以左向不是拍脑袋选的。但要说清楚,这个选择牺牲的正是抗噪声的余地,底层原因是视野不足,不是实现者不够聪明。
4.2 上下文预测凭什么更稳
比左向更稳的预测器,核心思路都是同一个:从更宽的邻域里找规律,而且允许预测器根据局部情况切换模式。取左、上、左上三个邻居做中值滤波,脉冲噪声会被直接淘汰;用一个滑动窗口估局部均值,随机噪声会被平均掉;更高级的做法是根据梯度方向选模式,平坦区用均值,水平边缘用左向,垂直边缘用上方。
这些方案在无噪声时的优势是残差更稀疏,在强噪声时的优势则是“不容易被单个噪声点带偏”。我用一张表把几种预测方式的强弱列一下:
| 预测方式 | 存储/延迟 | 无噪声残差稀疏性 | 强噪声典型表现 |
|---|---|---|---|
| 一阶左向 DPCM | 最低 | 尚可 | 横向拖影、噪声逐像素传播 |
| 左+上+左上中值 | 需行缓冲 | 更好 | 脉冲噪声被有效排除 |
| 梯度自适应上下文 | 需更大窗口 | 最好 | 能区分边缘与坏点,残差更集中 |
| 均值/平滑类预测 | 需行缓冲 | 中等 | 随机噪声被抑制,但边缘会糊 |
我不强调某一类“最好”,因为在具体链路里永远有实现代价的问题。但结论很清楚:当噪声环境不稳定时,把预测视野从“一个像素”扩大到“一个邻域”,是最有效的抗崩坏手段。左向 DPCM 的失败更像是一个视野问题,它在信息维度上就没有给噪声抑制留出空间。
4.3 抗噪预测器的本质是在“甄别坏值”
从信号处理角度看,左向预测是“无条件的复制最近值”。它不思考“这个最近值值得参考吗”,也不判断“当前像素像不像噪声”。抗噪预测器则多了一个隐性步骤:先评估邻域一致性,再决定用哪种预测模式。这个步骤相当于把噪声检测嵌进了预测结构,不是靠外部模块事后擦屁股。
我在实际对比中见过一个现象:同样是强噪声输入,左向 DPCM 的残差熵比上下文预测高出一倍,而重建画面的主观质量差距更大。原因就在于上下文预测器的模式切换机制,能在噪声剧烈的地方自动退回到更保守的预测策略。左向没有退路,只能硬着头皮用一个不可靠的参考值。所以说,左向在强噪声下预测不准,本质是它缺少“价值判断机制”。
5. 补救路线:在保留低延迟的前提下让左向 DPCM 不崩
5.1 第一道防线:把噪声处理放在 DPCM 之前
如果在整个系统里拥有修改空间,最直接的办法是在 DPCM 之前加一级预处理。这不是给预测器补丁打得更漂亮,而是从源头消除“左侧参考值被污染”的问题。对于高斯噪声,可以做一个低通平滑;对于脉冲噪声,优先用中值滤波而不是均值滤波,因为中值对孤立尖峰更友好,而且不严重伤边缘。
我在一个灰度相机模组的串行编码链路里做过对比:弱光条件下未加预处理时,左向 DPCM 的残差熵高得离谱,解码画面布满了横向伪迹;在编码前插入一个 3×3 中值滤波后,残差熵下降接近一半,横向拖影基本消失。这个数字在不同场景下会变,但方向是一致的:先让预测器看到一个相对干净的输入,比在预测器内部做任何微调都更有效。
当然,预处理会引入额外延迟和资源占用。如果链路硬件已经定型,没法插入大窗口滤波器,那还有第二条路可走——在量化器和预测结构上做小改动。
5.2 在量化侧做针对性调整
当左向 DPCM 无法被替换时,量化器参数至少要针对强噪声重新标定。第一,加死区。残差绝对值小于某个门限时直接归零,这能避免量化器花比特去编码小幅度噪声抖动。死区大小的选择要看噪声幅度:门限太小滤不掉噪声,门限太大会抹掉真实细节,需要实测噪声分布来定。
第二,限制残差上限。给残差设定一个合理范围,丢弃超范围的极端值。很多人担心这种截断会丢真实边缘,但在强噪声环境里,超限残差的置信度本来就很低,大胆截断反而能抑制斜率过载造成的整行漂移。第三,在熵编码端根据噪声强度动态调整残差的概率模型,让编码器对齐真实的重尾分布,避免把大量码字分配给噪声符号。
这一套操作并不改变左向预测的因果结构,实现代价非常小,适合已经量产的硬件产品。我见过一些项目只做了死区调整,就把横向拖影从“完全不可接受”拉回到“勉强可用”的状态。但要注意,这属于事后止损,不是根除问题。
5.3 自适应扩展:向左看,但不只向左看
如果硬件还有一点点行缓冲或缓存资源,可以尝试给左向预测加一个上下文开关。做法很简单:预测器默认使用左向,但实时统计最近几个像素的残差波动,如果波动超过阈值,说明当前区域疑似噪声主导,这时切换成邻域中值或均值预测。
这样既保留了平滑区间的低延迟左向预测,又在强噪声区间自动切换到更稳的预测模式。本质上是用一个小小门限判断,帮左向预测器补上它最缺的“价值判断机制”。实测下来,这种自适应混合策略比单一左向预测的残差熵少很多,主观质量提升明显,而且不需要大缓存,一帧图像只需要保留相邻行的一部分像素。
对我个人而言,这是“鱼与熊掌”平衡得比较好的方案。因为很多项目无法接受二维预测带来的整行延迟,又无法忍受强噪声下的图像质量崩塌,自适应开关恰好卡在两者之间。当然,如果你能接受更大的延迟和内存,全上下文预测仍是更正统的选择。
最后说一段实战体会。我在不同链路里反复折腾左向 DPCM 强噪声问题,最大的感受是:先搞清噪声性质再决定修复方向。如果噪声以随机白噪为主,预处理效果立竿见影;如果噪声是脉冲型,中值类方案更对路;如果系统已经无法增加预处理器,量化器死区和自适应切换也能救命。千万别一上来就狂调预测系数,左向 DPCM 的问题重点多半不在那个“1”上,而在参考点的可信度、误差的单向累积路径,以及量化器与噪声分布的错配。顺序搞对了,排查时间能省下一大半。