本文将实现一个与人(鼠标)交互从而分割背景的程序。
GrabCut
- 1.理论介绍
- 2. 鼠标交互
- 3. GrabCut
1.理论介绍
用户指定前景的大体区域,剩下为背景区域,还可以明确指出某些地方为前景或者背景,GrabCut算法采用分段迭代的方法分析前景物体形成模型树,最后根据权重决定某个像素是前景还是背景。
算法:GrabCut(img, mask, rect, bgdModel, fgdModel, 5, //iteratormode)
img:要分割的图像
mask:生称的掩码(以原图像大小为基准),该算法会把mask分为4部分,像素点的值为0,1,2,3四种值吗,其中每种值代表不同的意思。
rect:用户指定的矩形区域,元组的形式(起始坐标x, y , width,height)
bgdModel:1行65列的0矩阵,元素类型为float64。
fgdModel:1行65列的0矩阵,元素类型为float64。
5:迭代次数iterator
mode:第一次找用RECT,以后迭代用MASK
2. 鼠标交互
下面是一个鼠标交互的程序,可以通过点击鼠标滑动鼠标在图像上作图。
不太清楚的读者可以参考下面博客:Opencv(图像处理)-基于Python-绘图功能
代码如下:
import cv2
import numpy as np'''
该api可以在图上作图
点击并滑动鼠标可以在图上画出矩形框
'''# 定义一个类来封装该方法
class MouseStich:startX = 0startY = 0rect_flag = Falsedef onmouse(self, event, x, y, flags, param):# print("onmouse")if event == cv2.EVENT_LBUTTONDOWN:self.rect_flag = Trueself.startX = xself.startY = y# print("LBUTTONDOWN")elif event == cv2.EVENT_LBUTTONUP:# print("LBUTTONUP")self.rect_flag = Falsecv2.rectangle(self.img,(self.startX, self.startY),(x, y),(0, 0, 255),3)elif event == cv2.EVENT_MOUSEMOVE:# print("MOUSEMOVE")# 每次都在新的图像上画if self.rect_flag == True:self.img = self.img2.copy()cv2.rectangle(self.img,(self.startX, self.startY),(x, y),(0, 255, 0),3)def run(self):print("run....")# 绑定鼠标事件的窗口cv2.namedWindow('input')cv2.setMouseCallback('input', self.onmouse)# 暂存一个img2self.img = cv2.imread('./image/lena.jpg')self. img2 = self.img.copy()# 读取图片,在该窗口显示while(1):# 展示原图,被画的图cv2.imshow('input', self.img)k = cv2.waitKey(100) & 0xffif k == ord('q'):breakMouseStich().run()
3. GrabCut
将GrabCut需要的参数构造好后,传进去,获得mask掩模,然后我们用np.where把像素值是1,3的位置改成255,目的是用bitwise_and函数提取出前景区域。
import cv2
import numpy as np'''
该api可以在图上作图
点击并滑动鼠标可以在图上画出矩形框
'''# 定义一个类来封装该方法
class MouseStich:startX = 0startY = 0rect_flag = Falserect = (0, 0, 0, 0)def onmouse(self, event, x, y, flags, param):# print("onmouse")if event == cv2.EVENT_LBUTTONDOWN:self.rect_flag = Trueself.startX = xself.startY = y# print("LBUTTONDOWN")elif event == cv2.EVENT_LBUTTONUP:# print("LBUTTONUP")self.rect_flag = Falsecv2.rectangle(self.img,(self.startX, self.startY),(x, y),(0, 0, 255),3)elif event == cv2.EVENT_MOUSEMOVE:# print("MOUSEMOVE")# 每次都在新的图像上画if self.rect_flag == True:# 每次都在新的图像上画moveself.img = self.img2.copy()cv2.rectangle(self.img,(self.startX, self.startY),(x, y),(0, 255, 0),3)# 构造矩形的信息self.rect = (min(self.startX, x), min(self.startY, y),abs(self.startX - x), abs(self.startY - y))def run(self):print("run....")# 绑定鼠标事件的窗口cv2.namedWindow('input')cv2.setMouseCallback('input', self.onmouse)self.img = cv2.imread('./image/lena.jpg')self. img2 = self.img.copy()# 定义一个与图片相同大小的掩码self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8)self.output = np.zeros(self.img.shape, dtype=np.uint8)# 读取图片,在该窗口显示while(1):# 展示原图,被画的图cv2.imshow('input', self.img)cv2.imshow('output', self.output)k = cv2.waitKey(100) & 0xffif k == ord('q'):breakif k == 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)MouseStich().run()
展示效果:先使用鼠标画出区域,然后按’g’分割图片。
以上是关于GrabCut算法的实战内容,如有问题欢迎在评论区讨论。