OpenCV 从零学习 · 第 19 讲:图像分割
函数:cv2.grabCut / cv2.watershed | 难度:★★★☆☆
前面都是"检测/定位",本讲进入把像素分成前景/背景的分割。GrabCut 做"交互式抠图"(给个框就抠出框内主体),分水岭做"粘连物体分离"(把挨在一起的一堆硬币各自切开)。
一、GrabCut 一键抠图
mask = np.zeros(img.shape[:2], np.uint8)
bgd = np.zeros((1,65), np.float64)
fgd = np.zeros((1,65), np.float64)
rect = (50, 50, 400, 300) # 框住主体的矩形 (x,y,w,h)
cv2.grabCut(img, mask, rect, bgd, fgd, 5, cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0), 0, 1).astype('uint8')
result = img * mask2[:,:,np.newaxis]
| 参数 | 含义 |
mask | 单通道掩膜,初始化全 0 |
rect | 包含主体的矩形(初始化用) |
iterCount | 迭代次数,如 5 |
mode | GC_INIT_WITH_RECT 用矩形初始化 |
mask 取值:0=确定背景,1=确定前景,2=可能背景,3=可能前景。抠图时把 0/2(背景)置 0、1/3(前景)置 1 即得主体。
场景 A · 商品图快速抠底
给个大致矩形框住商品,GrabCut 自动把商品从背景里分离,得到去背图——电商抠图的最简方案(比纯阈值更准,能处理头发边缘)。
给个大致矩形框住商品,GrabCut 自动把商品从背景里分离,得到去背图——电商抠图的最简方案(比纯阈值更准,能处理头发边缘)。
二、分水岭分离粘连物体
# markers: 已标注的种子图,int32,背景=0,不同物体标 1,2,3...
markers = cv2.watershed(img, markers)
img[markers == -1] = [0, 0, 255] # 边界标红
分水岭要求你先给"种子标记"(哪些像素一定属于哪个物体/背景),它从种子向外"漫水"分割。常用套路:阈值+形态学得到确定前景,连通域给每个物体不同标号当种子。
场景 B · 重叠硬币/细胞计数
一堆挨在一起的硬币,单纯轮廓会把它们当成一个。用分水岭给每个硬币"注水"切开,再分别计数——显微镜细胞计数同理。
一堆挨在一起的硬币,单纯轮廓会把它们当成一个。用分水岭给每个硬币"注水"切开,再分别计数——显微镜细胞计数同理。
⚠ 新手必踩坑
- GrabCut 的
mask必须是uint8且尺寸同原图;bgdModel/fgdModel按上述形状建,别填错维度。 - GrabCut 只是"近似抠图",复杂边缘(毛发透明)不完美,需配合精细 matting 进阶。
- 分水岭的
markers必须是 int32 且 -1 表示边界;种子给不准(没有可靠前景)会分割崩坏。 - 分水岭对噪声敏感,正式用前通常要
distanceTransform+ 阈值来生成靠谱种子(进阶内容)。
本讲要点
- GrabCut:给矩形框 → 自动抠前景,mask 值 0/2 背景、1/3 前景。
- 分水岭:需先给种子标记(int32),用于分离粘连物体。
- 两者都是"分割"而非"检测",输出是逐像素的前景/背景标签。
下一讲(收官):第 20 讲 · 透视变换与文档矫正(getPerspectiveTransform / warpPerspective)——把拍歪的文档/名片矫正成正视图,给本系列一个实用的"毕业项目"。
