飞道的博客

【数字图像处理5.2】区域生长算法、分裂合并算法和分水岭分割法 python

435人阅读  评论(0)

实验前言与目的

这些算法都属于【基于形态学的图像分割】这一范畴,我们最终做的一切,都是为了图像分割(比如分成黑白两块,相对提取物体主题,为物体描轮廓…)

编写程序完成以下算法,并进行比较,得出结论。

  1. 区域生长算法
  2. 区域分裂合并算法
  3. 分水岭分割算法

1、区域生长算法

参考:https://blog.csdn.net/weixin_43487953/article/details/97395528

注意

  • 区域生长是一种图像分割方法。
  • 区域生长从某个像素出发,按照一定的准则,逐步加入邻近像素,当满足一定的条件时,区域生长终止,进而实现目标的提取。
  • 区域生长的好坏决定于:1.初始点(种子点)的选取。2.生长准则。3.终止条件。
  • 个人感悟:这个算法平均感觉都挺慢的,我运行一个狗子的图,即使换了不同的种子,平均也在10S~20S之间。

PPT思路

本程序思路:

  1. 先生成同样大小空白矩阵
  2. 设置一个种子点,通过计算判断周围是否有符合条件新种子,编写在空白矩阵中
  3. 通过循环可以获得含有种子标志的新矩阵,将矩阵与原图相乘可以得到利用区域生长分割出的图像。
    备注:由于代码不长,所以思路主要用备注写代码里了、

输入图像经过区域生成算法之后的效果:

代码

################################################
#################区域生成算法###################
################################################
def 区域生成法(img):
    img_array = np.array(img)#图片转为数组方便操作

    [m,n]=img_array.shape#返回图片的长和宽

    a = np.zeros((m,n)) #建立等大小空矩阵

    a[70,70]=1 #设立种子点
    k = 40 #设立生长阈值

    isMyArea=1 
    #开始循环遍历周围像素,种子长大。
    while isMyArea==1:
        isMyArea=0
        lim = (np.cumsum(img_array*a)[-1])/(np.cumsum(a)[-1])
        for i in range(2,m):
            for j in range(2,n):
                if a[i,j]==1:
                    for x in range(-1,2):
                        for y in range(-1,2):
                            if a[i+x,j+y]==0:
                                if (abs(img_array[i+x,j+y]-lim)<=k) :
                                    isMyArea = 1
                                    a[i+x,j+y]=1
                                    print("我是区域,我正在生长...")
    data = img_array*a #矩阵相乘获取生长图像的矩阵
    new_img = Image.fromarray(data) #data矩阵转化为二维图片

    return new_img

2、区域分裂合并算法

参考:
https://www.jianshu.com/p/fa573431ef3d

  • 个人感觉,这个分割算法有点【微积分】那种感觉了。
  • 不用像区域生长一样还要选个“种子点”,算法比较“稳”?

算法的思想总的来说,就是:

  1. 先把图像分成4块
  2. 若这其中的一块符合分裂条件,那么这一块又分裂成4块
  3. 分裂到一定数量时,以每块为中心,检查相邻的各块,满足一定条件,就合并。
  4. 如此循环往复进行分裂和合并的操作。
  5. 最后合并小区,即把一些小块图像的合并到旁边的大块里。

注意

  • 本次切割图像不使用专门的库,而是直接通过控制img[x,y,weight,height]来最原图片的一个个小区域直接操作(具体操作是二值化)。
  • 从最终成像来看,区域分裂与合并算法的确具有一定的连续性,不会出现黑图中夹杂着一丝白丝儿的那种噪声。

效果图

代码

import numpy as np
import cv2 
import matplotlib.pyplot as plt # plt 用于显示图片


#判断方框是否需要再次拆分为四个
def judge(w0, h0, w, h):
    a = img[h0: h0 + h, w0: w0 + w]
    ave = np.mean(a)
    std = np.std(a, ddof=1)
    count = 0
    total = 0
    for i in range(w0, w0 + w):
        for j in range(h0, h0 + h):
        #注意!我输入的图片数灰度图,所以直接用的img[j,i],RGB图像的话每个img像素是一个三维向量,不能直接与avg进行比较大小。
            if abs(img[j, i] - ave) < 1 * std:
                count += 1
            total += 1
    if (count / total) < 0.95:#合适的点还是比较少,接着拆
        return True
    else:
        return False

##将图像将根据阈值二值化处理,在此默认125
def draw(w0, h0, w, h):
    for i in range(w0, w0 + w):
        for j in range(h0, h0 + h):
            if img[j, i] > 125:
                img[j, i] = 255
            else:
                img[j, i] = 0


def function(w0, h0, w, h):
    if judge(w0, h0, w, h) and (min(w, h) > 5):
        function(w0, h0, int(w / 2), int(h / 2))
        function(w0 + int(w / 2), h0, int(w / 2), int(h / 2))
        function(w0, h0 + int(h / 2), int(w / 2), int(h / 2))
        function(w0 + int(w / 2), h0 + int(h / 2), int(w / 2), int(h / 2))
    else:
        draw(w0, h0, w, h)

##############################################
######################main_##################
###############################################

img = cv2.imread('pic/leave.jpg', 0)
img_input = cv2.imread('pic/leave.jpg', 0)#备份

height, width = img.shape

function(0, 0, width, height)

cv2.imshow('input',img_input)
cv2.imshow('output',img)

cv2.waitKey()
cv2.destroyAllWindows()

3、分水岭分割算法

参考opencv4 python 中文文档

思路:

  1. 分水岭算法实际上是根据图像的灰度值和形态学知识(膨胀啊,腐蚀啊),用一种标记来确定对象区域。
  2. 用另一种标记确定非对象的区域。
  3. 最后用 0 标记我们不确定的区域。
  4. 然后应用分水岭算法。然后我们的标记将使用我们给出的标签进行更新,对象的边界值将为 -1 。

实现:由于这部分代码有点长,所以将分步展开讲

  1. 先做图像的预处理,包括二值化和去噪(不去噪会导致图像的过度分割)


2. 在图像划分出对象区域和非对象区域,并且通过二者相减,获得不确定区域:
3. 为区域做标记:
4. 执行分水岭算法,将分水岭画成明显的线打到图上:
效果图

代码

################################################
##################分水岭########################

def 分水岭算法(img):
    #转化成灰度图,方便处理
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

    #二值化
    ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

    #去除噪音(要不然最终成像会导致过度分割)
    kernel=np.ones((3,3),np.uint8)
    opening=cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)

    #确定非对象区域
    sure_bg=cv2.dilate(opening,kernel,iterations=3)#进行膨胀操作

    #确定对象区域
    dist_transform=cv2.distanceTransform(opening,1,5)
    ret,sure_fg=cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)

    #寻找未知的区域
    sure_fg=np.uint8(sure_fg)
    unknown=cv2.subtract(sure_bg,sure_fg)#非对象区域减去对象区域就是不确定区域

    # 为对象区域类别标记
    ret, markers = cv2.connectedComponents(sure_fg)
    # 为所有的标记加1,保证非对象是0而不是1
    markers = markers+1
    # 现在让所有的未知区域为0
    markers[unknown==255] = 0

    #执行分水岭算法
    markers = cv2.watershed(img,markers)
    img[markers == -1] = [255,0,0]

    ####################################################
    ########################打印########################

    #解决中文显示问题
    plt.rcParams['font.sans-serif']=['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    plt.subplot(231), plt.imshow(gray, 'gray'), plt.title('输入图片'),plt.axis('off')
    plt.subplot(232), plt.imshow(opening,'gray'), plt.title('二值化去噪之后'),plt.axis('off')
    plt.subplot(233), plt.imshow(sure_bg,'gray'), plt.title('确定非对象区域'),plt.axis('off')
    plt.subplot(234), plt.imshow(dist_transform,'gray'), plt.title('确定对象区域'),plt.axis('off')
    plt.subplot(235), plt.imshow(unknown,'gray'), plt.title('未知区域'),plt.axis('off')
    plt.subplot(236), plt.imshow(img,'gray'), plt.title('分水岭算法'),plt.axis('off')

    plt.show()

项目代码

import cv2
from PIL import Image
import matplotlib.pyplot as plt # plt 用于显示图片
import numpy as np

################################################
#################区域生成算法###################
def 区域生成法(img):
    img_array = np.array(img)#图片转为数组方便操作

    [m,n]=img_array.shape#返回图片的长和宽

    a = np.zeros((m,n)) #建立等大小空矩阵

    a[100,100]=1 #设立种子点
    k = 30 #设立生长阈值,灰度小于此值,则纳入区域。

    isMyArea=1 
    while isMyArea==1:
        isMyArea=0
        lim = (np.cumsum(img_array*a)[-1])/(np.cumsum(a)[-1])
        for i in range(2,m):
            for j in range(2,n):
                if a[i,j]==1:
                    for x in range(-1,2):
                        for y in range(-1,2):
                            if a[i+x,j+y]==0:
                                if (abs(img_array[i+x,j+y]-lim)<=k) :
                                    isMyArea = 1
                                    a[i+x,j+y]=1
                                    print("我是区域,我正在生长...")
    data = img_array*a #矩阵相乘获取生长图像的矩阵
    new_img = Image.fromarray(data) #data矩阵转化为二维图片

    return new_img



################################################
################区域分裂与合并###################
##################################################


import numpy as np
import cv2 
import matplotlib.pyplot as plt # plt 用于显示图片


#判断方框是否需要再次拆分为四个
def judge(w0, h0, w, h):
    a = img[h0: h0 + h, w0: w0 + w]
    ave = np.mean(a)
    std = np.std(a, ddof=1)
    count = 0
    total = 0
    for i in range(w0, w0 + w):
        for j in range(h0, h0 + h):
            if abs(img[j, i] - ave) < 1 * std:
                count += 1
            total += 1
    if (count / total) < 0.95:
        return True
    else:
        return False

##将图像将根据阈值二值化处理,在此默认125
def draw(w0, h0, w, h):
    for i in range(w0, w0 + w):
        for j in range(h0, h0 + h):
            if img[j, i] > 125:
                img[j, i] = 255
            else:
                img[j, i] = 0


def function(w0, h0, w, h):
    if judge(w0, h0, w, h) and (min(w, h) > 5):
        function(w0, h0, int(w / 2), int(h / 2))
        function(w0 + int(w / 2), h0, int(w / 2), int(h / 2))
        function(w0, h0 + int(h / 2), int(w / 2), int(h / 2))
        function(w0 + int(w / 2), h0 + int(h / 2), int(w / 2), int(h / 2))
    else:
        draw(w0, h0, w, h)

################################################
##################分水岭########################

def 分水岭算法(img):
    #转化成灰度图,方便处理
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

    #二值化
    ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

    #去除噪音(要不然最终成像会导致过度分割)
    kernel=np.ones((3,3),np.uint8)
    opening=cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)

    #确定非对象区域
    sure_bg=cv2.dilate(opening,kernel,iterations=3)#进行膨胀操作

    #确定对象区域
    dist_transform=cv2.distanceTransform(opening,1,5)
    ret,sure_fg=cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)

    #寻找未知的区域
    sure_fg=np.uint8(sure_fg)
    unknown=cv2.subtract(sure_bg,sure_fg)#非对象区域减去对象区域就是不确定区域

    # 为对象区域类别标记
    ret, markers = cv2.connectedComponents(sure_fg)
    # 为所有的标记加1,保证非对象是0而不是1
    markers = markers+1
    # 现在让所有的未知区域为0
    markers[unknown==255] = 0

    #执行分水岭算法
    markers = cv2.watershed(img,markers)
    img[markers == -1] = [255,0,0]

    ####################################################
    ########################打印########################

    #解决中文显示问题
    plt.rcParams['font.sans-serif']=['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    plt.subplot(231), plt.imshow(gray, 'gray'), plt.title('输入图片'),plt.axis('off')
    plt.subplot(232), plt.imshow(opening,'gray'), plt.title('二值化去噪之后'),plt.axis('off')
    plt.subplot(233), plt.imshow(sure_bg,'gray'), plt.title('确定非对象区域'),plt.axis('off')
    plt.subplot(234), plt.imshow(dist_transform,'gray'), plt.title('确定对象区域'),plt.axis('off')
    plt.subplot(235), plt.imshow(unknown,'gray'), plt.title('未知区域'),plt.axis('off')
    plt.subplot(236), plt.imshow(img,'gray'), plt.title('分水岭算法'),plt.axis('off')

    plt.show()


###################################################
####################主函数#########################

import cv2
from PIL import Image
import matplotlib.pyplot as plt # plt 用于显示图片
import numpy as np

################################################
#################区域生成算法###################
def 区域生成法(img):
    img_array = np.array(img)#图片转为数组方便操作

    [m,n]=img_array.shape#返回图片的长和宽

    a = np.zeros((m,n)) #建立等大小空矩阵

    a[100,100]=1 #设立种子点
    k = 30 #设立生长阈值,灰度小于此值,则纳入区域。

    isMyArea=1 
    while isMyArea==1:
        isMyArea=0
        lim = (np.cumsum(img_array*a)[-1])/(np.cumsum(a)[-1])
        for i in range(2,m):
            for j in range(2,n):
                if a[i,j]==1:
                    for x in range(-1,2):
                        for y in range(-1,2):
                            if a[i+x,j+y]==0:
                                if (abs(img_array[i+x,j+y]-lim)<=k) :
                                    isMyArea = 1
                                    a[i+x,j+y]=1
                                    print("我是区域,我正在生长...")
    data = img_array*a #矩阵相乘获取生长图像的矩阵
    new_img = Image.fromarray(data) #data矩阵转化为二维图片

    return new_img



################################################
################区域分裂与合并###################
##################################################


import numpy as np
import cv2 
import matplotlib.pyplot as plt # plt 用于显示图片


#判断方框是否需要再次拆分为四个
def judge(w0, h0, w, h):
    a = img[h0: h0 + h, w0: w0 + w]
    ave = np.mean(a)
    std = np.std(a, ddof=1)
    count = 0
    total = 0
    for i in range(w0, w0 + w):
        for j in range(h0, h0 + h):
            if abs(img[j, i] - ave) < 1 * std:
                count += 1
            total += 1
    if (count / total) < 0.95:
        return True
    else:
        return False

##将图像将根据阈值二值化处理,在此默认125
def draw(w0, h0, w, h):
    for i in range(w0, w0 + w):
        for j in range(h0, h0 + h):
            if img[j, i] > 125:
                img[j, i] = 255
            else:
                img[j, i] = 0


def function(w0, h0, w, h):
    if judge(w0, h0, w, h) and (min(w, h) > 5):
        function(w0, h0, int(w / 2), int(h / 2))
        function(w0 + int(w / 2), h0, int(w / 2), int(h / 2))
        function(w0, h0 + int(h / 2), int(w / 2), int(h / 2))
        function(w0 + int(w / 2), h0 + int(h / 2), int(w / 2), int(h / 2))
    else:
        draw(w0, h0, w, h)








################################################
##################分水岭########################

def 分水岭算法(img):
    #转化成灰度图,方便处理
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

    #二值化
    ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

    #去除噪音(要不然最终成像会导致过度分割)
    kernel=np.ones((3,3),np.uint8)
    opening=cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)

    #确定非对象区域
    sure_bg=cv2.dilate(opening,kernel,iterations=3)#进行膨胀操作

    #确定对象区域
    dist_transform=cv2.distanceTransform(opening,1,5)
    ret,sure_fg=cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)

    #寻找未知的区域
    sure_fg=np.uint8(sure_fg)
    unknown=cv2.subtract(sure_bg,sure_fg)#非对象区域减去对象区域就是不确定区域

    # 为对象区域类别标记
    ret, markers = cv2.connectedComponents(sure_fg)
    # 为所有的标记加1,保证非对象是0而不是1
    markers = markers+1
    # 现在让所有的未知区域为0
    markers[unknown==255] = 0

    #执行分水岭算法
    markers = cv2.watershed(img,markers)
    img[markers == -1] = [255,0,0]

    ####################################################
    ########################打印########################

    #解决中文显示问题
    plt.rcParams['font.sans-serif']=['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    plt.subplot(231), plt.imshow(gray, 'gray'), plt.title('输入图片'),plt.axis('off')
    plt.subplot(232), plt.imshow(opening,'gray'), plt.title('二值化去噪之后'),plt.axis('off')
    plt.subplot(233), plt.imshow(sure_bg,'gray'), plt.title('确定非对象区域'),plt.axis('off')
    plt.subplot(234), plt.imshow(dist_transform,'gray'), plt.title('确定对象区域'),plt.axis('off')
    plt.subplot(235), plt.imshow(unknown,'gray'), plt.title('未知区域'),plt.axis('off')
    plt.subplot(236), plt.imshow(img,'gray'), plt.title('分水岭算法'),plt.axis('off')

    plt.show()


###################################################
####################主函数#########################

#区域生成法
#img = cv2.imread("pic/dog1.jpg",0)#读图
#result1 = 区域生成法(img)
#img2 = cv2.imread("pic/dog1.jpg",0)#读图


#区域分裂合并
img = cv2.imread('pic/leave.jpg', 0)
img_input = cv2.imread('pic/leave.jpg', 0)#备份
height, width = img.shape

function(0, 0, width, height)

cv2.imshow('input',img_input)
cv2.imshow('output',img)
cv2.waitKey()
cv2.destroyAllWindows()


#分水岭算法
#img3 = cv2.imread("pic/coin.jpg")#读图
#result3 = 分水岭算法(img3)

####################################################
########################打印########################

#解决中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False

#plt.subplot(221), plt.imshow(img, 'gray'), plt.title('输入图片'),plt.axis('off')
#plt.subplot(222), plt.imshow(pic1,'gray'), plt.title('2'),plt.axis('off')
#plt.subplot(222), plt.imshow(pic2,'gray'), plt.title('3'),plt.axis('off')
#plt.subplot(222), plt.imshow(pic3,'gray'), plt.title('4'),plt.axis('off')

#plt.show()

致谢

最后,感谢舍友再区域分裂与合并算法中的智力支持,合影留念。


转载:https://blog.csdn.net/qq_30154571/article/details/109557559
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场