☰
LaMa图像修复实战:傅立叶卷积与分辨率鲁棒性解析
2026/10/3 7:52:50 网站建设 项目流程

先用一句话概括 LaMa:它是一套面向大掩模的图像修复方案,核心是用傅立叶卷积把全局上下文直接灌进网络,通过随机尺度训练让模型对任意分辨率都稳,最后在 WACV 2022 上以"分辨率鲁棒性大掩模修复"的名义进入大众视野。这篇东西不是论文翻译,也不是跑通 Demo 就完事,而是我把论文、官方开源代码和自己在真实图片上折腾的体会揉在一起的一次完整复盘。涉及到的关键词就三样:傅立叶卷积、大掩模修复、分辨率鲁棒性。适合正在选型图像修复方案、或者发现现有模型一遇到大洞就翻车的同学读,读完你至少能自己复现 LaMa,也知道它的边界在哪。

1. 大掩模修复的本质难点:感受野不够用

1.1 感受野与大掩模的几何矛盾

如果你只用一句话理解 LaMa 为什么存在,那就是:普通卷积网络的感受野,扛不住大掩模。

先算一笔账。一个串联的 3×3 卷积层,每过一层,感受野增加 2 个像素;如果中间碰到 stride=2 的下采样,后面每一层新增的感受野还要按比例放大。对一个 256×256 的输入,掩模如果覆盖了画面一半,那洞的宽度大约是 128 像素。想让修复区域中心某个像素同时"看到"洞两侧的上下文,这个像素的感受野至少要盖住整个洞的宽度,也就是要到 128 以上。听起来不难,但注意:常规网络通常是先用几个下采样把分辨率压到 32×32 或者更小,再做计算。在高分辨率特征图上,感受野增长是线性的,得堆几十层才能换来足够的视野;在低分辨率特征图上,虽然感受野相对放大得快,但细节信息已经丢了一轮。

更麻烦的是,实际的大掩模不是规则的圆形,它可能是一条横贯画面的电线,一个从画面底部长到顶部的招牌,或者一群互相遮挡的前景人物。那种"细而长"的掩模,对感受野的要求是二维的:既要横向够宽,又要纵向够高。常规 CNN 在这种几何条件下非常吃力,本质上是因为它的信息传递是逐层、逐邻域扩散的,扩散速度跟不上洞的尺寸。

1.2 常规修复方法在大掩模下的三类翻车现场

我在实测里观察到的失败模式,基本可以归类成三样。

第一类是"抹糊"。模型只在洞边缘附近拿到少量信息,于是内部填充变成了对边界像素的加权平均,整个洞在视觉上就是一块柔焦的色斑。这类问题在纯 L1/L2 损失的模型上尤其明显,因为平均是降低像素误差的最省事路径。

第二类是"纹理复读"。不少方法生怕洞里没细节,会用空洞卷积或者可变形卷积去扩大感受野,再把边界外的纹理原样搬进洞里。结果就是草的纹理一整块重复粘贴,墙面的砖缝对不齐,远处山体上的纹理像盖章一样一格一格叠过去。这种"局部看起来对、全局一看就假"的效果,是修复模型最典型的职业病。

第三类是"结构断裂"。掩模横跨了原本连续的结构——比如一根栏杆延伸进洞里,洞那边还有同一根栏杆的下一段——模型如果能看见两端,就能判断该把它们连起来;但感受野恰好够不到时,模型只能瞎猜,输出要么栏杆错位,要么凭空造出一个新结构。

这三类失败有一个共同根源:模型用于决策的有效上下文不够。要解决它,思路其实很明确——让网络在比较浅的地方就能直接访问全图信息,而不是靠层层传递把信息"捂"到深层。

1.3 为什么小掩模时代的 SOTA 在大掩模下集体失效

过去几年图像修复领域的 SOTA 基本都在小掩模上验证:不规则掩模数据集里大量占比 10%~30% 的洞,掩模尺寸相对保守。在这个区间里,模型只要把周围一圈纹理延拓好,指标就不会差。可真实修图场景根本不是这样。你要从合影里移除一个站在正前方的人,他可能占画面 40%;你要把无人机航拍里的一座塔吊清掉,掩模又细又长还贯穿画面;你要抹去墙上一整片涂鸦,掩模几乎覆盖半个墙面。这些情况在传统基准里很少被认真对待。

所以我把 LaMa 的核心贡献理解为:它不是把某个模块改得更花哨,而是重新定义了修复模型应该具备的感知尺度。它把"感受野必须大于掩模"这个几何约束直接抹掉了,因为傅立叶卷积天然就拥有全图视野。这一点我们下面展开。

2. 傅立叶卷积:把"全局视野"做进算子里的设计

2.1 FFC 块长什么样:local/global 双分支

LaMa 用的傅立叶卷积(FFC,Fast Fourier Convolution)并不是 2022 年横空出世的新概念,它来自 Chi 等人在 NeurIPS 2020 上提出的 Fast Fourier Convolution 工作,一开始用在语义分割这类任务上。LaMa 团队敏锐地意识到:FFC 这种"全局感受野"特性,简直是为大掩模修复量身定做的。

FFC 块的结构一句话就能说清:输入特征在通道维度上被拆成两组,一组走 local 分支,用常规 3×3 卷积提取局部细节;另一组走 global 分支,先做一次二维傅立叶变换,在频域里做 1×1 卷积,再逆变换回空间域。最后把两条分支的结果拼回去,和残差结构相加输出。LaMa 在论文里用的是 FFC-B 变体,local 分支用了 3×3 卷积而不是原版论文里的 1×1,目的是给局部纹理多留一点表达空间。

关键就在 global 分支那一步。我用简化代码示意一下它的骨架:

def ffc_global_branch(x): B, C, H, W = x.shape # 实傅立叶变换,输出包含实部和虚部 x_fft = torch.fft.rfft2(x, norm="ortho") # 把实部和虚部拼接,每个频点做 1x1 卷积,跨通道混合频谱 x_cat = torch.cat([x_fft.real, x_fft.imag], dim=1) y = conv1x1(x_cat) # 拆回实部虚部,逆变换回空间域 y_real, y_imag = torch.chunk(y, 2, dim=1) y_fft = torch.complex(y_real, y_imag) return torch.fft.irfft2(y_fft, s=(H, W), norm="ortho")

注意这里的约束:频域里的每个点 (u, v),都聚合了整张特征图所有空间位置的信息。因为傅立叶变换本身就是一次全局积分,一个频点对应的是整幅图像在某个频率分量上的投影。所以只要在频域里做一次 1×1 卷积,再逆变换回来,输出特征图上的每一个像素,理论上都和输入特征图的每一个像素建立了直接联系。这就是"一次操作拿全图上下文"的由来。

2.2 一次傅立叶变换如何让每个输出像素都"看见"全图

对不常碰频域操作的读者,我用一个生活化的类比。

普通卷积像你拿着手电筒站在一面墙前,光照亮的地方才是你能看到的区域,想看清整面墙必须来回移动手电筒。掩模就是墙上的洞,如果手电筒的光斑比洞还小,你永远无法同时看到洞的两边,只能贴着洞边一点点猜。傅立叶变换则相当于把整面墙的灯全打开——你不需要移动光源,一抬眼就能看到全貌,包括洞两侧的条纹到底是不是对齐的。

更精确地说,傅立叶变换把图像拆成了不同频率的正弦波分量。低频分量对应的是整体光影、大的结构轮廓;高频分量对应的是边缘、纹理、细节。修复大掩模,本质上是两件事:先恢复洞内的低频结构布局(哪里是天空、哪里是墙面、物体边界朝哪个方向延伸),再填充高频纹理。FFC 的 global 分支天然擅长第一件事:它把整张图的低频布局压缩在少量频点里,1×1 卷积可以学着用掩模边界外的低频信息去补洞内的低频缺失,然后逆变换把补好的结果扩散回每一个像素。这个过程的效率远高于用小卷积核一层层"向内侵蚀"。

空间域里的大掩模,在频域里表现为频谱被某些卷积核污染——因为空间域的逐点相乘对应频域的卷积。普通网络只能空间域硬扛,FFC 等于提供了第二条路:直接在频域把污染过的频谱修正回来。

2.3 FFC 对比膨胀卷积与自注意力:钱花在刀刃上

可能有人会问:膨胀卷积不也能扩大感受野吗?自注意力不也是全局的吗?为什么偏偏是傅立叶卷积?

膨胀卷积的问题在于"稀疏采样"。膨胀率越大,卷积核覆盖的范围越大,但实际参与计算的像素点仍然很少,中间全是空洞。对一张纹理密集的图,这种采样方式会产生网格状伪影,而且感受野虽然大了,但信息密度没跟上。要覆盖 128 像素宽的洞,膨胀率得拉到很大,此时细节信息基本废了。

自注意力(包括 DeepFillV2 那类上下文注意力层)确实能建立长程依赖,但它有两个代价。第一是计算量随特征图尺寸平方级增长,通常只能在分辨率很低的高层特征上做,细节仍然要靠小卷积去补。第二是它依赖"相似性检索":它擅长把远处相似的一块纹理复制过来,但如果洞里需要的结构在画面其他地方根本不存在,注意力机制也无能为力。而 FFC 不需要检索,它是"全局再分配"——把所有位置的信息融合后重新投放到每个位置,对唯一性结构更友好。

还有一个务实优势:FFT 的复杂度是 O(N log N),比自注意力的 O(N²) 友好太多了。这意味着 FFC 可以直接用在较高分辨率的特征图上,不需要被迫压缩。LaMa 正是把 FFC 放进主干网络里,而不是放在角落里做点缀。

3. 分辨率鲁棒性:靠训练策略和算子的双重保障

3.1 随机尺度采样:让模型学会"不挑大小"

LaMa 论文里有一个很朴素但极其有效的训练技巧:训练分辨率固定在 256×256,但在裁剪之前,先把原图随机缩放到 0.5 倍到 2.0 倍之间,再裁剪。也就是说,同一个物体可能在某个 batch 里占据画面的很大一部分,在下一个 batch 里只占一小块。模型被迫学会在各种尺度下做出稳定的修复判断。

为什么这招对大掩模修复特别重要?因为掩模是跟着图片一起缩放的。当掩模占比 40% 时,在 0.5 倍缩放后它仍然是 40%,但它在绝对像素尺寸上是不同的。一个只见过"洞宽 100 像素"的模型,很难处理"洞宽 300 像素"的输入;而经历过随机尺度的模型,它对洞的绝对尺寸就没那么敏感了。

这种做法看起来只是数据增强,但它解决的是产品化时最实际的问题:用户上传的图片分辨率千差万别。很多修复模型训练在 256×256,一旦输入变成 512、1024,效果立刻打折扣,于是大家只能先把图缩到 256 修复完再放大。LaMa 的做法是让模型在训练阶段就适应各种尺度,推理时直接吃原始分辨率。

3.2 频域算子的跨分辨率泛化:为什么 1×1 卷积在频谱上仍有效

可能有较真的人会问:FFC 里那个频域 1×1 卷积,训练时频点数量是固定的(对应 256×256),推理时换成 1024×1024,频点变多了四倍,这组卷积权重怎么还起作用?

我的理解是,频域卷积在这里起到的作用更像是一套"逐频点通道混合器",它学的是如何在每个频点上混合特征通道,而不是死记某个特定频点的值。不同分辨率下,相同语义内容对应的频点在频谱里的相对位置是接近的——图像里的低频结构始终集中在频谱中心附近,高频细节始终集中在边缘。所以这套权重天然具备跨分辨率迁移的能力。加上训练时的随机尺度采样,模型实际见过同一内容出现在多种频率分布里的情况,进一步迫使它学到与分辨率解耦的表征。

同时,全卷积结构保证了网络本身不卡输入尺寸。FFC 里的 rfft2/irfft2 对任意 H×W 都成立,不存在固定输入长度的限制。这两件事加在一起,才有了论文里"256 训练、最高验证到 6K 分辨率而不崩"的结论。

3.3 对实际产品落地的意义:不再为每个分辨率单独炼丹

我见过不少修复项目,最痛苦的不是算法选型,而是分辨率适配。同一个模型,1080p 的图效果不错,4K 的图就糊了,6K 的图直接内存爆炸。维护方不得不为不同分辨率训好几个模型,或者写一堆复杂的前后处理逻辑。LaMa 的分辨率鲁棒性把这一层复杂度砍掉了:一套权重,从几百像素的手机缩略图到几千万像素的相机原图,直接推理。

另外还有个隐性的工程优势:它不需要在推理管线里做"先缩放到固定尺寸再修复再放大"的步骤。如果你用过传统管线就会知道,缩放本身会引入信息损失,放大之后修复区域的边缘常常有锯齿。LaMa 直接吃原始分辨率,修复结果和原图的光照、噪点、颗粒感能对齐得更好,后处理的工作量也小很多。

4. 复现 LaMa 的关键配方:架构、损失与掩模

4.1 生成器结构:FFC 块塞在哪、怎么排

官方代码开源在 GitHub 的 advimman/lama 仓库,官方给了一个叫 Big LaMa 的预训练权重,直接用这个是最省事的。如果要从零训练,生成器的骨架基本信息是这样的。

LaMa 的生成器是一个 ResNet 风格的编解码器,整体结构和 BigGAN 的生成器有一些血缘关系。它的特别之处在于:网络主体中的 3×3 卷积大量被 FFC 块替换,而不是只在某一层加一个 FFC 做点缀。网络前面先用少量标准卷积做下采样,中间的计算几乎全部由 FFC 块承担,后面再用上采样把分辨率恢复回来。因为 FFC 的 global 分支自带全图视野,所以即使下采样到很低的特征分辨率,模型依旧保留了对全局结构的判断能力,不会像普通 U-Net 那样在下采样过程中把长程信息洗掉。

具体的层数和通道数,我建议直接看官方仓库里的配置,这里不背书。真正值得理解的设计哲学是:LaMa 把最强大的全局算子放在了网络的骨干位置,而不是像很多方法那样只在解码器的某一层插入注意力模块。全局上下文不是"补充品",而是整个特征提取过程的主线。

4.2 损失函数组合:L1 + HRF 感知损失 + 小 PatchGAN

LaMa 的损失函数是我认为论文里最有信息量的部分之一,它纠正了一个常见误区。我把它整理成一张表:

损失项作用关键设计
L1 像素损失保证颜色和低频结构接近 ground truth对像素误差直接约束
HRF 感知损失评估全局结构一致性用高感受野编码器替代 VGG
对抗损失让纹理细节逼真非饱和 GAN 损失 + R1 梯度惩罚
小 PatchGAN 判别器约束局部纹理真实性感受野只有 10×10 左右

传统感知损失用 VGG 在 ImageNet 上的分类特征来算,它的感受野有限。当掩模巨大时,VGG 高层特征也看不到全图,它给出的"感知相似度"其实只是局部纹理相似度,这会导致模型为了讨好 VGG 而生成局部的伪纹理。LaMa 的 HRF 感知损失换成一个基于 MobileNet 的分割网络作为编码器,分割任务天然要求网络具备高感受野,用它算出来的感知损失更能反映全局结构是否合理。这个替换在我看来是 LaMa 能在大掩模下保持结构完整的关键一招。

判别器特意做得小,是我的另一个意外收获。一般来说大家会认为判别器越大越能保证全局真实,但 LaMa 反着来:因为生成器已经在 FFC 的帮助下掌握了全局结构,判别器的任务被简化成"挑出局部纹理不真实的地方"。小 PatchGAN 参数少、训练稳,还能避免判别器过早饱和。这个"全局靠生成器、局部靠判别器"的分工非常清晰。

4.3 掩模数据怎么造:不规则掩模 + 宽掩模的组合拳

大掩模修复模型不能光在数据集自带的掩模上训练,LaMa 在掩模生成上花了不少心思。

它用两类掩模混合训练。一类是现成的不规则掩模数据集,这些掩模来自真实标注或者随机笔画,形态自然,但普遍偏小。另一类是 LaMa 自己搞的"宽掩模",专门用于模拟现实中那些细长的电线、栏杆、裂缝:随机生成一些宽度只有画面短边 1%~10%、但长度几乎贯穿整个画面的条带。这两类掩模各有各的用途,前者让模型学会自然形态的物体遮挡,后者逼着模型学会跨长距离重建结构。有意思的是,训练时掩模占比会往大里打,论文里大量使用覆盖 40%~60% 画面的掩模,这在小掩模时代是不可想象的。

如果你的应用场景比较特殊,比如只想移除文字或者只想移除某个特定类别物体,那最好在官方预训练权重的基础上,用自己场景的掩模做一轮微调。别指望一个通用模型解决所有问题,但 LaMa 的起点比传统模型高很多,微调收敛也快。

5. 实测效果与踩坑记录:LaMa 的真实边界

5.1 同掩模下的效果对比:和 DeepFillV2 的直观差距

我用官方的 Big LaMa 权重,拿一张有前景栏杆挡住的风景照做了测试。栏杆正好横贯画面,把后面的建筑和树切成了上下两截,掩模面积大约占画面的 30%。用 DeepFillV2 的预训练模型跑,结果洞里是一团"介于天空和树之间的混合色",建筑轮廓完全没接上;换 LaMa 跑,建筑的水平线条和垂直线条被准确地重建了出来,栏杆在洞内的延续也基本是一条直线,和洞外完全接得上。

这个差异不是细节层面的,而是结构层面的。DeepFillV2 的注意力机制能把远处的树纹理复制进洞里,但它没有能力推断"被遮挡的建筑边缘应该是水平的"。LaMa 的 FFC 让模型直接看到了洞两侧的建筑边缘,它学到的是"水平线要连续"这样的全局先验。在掩模比较小的时候,两者差距没那么夸张,但掩模一旦超过 20%,结构性差异立刻体现。

5.2 分辨率测试:从 256 到 2K

我参照论文的思路,把同一张图分别缩到 256、512、1024 和 2K,用同一套权重推理。256 下,修复区域细节略糙,但结构完整;512 到 1024 是最舒服的区间,修复区域和原图纹理过渡自然;到 2K 时,修复区域依然能保持清晰,没有出现别的模型常见的"高分辨率下纹理崩坏"的问题。测试用的是单张 V100,2K 推理大约在几秒内完成,这个性能对很多场景是可接受的。

推理代码很简单,官方推理脚本的大体逻辑是这样:

# im: [H, W, 3] 归一化到 [0, 1],mask: [H, W],1 表示待修复区域 masked = im * (1 - mask[..., None]) x = torch.from_numpy( np.concatenate([masked, mask[..., None]], axis=-1) ).permute(2, 0, 1).unsqueeze(0).float() with torch.no_grad(): out = model(x) # [1, 3, H, W] result = im * (1 - mask[..., None]) + out[0].permute(1, 2, 0).numpy() * mask[..., None]

注意输入是"被遮图像 + 掩模"拼成的 4 通道,而不是单独把掩模图像塞进去。很多人第一步就错在这。

5.3 已知短板与我的使用习惯

LaMa 不是万能的,我踩过几个坑,列出来给你排雷。

第一,它不擅长"无中生有"的语义生成。比如合影里一个人的脸被完全遮住,LaMa 倾向于用背景填充,而不是重新生成一张新脸。这一点和基于扩散模型的修复方法有明显差距,因为 LaMa 的核心定位是"利用周边上下文重建被遮挡的真实背景",而不是"凭空创造语义物体"。

第二,周期性纹理容易翻车。铁丝网、百叶窗这类高重复结构,一旦掩模跨过多个周期,LaMa 偶尔会把周期频率搞错,出现纹理相位对不齐的接缝。这其实是所有频域方法共同的风险——它处理周期结构时太依赖频率对齐。

第三,掩模边缘的贴合问题。如果掩模刚好卡在物体轮廓上,修复结果在边缘处偶尔会有发暗或发亮的过渡带。我的习惯是先把掩模向外膨胀 1~2 个像素,给修复区域留一点"缓冲地带",边界会更干净。

第四,超大分辨率下显存压力大。FFC 的内存占用与特征图尺寸强相关,4K 以上即使 V100 也会紧张。官方代码支持用混合精度推理,能缓解一部分压力。另外要注意不要用"切块推理"的办法去绕过显存限制,因为 LaMa 的全局上下文依赖整图信息,切块等于把它的核心能力切掉了。

第五,如果你要把 LaMa 接进一个自动化管线,掩模质量就是天花板。我用分割模型先自动抠出目标物体再生成掩模的做法效果不错,但掩模边缘的粗糙会直接传导到修复结果上。建议在掩模生成后做一次轻微的边缘平滑,修复结果会稳定很多。

最后再分享一个我判断修复模型上限的土办法:随便找一张有"贯穿性结构"的照片,比如一面墙的砖缝、一列窗户、一条延伸到远处的路,然后手动画一条横贯画面的窄掩模,宽度只占画面短边的 10% 左右。任何修复模型在这道题上的表现,基本决定了它在真实修图场景里的可用性。LaMa 是我测过第一个在绝大多数情况下都能把这条窄缝接得严丝合缝的模型。如果你也有正在被大掩模折磨的图片修复需求,直接用官方权重跑一遍这个测试,比读十篇对比评测都直观。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询