数字图像处理——第十章图像分割

引言

这一章,我们的重点转到了图像提取出来的属性的图像处理方法,分割是该方向的一个主要步骤。

分割将图像细化分为构成它的子区域和物体,当感兴趣的物体或区域被检测出来时,就停止分割,分割的精度决定了计算分析过程中的最终成败,我们应该在减少图像无关细节的影响的同时,增强感兴趣物体。

10.1 基础知识

令R表示图像占据的整个空间区域,把R分为n个子区域 R 1 , R 2 . . . R n R_1,R_2...R_n R1,R2...Rn,需要满足以下的条件。

  1. ⋃ i = 1 n R i = R \bigcup ^{n} _{i=1} R{i}=R i=1nRi=R
  2. R i 为 4 连 接 或 8 连 接 R_{i}为4连接或8连接 Ri484领域:(x+1,y),(x-1,y),(x,y+1),(x,y-1) ∈ N 4 ( p ) \in N_4(p) N4(p);8领域:(x-1,y-1),(x-1,y+1),(x+1,y+1),(x+1,y-1) ∈ N 8 ( p ) \in N_8(p) N8(p),注意均为4个像素。

邻接性:考虑二值图像,灰度值为0或1,V为邻接性灰度值的集合,假设令V={1},4邻接的定义是p=q(灰度值),且q在N_4(p)中,8邻接类似。

  1. R i ∩ R j = ∅ R_{i}\cap R_{j}=\emptyset RiRj=
  2. Q ( R i ) = T r u e : R i 中 所 有 的 像 素 具 有 相 同 的 灰 度 值 Q(R_{i})=True:R_{i}中所有的像素具有相同的灰度值 Q(Ri)=True:Ri
  3. Q ( R j ∪ R i = F a l s e ) : 分 割 后 区 域 的 特 性 不 同 Q(R_{j}\cup R_{i}=False):分割后区域的特性不同 QRjRi=False):

像素值的标准差可以用于区分纹理区域和恒定区域的一个属性,标准差是针对总体而言,感兴趣的区域标准差为正,我们也可以计算区域的边界。

10.2 点、线和边缘检测

也叫做不连续检测,我们可将孤立点视作一条线,边缘视作相连边缘像素的集合。

检测方法:一阶/二阶微分
一阶: α f α x = f ′ ( x ) = f ( x + 1 ) − f ( x ) \frac{\alpha f}{\alpha x}=f'(x)=f(x+1)-f(x) αxαf=f(x)=f(x+1)f(x)
二阶: α 2 f α x 2 = f ( x + 2 ) − 2 f ( x + 1 ) + f ( x ) = f ( x + 1 ) + f ( x − 1 ) − 2 f ( x ) \frac{\alpha ^ {2}f }{\alpha x^{2}}=f(x+2)-2f(x+1)+f(x)=f(x+1)+f(x-1)-2f(x) αx2α2f=f(x+2)2f(x+1)+f(x)=f(x+1)+f(x1)2f(x)

用剖面线的一阶/二阶微分值来检测点、线和边缘,一阶导数会产生粗的边缘(灰度过渡),二阶导数会产生细的边缘,这是很好理解的,只需明白一阶/二阶导数在斜坡的值就能明白,二阶导数只有在不同线段斜率的突变点处值才不为零,会产生双边缘响应。二阶导数的符号可用于确定边缘的过渡是从亮到暗还是从暗到亮,<0时,变亮。

且通过观察,二阶导数在遇到孤立的噪声,精细细节时,响应幅度远大于一阶响应,所以,无论时增强细节还是噪声,二阶导数远强于一阶导数。

计算图像的一阶导数和二阶导数的方法是使用空间滤波器。
R = ∑ k = 1 9 w k z k , z k 是 像 素 的 灰 度 , w k 是 系 数 R=\sum ^{9} _{k=1} w_{k}z_{k},z_{k}是像素的灰度,w_{k}是系数 R=k=19wkzk,zkwk空间滤波器也是掩膜,用于卷积。

10.2.1 孤立点的检测

孤立点的检测,基于二阶导数,使用拉式算子,我们可以在四邻域各向同性的基础上,将点扩展至掩膜的四个对角,如果在某掩膜的中心点上,响应的绝对值超过了某个指定的阈值,那么该店就被检测到,输出图像中,这样的点被标注为1,非检测出的点被标记为0,从而产生二值图像。

掩膜:
在这里插入图片描述

表达式:

g ( x , y ) = { 1 , ∣ R ( x , y ) ∣ ≥ T 0 ,   其 他 g(x,y)= \begin{cases} 1,\quad |R(x,y)|\geq T\\ 0, \ 其他 \end{cases} g(x,y)={1,R(x,y)T0, 

matlab:g=abs(imfilter((double(f),w))) ≥ T \geq T T

10.2.2 线检测

线检测的复杂度比孤立点的检测更高,我们检测线时,结果中可能同时存在正像素和负像素,而绝对值的处理会使线的宽度加倍,更合适的方法是仅使用正值,这就需要调整比例。当线的宽度比拉普拉斯模板尺寸宽时,这些线就被零值“山谷”分开。

在线检测中,我们的兴趣在于检测特定方向的线,我们有4个检测模板,分别是 0 0 , + ( − ) 4 5 0 , 9 0 0 0^0,+(-)45^{0},90^{0} 00,+()450,900,若某个方向的响应更大,说明越接近某个方向的线。对于感兴趣的方向,选择用一个比其他方向更大的系数加权,且每个模板中的系数之和为零,这表明恒定灰度区域中的响应为零。
在这里插入图片描述

10.2.3 边缘模型

边缘模型按照灰度剖面来分类。

  • 台阶模型:存在一像素宽
  • 斜坡模型:不存在一像素宽,属于边缘模糊
  • 屋顶模型(一条穿过图像区域的一像素宽的线),屋顶的宽度由区域的线的宽度和尖锐度决定

几个关于导数的结论:

  • 一阶导数的幅度可用于检测图像的点是否存在一个边缘
  • 二阶导数的符号可确定一个边缘像素是位于边缘的暗侧还是亮侧
  • 二阶导数的零交叉点可用于定位粗边缘的中心

边缘检测的3 steps

  • 平滑处理
  • 边缘点的检测
  • 边缘定位

10.2.4 基本边缘检测

我们要在图像中寻找边缘的强度和方向,我们所用的工具是梯度,定义如下:
▽ f = g r a d ( f ) = [ g x g y ] = [ α f α x α f α y ] \bigtriangledown f=grad(f)=\left[\begin{matrix}g_{x}\\g_{y}\\\end{matrix} \right]=\left[ \begin{matrix} \frac{\alpha f}{\alpha x}\\ \frac{\alpha f}{\alpha y}\\ \end{matrix} \right] f=grad(f)=[gxgy]=[αxαfαyαf]
梯度指出了变化最大率的方向,向量的大小为两个偏导数模平方的根号,近似为: M ( x , y ) ≈ ∣ g x ∣ + ∣ g y ∣ M(x,y)\approx |g_{x}|+|g_{y}| M(x,y)gx+gy
方向: α ( x , y ) = a r c t a n [ g x g y ] \alpha(x,y)=arctan\left[\begin{matrix}g_{x}\\g_{y}\\\end{matrix} \right] α(x,y)=arctan[gxgy]注意x轴正方向向下,y轴正方向向右。

几种算子与其计算方法:

  • Roberts算子(对角线边缘感兴趣):
    在这里插入图片描述
  • Sobel算子(有中心权重,可以平滑图像,抑制噪声):
    在这里插入图片描述
  • 拉普拉斯算子:

拉普拉斯算子是有缺点的,由于各向同性,所以不能辨别方向,噪声也更大;优点是可以进行边缘定位及判断像素是暗的一边还是亮的一边。

在这里插入图片描述
同时,我们也可以采用阈值处理的方法使得图像的边缘更少,且边缘要清晰得多,相当于平滑处理。

现在更为先进的边缘处理技术是Marr-Hidreth边缘检测器和坎农边缘检测器。

Marr算子也被称为高斯拉普拉斯算子,定义如下:
▽ 2 G ( x , y ) = [ x 2 + y 2 − 2 σ 2 σ 4 e − ( x 2 + y 2 ) 2 σ 2 ] \bigtriangledown ^{2}G(x,y)=[\frac{x^2+y^2-2\sigma ^2}{\sigma ^4}e^{\frac{-(x^2+y^2)}{2\sigma ^2}}] 2Gx,y=[σ4x2+y22σ2e2σ2(x2+y2)]零交叉出现在 x 2 + y 2 = 2 σ 2 x^2+y^2=2\sigma^2 x2+y2=2σ2处,定义了一个中心位于原点,半径为根号2 σ \sigma σ的圆,模板正的中心项由紧邻的负区域包围,该区域的值随着到原点距离函数而递减,外层区域值为零,系数之和为零,以便模板的响应在恒定区域为0。

优点:

  • 能计算每一点处的一阶导数或是二阶导数
  • 可以被调整,使得大算子用于检测边缘模糊,小算子用于检测锐度集中的精细细节

算法定义如下:
g ( x , y ) = ▽ 2 G ( x , y ) ★ f ( x , y ) g(x,y)=\bigtriangledown ^{2}G(x,y)★f(x,y) g(x,y)=2Gx,yf(x,y)
寻找g(x,y)的零交叉来寻找边缘的位置。

寻找零交叉的方法:零交叉意味着至少有两个相对的邻域像素符号的不同,左/右,上/下及两个对角。

10.3 边缘连接和边界检测

因噪声等产生的边缘像素断裂无法形成边缘,我们要引入边缘的连接使得这些断裂的点聚合。

10.3.1 局部处理

分析边缘点邻域内像素,根据相似准则进行连接,相似点性质:
∣ M ( s , t ) − M ( x , y ) ≤ E ∣ (1) |M(s,t)-M(x,y)\leq E| \tag{1} Ms,tM(x,y)E(1) ∣ α ( s , t ) − α ( x , y ) ∣ ≤ A (2) |\alpha(s,t)-\alpha(x,y)|\leq A \tag{2} α(s,t)α(x,y)A(2)条件都要得到满足,(1)式为梯度大小,(2)式为角度方向。

10.3.2 区域处理

区域处理对于多边形尤其具有吸引力,要拟合二维曲线,对边界的基本特性产生一个近似,例如端点和凹点。

10.3.3 霍夫变换全局处理

在图像中,一条线(或是边缘)都应该满足 y i = a x i + b y_i=ax_i+b yi=axi+b,我们也画出ab平面。

基本思想是若 ( x 1 , y 1 ) 与 ( x 2 , y 2 ) (x_1,y_1)与(x_2,y_2) x1,y1x2,y2共线,这两点在ab平面上的直线将有一个交点,相交直线最多的点,对应xy平面上的直线就是我们的解,即共线,边缘连接。

10.4 阈值处理

阈值关系如下: T ( x , y ) = T [ x , y , p ( x , y ) , f ( x , y ) ] T(x,y)=T[x,y,p(x,y),f(x,y)] T(x,y)=T[x,y,p(x,y),f(x,y)]p(x,y)是局部性质,f(x,y)是全局性质。

阈值处理步骤:

  • 用选定的阈值分割图像

  • G 1 ∈ ( T 1 > T ) , G 2 ∈ ( T 2 < T ) G_1 \in(T_1>T),G_2 \in(T_2<T) G1(T1>T),G2(T2<T),分别计算灰度平均值 m 1 , m 2 m_1,m_ 2 m1m2

  • 计算新的 T , T = 1 2 ( m 1 + m 2 ) T,T=\frac{1}{2}(m_1+m_ 2) TT=21(m1+m2)

  • 迭代,直到T值小于预定义的参数 △ T \triangle T T

改善全局阈值处理

  • 处理之前图像平滑,改善直方图的形状
  • 边缘改进(平滑不可制造波谷时)
  • 使用梯度,拉普拉斯等工具

10.5 基于区域的分割

区域生长

一致性准则:

  • 邻近点的灰度值与物体的平均灰度值的差<2
  • 该像素最少和区域中的一像素8连接

分裂和聚合

Q ( R i ) = F a l s e 时 Q(R_i)=False时 Q(Ri)=False,分裂4个不相交的象限,不能再分裂时,需满足条件 Q ( R j ∪ R i ) = T R U E Q(R_j \cup R_i)=TRUE QRjRi=TRUE

形态学分水岭分割

关键是找到“汇水盆地”和“脊线”,使用距离变换/梯度/控制标记符。

10.6 实验部分

  • 线检测
img=cv.imread('pic/xinhanjiepan.tif',0)

cv.imshow('yuan tu',img)
kernel1=np.array([[2,-1,-1],[-1,2,-1],[-1,-1,2]])
#kernel2=np.array([[-1,-1,2],[-1,2,-1],[2,-1,-1]])
img01=cv.filter2D(img,-1,kernel1)
cv.imshow('45',img01)
img02=img01[0:120,0:120]
img04=img01[360:486,360:486]
img03=cv.resize(img02,None,fx=4,fy=4)
img05=cv.resize(img04,None,fx=4,fy=4)
cv.imshow('zuoshangfang',img03)
cv.imshow('youxiafang',img05)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述
实验分析: 我们使用加上对角线元素的拉普拉斯模板对线进行检测,但是不能使用绝对值来处理负值,否则将导致加倍线的宽度,对于线检测来说,重点是特定方向的检测,注意左上方的线检测比右下方的线来得亮,说明在+45度存在着更加相关的线段。

  • 点检测
img=cv.imread('pic/dot.tif')
cv.imshow('yuan tu',img)
kernel=np.array([[1,1,1],[1,-8,1],[1,1,1]])
img01=cv.filter2D(img,-1,kernel)
cv.imshow('juan ji tu',img01)
img02=np.uint8(img01)
ret,thresh = cv.threshold(img02, 225, 255, cv.THRESH_BINARY)
cv.imshow('er zhi tu xiang',thresh)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述
实验分析: 图三中的孤立点由于经拉普拉斯模板检测后响应不为0,且超过了设置的阈值而被标为1,从而在图像中显示为白点。

  • 边缘检测
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
#读取图像
img = cv2.imread('pic/building.tif')
img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #转成RGB 方便后面显示
 
#灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
#高斯滤波 平滑操作
gaussianBlur = cv2.GaussianBlur(grayImage, (3,3), 0)
 
#阈值处理
ret, binary = cv2.threshold(gaussianBlur, 127, 255, cv2.THRESH_BINARY)
 
#Roberts算子
# gx分量
kernelx = np.array([[-1,0],[0,1]], dtype=int)
# gy分量
kernely = np.array([[0,-1],[1,0]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
# 绝对值
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
# 融合
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
 
#Prewitt算子
kernelx = np.array([[1,1,1],[0,0,0],[-1,-1,-1]], dtype=int)
kernely = np.array([[-1,0,1],[-1,0,1],[-1,0,1]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX,0.5,absY,0.5,0)
 
#Sobel算子
x = cv2.Sobel(binary, cv2.CV_16S, 1, 0)
y = cv2.Sobel(binary, cv2.CV_16S, 0, 1)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
 
#Laplacian算子
dst = cv2.Laplacian(binary, cv2.CV_16S, ksize = 3)
Laplacian = cv2.convertScaleAbs(dst)
 
# #效果图
# titles = ['Source Image', 'Binary Image', 'Roberts Image',
#           'Prewitt Image','Sobel Image', 'Laplacian Image']
# images = [原图, binary, Roberts, Prewitt, Sobel, Laplacian]
# for i in np.arange(6):
#    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
#    plt.title(titles[i])
#    plt.xticks([]),plt.yticks([])
# plt.show()

# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
 
# # 显示图形
plt.subplot(231),plt.imshow(img_RGB),plt.title('原始图像'), plt.axis('off') #坐标轴关闭
plt.subplot(232),plt.imshow(binary, cmap=plt.cm.gray ),plt.title('二值图'), plt.axis('off')
plt.subplot(233),plt.imshow(Roberts, cmap=plt.cm.gray ),plt.title('Roberts算子'), plt.axis('off')
plt.subplot(234),plt.imshow(Prewitt, cmap=plt.cm.gray ),plt.title('Prewitt算子'), plt.axis('off')
plt.subplot(235),plt.imshow(Sobel, cmap=plt.cm.gray ),plt.title('Sobel算子'), plt.axis('off')
plt.subplot(236),plt.imshow(Laplacian, cmap=plt.cm.gray ),plt.title('Laplacian算子'), plt.axis('off')
 
plt.show()

 

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实验分析: Roberts算子边缘定位精度较高,定位的准确度较差,边缘较不清晰,不具备抑制噪声的能力。适用于边缘正负45度较多的图像;

Sober算子对抑制噪声有一定的作用(中心权重为2),可以达到平滑的效果,适用于对建筑边缘的提取;

Prewitt算子适用于灰度渐变的图像边缘,比如墙砖部分,但抑制噪声的效果比不上S算子;

Laplacian算子适用于阶跃型的边缘点,由于对噪声相当敏感,对于非阶跃型的边缘点,可能会导致双边缘现象,并丢失掉一些边缘信息,图中边缘的宽度明显宽于其他算子图。

  • 全局阈值处理
#计算新阈值
img=cv.imread('pic/new-zhiwen.tif',0)
def threshold(img,T):
    G1=G2=0
    count1=count2=0
    h,w=img.shape
    for i in range(h):
        for j in range(w):
            if img[i,j]>T:
                G1+=img[i,j]
                count1+=1
            else:
                G2+=img[i,j]
                count2+=1
    m1=G1/count1
    m2=G2/count2
    T=0.5*(m1+m2)
    #一个新阈值
    return T
print(threshold(img,150))
#迭代
def best_threshold(img,T):
    h,w=img.shape
   # 迭代
    for k in range(50):

        T1=threshold(img,T)
        if abs(T-T1)<=0.01:
            break
        else:
            T=threshold(img,T1)
            T1=0.5*(T+T1)
            
    for i in range(h):
        for j in range(w):
             if img[i,j]>T:
                    img[i,j]=255
             else: 
                img[i,j]=0
    print(k+1)
    print(T)
    return img
img01=best_threshold(img,127)
show(img01)

在这里插入图片描述
实验分析:根据全局阈值处理的算法,第一次计算的新阈值为127.61,经过两次迭代,满足了相对阈值的要求,最终的阈值是125.38,目标和背景之间存在着相当清晰的界限,这个算法非常适用于直方图清晰的图像中(存在波谷)。

  • 自适应阈值处理
def adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None):

自适应方法:
OpenCV给我们提供了两种:cv2.ADAPTIVE_THRESH_MEAN_C与cv2.ADAPTIVE_THRESH_GAUSSINA_C。它们都是逐个像素的计算自适应阈值,自适应阈值等于每个像素由参数blockSize所指定的邻域的加权平均值减去常量C。
其中,两种不同的方法计算邻域的加权平均值不同:
(1)cv2.ADAPTIVE_THRESH_MEAN_C:邻域所有像素点的权重值是一致的
(2)cv2.ADAPTIVE_THRESH_GAUSSINA_C:与邻域各个像素点到中心点的距离有关,通过高斯方程得到各个点的权重值
阈值类型:
cv2.THRESH_BINARY或者cv2.THRESH_BINARY_INV中的一个

  • 最佳阈值处理函数Otsu

需要注意的是,在使用Otsu方法时,要把阈值设为0。此时的cv2.threshold()会自动寻找最优阈值,并将该阈值返回。

img=cv.imread('pic/kobe_mamba.jpg',0)
t1,binary_img=cv.threshold(img,150,255,cv.THRESH_BINARY)
t2,otsu_img=cv.threshold(img,0,255,cv.THRESH_OTSU)
print(t2)
image=[binary_img,otsu_img]
title=['binary img','otsu img']
for i in range(2):
    plt.subplot(1,2,(i+1))
    plt.imshow(image[i],plt.cm.gray)
    plt.title(title[i])
    plt.axis('off')
plt.show()

在这里插入图片描述
实验分析: 基本全局阈值处理时的分割效果不佳,背景和目标之间发生重叠,是因为背景和目标的灰度级比较相近而不能够完全分离开,使用otsu’s(最佳阈值处理函数)算法进行分割时,可以完全将目标和背景分割开来,86为上图分离的最佳阈值。

  • 分割和聚合
import numpy as np
import cv2 
import matplotlib.pyplot as plt 


#判断方框是否需要再次拆分为四个,选取的天鹅座(566,566),选取左上角(0,0)
def judge(w0, h0, w, h):
    a = img[h0: h0 + h, w0: w0 + w]
    ave = np.mean(a)
    # ddof=1,无偏差的标准差
    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) <5 and std>10:
                # 只控制count
                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

#这个函数懵懵的,else难道不是执行一次之后就结束吗,似乎是把区域给遍历。
def function(w0, h0, w, h):
    # 判断 judge函数 且确定最小的(w,h)
    if judge(w0, h0, w, h) and (min(w, h) > 5):
        # 拆分成四个板块,满足则继续拆分,且同时执行4个语句
        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)

img = cv2.imread('pic/tianezuo.tif', 0)
img_input = cv2.imread('pic/tianezuo.tif', 0)

height, width = img.shape

function(0, 0, width, height)

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

cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

总结

本章中多数分割算法均基于灰度值的两个基本性质,不连续性相似性。不连续性(即不连续的灰度),包括点、线、边缘的灰度突变。相似性包括阈值处理、区域生长、区域分割和聚合,在图像识别中,图像分割是一种基本的预处理步骤,选择何种分割方法要考虑具体的情形。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值