4. 经典去马赛克算法

各位好,欢迎来到去马赛克这一章。说实话,这是ISP pipeline里最“古老”但也最考验功底的一环。Bayer RAW数据到了我们手里,每个像素只有一个颜色通道——R、G、B各占一个位置。怎么把缺失的另外两个颜色猜出来?这就是去马赛克要干的事。

我刚开始做图像调试那会儿,总觉得去马赛克不就是个插值嘛,随便填填就行。后来在项目里被各种伪彩色、锯齿、拉链效应折磨得够呛,才明白这里面门道有多深。今天咱们就把三种经典算法掰开揉碎了讲清楚。

4.1 双线性插值法——最朴素的思路

双线性插值,说白了就是“看邻居的脸色办事”。每个像素缺失的颜色,直接用周围同色通道的像素做加权平均。

举个例子,Bayer阵列里一个绿色像素位置,缺了红色和蓝色。那红色值怎么算?取上下左右四个红色像素的平均值。蓝色同理。

核心公式(以G像素位置插值R为例):

R(i,j) = [R(i-1,j) + R(i+1,j) + R(i,j-1) + R(i,j+1)] / 4

其中(i,j)是当前G像素坐标,四个邻居R像素取平均。

这个算法简单到什么程度?我在FPGA上实现过,只需要几个行缓存和加法器,延迟极低。但代价也很明显——边缘处一塌糊涂。

双线性插值的典型问题:

  • 拉链效应(Zipper Effect):沿着边缘会出现明暗交替的锯齿,像拉链一样。
  • 伪彩色(False Color):在高频纹理区域,插值出来的颜色完全不对,出现红绿蓝的杂色斑点。
  • 边缘模糊:因为平均操作天然会平滑细节,边缘锐度损失严重。

我曾经在一个低端监控摄像头项目里用过纯双线性,结果客户反馈说“画面像蒙了一层雾,边缘还有彩虹纹”。没办法,后来只能换算法。所以双线性现在基本只用在预览模式或者资源极度受限的场景。

4.2 边缘导向插值法(Hamilton-Adams算法)

这是去马赛克算法的一个里程碑。Hamilton和Adams在1997年提出了这个思路:先判断边缘方向,再沿着边缘插值,而不是跨边缘插值。

你想想看,如果当前像素在一条水平边缘上,那垂直方向的像素变化剧烈,水平方向变化平缓。这时候用垂直方向的邻居去插值,就会引入错误。正确的做法是沿着水平方向插值。

Hamilton-Adams的核心思想:

  • 计算水平和垂直两个方向的梯度
  • 选择梯度较小的方向作为插值方向
  • 在选定的方向上,用二阶校正项补偿色差

具体到G通道的插值,算法会先算两个方向的梯度:

// 水平梯度
ΔH = |G(i,j-1) - G(i,j+1)| + |2*R(i,j) - R(i,j-2) - R(i,j+2)|

// 垂直梯度  
ΔV = |G(i-1,j) - G(i+1,j)| + |2*R(i,j) - R(i-2,j) - R(i+2,j)|

然后比较ΔH和ΔV:

  • 如果ΔH < ΔV,说明水平方向更平滑,沿水平方向插值
  • 如果ΔH > ΔV,沿垂直方向插值
  • 如果两者接近,取两个方向的平均

这个算法在边缘处表现比双线性好得多。我记得在手机项目里调试人像模式时,Hamilton-Adams在头发丝边缘的伪彩色控制得相当不错,拉链效应也基本看不到了。

Hamilton-Adams vs 双线性 对比:

指标 双线性插值 Hamilton-Adams
PSNR(典型值) ~32 dB ~36 dB
伪彩色抑制 差 中等
边缘保持 差 好
计算复杂度 低 中
硬件实现难度 简单 中等

不过Hamilton-Adams也有短板。它在纹理特别密集的区域,比如草地、布料纹理,还是会出现一些伪彩色。因为梯度判断在高频区域容易出错,方向选错了,结果还不如双线性。

4.3 基于梯度的自适应插值

既然Hamilton-Adams只用了水平和垂直两个方向,那能不能扩展到更多方向?这就是基于梯度的自适应插值的思路——用梯度信息指导插值权重,而不是简单地二选一。

我个人比较喜欢这个方向。它不像Hamilton-Adams那样“非此即彼”,而是让每个方向的贡献度由梯度大小动态决定。

具体做法是这样的:

  1. 计算多方向梯度:除了水平和垂直,还可以加上45°和135°对角线方向。
  2. 计算权重:梯度越小,说明该方向越平滑,权重越大。通常用 w = 1 / (gradient + ε) 来算。
  3. 加权插值:每个方向先做线性插值,然后按权重加权平均。
// 四方向梯度计算示例
float grad_H = abs(G(i,j-1) - G(i,j+1));
float grad_V = abs(G(i-1,j) - G(i+1,j));
float grad_D45 = abs(G(i-1,j+1) - G(i+1,j-1));
float grad_D135 = abs(G(i-1,j-1) - G(i+1,j+1));

// 权重计算(加小常数防止除零)
float w_H = 1.0 / (grad_H + 0.001);
float w_V = 1.0 / (grad_V + 0.001);
float w_D45 = 1.0 / (grad_D45 + 0.001);
float w_D135 = 1.0 / (grad_D135 + 0.001);

// 归一化后加权插值
float total_w = w_H + w_V + w_D45 + w_D135;
float G_interp = (w_H * G_H + w_V * G_V + w_D45 * G_D45 + w_D135 * G_D135) / total_w;

工程落地经验:

我在高通平台上实现这个算法时,发现梯度计算对噪声非常敏感。建议在梯度计算前先做一层轻量的去噪,比如用3x3中值滤波。否则噪声会被梯度放大,导致插值权重乱跳,画面出现“水波纹”。

基于梯度的自适应插值在纹理区域的表现明显优于Hamilton-Adams。因为它不是硬判决,而是软权重,即使某个方向判断错了,其他方向还能拉回来一些。

但代价也很实在——计算量翻倍。四个方向的梯度计算加上权重归一化,在硬件上需要更多的行缓存和乘法器。高通骁龙平台从SM8250开始,ISP硬件里就集成了类似的多方向梯度插值模块,软件上我们只需要配参数就行。

4.4 三种算法的对比与选型建议

说了这么多,到底项目里该用哪个?我根据自己的经验给个参考:

应用场景 推荐算法 理由
低端摄像头、预览模式 双线性插值 资源占用少,延迟低
中端手机、监控摄像头 Hamilton-Adams 边缘质量好,性价比高
高端旗舰、专业相机 基于梯度的自适应插值 纹理细节保留最好
AI ISP方案 以上算法做预处理+CNN后处理 传统算法提供稳定初值

嗯,这里要注意一点。不管选哪个算法,去马赛克都不是孤立的一步。它和前面的黑电平校正、后面的色彩校正矩阵(CCM)是联动的。我曾经在调试时发现去马赛克出来的伪彩色,在CCM里被放大了三倍。后来调整了去马赛克的色差阈值,才压下去。

4.5 本章知识结构图

下面这张图把三种算法的核心逻辑和关系梳理了一下,方便你对照理解:

经典去马赛克算法知识结构图 经典去马赛克算法知识结构 Bayer RAW 去马赛克 双线性插值法 边缘导向插值法 (H-A) 基于梯度的自适应插值 4邻域平均 拉链效应 水平/垂直 梯度判断 沿边缘 方向插值 多方向 梯度计算 权重归一化 加权插值 三种算法核心对比 维度 双线性 Hamilton-Adams 梯度自适应 边缘保持 差 好 优秀 伪彩色 严重 中等 轻微 计算量 低 中 高 硬件友好度 高 中 低

这张图把三种算法的核心差异和适用场景都标出来了。你可以把它当作选型时的快速参考。

避坑指南:

我曾经在一个项目里直接套用了高通默认的去马赛克参数,结果在暗光场景下全是彩色噪点。后来发现是梯度计算的阈值设得太低,把噪声当成了纹理。记住:去马赛克的参数一定要和噪声水平联动。暗光下适当提高梯度阈值,减少对噪声的响应。