一、点、线和边缘检测
1背景知识
对一幅图中灰度的突变,局部变化可以用微分来检测。因为变化短促,可以用一阶微分和二阶微分描述。数字函数的导数可用差分来定义。一维函数f(x)在点x处一阶导数的近似:
当考虑两变量的图像函数f(x,y)时,为了表示的一致性,使用偏微分。此时将处理沿两个空间轴的偏微分。
关于点x的二阶导数:
有关结论:
- 一阶导数通常在图像中产生较粗的边缘;
- 二阶导数对精细细节,如细线、孤立点和噪声有较强的相应;
- 二阶导数在灰度斜坡和灰度台阶过渡处会产生双边缘响应;
- 二阶导数的符号可用于确定边缘是从亮到暗还是从暗到亮。
计算图像中每个像素位置处的一阶导数和二阶导数的另一种方法是使用空间滤波器。
对3*3滤波器掩膜来说,导数是计算模板系数与被该模板覆盖的区域中的灰度值的乘积之和。即模板在该区域中心点处的响应如下:
其中zk是像素的灰度,该像素的空间位置对应于模板中第k个系数的位置。
2孤立点的检测
孤立点的检测依赖于二阶导数,因此使用拉普拉斯模板。拉普拉斯模板可以将孤立的点检测出来:这个模板的作用就是当模板中心是孤立点时,模板的响应最强,而在非模板中心时,响应为零。也就是如下图所示,孤立点的灰度和周围的像素的灰度很大程度的不同,因此使用这类模板,很容易检测出这个孤立点。对于一个导数模版,系数之和为零表明在恒定灰度区模版响应将是零。
3线检测
线检测同样可使用拉普拉斯模板。但拉普拉斯检测子是各向同性的,因此其响应与方向无关,因此设置四个不同方向的模板。
不同的模板对特定方向上的线感兴趣,也就是在不同的情况下使用不同的模板,并对其输出进行阈值处理。如果对检测图像中由给定模板定义的方向上的所有线感兴趣,则只需简单地对该图像运行这个模板,并对结果的绝对值进行阈值处理,留下的点是有最强响应的点,对于1个像素宽度的线来说,相应的点最接近于模板定义的方向。
4 边缘模型
边缘模型按照灰度剖面来分类。
台阶模型:存在一像素宽
斜坡模型:不存在一像素宽,属于边缘模糊
屋顶模型(一条穿过图像区域的一像素宽的线),屋顶的宽度由区域的线的宽度和尖锐度决定
几个关于导数的结论:
- 一阶导数的幅度可用于检测图像的点是否存在一个边缘
- 二阶导数的符号可确定一个边缘像素是位于边缘的暗侧还是亮侧
- 二阶导数的零交叉点可用于定位粗边缘的中心
边缘检测的3 steps
- 平滑处理
- 边缘点的检测
- 边缘定位
5基本边缘检测
图像梯度及其性质
图像f(x,y)位置处寻找边缘的强度和方向用梯度表征,用 表示,并用向量来定义:
该向量有一个重要的几何性质,即指出了f在位置(x,y)处的最大变化率的方向。
向量的大小(长度)表示为M(x,y),即
梯度向量的方向由下列对于x轴度量的角度给出:
任意点(x,y)处一个边缘的方向与该点处梯度向量的方向α(x,y)正交。
梯度算子
premitt算子编程实现
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
def show(img):
if img.ndim==2:
plt.imshow(img,cmap='gray')
else:
plt.imshow(cv.cvtColor(img,cv.COLOR_BGR2RGB))
plt.show()
img = cv.imread('pic/gift500x500.jpg',0)
show(img)
#Prewitt算子
kx = np.array([[-1,0,1],[-1,0,1],[-1,0,1]], dtype=np.float32)
ky = np.array([[-1,-1,-1],[0,0,0],[1,1,1]],dtype=np.float32)
#水平方向
imgX = cv.filter2D(img, cv.CV_64F,kx)
show(np.abs(imgX).clip(0,255))
#竖直方向
imgY = cv.filter2D( img, cv.CV_64F,ky)
show(np.abs(imgY).clip(0,255))
#两种合成梯度
imgXY = np.sqrt( imgX**2 + imgY**2)
imgXY2 = np.abs( imgX) + np.abs(imgY)
show(np.hstack( [imgXY.clip(0,255), imgXY2.clip(0,255)]))
原图、水平方向、竖直方向、两种合成梯度:
Roberts算子编程实现
#Roberts算子
kx = np.array([[-1,0],[0,1]], dtype=np.float32)
ky = np.array([[0,-1],[1,0]],dtype=np.float32)
#余下同上
原图、水平方向、竖直方向、两种合成梯度:
img = cv.imread('pic/notebook500x333.jpg',0)
show(img)
#Laplacian算子
imp_lap = cv.Laplacian(img,cv.CV_64F)
show(np.abs(imp_lap))
#LoG算子
img_blur = cv.GaussianBlur(img,(3,3),1)
img_log = cv.Laplacian(img_blur, cv.CV_64F)
show( np.abs( img_log))
6 更先进的边缘检测技术
原图、Laplacian算子、LoG算子
Canny边缘检测:
#Canny边缘检测
img_edge = cv.Canny(img,20,200)
show(img_edge)
阈值处理
1基础知识
样的组成方式,物体像素和背景像素所具有的灰度值组合成了两种支配模式。从背景中提取物体的一种明显方法是选择一个将这些模式分开的阈值T。然后,f(x,y)>T的任何点(x, y)称为一个对象点;否则将该点称为背景点。换句话说,分割后的图像g(x, y)由下式给出:
双阈值分割:
这里,如果f(x,y)≤T,,则多阈值处理把点(x,y)分类为属于背景;如果T,<f(x,y)≤T,,则分为一个物体;如果f(x,y)>T,,则为分另一个物体。即分割的图像由下式给出:
2基本的全局阈值处理
迭代法阈值分割:
- 为全局阈值T选择一个初始估计值。
- 在式(10.3-1)中用T分割该图像。这将产生两组像素:G,由灰度值大于T的所有像素组成,G,由所有小于等于T的像素组成。
- 对G和G,的像素分别计算平均灰度值(均值)m,和 mg。
- 计算一个新的阈值:
- 重复步骤2到步骤4,直到连续迭代中的T值间的差小于一个预定义的参数AT为止。
img = cv.imread('pic/eagle500x500.jpg',0)
show(img)
T = img.mean( )
while True:
t0 = img[img < T].mean( )
t1 = img[img >= T].mean( )
t= (t0 + t1) / 2
if T == t:
break
T = t
T = int(T)
print(f"Best threshold = {T}")
3用Otsu方法的最佳全局阈值处理
对于给定的阈值T,可以将图像分为目标和背景。其中背景点数占图像比例为po,平均灰度值为mo。而目标点数占图像比例为p1,平均灰度值为m1,其中满足
整幅图像的平均灰度值为常数,跟阈值无关,且为类间方差为
代入po +p1 =1和,可化简为
遍历灰度值,找出能使最大的值。
Sigma = -1
T = 0
for t in range(0,256):
bg = img[img <= t]
obj = img[img > t]
p0 = bg.size / img.size
p1 = obj.size / img.size
m0 = 0 if bg.size ==0 else bg.mean()
m1 = 0 if obj.size ==0 else obj.mean()
sigma = p0 * p1 * (m0 - m1)**2
if sigma > Sigma:
Sigma = sigma
T = t
print(f"Best threshold = {T}")
4用图像平滑改善全局阈值处理
噪声对于图像处理而言会产生巨大的影响,它会将一个简单的阈值处理问题变成一个不可解决的问题,所以在选择阈值处理进行分割处理时,需要在阈值处理之间先进行平滑图像。
从上图可以看出,当不对图像中的噪声加以处理时,使用Otsu方法进行分割时会出现很多黑点,分割很不成功。而进行平滑后的图像其直方图改进明显,利用Otsu阈值处理法处理后的图像也更加完美。
5利用边缘改进全局阈值处理
对于边界明显的图像,前景和背景面积悬殊,但是整体灰度值相近,不易用otsu直接找出正确的阈值,可以使用边缘改进的阈值处理。
算法如下,f(x,y)是输入图像:
- 计算一幅边缘图像,无论是f(x,y)梯度的幅值还是拉普拉斯的绝对值均可。
- 制定一个阈值T。
- 用步骤2中的阈值对步骤1中的图像进行阈值处理,产生一幅二值图像gT(x,y)。在从f(x,y)中选取对应于“强”边缘像素的下一步中,该图像用做一幅模板图像。
- 仅用f(x,y)中对应于gT(x,y)中像素值为1的位置的像素计算直方图。
- 用步骤4中的直方图全局地风格f(x,y),例如使用Otsu方法。
6多阈值处理
我们可将阈值处理方法扩展到任意数量的阈值,因为以这种方法为基础的可分性测度也可以扩展到任意数量的分类。在K个类C1,C2,...,Ck的情况下,类间方差可归纳为下面的表达式:
7可变阈值处理
图像分块:可变阈值处理最简单的方法之一是把一幅图像分成不重叠的矩形。这种方法用于补偿光照和/或反射的不均匀性。选择的矩形要足够小,以便每个矩形的光照都近似是均匀的。
基于局部图像特性的可变阈值处理:用一幅图像中每个点的一个邻域内的像素的标准差和均值来说明局部阈值处理的基本方法。
三、基于区域的分割
1区域生长
区域生长是根据预先定义的生长准则将像素或子区域组合为更大区域的过程。基本方法是从一组“种子”点开始,将与种子预先定义的性质相似的那些邻域像素添加到每个种子上来形成这些生长区域(如特定范围的灰度或颜色)。
令f (x, y)表示一个输入图像阵列;S(x, y)表示一个种子阵列,阵列中种子点位置处为1,其他位置处为0;Q表示在每个位置(x, y)处所用的属性。假设阵列f和S的尺寸相同。基于8连接的一个基本区域生长算法可说明如下。
- 在S(x, y)中寻找所有连通分量,并把每个连通分量腐蚀为一个像素;把找到的所有这种像素标记为1,把S中的所有其他像素标记为0。
- 在坐标对(x, y)处形成图像f。:如果输入图像在该坐标处满足给定的属性Q,则令
,否则令
。
- 令g是这样形成的图像:即把
中为8连通种子点的所有1值点,添加到S中的每个种子点。
- 用不同的区域标记(如1,2,3,…)标出 g中的每个连通分量。这就是由区域生长得到的分割图像
2区域分裂与聚合
上一节中讨论的过程是从一组种子点来生长区域。另一种可供选择的方法是首先将一幅图像细分为一组任意的不相交区域,然后聚合和或分裂这些区域,并力图满足分割条件。
令R表示整幅图像区域,并选择一个属性Q。对R进行分割的一种方法是依次将它细分为越来越小的四象限区域,以便对于任何区域R,有Q(R)=TRUE。我们从整个区域开始。如果Q(R)= FALSE,那么将该图像分割为4个象限区域。如果对于每个象限区域Q为FALSE,则将该象限区域再次细分为四个子象限区域,依此类推。
四、用形态学分水岭的分割
分水岭算法是一种借鉴了形态学理论的分割方法,在该方法中,将一幅图像看成一个拓扑地形图,其中图像的灰度值对应地形高度值。高灰度值对应山峰,低灰度值对应山谷。水总是朝地势低的地方流动,直到某一局部低洼处才停下来,这个低洼处被称为汇水盆地。最终所有的水会分聚在不同的汇水盆地,汇水盆地之间的山脊被称为分水岭。对图像进行分割,就是要在灰度图像中找出不同的“汇水盆地”和“分水岭”。也就是感兴趣的区域内部及其边缘。
一般考虑到各区域内部像素的灰度比较接近,而相邻区域像素间的灰度差距较大,可以先计算一幅图像的梯度图,再寻找梯度图的分水岭。在梯度图中,小梯度值对应区域内部,大梯度值对应区域的边界,分水岭算法就是在寻找大梯度值像素的位置。
直接应用分水岭分割算法的效果往往并不好,通常会由于噪声和梯度的其他局部不规则性造成过度分割。更有甚者,过度分割可能导致不可用的结果。一种解决方案是,通过融入预处理步骤来限制允许存在的区域数量。用于控制过度分割的一种方法是基于标记的。标记是指属于一幅图像的连通分量。与感兴趣物体相联系的标记称为内部标记,与背景相关联的标记称为外部标记。