边缘检测在图像的检测中是经常会用到的。图片的边缘会包含大量的信息,因此在图像的分割、识别、分析中通常可以取边缘作为图像特征。边缘检测最经典的应用就是图像的锐化了,想必大家都用过。
为了进行边缘检测,我们通常会用到以下的一些算子,即一阶算子(梯度算子)和二阶算子(拉普拉斯算子)。
拉普拉斯算子其实就是二阶微分算子,具有各向同性。他也会增强噪声,因此在使用拉普拉斯算子之前需要进行平滑处理。他的主要用处其实是判断一个像素是处于图像的亮区还是暗区。
拉普拉斯算子定义式:
常用模板公式:(1)\begin{bmatrix}0&1&0\\1&-4&1\\0&1&0\end{bmatrix}\begin{bmatrix}1&1&1\\1&-8&1\\1&1&1\end{bmatrix}
拉普拉斯模板有多个变种,上面是常用的两个,对于每一个像素,我们把他作为模板的中心元素,套用公式进行更新。需要注意的是,与梯度算子不同,他是各向同性的,因此不需要G_x,G_y两个模板,也就不能区分边缘的方向了。
还有一个常用的高斯-拉普拉斯算子(Gauss-Laplacian Operator),将平滑滤波器和拉普拉斯边缘检测算子结合起来的:
高斯-拉普拉斯算子模板:\begin{bmatrix}-2&-4&-4&-4&-2\\-4&0&8&0&-4\\-4&8&24&8&-4\\-4&0&8&0&-4\\-2&-4&-4&-4&-2\end{bmatrix}
另外,拉普拉斯算子有一个很实用的用途,就是对图像进行锐化,这时候只要将常用模板稍微改动一下即可得到锐化模板:
(1)\begin{bmatrix}0&-1&0\\-1&5&-1\\0&-1&0\end{bmatrix}\begin{bmatrix}-1&-1&-1\\-1&9&-1\\-1&-1&-1\end{bmatrix}
OK,最后就是用代码实现一下了,总体还是很简单的,而且类似于OpenCV之类的库都有实现的函数。不过我还是用pil来简单实现一下:
import Image,matplotlib,sys,ImageEnhance,ImageFont,ImageDraw
import numpy as np
def gauss(im_arr):
mat=np.array([[-2,-4,-4,-4,-2],[-4,0,8,0,-4],[-4,8,24,8,-4],[-4,0,8,0,4],[-2,-4,-4,-4,-2]])
height,width=im_arr.shape
ans=np.zeros((height-4,width-4))
for i in range(height-4):
for j in range(width-4):
ans[i,j]=np.sum(im_arr[i:i+5,j:j+5]*mat)
return ans
def laplacian(im_arr):
mat=np.array([[1,1,1],[1,-8,1],[1,1,1]])
height,width=im_arr.shape
ans=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
ans[i,j]=np.sum(im_arr[i:i+3,j:j+3]*mat)
return ans
def sharpen(im_arr):
mat=np.array([[-1,-1,-1],[-1,9,-1],[-1,-1,-1]])
height,width=im_arr.shape
ans=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
ans[i,j]=np.sum(im_arr[i:i+3,j:j+3]*mat)
return ans
def robert(im_arr):
height,width=im_arr.shape
ans=np.zeros((height-1,width-1))
for i in range(height-1):
for j in range(width-1):
ans[i,j]=np.abs(im_arr[i,j]-im_arr[i+1,j+1])+np.abs(im_arr[i+1,j]-im_arr[i,j+1])
return ans
def sobel(im_arr):
matx=np.array([[-1,0,1],[-2,0,2],[-1,0,1]])
maty=np.array([[1,2,1],[0,0,0],[-1,-2,-1]])
height,width=im_arr.shape
gx=np.zeros((height-2,width-2))
gy=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
gx[i][j]=np.sum(matx*im_arr[i:i+3,j:j+3])
gy[i][j]=np.sum(maty*im_arr[i:i+3,j:j+3])
ans=np.abs(gx)+np.abs(gy)
return ans
def prewitt(im_arr):
matx=np.array([[1,0,-1],[1,0,-1],[1,0,-1]])
maty=np.array([[-1,-1,-1],[0,0,0],[1,1,1]])
height,width=im_arr.shape
gx=np.zeros((height-2,width-2))
gy=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
gx[i][j]=np.sum(matx*im_arr[i:i+3,j:j+3])
gy[i][j]=np.sum(maty*im_arr[i:i+3,j:j+3])
ans=np.abs(gx)+np.abs(gy)
return ans
def isotropic(im_arr):
matx=np.array([[1,0,-1],[1.414,0,-1.414],[1,0,-1]])
maty=np.array([[-1,-1.414,-1],[0,0,0],[1,1.414,1]])
height,width=im_arr.shape
gx=np.zeros((height-2,width-2))
gy=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
gx[i][j]=np.sum(matx*im_arr[i:i+3,j:j+3])
gy[i][j]=np.sum(maty*im_arr[i:i+3,j:j+3])
ans=np.abs(gx)+np.abs(gy)
return ans
def show(im_arr,name):
im=Image.fromarray(im_arr)
draw=ImageDraw.Draw(im)
font = ImageFont.truetype('/usr/share/fonts/truetype/liberation/LiberationMono-BoldItalic.ttf', 18)
draw.text((0,0),name,font=font,fill='red')
del draw
im=im.convert('L')
im.save(name+'.png')
if __name__=='__main__':
im=Image.open(sys.argv[1])
im=im.convert('L')
im.save('source.png')
show(laplacian(np.array(im,'int64')),'Laplace')
show(sobel(np.array(im,'int64')),'Sobel')
show(robert(np.array(im,'int')),'Robert')
show(prewitt(np.array(im,'int64')),'Prewitt')
show(isotropic(np.array(im,'int64')),'Isotropic')
show(sharpen(np.array(im,'int64')),'sharpen')
show(gauss(np.array(im,'int64')),'gauss-laplace')
最后用他来处理一下lena.png,会的到上面的结果。