上一周在调一条串行图像编码链路起因是传感器端噪声变大后画面里出现了一整片横向拖出的杂点。负责链路的人第一反应是“去噪不够”第二反应是把注意力集中在预测编码这一段左向 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”上而在参考点的可信度、误差的单向累积路径以及量化器与噪声分布的错配。顺序搞对了排查时间能省下一大半。
阅读完成 · 觉得有帮助?