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

引言

我们已经从输入和输出都是图像的处理方法,转到了输入为图像而输出为从这些图像中提取出来的属性的图像处理方法。且分割是这一个方向的另一个主要步骤。所谓分割,就是将图像分为构成它的子区域或物体,根据要解决的问题决定细分的程度,超过识别这些元素所需细节的分割是没有意义的。

10.1基础知识

假定令R表示一幅图像所占据的整个空间区域,由此就可以将图像分割为若干个子区域,并且满足
在这里插入图片描述
根据条件(a)我们知道,分割必须是完全的;即每个像素必须在一个区域内;条件(b)要求一个区域中的点以某些预定义的方式来连接(即这些点必须是4连接的或8连接的);条件©指出,各个区域必须是不相交的。条件(d)涉及分割后的区域中的像素必须满足的属性;最后两个连接区域在属性Q上的意义必须是不同的。分割的目的是将图像划分为不同区域。

10.2点、线和边缘检测

10.2.1背景知识

10.2.2孤立点的检测

已知点的检测应以二阶导数为基础,则使用拉普拉斯:
在这里插入图片描述
在这里插入图片描述
可以得到输出
在这里插入图片描述

然后再可得如下所示模板
在这里插入图片描述

10.2.3线检测

对于不同方向上的线检测所用的拉普拉斯模板也不同,特定方向的拉普拉斯模板如下:
在这里插入图片描述

第一个模板对水平线有最大响应,第二个模板对45度方向有最大响应,第三个模板对垂直线有最大响应,第四个模板对-45度方向有最大响应。
  我们可以用1,2,3,4来表示水平和45度和垂直以及-45度,在图像中心的点,如果i>j,则此点被认为与在模板i方向上的线更相关。在灰度恒定的区域,上述4个模板的响应为零。

10.2.4边缘检测

边缘检测是基于灰度突变来分割图像的最常用的方法。台阶边缘是指在1个像素的距离上发生两个灰度级间理想的过渡。下图a显示了一个垂直台阶边缘的一部分和通过该边缘的一个水平剖面。
在这里插入图片描述

我们所知道的数字图像都存在被模糊且带有噪声的边缘,其模糊的程度主要取决于聚焦机理的限制,噪声水平主要取决于成像系统的电子元件。

10.2.5基本边缘检测

检测灰度变化可以使用一阶或二阶导数来完成,为了在一幅图像f的(x,y)位置处寻找边缘的强度和方向,因此选择的工具就是梯度,用向量定义如下:
在这里插入图片描述

梯度指出了f在(x,y)处的最大变换率的方向。

其长度可以用它的模来表示,是梯度向量方向变化率的值,即:
在这里插入图片描述
梯度算子

先前提及的图像梯度中存在着偏导数的计算,由于我们所处理的均为数字量,因此就要求关于一点的邻域上的偏导数的数字近似,由此就可以得到:
在这里插入图片描述
在这里插入图片描述

下面是代码部分实现梯度算子部分:

#边缘检测的梯度算子 (Roberts 算子, Prewitt 算子, Sobel 算子)
import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread("", flags=0)  # 读取为灰度图像

# 自定义卷积核
# Roberts 边缘算子
kernel_Roberts_x = np.array([[1, 0], [0, -1]])
kernel_Roberts_y = np.array([[0, -1], [1, 0]])
# Prewitt 边缘算子
kernel_Prewitt_x = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])
kernel_Prewitt_y = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]])
# Sobel 边缘算子
kernel_Sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
kernel_Sobel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])

# 卷积运算
imgBlur = cv2.blur(img, (3, 3))  # Blur 平滑后再做 Laplacian 变换
imgRoberts_x = cv2.filter2D(img, -1, kernel_Roberts_x)
imgRoberts_y = cv2.filter2D(img, -1, kernel_Roberts_y)
imgRoberts = np.uint8(cv2.normalize(abs(imgRoberts_x) + abs(imgRoberts_y), None, 0, 255, cv2.NORM_MINMAX))
imgPrewitt_x = cv2.filter2D(img, -1, kernel_Prewitt_x)
imgPrewitt_y = cv2.filter2D(img, -1, kernel_Prewitt_y)
imgPrewitt = np.uint8(cv2.normalize(abs(imgPrewitt_x) + abs(imgPrewitt_y), None, 0, 255, cv2.NORM_MINMAX))
imgSobel_x = cv2.filter2D(img, -1, kernel_Sobel_x)
imgSobel_y = cv2.filter2D(img, -1, kernel_Sobel_y)
imgSobel = np.uint8(cv2.normalize(abs(imgSobel_x) + abs(imgSobel_y), None, 0, 255, cv2.NORM_MINMAX))

plt.figure(figsize=(12, 8))
plt.subplot(341), plt.title('Origin'), plt.imshow(img, cmap='gray'), plt.axis('off')
plt.subplot(342), plt.title('Roberts'), plt.imshow(imgRoberts, cmap='gray'), plt.axis('off')
plt.subplot(346), plt.title('Roberts_X'), plt.imshow(imgRoberts_x, cmap='gray'), plt.axis('off')
plt.subplot(3, 4, 10), plt.title('Roberts_Y'), plt.imshow(imgRoberts_y, cmap='gray'), plt.axis('off')
plt.subplot(343), plt.title('Prewitt'), plt.imshow(imgPrewitt, cmap='gray'), plt.axis('off')
plt.subplot(347), plt.title('Prewitt_X'), plt.imshow(imgPrewitt_x, cmap='gray'), plt.axis('off')
plt.subplot(3, 4, 11), plt.title('Prewitt_Y'), plt.imshow(imgPrewitt_y, cmap='gray'), plt.axis('off')
plt.subplot(344), plt.title('Sobel'), plt.imshow(imgSobel, cmap='gray'), plt.axis('off')
plt.subplot(348), plt.title('Sobel_X'), plt.imshow(imgSobel_x, cmap='gray'), plt.axis('off')
plt.subplot(3, 4, 12), plt.title('Sobel_Y'), plt.imshow(imgSobel_y, cmap='gray'), plt.axis('off')
plt.tight_layout()
plt.show()


实验结果如下
在这里插入图片描述
x方向上的梯度分量,水平细节更清楚;y方向上的梯度分量,垂直细节更清楚;梯度图像则水平和垂直细节都很清楚。

10.3阈值处理

10.3.1基础知识

阈值处理后的图像g(x,y)定义为:
在这里插入图片描述
当T时一个适用于整个图像的常数时,上式给出的处理称为全局阈值处理;当T值在一幅图像上改变时,我们使用可变阈值处理这一术语;术语局部阈值处理或区域阈值处理有时用于表示可变阈值处理,这时图像中任何点(x,y)处的T值取决于(x,y)的领域的特性;若T取决于空间坐标(x,y)本身,则可变阈值处理通常称为动态阈值处理或自适应阈值处理。

10.3.2基本的全局阈值处理

通常情况下,图像之间有较大的变化,纵使使用了全局阈值的方法计算其阈值也是需要的,这里就需要一个能够自动估计阈值的算法:

1、为全局阈值T选择一个初始估计值。

2、在公式中用 T分割该图像。这将产生两组像素: 由灰度值大于T的所有像素组成,由所有小于等于T的像素组成。

3、对和的像素分别计算平均灰度值(均值) m和m2。

4、计算一个新的阈值: T=(m+n)/0.5
5、重复步骤2到步骤4,直到连续迭代中的T值间的差小于一个预定义的参数 △T为止。

下面是代码部分实现全局阈值处理

import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('', 0)
# 精度
eps = 1
iry = np.array(img)
r, c = img.shape
avg = 0
for i in range(r):
    for j in range(c):
        avg += iry[i][j]
T =int(avg/(r*c))
while 1:
    G1, G2, cnt1, cnt2 = 0, 0, 0, 0
    for i in range(r):
        for j in range(c):
            if iry[i][j] >= T: G1 += iry[i][j]; cnt1 += 1
            else: G2 += iry[i][j]; cnt2 += 1
    u1 = int(G1 / cnt1)
    u2 = int(G2 / cnt2)
    T2 = (u1 + u2) / 2
    dis = abs(T2 - T)
    if(dis <= eps): break
    else :T = T2
new_img = np.zeros((r, c),np.uint8)
for i in range(r):
    for j in range(c):
        if iry[i][j] >= T: new_img[i][j] = 255
        else: new_img[i][j] = 0

cv2.imshow('1', img)
cv2.imshow('2', new_img)
cv2.waitKey()
plt.hist(img.ravel(),256,[0,256])
plt.show()


实验结果如下
在这里插入图片描述
在这里插入图片描述
分析:从实验结果来看,物体和背景分离的比较好。

10.3.3用Qtsu方法的最佳全局阈值处理

Otsu算法的步骤:
1、计算输入图像的归一化直方图,并使用p_{i}表示直方图上的各个分量,所有分量之和为1。
2、对于0<k<L-1,计算雷击;
3、同样的范围,计算累积均值m(k);
4、计算全局灰度均值mg
5、计算类间方差;
6、得到阈值;
7、最后计算得到可分性度量。

10.4基于区域的分割

分割的目的就是将一幅图像划分为多个区域。在上面的讨论中主要是基于灰度级、像素特性等来研究分割。这里将讨论以直接寻找区域为基础的分割技术。

10.4.1区域生长

区域增长的算法实现:
1、根据图像的不同应用选择一个或一组种子,它或者是最亮或最暗的点,或者是位于点簇中心的点;
2、选择一个描述符(条件);
3、从该种子开始向外扩张,首先把种子像素加入结果集合,然后不断将与集合中各个像素连通、且满足描述符的像素加入集合;
4、上一过程进行到不再有满足条件的新结点加入集合为止。

下面是代码部分实现区域增长

import numpy as np
import cv2


class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def getX(self):
        return self.x

    def getY(self):
        return self.y


def getGrayDiff(img, currentPoint, tmpPoint):
    return abs(int(img[currentPoint.x, currentPoint.y]) - int(img[tmpPoint.x, tmpPoint.y]))


def selectConnects(p):
    if p != 0:
        connects = [Point(-1, -1), Point(0, -1), Point(1, -1), Point(1, 0), Point(1, 1), \
                    Point(0, 1), Point(-1, 1), Point(-1, 0)]
    else:
        connects = [Point(0, -1), Point(1, 0), Point(0, 1), Point(-1, 0)]
    return connects


def regionGrow(img, seeds, thresh, p=1):
    height, weight = img.shape
    seedMark = np.zeros(img.shape)
    seedList = []
    for seed in seeds:
        seedList.append(seed)
    label = 1
    connects = selectConnects(p)
    while (len(seedList) > 0):
        currentPoint = seedList.pop(0)

        seedMark[currentPoint.x, currentPoint.y] = label
        for i in range(8):
            tmpX = currentPoint.x + connects[i].x
            tmpY = currentPoint.y + connects[i].y
            if tmpX < 0 or tmpY < 0 or tmpX >= height or tmpY >= weight:
                continue
            grayDiff = getGrayDiff(img, currentPoint, Point(tmpX, tmpY))
            if grayDiff < thresh and seedMark[tmpX, tmpY] == 0:
                seedMark[tmpX, tmpY] = label
                seedList.append(Point(tmpX, tmpY))
    return seedMark


img = cv2.imread('cat.jpg', 0)
seeds = [Point(10, 10), Point(82, 150), Point(20, 300)]
binaryImg = regionGrow(img, seeds, 10)
cv2.imshow('1', img)
cv2.imshow('2', binaryImg)
cv2.waitKey(0)

实验结果如下
在这里插入图片描述
在这里插入图片描述
分析,该实验分析出了运动员的区域形状

10.4.2区域分裂和聚合

区域分裂合并算法的基本思想是先确定一个分裂合并的准则。
即区域特征一致性的测度,当图像中某个区域的特征不一致时就将该区域分裂成4 个相等的子区域,当相邻的子区域满足一致性特征时则将它们合成一个大区域,直至所有区域不再满足分裂合并的条件为止。
当分裂到不能再分的情况时,分裂结束,然后它将查找相邻区域有没有相似的特征,如果有就将相似区域进行合并,最后达到分割的作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值