引言
图像分割是计算机视觉中比目标检测更进一步的任务------不仅要知道"图里有什么、在哪",还要精确到像素级别,把每个物体的轮廓抠出来。这项技术在医学影像、自动驾驶、AR特效、图像编辑等领域都有广泛应用。
本文用三种方法完成同一件事:把目标从背景中分离出来。从深度学习到传统算法,从实例分割到前景提取,一次讲清楚各自的适用场景和效果差异。
项目简介
分别使用Mask R-CNN(深度学习)、GrabCut(图割算法)、K-Means聚类(传统图像处理)三种方法实现图像分割,对比不同技术路线的效果和适用场景。
- 方法一:Mask R-CNN------实例级分割,每个物体独立mask
- 方法二:GrabCut------交互式前景提取,需给定目标区域
- 方法三:K-Means------无监督像素聚类,按颜色分区
功能特性
- Mask R-CNN:检测+分割一体化,输出类别、边界框和像素级mask
- GrabCut:用户只需框出目标区域,算法自动迭代分离前景/背景
- K-Means:无需训练数据,直接基于像素颜色聚类分区
- 三种方法覆盖深度学习、图优化、传统图像处理三条路线
环境配置
bash
pip install torch torchvision pillow opencv-python matplotlib numpy
依赖项:
- PyTorch + Torchvision:加载Mask R-CNN预训练模型
- OpenCV:GrabCut与K-Means实现
- PIL / Matplotlib:图像读取与可视化
核心实现
方法一:Mask R-CNN 实例分割
python
import torch
import torchvision
from PIL import Image
import matplotlib.pyplot as plt
import torchvision.transforms as T
import numpy as np
# 加载预训练的Mask R-CNN模型(R50-FPN骨干)
model = torchvision.models.detection.maskrcnn_resnet50_fpn(pretrained=True)
model.eval()
# 读取图像并转换为张量
img = Image.open("image.jpeg").convert("RGB")
transform = T.Compose([T.ToTensor()])
img_tensor = transform(img)
# 推理
with torch.no_grad():
prediction = model([img_tensor])[0]
img_np = np.array(img)
plt.figure(figsize=(10, 8))
plt.imshow(img_np)
plt.title("Mask R-CNN Segmentation")
plt.axis("off")
# 提取检测结果
boxes = prediction["boxes"].numpy()
scores = prediction["scores"].numpy()
masks = prediction["masks"].numpy()
# 遍历高置信度检测结果,绘制mask和边界框
for i in range(len(scores)):
if scores[i] > 0.5:
mask = masks[i, 0]
# 半透明叠加mask
plt.imshow(mask, alpha=0.5)
x1, y1, x2, y2 = boxes[i]
plt.gca().add_patch(
plt.Rectangle(
(x1, y1),
x2 - x1,
y2 - y1,
fill=False,
edgecolor="red",
linewidth=2
)
)
plt.show()

核心思路:
- 加载torchvision预训练的maskrcnn_resnet50_fpn模型,骨干网络ResNet50搭配FPN特征金字塔,直接用于推理无需额外训练;
- model.eval()切换到推理模式,with torch.no_grad()禁用梯度计算以节省内存并加速;
- 模型返回prediction字典,包含boxes(边界框坐标)、scores(置信度分数)、masks(二值掩码,形状N,1,H,W)三个关键字段;
- 遍历scores>0.5的高置信度检测结果,用alpha=0.5半透明叠加mask到原图,并用红色空心矩形绘制边界框
关键参数说明:
- pretrained=True:加载在COCO数据集上预训练的权重,可直接检测80类常见目标;
- masksi, 0:每个检测结果对应一个mask,取第一个通道得到二值图;
- scores > 0.5:置信度阈值,低于0.5的检测结果被过滤,可根据实际需求调整
方法二:GrabCut 前景提取
python
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像并转换颜色空间
img = cv2.imread("image.jpeg")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 初始化GrabCut所需变量
mask = np.zeros(img.shape[:2], np.uint8)
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)
h, w = img.shape[:2]
# 定义前景区域:图像中央偏内区域
rect = (int(w*0.15), int(h*0.15), int(w*0.7), int(h*0.7))
# 执行GrabCut,迭代8次
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 8, cv2.GC_INIT_WITH_RECT)
# 提取确定前景和可能前景区域
mask2 = np.where((mask == cv2.GC_FGD) | (mask == cv2.GC_PR_FGD), 1, 0).astype("uint8")
# 将mask应用到原图
result = img * mask2[:, :, np.newaxis]
print("Unique mask values:", np.unique(mask2))
# 并排显示原图与分割结果
plt.figure(figsize=(12,5))
plt.subplot(1,2,1)
plt.imshow(img)
plt.title("Input Image")
plt.axis("off")
plt.subplot(1,2,2)
plt.imshow(result)
plt.title("GrabCut Output")
plt.axis("off")
plt.show()

核心思路:
- 用户用矩形框标记目标区域后,算法用两个高斯混合模型(GMM)分别建模前景和背景的颜色分布;
- 通过最小化能量函数(包含颜色相似性项和空间平滑项)迭代优化每个像素的前景/背景标签;
- 每次迭代后重新估计GMM参数并更新分割结果,逐步精化前景区域边界;
- 最终将确定前景(GC_FGD)和可能前景(GC_PR_FGD)合并为二值mask,与原图逐像素相乘完成背景去除
关键参数说明:
- rect:初始前景矩形区域,格式为(x, y, width, height),示例取图像15%-85%范围,需确保目标完全落在矩形内;
- bgdModel和fgdModel:各65个分量(5个高斯分量 × 13维特征,13 = 3通道 × 4种像素邻域关系 + 1),用于GMM颜色建模;
- 迭代次数=8:迭代次数越多分割越精细,但计算量也越大,8次为常用经验值;
- cv2.GC_INIT_WITH_RECT:初始化模式,表示用矩形框初始化分割区域;
- GC_FGD(值为1):确定前景像素;GC_PR_FGD(值为3):可能前景像素;两者合并为最终前景mask;
- mask2:,:,np.newaxis:将二维mask(H,W)扩展为三维(H,W,1),利用numpy广播机制与原图逐像素相乘
方法三:K-Means 颜色聚类分割
python
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread("image.jpeg")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 将图像展平为像素列表,每像素3通道(RGB)
pixel_values = img.reshape((-1, 3))
pixel_values = np.float32(pixel_values)
# K-Means聚类参数
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
k = 3 # 聚类数
# 执行K-Means
_, labels, centers = cv2.kmeans(pixel_values, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# 将聚类中心转为uint8
centers = np.uint8(centers)
# 用聚类中心颜色重建图像
segmented_image = centers[labels.flatten()]
segmented_image = segmented_image.reshape(img.shape)
# 并排显示原图与分割结果
plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.imshow(img)
plt.title("Input Image")
plt.axis("off")
plt.subplot(1,2,2)
plt.imshow(segmented_image)
plt.title("OpenCV Segmentation Output (K-Means)")
plt.axis("off")
plt.show()

核心思路:
- 将每个像素的RGB值视为三维空间中的一个点,通过K-Means迭代聚类将颜色相似的像素归为同一组;
- img.reshape((-1,3))将H×W×3的图像展平为(H*W, 3)的像素列表,作为聚类输入;
- 聚类收敛后,每个簇用其聚类中心的颜色替换该簇内所有像素,完成颜色分区重建
关键参数说明:
- k=3:聚类数量,将图像分为3个颜色区域,可根据图像内容和需求调整;
- criteria:停止条件,由TERM_CRITERIA_EPS(精度达到0.2时停止)和TERM_CRITERIA_MAX_ITER(最多迭代100次)组合;
- cv2.KMEANS_RANDOM_CENTERS:初始聚类中心随机选择方式;
- labels:每个像素所属的聚类标签;centers:每个聚类的平均RGB颜色值;
- 注意:K-Means仅按颜色空间距离分区,不保证每个区域对应一个语义物体,适合简单场景或预处理
效果展示
三种方法的效果对比:
| 方法 | 技术路线 | 输出类型 | 适用场景 |
|---|---|---|---|
| Mask R-CNN | 深度学习 | 实例mask+框+类别 | 多物体精确分割、自动驾驶 |
| GrabCut | 图割算法 | 二值前景mask | 单目标抠图、图像编辑 |
| K-Means | 像素聚类 | 颜色分区图 | 简单颜色分割、预处理 |
- Mask R-CNN:给出每个物体的独立mask和类别标签,但需要GPU和预训练模型
- GrabCut:只需用户框出目标即可获得高质量前景提取,但对复杂背景效果下降
- K-Means:最快最轻量,但只能按颜色分区无语义理解
总结
方案优势:
- 三条技术路线覆盖深度学习、图优化、传统图像处理,适用不同资源条件
- Mask R-CNN一行代码加载预训练模型,可直接用于80类COCO目标
- GrabCut仅需OpenCV即可运行,无深度学习依赖
- K-Means最为轻量,适合简单场景或预处理步骤
选型建议:
- 需要识别+分割多个特定类别物体:Mask R-CNN
- 只需从背景中抠出一个目标:GrabCut
- 资源受限或仅需按颜色分区:K-Means
觉得有用,记得点个赞和在看