3. 传统去马赛克算法(上):双线性插值、双三次插值、边缘导向插值、Hamilton-Adams算法
好,咱们正式开始聊去马赛克算法。这一章我先带大家看几个经典的传统方法。说实话,虽然现在深度学习很火,但这些传统算法在很多嵌入式设备、低功耗场景里依然在用。我自己在做一个工业相机项目时,就遇到过必须用传统方法的情况——因为芯片算力不够,跑不了神经网络。
先看一张整体关系图,帮你建立全局认知。
3.1 双线性插值:最朴素的方法
双线性插值,说白了就是「加权平均」。对于 Bayer 格式中缺失的像素值,用周围最近的同色通道像素做线性加权。
举个例子,假设你在绿色通道上要插值一个 G 像素:
// 以 G 通道为例,在 Bayer RGGB 模式中
// 已知四个角上的 G 值:G1, G2, G3, G4
// 中心待插值点 Gx
Gx = (G1 + G2 + G3 + G4) / 4
// 如果是 R 或 B 通道,则取最近的 2 个或 4 个同色像素
这个方法的好处是——快。真的很快。我在一个 FPGA 项目里用过,一个时钟周期就能算完。但缺点也很明显:
- 边缘模糊:图像边缘会被「平均」掉,出现锯齿和伪影
- 伪色严重:在纹理密集区域,会出现彩色噪点
- 细节丢失:高频信息基本被抹平了
3.2 双三次插值:更平滑,但更慢
双三次插值不是取 4 个点,而是取 16 个点(4x4 邻域)。它用三次多项式来拟合像素值的变化曲线。
核心公式长这样:
// 双三次插值核函数
// a 一般取 -0.5 或 -0.75
function cubic(x, a) {
if (|x| <= 1) {
return (a + 2)*|x|^3 - (a + 3)*|x|^2 + 1
} else if (|x| < 2) {
return a*|x|^3 - 5*a*|x|^2 + 8*a*|x| - 4*a
} else {
return 0
}
}
// 对 4x4 邻域做两次一维卷积
// 先水平方向,再垂直方向
双三次插值的效果比双线性好不少,边缘更平滑,伪影也少一些。但代价是计算量大了 4-5 倍。
| 算法 | 邻域大小 | 计算量 | 图像质量 | 适用场景 |
|---|---|---|---|---|
| 双线性插值 | 2x2 | 低 | 一般 | 实时预览、低功耗设备 |
| 双三次插值 | 4x4 | 中 | 较好 | 图像编辑、打印输出 |
| 边缘导向插值 | 自适应 | 较高 | 好 | 消费级相机、手机拍照 |
| Hamilton-Adams | 自适应 | 高 | 优秀 | 专业相机、工业检测 |
3.3 边缘导向插值:让算法「看」方向
前面两种方法都是「无脑平均」,不管图像内容是什么。边缘导向插值就不一样了——它会先判断当前像素是在平坦区域还是边缘区域,然后沿着边缘方向插值。
具体做法是:
- 计算梯度:在水平和垂直方向分别计算梯度值
- 判断方向:比较两个方向的梯度大小,确定边缘方向
- 定向插值:沿着边缘方向取像素做插值,避免跨边缘平均
// 边缘导向插值伪代码
// 以 G 通道插值为例
// 1. 计算水平和垂直梯度
grad_h = |G(i, j-1) - G(i, j+1)|
grad_v = |G(i-1, j) - G(i+1, j)|
// 2. 判断方向
if (grad_h < grad_v) {
// 水平边缘,沿水平方向插值
G(i, j) = (G(i, j-1) + G(i, j+1)) / 2
} else if (grad_v < grad_h) {
// 垂直边缘,沿垂直方向插值
G(i, j) = (G(i-1, j) + G(i+1, j)) / 2
} else {
// 平坦区域,用四个方向平均
G(i, j) = (G(i-1, j) + G(i+1, j) + G(i, j-1) + G(i, j+1)) / 4
}
这个方法比前两个好很多。我做过对比测试,在 Kodak 标准测试集上,边缘导向插值的 PSNR 比双线性高 3-5 dB。但问题在于——它只考虑了 G 通道,R 和 B 通道还是用传统方法插值,所以伪色问题依然存在。
3.4 Hamilton-Adams 算法:引入色差概念
Hamilton-Adams 算法(1997 年提出)是传统去马赛克算法的一个里程碑。它的核心思想是:利用色差(R-G, B-G)的平滑性来指导插值。
为什么色差是平滑的?你想想看,在自然图像中,相邻像素的颜色变化通常是连续的。R 和 G 的差值在局部区域变化很慢。这个观察就是 H-A 算法的理论基础。
算法流程:
- 先插值 G 通道:用边缘导向方法,但加入色差修正项
- 再插值 R/B 通道:利用已插值的 G 通道和色差信息
- 迭代优化:对色差图做后处理,去除残留伪影
// Hamilton-Adams 算法核心步骤
// 以 R 像素位置插值 G 值为例
// 水平方向的色差修正
delta_h = (R(i, j-2) - G(i, j-1)) + (R(i, j+2) - G(i, j+1))
// 垂直方向的色差修正
delta_v = (R(i-2, j) - G(i-1, j)) + (R(i+2, j) - G(i+1, j))
// 结合边缘方向选择
if (|delta_h| < |delta_v|) {
G(i, j) = (G(i, j-1) + G(i, j+1)) / 2 + delta_h / 2
} else {
G(i, j) = (G(i-1, j) + G(i+1, j)) / 2 + delta_v / 2
}
这个算法的厉害之处在于:它同时考虑了边缘方向和色差连续性。我在一个工业相机项目中用过 H-A 算法,处理 1200 万像素的图像,在 ARM Cortex-A72 上能做到 30fps,而且图像质量接近当时的中端单反。
3.5 四种算法的对比总结
最后,我用一个表格把这四种算法放在一起对比。你在实际项目中可以根据需求选择:
| 算法 | 核心思想 | PSNR 提升 | 计算复杂度 | 主要缺陷 |
|---|---|---|---|---|
| 双线性插值 | 邻域加权平均 | 基准 | O(N) | 边缘模糊、伪色严重 |
| 双三次插值 | 4x4 邻域多项式拟合 | +1~2 dB | O(4N) | 过冲、计算量大 |
| 边缘导向插值 | 梯度检测 + 定向插值 | +3~5 dB | O(2N) | R/B 通道仍粗糙 |
| Hamilton-Adams | 色差平滑 + 边缘导向 | +5~8 dB | O(3N) | 复杂纹理仍有伪色 |