OpenCv—图像分割

本文介绍了OpenCV库中用于图像分割的四种主要方法:分水岭法、GrabCut交互式分割、MeanShift平滑滤波和背景扣除(包括MOG和MOG2)。通过实例代码展示了如何使用这些方法处理图像,实现前景与背景的分离。
摘要由CSDN通过智能技术生成

OpenCv—图像分割


  • 本质是前景与后景分割
  • 传统的图像分割:分水岭法、GrabCut法、MeanShift法、背景扣除

1. 分水岭法

  • 分水岭法处理步骤

    • 标记背景、标记前景、标记未知域、进行分割
  • API : watershed(img,masker)

    • masker:前景、背景设置不同的值以区分他们
  • 1 获取背景:形态学处理;

    • 二值化、开运算、膨胀
  • 2 获取前景:距离变换:

    • distanceTransform(img,distanceType,maskSize)
      • distanceType:DIST_L1、DIST_L2
      • maskSize: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:可能是前景,3
      • model:
        • 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:默认200
      • nmixtures:默认5
      • backgroundRatio:默认0.7
      • noiseSigma:默认0,自动降噪
  • 2 mog2去背景

    • createBackgroundSubtractorMOG2(...)
    • 对亮度产生的阴影有更好的识别
    • 优点:可以计算出阴影部分,缺点:会产生很多噪点
    • 与mog参数区别:
      • history:默认500
      • detectShadows:是否检测阴影,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()
  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值