OpenCv—图像分割
- 本质是前景与后景分割
- 传统的图像分割:分水岭法、GrabCut法、MeanShift法、背景扣除
1. 分水岭法
-
分水岭法处理步骤
- 标记背景、标记前景、标记未知域、进行分割
-
API :
watershed(img,masker)
masker
:前景、背景设置不同的值以区分他们
-
1 获取背景:形态学处理;
- 二值化、开运算、膨胀
-
2 获取前景:距离变换:
distanceTransform(img,distanceType,maskSize)
distanceType
:DIST_L1、DIST_L2maskSize
:L1用3、L2用5
-
3 求连通域:
connectedComponents(img,connectivity,....)
connectivity
:4领域,8领域(默认)- 返回ret与masker
-
4 图像分割
watershed(img,masker)
import cv2
import numpy as np
from matplotlib import pyplot as plt
# 获取背景
# 1.通过二值化得到黑白图片
# 2.通过形态学获取背景
img = cv2.imread('../data/img_5.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# cv2.THRESH_OTSU 自适应阈值
ret,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY + 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()
# 获取未知区域
fg = np.uint8(fg)
unknow = cv2.subtract(bg,fg)
# 创建连通域
ret,marker = cv2.connectedComponents(fg)
marker += 1
marker[unknow==255] = 0
result = cv2.watershed(img,marker)
img[result == -1] = [0,0,255]
cv2.imshow('img',img)
cv2.imshow('unknow',unknow)
cv2.imshow('fg',fg)
cv2.imshow('thresh',thresh)
cv2.imshow('bg',bg)
cv2.waitKey()
2. GrabCut法
-
通过交互的方式获得前景物体
-
用户指定前景的大体区域,剩下的为背景区域
-
用户还可以指定某些地方为前景或背景
-
原理:
- 采用分段迭代的方法分析前景物体形成模型树
- 根据权重决定某个像素是前景还是背景
-
步骤:
- 主体结构
- 鼠标事件的处理
- 实现前景与背景的分离
-
API:
grabCut(img,mask,rect,bgdModel,fgdModel,iterator,mode)
mask
:BGD:背景,0、FGD:前景,1、PR_BGD:可能是背景,2、PR_FGD:可能是前景,3model
:bgdModel:np.float64 type zero array of size (1,65)
fgdModel
同上
mode
:GC_INIT_WITH_RECT、GC_INIT_WITH_MASK
代码示例:
import cv2
import numpy as np
class App:
flag_rect = False
rect = (0,0,0,0)
startX = 0
startY = 0
def onmouse(self,event,x,y,flag,param):
if event == cv2.EVENT_LBUTTONDOWN:
self.flag_rect = True
self.startX = x
self.startY = y
print("down")
elif event == cv2.EVENT_LBUTTONUP:
self.flag_rect = False
cv2.rectangle(self.img,
(self.startX,self.startY),
(x,y),
(0,0,255),
3)
self.rect= (min(self.startX,x),min(self.startY,y),
abs(self.startX-x),
abs(self.startY-y))
print('up')
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),
(255, 0, 0),
3)
print('move')
print("onmouse")
def run(self):
print('run')
cv2.namedWindow('input')
cv2.setMouseCallback('input',self.onmouse)
self.img = cv2.imread('../data/lena.png')
self.img2 = self.img.copy()
self.mask = np.zeros(self.img.shape[:2],dtype=np.uint8)
self.output = np.zeros(self.img.shape,np.uint8)
while(1):
cv2.imshow('input',self.img)
cv2.imshow('output',self.output)
key = cv2.waitKey(100)
if key == 27:
break
if key == ord('g'):
bgdmodel = np.zeros((1,65),np.float64)
fgdmodel = np.zeros((1,65),np.float64)
cv2.grabCut(self.img2,self.mask,self.rect,
bgdmodel,fgdmodel,
1,
cv2.GC_INIT_WITH_RECT)
mask2 = np.where((self.mask==1)|(self.mask==3),255,0).astype('uint8')
self.output = cv2.bitwise_and(self.img2,self.img2,mask = mask2)
App().run()
3. MeanShift法
- 严格来说该方法并不是用来图像分割的,而是在色彩层面的平滑滤波
- 它会中和色彩分布相近的颜色,平滑色彩细节,侵蚀掉面积较小的颜色区域
- 它以图像上任一点P为圆心,半径为sp,色彩幅值为sr进行不断迭代
- API:
pyrMeanShiftFiltering(img,double sp,double sr,maxLevel=1,termcrit=TermCriteria...)
代码示例:
import cv2
import numpy as np
img = cv2.imread('../data/img.png')
img = cv2.resize(img,(640,480))
mean_img = cv2.pyrMeanShiftFiltering(img,20,30)
img_canny = cv2.Canny(mean_img,150,300)
contours,_ = cv2.findContours(img_canny,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,contours,-1,(0,0,255),2)
cv2.imshow('img',img)
cv2.imshow('mean_img',mean_img)
cv2.imshow('img_canny',img_canny)
cv2.waitKey()
4. 背景扣除
-
1 mog去背景
- 混合高斯模型为基础的分割算法
createBackgroundSubtractorMOG(history,nmixtures,backgroundRatio,noiseSigma)
history
:默认200nmixtures
:默认5backgroundRatio
:默认0.7noiseSigma
:默认0,自动降噪
-
2 mog2去背景
createBackgroundSubtractorMOG2(...)
- 对亮度产生的阴影有更好的识别
- 优点:可以计算出阴影部分,缺点:会产生很多噪点
- 与mog参数区别:
history
:默认500detectShadows
:是否检测阴影,True
-
3 GMG去背景
- 静态背景图像估计和每个像素的贝叶斯分割抗噪性更强
- 改进MOG2
- 优点:可以计算出阴影部分,同时减少了噪点;缺点:如果采用默认值,开始时没有任何信息提示,可调整参数为20…
createBackgroundSubtractorGMG(...)
initializationFrames
:初始帧数,120
代码示例:
import cv2
cap = cv2.VideoCapture('../data/video.mp4')
# 创建mog对象
# mog = cv2.createBackgroundSubtractorMOG2()
# mog = cv2.bgsegm.createBackgroundSubtractorMOG()
mog = cv2.bgsegm.createBackgroundSubtractorGMG()
while True:
ret, frame = cap.read()
if ret == True:
fgmask = mog.apply(frame)
cv2.imshow('video', fgmask)
key = cv2.waitKey(1)
if key == 27:
break
cap.release()
cv2.destroyAllWindows()
undSubtractorGMG()
while True:
ret, frame = cap.read()
if ret == True:
fgmask = mog.apply(frame)
cv2.imshow('video', fgmask)
key = cv2.waitKey(1)
if key == 27:
break
cap.release()
cv2.destroyAllWindows()