十四、图像分割与修复
1、图像分割
图像分割: 将前景物体从背景中分离出来。
图像分割的方法:
- 传统的图像分割方法
- 分水岭法
- GrabCut法
- MeanShift法
- 背景抠图
- 基于深度学习的图像分割方法
1.1 分水岭法
1、分水岭法的原理:
2、分水岭法的问题:
图像存在过多的极小区域,从而产生许多小的集水盆。
3、分水岭法的基本步骤:
- 标记背景
- 标记前景
- 标记未知域
- 进行分割
4、实战:分割硬币
watershed()
用法:
cv2.watershed(image, markers)
参数说明:
- markers:前景、背景设置不同的值用以区分它们
原图像:
获取背景:
获取前景:
获取未知域:
4.1 获取背景:
# 获取背景 # 1.通过二值法得到黑白图像 # 2.通过形态学获取背景 ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 开运算 kernel = np.ones((3, 3), np.int8) open1 = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) # 膨胀 bg = cv2.dilate(open1, kernel, iterations=1) cv2.imshow('thresh', thresh) cv2.imshow('bg', bg)
4.2 获取前景:
距离变换:distanceTransform()
用法
cv2.distanceTransform(src, distanceType, maskSize, dst: None, dstType: None)
参数说明:
- distanceType:DIST_L1(绝对值),DIST_L2(勾股定理)
- maskSize:L1:3,L2:5
# 获取前景 dist = cv2.distanceTransform(open1, cv2.DIST_L2, 5) ret, fg = cv2.threshold(dist, 0.7*dist.max(), 255, cv2.THRESH_BINARY) # plt.imshow(dist, cmap='gray') # plt.show() # exit() cv2.imshow('dist', dist) cv2.imshow('fg', fg)
使用Matplotlib画出dist:
4.3 获取未知域:
求连通域:connectedComponents()
用法
cv2.connectedComponents(image, labels: None, connectivity: None, ltype: None)
参数说明:
- connectivity:4,8(默认)
# 获取未知域 fg = np.uint8(fg) unknown = cv2.subtract(bg, fg) # 创建连通域 ret, marker = cv2.connectedComponents(fg) cv2.imshow('unknown', unknown)
4.4 进行分割:
# 进行分割 result = cv2.watershed(img, marker) img[result == -1] = [0, 0, 255] cv2.imshow('img', img)
代码实现(完整代码):
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('../resource/money.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 获取背景 # 1.通过二值法得到黑白图像 # 2.通过形态学获取背景 ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 开运算 kernel = np.ones((3, 3), np.int8) open1 = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) # 膨胀 bg = cv2.dilate(open1, kernel, iterations=1) # 获取前景 dist = cv2.distanceTransform(open1, cv2.DIST_L2, 5) ret, fg = cv2.threshold(dist, 0.7 * dist.max(), 255, cv2.THRESH_BINARY) # plt.imshow(dist, cmap='gray') # plt.show() # exit() # 获取未知域 fg = np.uint8(fg) unknown = cv2.subtract(bg, fg) # 创建连通域 ret, marker = cv2.connectedComponents(fg) marker += 1 marker[unknown == 255] = 0 # 进行分割 result = cv2.watershed(img, marker) img[result == -1] = [0, 0, 255] # cv2.imshow('thresh', thresh) # cv2.imshow('bg', bg) # cv2.imshow('dist', dist) # cv2.imshow('fg', fg) # cv2.imshow('unknown', unknown) cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyAllWindows()
1.2 GrabCut法
GrabCut:通过交互的方式获得前景物体。
基本原理:
(1):用户可以指定前景的大体区域,剩下的为背景区域。
(2):用户还可以明确指定某些地方为前景或背景。
(3):GrabCut采用分段迭代的方法分析前景物体形成模型树。
(3):最后根据权重决定某个像素是前景还是背景。
实战步骤:
(1):主体结构
(2):鼠标事件的处理
(3):调用GrabCut实现图像分割
1、主体结构:
class App: def onmouse(self, event, x, y, flags, param): print('onmouse') def run(self): print('run') cv2.namedWindow('input') cv2.setMouseCallback('input', self.onmouse) img = cv2.imread('../resource/lena.bmp') cv2.imshow('input', img) cv2.waitKey(0) cv2.destroyAllWindows() App().run()
2、鼠标事件的处理
class App: flag_rect = False startX = 0 startY = 0 def onmouse(self, event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: self.flag_rect = True self.startX = x self.startY = y print('LBUTTONDOWN: 左键按下') elif event == cv2.EVENT_LBUTTONUP: self.flag_rect = False cv2.rectangle(self.img, (self.startX, self.startY), (x, y), (0, 0, 255), 3) print('LBUTTONUP: 左键抬起') elif event == cv2.EVENT_MOUSEMOVE: if self.flag_rect == True: self.img = self.img2.copy() cv2.rectangle(self.img, (self.startX, self.startY), (x, y), (0, 255, 0), 3) print('MOUSEMOVE: 鼠标移动') print('onmouse') def run(self): print('run') cv2.namedWindow('input') cv2.setMouseCallback('input', self.onmouse) self.img = cv2.imread('../resource/lena.bmp') self.img2 = self.img.copy() while (1): cv2.imshow('input', self.img) key = cv2.waitKey(100) if key == 27: break App().run()