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那样“非此即彼”,而是让每个方向的贡献度由梯度大小动态决定。
具体做法是这样的:
- 计算多方向梯度:除了水平和垂直,还可以加上45°和135°对角线方向。
- 计算权重:梯度越小,说明该方向越平滑,权重越大。通常用
w = 1 / (gradient + ε)来算。 - 加权插值:每个方向先做线性插值,然后按权重加权平均。
// 四方向梯度计算示例
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 本章知识结构图
下面这张图把三种算法的核心逻辑和关系梳理了一下,方便你对照理解:
这张图把三种算法的核心差异和适用场景都标出来了。你可以把它当作选型时的快速参考。
避坑指南:
我曾经在一个项目里直接套用了高通默认的去马赛克参数,结果在暗光场景下全是彩色噪点。后来发现是梯度计算的阈值设得太低,把噪声当成了纹理。记住:去马赛克的参数一定要和噪声水平联动。暗光下适当提高梯度阈值,减少对噪声的响应。