Laplace算子
Label算子是一个二阶微分算子,它实际上是一个x方向的二阶导数和y方向的二阶导数之和的近似微分。该算子比较适合应用于只关心边缘位置而不考虑其周围像素灰度差值的图像边缘检测场景。Laplace算子对孤立像素的响应比对边缘像素的响应要更强烈些,因此适用于无噪声的图像处理。存在噪声的情况下,使用Laplace算子检测边缘前需要先对图像进行低通滤波。所以,通常的分割算法都是把Laplace算子和平滑算子结合起来生成一个新的模板。
为了更适合数字图像处理,可将Laplace算子表示为如下离散形式:
▽²f=(f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1))
常用的离散Laplace算子:
其拓展算子为:
Laplace算子一般不以其原始形式用于边缘检测,因其作为一个二阶导数,Laplace算子对噪声有无法接受的敏感性,一般使用的是高斯型Laplace算子(Laplace of Gaussian,LoG)。再LoG公式中使用高斯函数的目的是对图像进行平滑处理,使用Laplace算子的目的就是提供一幅用零交叉确定边缘位置的图像。图像的平滑处理减少了噪声的影响,且他的主要作用是抵消由Laplace算子的二阶导数引起的逐渐增加的噪声影响。
利用Laplace算子进行图像边缘检测的代码如下:
#Laplace算子 import numpy as np from PIL import Image import matplotlib.cm as cm import matplotlib.pyplot as plt import scipy.signal as signal #定义Laplace算子 Operator1=np.array([[0,1,0],[1,-4,1],[0,1,0]]) #定义Laplace拓展算子 Operator2=np.array([[1,1,1],[1,-8,1],[1,1,1]]) image=Image.open('D:\Image\one.jpg').convert('L') image_array=np.array(image) #卷积计算 image_oper1=signal.convolve2d(image,Operator1,mode='same') image_oper2=signal.convolve2d(image,Operator2,mode='same') #由于卷积后的元素的值不一定为0~255,所以需要归一化为0~255 image_oper1=image_oper1*(255/float(image_oper1.max())) image_oper2=image_oper2*(255/float(image_oper2.max())) #将大于灰度平均值的灰度值变成255(白色),方便观察 image_oper1[image_oper1>image_oper1.mean()]=255 image_oper2[image_oper2>image_oper2.mean()]=255 #显示边缘检测结果 plt.subplot(2,1,1) plt.imshow(image_array,cmap=cm.gray) plt.axis('off') plt.subplot(2,2,3) plt.imshow(image_oper1,cmap=cm.gray) plt.axis('off') plt.subplot(2,2,4) plt.imshow(image_oper2,cmap=cm.gray) plt.axis('off') plt.show()
为了更好的边缘检测效果,可先对图像进行模糊平滑处理,除去图像中的高频噪声,高频噪声一般可采用高斯算法来进行处理,该算法示例代码如下:
def func(x,y,sigma=1): return 100*(1/(2*np.pi*sigma))*np.exp(-((x-2)**2)/(2.0*sigma**2))
使用高斯算法降噪后的代码:
#Laplace算子 import numpy as np from PIL import Image import matplotlib.cm as cm import matplotlib.pyplot as plt import scipy.signal as signal #高斯算法示例代码 def func(x,y,sigma=1): return 100*(1/(2*np.pi*sigma))*np.exp(-((x-2)**2)/(2.0*sigma**2)) Operator1=np.fromfunction(func,(5,5),sigma=5) #定义Laplace算子 Operator2=np.array([[0,1,0],[1,-4,1],[0,1,0]]) image=Image.open('D:\Image\one.jpg').convert('L') image_array=np.array(image) #卷积计算 #通过生成的高斯算子与原图像进行卷积计算来对图像进行平滑处理 image_blur=signal.convolve2d(image_array,Operator1,mode='same') image_oper1=signal.convolve(image_blur,Operator2,mode='same') #由于卷积后的元素的值不一定为0~255,所以需要归一化为0~255 image_oper1=image_oper1*(255/float(image_oper1.max())) #将大于灰度平均值的灰度值变成255(白色),方便观察 image_oper1[image_oper1>image_oper1.mean()]=255 #显示边缘检测结果 plt.subplot(1,2,1) plt.imshow(image_array,cmap=cm.gray) plt.axis('off') plt.subplot(1,2,2) plt.imshow(image_oper1,cmap=cm.gray) plt.axis('off') plt.show()
Conny算子
Conny算子是一种基于图像梯度计算的图像边缘检测算法,与上文提及的基于Laplace算法的图像边缘检测方法类似,亦属于先平滑后求导的方法。利用Conny算子实现图像边缘检测的过程分为以下几个步骤:
`①.图像灰度化```
②.对图像进行高斯平滑滤波
首先生成二维高斯分布矩阵:
p (x,y) = p (x)p (y) = frac {1} {2pi} exp (-frac {x^2+y^2} {2}) tag {5} 为了向量化公式,用向量 textbf {v}= [x y]^T
然后,将其与灰度图像进行卷积实现图像滤波:
fs(x,y)=f(x,y)*p(x,y)
③.计算梯度幅值和方向。
求变化率时,对于一元函数,即为求导,对于二元函数即为求偏导。在数字图像处理中,用一阶有限差分近似方法求得灰度值的梯度幅值(变化率)。
④.对梯度幅值进行非极大值抑制(Non-Maximum Suppression,NMS)。
寻找像素点局部最大值,沿着梯度方向,比较它前面和后面的梯度幅值。在沿其梯度方向上领域的梯度幅值最大则保留,反之则抑制。这一步主要是排除非边缘像素,仅保留部分细线条(候选边缘)。
⑤.用双阈值法检测和连接边缘。
——>选取梯度幅值为高阈值TH和低阈值TL,TH:TL为2:1或3:1.
——>如果某一像素位置的梯度幅值超过TH,则该像素被保留为边缘像素。
——>如果某一像素位置的梯度幅值小于TL,则该像素排除。
——>如果某一像素位置的梯度幅值在TH和TL之间,则该像素仅仅在连接到一个高于原像素时被保留。
利用Canny算子进行图像边缘检测示例代码:
#Canny算子 import matplotlib.pyplot as plt import matplotlib.cm as cm import numpy as np import math #载入原图 img=plt.imread('D:\Image\one.jpg') sigma1=sigma2=1 #设定高斯滤波器标准差,缺省值为1 sum=0 gaussian=np.zeros([5,5])#初始化5*5高斯算子矩阵 for i in range(5): for j in range(5): #生成二维高斯分布矩阵 gaussian[i,j]=math.exp(-1/2*(np.square(i-3)/np.square(sigma1)+(np.square(j-3)/np.square(sigma2))))/(2*math.pi*sigma1*sigma2) sum=sum+gaussian[i,j] gaussian=gaussian/sum def rgb2gray(rgb): #rgb图像转换为灰度图像 return np.dot(rgb[...,:3],[0.299,0.587,0.114]) #高斯滤波 gray=rgb2gray(img) W,H=gray.shape new_gray=np.zeros([W-5,H-5]) for i in range(W-5): for j in range(H-5): #与高斯矩阵卷积实现滤波 new_gray[i,j]=np.sum(gray[i:i+5,j:j+5]*gaussian) #通过求梯幅值使图像增强 W1,H1=new_gray.shape dx=np.zeros([W1-1,H1-1]) dy=np.zeros([W1-1,H1-1]) d=np.zeros([W1-1,H1-1]) for i in range(W1-1): for j in range(H1-1): dx[i,j]=new_gray[i,j+1]-new_gray[i,j] dy[i,j]=new_gray[i+1,j]-new_gray[i,j] #图像梯度幅值作为图像强度值 d[i,j]=np.sqrt(np.square(dx[i,j])+np.square(dy[i,j])) #非极大值抑制NMS W2,H2=d.shape NMS=np.copy(d) NMS[0,:]=NMS[W2-1,:]=NMS[:,0]=NMS[:,H2-1]=0 for i in range(1,W2-1): for j in range(1,H2-1): if d[i,j]==0: NMS[i,j]=0 else: gradX=dx[i,j] gradY=dx[i,j] gradTemp=d[i,j] #如果Y方向梯度幅值较大 if np.abs(gradY)>np.abs(gradX): weight=np.abs(gradX)/np.abs(gradY) grad2=d[i-1,j] grad4=d[i+1,j] #如果x,y方向梯度幅值的符号相同 if gradX*gradY>0: grad1=d[i-1,j-1] grad3=d[i+1,j+1] #如果x,y方向梯度幅值的符号相反 else: grad1=d[i-1,j+1] grad3=d[i+1,j-1] else: weight=np.abs(gradY)/np.abs(gradX) grad2=d[i,j-1] grad4=d[i,j+1] if gradX*gradY>0: grad1=d[i+1,j-1] grad3=d[i+1,j+1] else: grad1=d[i-1,j-1] grad3=d[i+1,j+1] gradTemp1=weight*grad1+(1-weight)*grad2 gradTemp2=weight*grad3+(1-weight)*grad4 if gradTemp>=gradTemp1 and gradTemp>=gradTemp2: NMS[i,j]=gradTemp else: NMS[i,j]=0 W3,H3=NMS.shape DT=np.zeros([W3,H3]) #定义高低阈值TH,TL TL=0.2*np.max(NMS) TH=0.3*np.max(NMS) for i in range(1,W3-1): for j in range(1,H3-1): if (NMS[i,j]<TL): DT[i,j]=0 elif (NMS[i,j]>TH): DT[i,j]=1 elif ((NMS[i-1,j-1:j+1]<TH).any() or (NMS[i+1,j-1:j+1]).any() or (NMS[i,[j-1,j+1]]<TH).any()): DT[i,j]=1 plt.subplot(2,2,1) plt.imshow(new_gray,cmap=cm.gray) #原始图像 plt.axis('off') plt.subplot(2,2,2) plt.imshow(d,cmap=cm.gray) #高斯滤波图像 plt.axis('off') plt.subplot(2,2,3) plt.imshow(NMS,cmap=cm.gray) #非极大值抑制图像 plt.axis('off') plt.subplot(2,2,4) plt.imshow(DT,cmap=cm.gray) #双阈值检测边缘图像 plt.axis('off') plt.show()
OpenCV中也封装了Canny图形边缘检测函数,其函数原型:
cv2.Canny(image,threshold1,threshold2[,edges[,apertureSize[,L2gradient]]])
参数说明如下:
(1).image:需要处理的原图,该图像必须为单通道的灰度图像。
(2).threshold1:阈值1。
(3).threshold2:阈值2。(较大的阈值,用于检测明显的边缘,但是会出现断断续续的,所以需要使用threshold2进行连接)。
(4).edges:函数返回一幅二值图像(黑白),其中包含检测出来的边缘。
(5).apertureSize:Sobel算子。
(6).L2gradient:一个布尔值,如果为True,则使用更精确的L2范数进行计算(即两个方向导数的平方和再开方);如果为False,则使用L1范数进行计算(直接将两个方向导数的绝对值)。
基于opencv的Canny函数的图像边缘检测示例代码:
#OPenCV的canny算子 import cv2 import numpy as np #Canny只能处理灰度图像 img=cv2.imread('D:\Image\\two.jpg',0) #通过高斯平滑处理对原图像进行降噪 img=cv2.GaussianBlur(img,(3,3),0) canny=cv2.Canny(img,50,150) #apertureSize默认为3 cv2.imshow('Canny边缘检测',canny) cv2.waitKey(0) cv2.destroyAllWindows()