OpenCV的图像分割与修复

12人浏览 / 0人评论 / 添加收藏

图像分割与修复是 OpenCV 中两大经典而实用的图像处理模块。下面从原理、常用方法和代码示例三个层面展开说明。

一、图像分割

图像分割的目标是将图像划分成若干具有语义或特征一致性的区域。OpenCV 提供了从传统阈值到现代深度学习在内的多种分割手段。

1. 基于阈值的分割

适用场景: 目标与背景灰度差异明显。

·全局阈值

ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

   · Otsu 自动阈值
适用于双峰直方图,可自动寻找最佳阈值。

ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

   · 自适应阈值
光照不均时,对每个像素根据其邻域计算阈值。

thresh = cv2.adaptiveThreshold(gray, 255,
                              cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                              cv2.THRESH_BINARY, 11, 2)

2. 基于边缘的分割(Canny + 轮廓)

Canny 检测边缘后,可以通过轮廓提取闭合区域。

edges = cv2.Canny(gray, 50, 150)
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
mask = np.zeros_like(gray)
cv2.drawContours(mask, contours, -1, 255, thickness=cv2.FILLED)

3. 分水岭算法

通过将图像看作拓扑地貌,从标记点开始“注水”,分割粘连物体。

# 预处理:去噪、二值化、距离变换
blurred = cv2.GaussianBlur(gray, (5,5), 0)
_, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
dist = cv2.distanceTransform(binary, cv2.DIST_L2, 5)
_, sure_fg = cv2.threshold(dist, 0.7*dist.max(), 255, 0)   # 确定前景
sure_bg = cv2.dilate(binary, kernel, iterations=3)          # 确定背景
unknown = cv2.subtract(sure_bg, sure_fg)

# 标记
_, markers = cv2.connectedComponents(sure_fg)
markers = markers + 1
markers[unknown == 255] = 0

# 应用分水岭
markers = cv2.watershed(img, markers)
img[markers == -1] = [0, 0, 255]  # 边界标红

4. GrabCut 交互式分割

用户只需框选前景区域,算法通过图割迭代优化分割。

mask = np.zeros(img.shape[:2], np.uint8)
bgd_model = np.zeros((1,65), np.float64)
fgd_model = np.zeros((1,65), np.float64)

rect = (x, y, w, h)  # 前景矩形
cv2.grabCut(img, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT)

# 生成二值掩码
mask2 = np.where((mask==2)|(mask==0), 0, 1).astype('uint8')
result = img * mask2[:,:,np.newaxis]

5. 基于深度学习的分割(OpenCV DNN 模块)

可加载 U-Net、Mask R-CNN、DeepLab 等模型。

示例:语义分割(ENet / DeepLab)

net = cv2.dnn.readNet('model.pb')
blob = cv2.dnn.blobFromImage(img, 1/255.0, (512,512), swapRB=True, crop=False)
net.setInput(blob)
output = net.forward()
seg_map = np.argmax(output[0], axis=0)  # 每个像素的类别索引

这种方式可直接获得端到端的精确分割,前提是需有预训练模型。

二、图像修复

图像修复(Inpainting)用于去除图像中的瑕疵、文字、水印或物体,通过周边纹理智能填充缺失区域。

1. 修复流程

制作掩码:将待修复区域标记为白色(255),背景为黑色(0)。

调用 cv2.inpaint 并选择修复算法。

2. 两种算法

cv2.INPAINT_NS(Navier-Stokes 方法)
基于流体动力学,从区域边界向内部平滑传播信息。

cv2.INPAINT_TELEA(Telea 方法)
快速行进法,按像素距离加权平均邻域值,速度快且效果好。

3. 代码示例

# 读入损坏图与掩码
img = cv2.imread('damaged.jpg')
mask = cv2.imread('mask.jpg', 0)  # 单通道,待修复区域=255

# 修复
restored_ns = cv2.inpaint(img, mask, inpaintRadius=3, flags=cv2.INPAINT_NS)
restored_telea = cv2.inpaint(img, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA)

注意:

inpaintRadius 决定参与计算的邻域半径,一般取 3-5。

若需去除物体,先用目标检测/分割生成掩码(如阈值、轮廓、GrabCut)。

三、分割 + 修复综合案例:自动去水印

分割水印区域
通过颜色阈值或模板匹配定位水印,生成掩码。

lower = (200, 200, 200)  # 假设水印为浅灰色
upper = (255, 255, 255)
mask = cv2.inRange(img, lower, upper)

修复
直接用掩码修复。

result = cv2.inpaint(img, mask, 3, cv2.INPAINT_TELEA)

四、性能与局限

分割
传统方法对简单场景快速有效,但复杂场景需深度学习模型;OpenCV 的 DNN 模块支持多种 ONNX/PB 模型,但需注意预处理和类别映射。

修复
适合小面积纹理/噪声去除,大面积物体移除可能产生模糊或伪影;近年来基于深度学习的 GAN/Cnn 修复效果更好(需额外库如 PyTorch),但 OpenCV 实现的传统修复仍是最轻量级的选择。

掌握上述 API 和技巧,就能在大部分图像项目中快速实现分割与修复。

全部评论