97 个 OpenCV 实例(十四):图像分割,GrabCut 抠图 + inpaint 去水印

上一篇让程序「盯住」了运动的东西。这一篇换两个反方向的活------一个把前景从背景里抠出来,一个把划痕水印填回去。图像分割和图像修复,看起来一进一出,其实用的是同一套底层思想:让程序理解「这块像素属于哪」。而 OpenCV 里这两个功能的代表作,就是 GrabCut 和 inpaint。

一、效果先行

先看抠图。左边原图用绿框框住球员,右边三种配置的抠图结果------第一张 1 次迭代还残留着黑色块状伪影,第二张 5 次迭代干净了但足球被当成背景丢了,第三张手动涂了几笔后足球保住了:

再看去水印。我在水果图上程序化划了三条划痕、盖了一个水印块,然后让两种算法各自修复:左边 TELEA、中间 NS,几乎看不出差别,右边是把修复误差放大 10 倍的热图------划痕处最亮,说明那里最难修:

两张图记住两件事:GrabCut 的矩形框和掩码两种玩法结果可能完全不同;inpaint 只能「糊」不能「还原」。

二、GrabCut:把图「割」成两块

GrabCut 名字里的 「Cut」 是图割(Graph Cut)。它把整幅图像建模成一张图:每个像素是一个节点,相邻像素之间连边,每个像素还和「前景」「背景」两个虚拟终点各连一条边。边的权重编码了像素间的颜色相似度和「它更像前景还是背景」的先验。

然后做最小割------找一组边,切断后把图分成前景、背景两块,且割掉的边总权重最小。翻译成人话:前景和背景的边界,恰好落在颜色差异最大的地方

OpenCV 的实现里,前景和背景的颜色分布各自用一个高斯混合模型(GMM)建模。它不停循环「用当前分割结果重新估计 GMM → 用新 GMM 重新切图」,迭代次数越多,边界越精细。

三、矩形框 vs 掩码:足球去哪了

GrabCut 有两种初始化方式,这是本课最有意思的区别:

cpp 复制代码
// 方式一:矩形框初始化(最省事,用户框一下就行)
grabCut(img, mask, rect, bgdModel, fgdModel, 5, GC_INIT_WITH_RECT);
// 框外自动当背景,框内当"可能前景",靠 GMM 自动分辨

// 方式二:掩码初始化(手动涂前景/背景笔刷)
grabCut(img, mask, rect, bgdModel, fgdModel, 5, GC_INIT_WITH_MASK);
// mask 里已经标好了确定前景/确定背景,算法按这些种子分割

回到效果图,那个黄色足球就是关键线索:矩形框模式下,足球是黄色的、亮度高,和背景看台、草地颜色都不像,但 GMM 没有任何「足球是前景」的先验,直接把它归到了背景------颜色分割对「和背景撞色」的前景无能为力。而掩码模式手动在球衣上涂几笔前景、草地上涂几笔背景,等于给算法注入硬约束,足球就保住了。

这就是两种方式的取舍:矩形框全自动但可能误判,掩码要人工交互但更准。

四、inpaint:去水印的两种算法

inpaint 的思路很朴素:用损伤区域周围的像素「填充」损伤区。划痕、水印、遮挡,都能修。OpenCV 给了两种算法:

cpp 复制代码
inpaint(damaged, mask, telea, 3, INPAINT_TELEA); // 快速行进法
inpaint(damaged, mask, ns,     3, INPAINT_NS);    // Navier-Stokes

TELEA(快速行进法)从损伤边缘向内推进,用「到边界的距离」加权邻近像素填充,快而简单。NS 把图像强度当流体,沿等照度线(等亮度线)方向传播信息,对直线边缘更平滑。

mask 是关键------白色区域就是要修的地方。所以我先程序化画了三条划痕加一个水印块,图和 mask 同步画,这样损伤位置是「已知的」,修完能精确对账:

五、实测对账

这系列一直坚持「已知值 vs 检测值」的对账,这次也不例外:

GrabCut 三种配置的前景像素占比------矩形框 1 次迭代 0.133,5 次迭代 0.112,掩码 5 次迭代 0.114。从 0.133 降到 0.112,是 GMM 在迭代中把「框内误入的背景」一点点修正掉。

inpaint 的修复质量用 PSNR 衡量(越高越好,40 dB 以上视觉无损)。损伤区域的 PSNR:TELEA 22.43 dB,NS 22.35 dB,几乎打平。但注意------全图 PSNR 高达 35.6 dB,因为损伤区只占一小部分,大部分像素没动,这个数字是虚高的。评估修复质量,一定要看损伤区域的 PSNR

22 dB 偏低,意味着有明显的涂抹痕迹。这正好印证了 inpaint 的本质:它只能「糊过去」,恢复不出水果果肉那种高频纹理。大块遮挡,还是要靠深度学习修复。

六、踩坑记录

现象 原因 正确姿势
grabCut 掩码几乎全黑 当二值图显示啥都看不见 mask 值是 0/1/2/3 四类标签,不是 0/255 用 `(mask==GC_FGD)
手动预置 mask 失效 涂的笔刷没起作用 GC_INIT_WITH_RECT 会覆盖预置 mask 想用笔刷必须走 GC_INIT_WITH_MASK
inpaint 编译报错 未声明 inpaint 在 photo 模块不在 imgproc #include <opencv2/photo.hpp>
PSNR 虚高 修复看着差但分数高 全图算把没动的像素也算进去 用 mask 限定只算损伤区

七、AI 与 LLM Wiki:这一课沉淀了什么

LLM Wiki 从 20 页涨到 22 页,这一课新增「图像分割:GrabCut图割」和「图像修复:inpaint去水印」两个概念页,沉淀了图割最小割思想、GMM 迭代、两种初始化模式的区别、以及「损伤区 PSNR 对账」这个验证方法。四张截图归档进素材目录,进度表上 grabcut 和 inpaint 勾选完成,从 26 个变成 28 个,进度 29%。

这一课最值得进知识库的,是「用已知损伤对账」这个方法论------不是肉眼看图说「修得不错」,而是先制造已知损伤,再算 PSNR 精确度量。这种「造一个已知问题去验证解法」的思路,比 GrabCut 和 inpaint 本身的 API 更值钱。

写在最后

97 个实例,今天完成第 28 个,进度 29%。图像分割这一站,GrabCut 给了「全局图割」的分割思路,inpaint 给了「周边填充」的修复思路,一进一出,把「这块像素属于哪」这个核心问题讲透了。

下一篇做几何校正------透视变换(warpPerspective)和 ECC 图像对齐,一个是文档扫描矫正,一个是亚像素配准。到时候见。

相关推荐
Capricorn198817 分钟前
Ox Alpha 1M 上下文科研引用失真怎么排查?知芽 Notebook Skill 技术链路实操
人工智能·笔记·python·深度学习·知识图谱·论文笔记
火星上的鱼20 分钟前
2025年中国餐饮行业发展分析报告
大数据·人工智能
新知图书21 分钟前
14.2 多模态试驾预约Agent的系统架构
人工智能·深度学习·ai agent·智能体
2603_9663027524 分钟前
专业AI视频生成工具怎么选:主链路、单点与后期的分工逻辑
人工智能
jianqiang.xue24 分钟前
专栏简介:《嵌入式 AI 智能体设计实战》
人工智能·stm32·单片机·物联网·架构·esp32
宋哥转AI26 分钟前
深入理解 AI Agent · 多 Agent 编排 #03:Supervisor 与 Orchestrator——Dream-SaaS 双层编排架构拆解
java·人工智能·ai
逻辑君26 分钟前
ANNA 7.6 知识智能引擎白皮书
人工智能·机器学习
叠层归一研究院27 分钟前
基于区分本源的此刻存在唯一性本体论推演
人工智能·算法
维核科技29 分钟前
三个业务共用一台服务器,显存不够用了
大数据·服务器·人工智能