图像分割(1)

引言

笔记总结。

所谓图像分割指的是根据灰度、 颜色、 纹理和形状等特征把图像划分成若干互不交迭的区域, 并使这些特征在同一区域内呈现出相似性,而在不同区域间呈现出明显的差异性。 经典的数字图像分割算法一般是基于灰度值的两个基本特性之一: 不连续性和相似性。

(1)基于阈值、 基于边缘

阈值法 的基本思想是基于图像的灰度特征来计算一个或多个灰度阈值, 并将图像中每个像素的灰度值与阈值相比较, 最后将像素根据比较结果分到合适的类别中。
边缘法 所谓边缘是指图像中两个不同区域的边界线上连续的像素点的集合, 是图像局部特征不连续性的反映, 体现了灰度、 颜色、 纹理等图像特性的突变。

基于阈值的分割

o 设定某一灰度阈值T, 能将图像分成两部分: 大于T的像素群和小于T的像素群定一个合适的阈值就可准确地将图像分割开来。
o 算法的关键是确定合适的阈值。

基于边缘的分割

基于边缘检测的图像分割方法的基本思路是先确定图像中的边缘像素, 然后再把这些像素连接在一起就构成所需的区域边界。

(2)基于区域、 基于图论

基于区域的分割方法

此类方法是将图像按照相似性准则分成不同的区域, 主要包括种子区域生长法、 区域分裂合并法和分水岭法等几种类型。

区域生长法

区域生长是根据一种事先定义的准则将像素或者子区域聚合成更大区域的过程, 并且要充分保证分割后的区域满足以下条件: 内部连通、 互斥, 且各子集的并集能构成全集。

区域生长的条件实际上就是根据像素灰度间的连续性而定义的一些相似性准则, 而区域生长停止的条件则是一个终止规则。

算法定义了最大像素灰度值距离, 当待加入像素点的灰度值和已经分割好的区域所有像素点的平均灰度值的差的绝对值不大于最大像素灰度值距离时, 该像素点加入到已经分割到的区域。 相反, 则区域生长算法停止。

分水岭算法

分水岭算法是一种图像区域分割法, 分水岭算法可以将图像中的边缘转化成“山脉”, 将均匀区域转化为“山谷”, 这样有助于分割目标。

在分割的过程中, 它会把跟临近像素间的相似性作为重要的参考依据, 从而将在空间位置上相近并且灰度值相近(求梯度) 的像素点互相连接起来构成一个封闭的轮廓。想象为水往上涨,慢慢的淹没上来。,最后剩下上面的轮廓。
分水岭算法常用的操作步骤:
彩色图像灰度化, 然后再求梯度图,最后在梯度图的基础上进行分水岭算法, 求得分段图像的边缘线。

前景背景分割

前景背景分割: 前景一般包含物体, 需要交互提供初始标记.

基于图论的分割方法

此类方法把图像分割问题与图的最小割(mincut) 问题相关联。 首先将图像映射为带权无向图, 图中每个节点对应于图像中的每个像素,每条边的权值表示了相邻像素之间在灰度、 颜色或纹理方面的非负相似度。

Graph Cuts分割

Graph Cuts是基于图论的分割方法。• 第一种普通顶点对应于图像中的每个像素。 每两个邻域顶点(对应于图像中每两个邻域像素) 的连接就是一条边。• 另外两个终端顶点, 叫S(source: 代表前景)和T(sink: 代表背景) 。 每个普通顶点和这2个终端顶点之间都有连接, 组成第二种边。• Graph Cuts中的Cuts是指这样一个边的集合, 该集合中所有边的断开会导致残留”S”和”T”图的分开, 所以就称为“割”。如果一个割, 它的边的所有权值之和最小, 那么这个就称为最小割, 也就是图割的结果。

目标函数:一种基于能量的运算表达。 λ \lambda 起着一个平衡的作用,调节权重。B(A)像素与像素之间相似不相似的表达。

GrabCut分割

前景/背景的颜色模型
• 假定前景和背景为高斯混合模型(GMM) , 对于每个像素, 要么来自于目标GMM的某个高斯分量, 要么就来自于背景GMM的某个高斯分量。 用边界项来体现邻域像素之间不连续的惩罚,如果两邻域像素差别很小, 那么它属于同一个目标或者同一背景的可能性就很大, 反之则可能是边缘; 通过K-means算法迭代获得。

两组点分别通过两个不同的正态分布随机生成而来。 如果没有GMM, 那么只能用一个的二维高斯分布来描述。 显然, 这不太不合理。 应该用两个不同参数的正态分布来描述。

K-means

关于K-Means的几个问题:
(1) K值怎么定?
——主要取决于经验, 通常的做法是多尝试几个K值, 看分成几类的结果更好解释, 更符合分析目的等。
(2) 初始的K个质心怎么选?
——最常用的方法是随机选, 初始质心的选取对最终聚类结果有影响, 因此算法一定要多执行几次, 哪个结果更合理, 就用哪个结果。 有一些优化的方法, 例如: 选择彼此距离最远的点。
(3) K-Means会不会陷入一直选质心的过程?
——不会, K-Means一定会收敛, 大致思路是利用SSE的概念(也就是误差平方和) , 可证明这个函数是可以最终收敛的函数。

代码实践


# Python 2/3 compatibility
from __future__ import print_function

import numpy as np
import cv2 as cv
import sys





BLUE = [255,0,0]        # rectangle color
RED = [0,0,255]         # PR BG
GREEN = [0,255,0]       # PR FG
BLACK = [0,0,0]         # sure BG
WHITE = [255,255,255]   # sure FG

DRAW_BG = {'color' : BLACK, 'val' : 0}
DRAW_FG = {'color' : WHITE, 'val' : 1}
DRAW_PR_FG = {'color' : GREEN, 'val' : 3}
DRAW_PR_BG = {'color' : RED, 'val' : 2}

# setting up flags
rect = (0,0,1,1)
drawing = False         # flag for drawing curves
rectangle = False       # flag for drawing rect
rect_over = False       # flag to check if rect drawn
rect_or_mask = 100      # flag for selecting rect or mask mode
value = DRAW_FG         # drawing initialized to FG
thickness = 3           # brush thickness

def onmouse(event,x,y,flags,param):
    global img,img2,drawing,value,mask,rectangle,rect,rect_or_mask,ix,iy,rect_over

    # Draw Rectangle
    if event == cv.EVENT_RBUTTONDOWN:
        rectangle = True
        ix,iy = x,y

    elif event == cv.EVENT_MOUSEMOVE:
        if rectangle == True:
            img = img2.copy()
            cv.rectangle(img,(ix,iy),(x,y),BLUE,2)
            rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
            rect_or_mask = 0

    elif event == cv.EVENT_RBUTTONUP:
        rectangle = False
        rect_over = True
        cv.rectangle(img,(ix,iy),(x,y),BLUE,2)
        rect = (min(ix,x),min(iy,y),abs(ix-x),abs(iy-y))
        rect_or_mask = 0
        print(" Now press the key 'n' a few times until no further change \n")

    # draw touchup curves

    if event == cv.EVENT_LBUTTONDOWN:
        if rect_over == False:
            print("first draw rectangle \n")
        else:
            drawing = True
            cv.circle(img,(x,y),thickness,value['color'],-1)
            cv.circle(mask,(x,y),thickness,value['val'],-1)

    elif event == cv.EVENT_MOUSEMOVE:
        if drawing == True:
            cv.circle(img,(x,y),thickness,value['color'],-1)
            cv.circle(mask,(x,y),thickness,value['val'],-1)

    elif event == cv.EVENT_LBUTTONUP:
        if drawing == True:
            drawing = False
            cv.circle(img,(x,y),thickness,value['color'],-1)
            cv.circle(mask,(x,y),thickness,value['val'],-1)

if __name__ == '__main__':

    # print documentation
    print(__doc__)

    # Loading images
    if len(sys.argv) == 2:
        filename = sys.argv[1] # for drawing purposes
    else:
        print("No input image given, so loading default image, ../data/lena.jpg \n")
        print("Correct Usage: python grabcut.py <filename> \n")
        filename = 'lenna.jpg'

    img = cv.imread(filename)
    img2 = img.copy()                               # a copy of original image
    mask = np.zeros(img.shape[:2],dtype = np.uint8) # mask initialized to PR_BG
    output = np.zeros(img.shape,np.uint8)           # output image to be shown

    # input and output windows
    cv.namedWindow('output')
    cv.namedWindow('input')
    cv.setMouseCallback('input',onmouse)
    cv.moveWindow('input',img.shape[1]+10,90)

    print(" Instructions: \n")
    print(" Draw a rectangle around the object using right mouse button \n")

    while(1):

        cv.imshow('output',output)
        cv.imshow('input',img)
        k = cv.waitKey(1)

        # key bindings
        if k == 27:         # esc to exit
            break
        elif k == ord('0'): # BG drawing
            print(" mark background regions with left mouse button \n")
            value = DRAW_BG
        elif k == ord('1'): # FG drawing
            print(" mark foreground regions with left mouse button \n")
            value = DRAW_FG
        elif k == ord('2'): # PR_BG drawing
            value = DRAW_PR_BG
        elif k == ord('3'): # PR_FG drawing
            value = DRAW_PR_FG
        elif k == ord('s'): # save image
            bar = np.zeros((img.shape[0],5,3),np.uint8)
            res = np.hstack((img2,bar,img,bar,output))
            cv.imwrite('grabcut_output.png',res)
            print(" Result saved as image \n")
        elif k == ord('r'): # reset everything
            print("resetting \n")
            rect = (0,0,1,1)
            drawing = False
            rectangle = False
            rect_or_mask = 100
            rect_over = False
            value = DRAW_FG
            img = img2.copy()
            mask = np.zeros(img.shape[:2],dtype = np.uint8) # mask initialized to PR_BG
            output = np.zeros(img.shape,np.uint8)           # output image to be shown
        elif k == ord('n'): # segment the image
            print(""" For finer touchups, mark foreground and background after pressing keys 0-3
            and again press 'n' \n""")
            if (rect_or_mask == 0):         # grabcut with rect
                bgdmodel = np.zeros((1,65),np.float64)
                fgdmodel = np.zeros((1,65),np.float64)
                cv.grabCut(img2,mask,rect,bgdmodel,fgdmodel,1,cv.GC_INIT_WITH_RECT)
                rect_or_mask = 1
            elif rect_or_mask == 1:         # grabcut with mask
                bgdmodel = np.zeros((1,65),np.float64)
                fgdmodel = np.zeros((1,65),np.float64)
                cv.grabCut(img2,mask,rect,bgdmodel,fgdmodel,1,cv.GC_INIT_WITH_MASK)

        mask2 = np.where((mask==1) + (mask==3),255,0).astype('uint8')
        output = cv.bitwise_and(img2,img2,mask=mask2)

    cv.destroyAllWindows()

OpenCV的GrabCut

猜你喜欢

转载自blog.csdn.net/fb_941219/article/details/83512643