资讯详情 本地部署LaMa模型:三分钟搭建AI图像修复橡皮擦工具
📅 2026/10/23 10:51:25
前两天翻相册的时候我特别烦——好好一张照片垃圾桶、路人、电线杆全在里面想发朋友圈都得先修半天。以前我得打开修图软件用内容识别填充一点一点蹭现在不一样了我把照片拖进自己搭的小工具里用鼠标刷一扫几秒钟就把碍眼的东西抹干净背景还自动补得跟原图一样。整个过程从想法到跑起来我只花了大概三分钟。这个“AI魔法橡皮擦”本质上是一个本地搭建的智能图像去除工具核心用的是一种叫 LaMa 的图像修复模型。它不需要联网、不消耗在线次数、也不吃高配显卡普通电脑就能跑。这篇文章我把完整的搭建思路、代码、踩坑点全写出来照着操作你也可以在三分钟左右拥有一个能抹掉杂物、水印、电线、行人的本地AI橡皮擦。1. 项目拆解这个“魔法橡皮擦”到底在做什么1.1 一句话讲透图像修复原理很多人一听到AI修图就想到重绘、生成好像特别玄乎。其实“橡皮擦”背后的技术叫图像修复Image Inpainting逻辑非常简单你把照片里想去掉的东西涂成一个区域在AI眼里这个区域就是“缺失部分”它的工作就是用周围的环境线索去猜测缺失的部分应该长什么样再填进去。这跟你小时候玩填色游戏差不多——画纸破了个洞你看着旁边的天空是蓝的、草地是绿的就能把洞补成一样的颜色。AI做的是同一件事只不过它能理解纹理走向、透视关系、光影方向这些更复杂的线索。比如你要抹掉一面墙上的空调外机它能根据墙面砖缝的走向把那些砖纹原样接上而不是填一块简单的模糊色块。这里的“魔法”不在渲染引擎而在那个用来做判断的模型。模型看过海量真实照片知道“墙面通常是什么样子”“地面砖缝常见是横的还是竖的”所以它能输出异常自然的修复结果。1.2 为什么选 LaMa 而不是扩散模型当时我对比过好几类方案最后选了 LaMa不是因为它最贵而是因为它在“大面积涂抹普通硬件快速出图”这三个维度上平衡得最好。扩散模型Diffusion Model这类方案效果确实惊艳尤其是针对复杂物体比如被物体遮挡的完整人体、墙面背后的复杂结构都能脑补得非常合理。但它的硬伤也很明显推理速度慢一张图少说也要十几秒吃显存也凶手里只有4G显存的卡很容易爆掉。而且部署环境复杂装依赖都够喝一壶的。LaMa 全称是 Large Mask Inpainting是专门为“大块涂改”设计的模型。它的拿手好戏正是把大范围遮挡的区域修复得纹理连续。我实测下来一张 1080p 的照片跑一次只要两三秒4G显存就能轻松运行就算纯CPU跑也就十几秒出图。对“快速修一张照片”这种需求来说这个节奏太舒服了。1.3 这套方案能用在哪、不适合用在哪先说实话它不是万能的。我把它的能力边界摸了一遍能很好处理的对象照片里的路人、垃圾桶、路牌、电线杆这类“多出来的物体”水印、日期戳、Logo 和文字墙上的污渍、地面上的杂物风景照里飞进画面的鸟类或无人机处理效果一般或者不适合的对象被整个物体挡住的人物肢体比如一个人站在另一个人前面后面的部分完全不可见填出来是“脑补”痕迹有明显几何结构的场景比如人脸、车牌、复杂建筑线条修复后可能出现轻微的纹理错位想“无中生有”造内容比如让照片里多一只狗——这不是它的活我的用法总结是要抹掉“干扰项”用它要创造“新内容”得换扩散模型。搞清楚这个区别你就不会拿它乱来了。2. 三分钟上手的环境准备2.1 Python 虚拟环境与依赖清单先说结论整个依赖只有几个包不需要搞什么重型框架。我当时用 Python 3.10 的虚拟环境实测下来最稳。为什么不推荐 Python 3.11主要是 PyTorch 在某些低版本 CUDA 环境下编译出来的 wheel 对 3.11 支持有坑为省事直接用 3.10。创建虚拟环境的命令python3.10 -m venv venv source venv/bin/activate然后是依赖清单直接用 pip 装pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install gradio4.44.1 opencv-python pillow numpy albumentations hydra-core pytorch-lightning这里有几个版本相关的细节值得记住包名版本建议用途gradio4.44.1 或同批次 4.x提供绘图涂抹界面低于 4.o 的 API 差别很大torch/torchvision2.x 对应 CUDA模型推理的主力框架opencv-python最新即可图像读写、mask 膨胀、后期融合albumentations1.3 左右LaMa 数据预处理时用的增强工具推理时也会调用hydra-core1.3 左右读取模型配置文件路径写错会直接白屏注意Gradio 的版本别乱升级。我吃过亏到 5.x 之后 ImageEditor 组件的返回结构变了代码里取 mask 的方式得跟着改不如直接锁在 4.44.1省心。2.2 模型权重下载与目录摆放LaMa 官方没有给你现成的推理脚本但社区里有不少打包好的权重文件搜索“big-lama checkpoint”或者“LaMa Pytorch 权重”就能找到。我用的是一套包含两个模型的权重组合Big LaMa 512主模型处理粗略的修复填充LaMa Refine精修模型把填充区域的边缘和纹理磨得更自然下载之后目录结构我建议这样摆跟脚本读取逻辑对应lama_eraser/ ├── venv/ ├── app.py └── models/ ├── big-lama/ │ ├── config.yaml │ └── big-lama-regular.pt └── lama_refine/ ├── config.yaml └── refine.pt权重文件的文件名和 config.yaml 里的读取路径是严格对应的别乱改名。我一开始图省事把 refine.pt 改成了 model.pt结果 hydra 加载配置时直接给我抛 KeyError折腾了十分钟才发现是目录和配置不匹配。2.3 没有独显也能跑CPU 推理的取舍这套东西对硬件的要求比想象中低。我用纯 CPU 的 MacBook Air 实测过一张 1200 像素见方的图推理一次大约 12 秒换成 N 卡哪怕是 1650 这种老卡也能控制在 3 秒内。如果你只有 CPU也不影响体验只是点完“运行”要稍微等一会儿。代码里设备选择这样写device cuda if torch.cuda.is_available() else cpu但注意纯 CPU 跑的时候建议把输入图的长边缩到 1024 以内不然预处理和推理加起来可能要两分钟体验很差。3. 核心推理代码与橡皮擦界面实现3.1 模型加载与预处理脚本直接上代码。下面的脚本是我自己在用的版本注释都写清楚了复制后改一下模型路径就能跑通。import os import cv2 import torch import numpy as np from PIL import Image import gradio as gr import torch.nn.functional as F # 模型路径按上面的目录结构调整 BASE_DIR os.path.dirname(os.path.abspath(__file__)) DEVICE cuda if torch.cuda.is_available() else cpu def load_model(): 加载 LaMa 主模型和 Refine 精修模型 big_lama torch.jit.load( os.path.join(BASE_DIR, models/big-lama/big-lama-regular.pt), map_locationDEVICE, ).eval() refine torch.jit.load( os.path.join(BASE_DIR, models/lama_refine/refine.pt), map_locationDEVICE, ).eval() return big_lama, refine BIG_LAMA, REFINE load_model() def preprocess(image: np.ndarray, mask: np.ndarray, max_side: int 1024): 把原图和 mask 统一缩放到模型输入尺寸 h, w image.shape[:2] scale max_side / max(h, w) if scale 1.0: new_w, new_h int(w * scale), int(h * scale) image cv2.resize(image, (new_w, new_h), interpolationcv2.INTER_LINEAR) mask cv2.resize(mask, (new_w, new_h), interpolationcv2.INTER_NEAREST) # LaMa 要求输入长宽是 32 的倍数不足的用边缘填充 pad_h (32 - image.shape[0] % 32) % 32 pad_w (32 - image.shape[1] % 32) % 32 image cv2.copyMakeBorder(image, 0, pad_h, 0, pad_w, cv2.BORDER_REFLECT_101) mask cv2.copyMakeBorder(mask, 0, pad_h, 0, pad_w, cv2.BORDER_CONSTANT, value0) # 归一化到 0~1 并转成 Tensor image_t torch.from_numpy(image.astype(np.float32) / 255.0).permute(2, 0, 1).unsqueeze(0) mask_t torch.from_numpy(mask.astype(np.float32) / 255.0).unsqueeze(0).unsqueeze(0) return image_t.to(DEVICE), mask_t.to(DEVICE), pad_h, pad_w, scale这段代码里有几个关键点我特别说一下mask 的 resize 插值必须用INTER_NEAREST。如果用线性插值边缘会出现半透明的灰色过渡区域模型会把这种模糊区域当成“半遮挡”修复结果边缘会很脏。填充用BORDER_REFLECT_101这是 OpenCV 的反射填充模式对边缘纹理的延续很有帮助。设备统一放在DEVICE变量里换机器只需要改这一处。3.2 Gradio 交互怎么把画笔变成遮罩界面我用的是 Gradio 的ImageEditor组件它自带画笔分层功能。你可以理解为一张纸上面盖了一层透明膜你在膜上涂红色的地方就是要去除的区域底层原图完全不动。核心交互代码def erase_objects(image: dict, mask: np.ndarray): if image is None: return None if mask is None or np.max(mask) 1: return image # Gradio ImageEditor 返回的 image 是包含背景层的 dict通常取 background if isinstance(image, dict): image image[background] if isinstance(image, dict): image image[composite] orig image.copy() # 把 mask 统一成 0/255二值化 mask (mask 10).astype(np.uint8) * 255 # 形态学膨胀让涂抹区域稍微扩一圈防止修复边缘出现残留 kernel np.ones((5, 5), np.uint8) mask cv2.dilate(mask, kernel, iterations2) image_t, mask_t, pad_h, pad_w, scale preprocess(image, mask) with torch.no_grad(): # 先用大模型粗修 coarse BIG_LAMA(image_t, mask_t) # 这里注意输入给 refine 的是完整图mask初步结果 refined REFINE(image_t, mask_t, coarse)[0] result refined.squeeze(0).permute(1, 2, 0).cpu().numpy() result (result * 255).clip(0, 255).astype(np.uint8) # 去掉预留的 padding还原到缩放后的尺寸 if pad_h 0: result result[:-pad_h, :] if pad_w 0: result result[:, :-pad_w] # 缩放回原图尺寸 if scale 1.0: result cv2.resize(result, (orig.shape[1], orig.shape[0])) # 用 mask 做融合保留原图非涂抹区域 mask_resized cv2.resize(mask, (orig.shape[1], orig.shape[0]), interpolationcv2.INTER_NEAREST) mask_3 (mask_resized / 255.0)[:, :, None] final (orig * (1 - mask_3) result * mask_3).astype(np.uint8) return final这里要特别讲清楚一个细节为什么要做“融合”这一步而不是直接用整张修复图因为 LaMa 是把整张图都跑了一遍理论上它会全图重绘但只保证遮罩区域修改得最多。为了让未涂抹区域的原图像素完全不变我就把“原图”和“模型输出”按 mask 权重做叠加。不属于涂抹区域的地方权重为0输出的就是原图涂抹区域内权重为1输出的就是修复后的结果。这个操作可以让你对比前后图时除了目标区域外其他地方零变化。Gradio 的界面部分很简单with gr.Blocks() as demo: gr.HTML(本地 AI 橡皮擦) with gr.Row(): with gr.Column(): img_in gr.ImageEditor(typenumpy, layersTrue, label拖入图片然后用画笔涂抹要去除的区域) run_btn gr.Button(开始修复, variantprimary) with gr.Column(): img_out gr.Image(typenumpy, label修复结果) run_btn.click(erase_objects, inputs[img_in, img_in], outputsimg_out) demo.launch(inbrowserTrue)注意到我把同一个img_in同时传给了erase_objects的两个参数一个是图像层一个是蒙版层。Gradio 的 ImageEditor 组件返回时会同时给出绘制层的数据这样正好映射到代码里的image和mask。3.3 后处理细节决定成败后处理这部分最容易被忽略但它是最终出图质量的分水岭。我前几次跑出来的结果总有几个问题涂抹边缘有一条白边、整张图颜色偏灰、未涂抹区域居然也被改动。后来逐个排查发现都是后处理姿势不对。先说白边问题。模型输出的结果在 mask 边缘往往有一圈过渡区域如果不处理放大看就像贴了个补丁。解决办法有两个用膨胀操作把 mask 稍微扩大让模型多修一点边缘地带最后融合时把 mask 做一次高斯模糊让过渡更柔和。我上文的代码里cv2.dilate就是干这个的。膨胀两轮差不多就够了多了会把不该修的区域也修进去少了边缘还是会露馅。这个参数其实是个经验值不同图可以微调。再说整图偏色。如果直接拿模型输出当最终结果偶尔会发现整张图饱和度低了一点。原因在于模型训练时对输入做了归一化输出时虽然从 0~1 还原回 0~255但色域映射跟原图 SOP 不一定完全一致。所以我在最后融合前用原图作为“底色”确保大范围背景颜色保留原图的风格。最后说未涂抹区域被改动。这个问题多半是 mask 没有正确二值化导致的。如果 mask 里有浅灰色像素模型的输入会被错误地理解成“半修复区域”输出就会连带周边一起变。二值化和膨胀这两步一个都不能省。3.4 顺手加一个批量处理的隐藏功能只有单张处理的界面用了几次之后就不满足了。尤其是处理一整批带水印的素材图时一张张拖进去点按钮太磨人。我后来顺手加了批量模式直接指定文件夹把固定区域比如角落Logo自动生成 mask 批量跑。逻辑其实不复杂同一批图的水印位置基本固定你只要提供一张示范 mask然后对整个文件夹循环调用同一个推理函数就行。这个功能不强求但建议你保留个扩展接口后续工作量会省很多。4. 实战效果与踩坑记录4.1 实测三张图的清理效果我拿三张典型图片跑了一遍感受很直观第一张是街景照片画面边缘有一根电线杆和几根拉线。涂抹修复大概4秒电线杆消失后后边的楼体边缘接得很好肉眼几乎看不出嫁接痕迹。第二张是桌面杂物照抹掉桌面上的一瓶矿泉水桌面木纹的走向保持得不错但瓶子的高光反射处留下了一点淡淡的亮斑放大到100%才看得出来。第三张是人物照片我想抹掉背景里一个拍糊的路人效果可以用“惊了”形容路人消失后露出的墙面纹理非常统一毫无违和感。总体评价对“多出来的物体”清除这个工具是实用级的但如果目标物体后面是高频纹理树叶、窗帘褶皱偶尔会出细小的波浪纹重新涂抹一次通常能修复。4.2 高频报错排雷清单我把自己踩过的坑和群友问过的问题整理成了表格报错现象原因解决办法启动报错找不到 config.yamlhydra 默认读取当前工作目录models 目录不在脚本同级确认目录结构按上文摆放或在代码里指定绝对路径torch.cuda.is_available() 返回 False安装的是 CPU 版 torch重新安装对应 CUDA 版本的 torch或直接跑 CPU 模式推理显存溢出输入图太大模型尺寸超过显存把 max_side 调到 768 或 512同时关掉其他占显存程序Gradio 界面画笔画不上ImageEditor 组件版本不匹配锁定 gradio4.44.1 重新安装修复后涂抹区域一片黑归一化时忘记把图像转成 float检查astype(np.float32) / 255.0这一步是否漏掉边缘有明显的方块感resize mask 时用了线性插值改成INTER_NEAREST并做二值化4.3 遇到抹不干净时怎么办这是最常被问到的问题“我涂了也跑了但东西还在边缘冒头怎么回事”我的经验是先排查两件事。第一mask 是不是完全覆盖了目标物体很多时候你看着屏幕觉得涂满了实际上目标物体的高光阴影部分超出了画笔范围AI 不知道这里也要修。第二目标物体是不是跟背景颜色太像比如白色栏杆在浅灰墙前面模型很难判断“栏杆”和“墙”的边界修复结果就会选择把栏杆当成墙的一部分保留下来。解决办法是把涂抹区域画得比目标物体大一圈或者分两次修。第一次涂大范围把整个干扰区域粗修掉第二次只涂第一次留下的瑕疵精修。这不是模型能力问题而是使用手法。任何橡皮擦工具哪怕是手工PS也会遵循这个逻辑。4.4 为什么不建议直接用在线工具我自己最开始也是用在线工具点两下就把图删了确实省心。但用多了就发现两个问题让人难受一是免费次数有限制用几次就要充值或者等广告二是有隐私顾虑修图必须先上传原图有时候修的是身份证扫描件、内部资料照片传上去总觉得心里不踏实。本地部署的好处是一次投入、永久免费而且所有图像都只在你的电脑里跑。3分钟换来的这个“AI橡皮擦”本质上是把主动权拿回自己手上。以后不管是修朋友圈照片还是批量处理素材想到就能开不需要排队也不需要担心时效问题。我个人在实际操作中的体会是这套东西最值钱的不是“省了几十块钱”而是那种“想怎么改就怎么改”的自由度。有一次我修一张活动合影全场几十号人背景板被拍进去一个灭火器这个项目里没法AI生成新的背景但用这橡皮擦抹掉灭火器不过几秒钟整张照片立刻干净了。这种小需求以前要么求人要么开PS慢慢磨现在三分钟搭好的工具随叫随到。最后再多说一个小技巧你可以把preprocess里的max_side改成 512修图速度还能再快一截适合大批量清稿时用如果是要放大打印或仔细修细节的照片建议保持 1024。这个参数就是精度和速度之间的天平按需调就好。