本文演示了图像的卷积算法的实现,并且进一步实现了基于卷积算法的图像平滑、图像锐化和边缘特征提取算法。最后进行了图像波谱特征指数的简单演示。
1.认识卷积
卷积运算在数字图像处理中又称滤波。卷积运算是指将某像素及其相邻像素的灰度值进行运算,得到该像素新的灰度值的方法。通常将算法中的系数用一个矩阵表示,称为卷积核或算子。
卷积运算可以实现图像的平滑和锐化,实现图像特征区域的提取,还可以用于深度神经网络的构建。
2.构建滤波器
灰度图像又称单波段图像、单通道图像,由于其只有一个灰度值,可以视为一个二维数组,比较容易进行数值运算操作,故首先考虑灰度图像滤波器的实现。
2.1灰度图像滤波器的构建
滤波器的工作方法可以按如下思路进行:
-
接收数据,即图像对象和以二维数组表示的卷积核;
-
构造待输出图像对象,并使之形状与输入图像相同;
-
构造双重for循环,以确保遍历每一个像素点;
-
将像素点周围3×3区域的灰度值建立为二维数组;
-
将像素值数组与卷积核数组进行按位相乘并求和,获得新的像素值;
-
遍历完成,输出结果。
在实际操作中还需要进行以下几个问题:
-
不能探测图片边缘,以避免不必要的麻烦;
-
进行数组运算时,保证数组均为
numpy.array
对象,以避免编译出错; -
保证图像灰度值为0~255的整数,即
uint8
类型。
滤波函数的实现代码如下:
def grayimgfliter(img,kernel_array):
#构建滤波函数,接收的数据为待处理图像和卷积核
height,width=img.shape
#获取图像高度和宽度
new_img=np.zeros((height,width),dtype='uint8')
#创建一个形状与图片一致的空二维数组
kernel_array=np.array(kernel_array)
#确保卷积核矩阵为np.array类型
for x in range(1,height-1):
for y in range(1,width-1):
#使用双重for循环遍历不在边缘的每一个像素
pix_array=np.array([[img[x-1,y-1],img[x,y-1],img[x+1,y-1]],
[img[x-1,y],img[x,y],img[x+1,y]],
[img[x-1,y-1],img[x,y-1],img[x+1,y-1]]])
#用像素和相邻像素的灰度值创建二维数组
new_pix_value=np.sum(pix_array*kernel_array)
#数组按位相乘并求和
if new_pix_value<0:
new_img[x,y]=0
elif new_pix_value>255:
new_img[x,y]=255
else:
new_img[x,y]=int(new_pix_value)
#以整数记录该点的新灰度值
return new_img
下面检验一下滤波函数的应用效果:
img=mpimg.imread('kite.jpg')
red_img=img[:,:,0]#提取红光波段
smooth_array=[[0.125,0.125,0.125],
[0.125,0,0.125] ,
[0.125,0.125,0.125]]#构造卷积核
new_red_img=grayimgfliter(red_img,myarray)
#使用滤波函数获得新图像
mpimg.imsave('red_kite.jpg',red_img)
mpimg.imsave('smooth_red_kite.jpg',new_red_img)
2.2彩色图像滤波器的构建
有了灰度图像的滤波器,就可以通过函数调用建立彩色图像的滤波器:
def colorimgfliter(img,kernel_array):
red_img=img[:,:,0]
green_img=img[:,:,1]
blue_img=img[:,:,2]
new_red_img=grayimgfliter(red_img,kernel_array)
new_green_img=grayimgfliter(green_img,kernel_array)
new_blue_img=grayimgfliter(blue_img,kernel_array)
new_img=np.dstack((new_red_img,new_green_img,new_blue_img))
return new_img
构造一个卷积核,检验函数的应用效果:
img=mpimg.imread('kite.jpg')
kernel_array=[[0.25,0,0.25],
[0,0,0],
[0.25,0,0.25]]
new_img=colorimgfliter(img,kernel_array)
mpimg.imsave('new_kite.jpg',new_img)
3.图像平滑与锐化
3.1图像平滑
图像平滑可以通过平滑算子的卷积运算实现,为了不使图片整体明暗发生较大变化,需保证算子元素总和尽量接近1 。例如:
img=mpimg.imread('kite.jpg')
smooth=[[0.125,0.125,0.125],
[0.125,0,0.125],
[0.125,0.125,0.125]]
new_img=colorimgfliter(img,smooth)
mpimg.imsave('smoothed_kite.jpg',new_img)
3.2图像锐化
图像锐化可以使图片的细节更明显,实现方法与图像平滑类似,同样需要注意算子元素总和尽量接近1。
img=mpimg.imread('kite.jpg')
sharp=[[0,-0.125,0],
[-0.125,1.5,-0.125],
[0,-0.125,0]]
new_img=colorimgfliter(img,sharp)
mpimg.imsave('sharped_kite.jpg',new_img)
4.边缘特征提取
4.1边缘特征提取算法
图像边缘即图像颜色发生强烈变化的区域,通过一定的算法使图像灰度值变化较大的地方突出显示,即可达到目的。对于不同的算子,有不同的运算过程。如拉普拉斯算子:
进行一次运算即可获得结果。而对于方向敏感的算子,如Prewitt算子:
需要先计算两个方向的卷积结果Gx和Gy,然后求出整体梯度 。
4.2边缘特征提取函数构建
边缘特征提取函数可以通过对滤波器函数的改造实现。某些特征提取算子通常通过两次滤波分别获得图像在纵横两个方向上的梯度特征,然后求出总体梯度,并设置阈值,突出梯度值较高的部分。
首先对滤波函数做一些修改,使其变成计算像素灰度值梯度的函数。仅需将原有的记录方式改变一下即可,使之保留卷积运算的原始结果。
def gradient(img,kernel_array):
height,width=img.shape
new_img=np.zeros((height,width),dtype=float)#输出矩阵元素为float类型
kernel_array=np.array(kernel_array)
for x in range(1,height-1):
for y in range(1,width-1):
pix_array=np.array([[img[x-1,y-1],img[x,y-1],img[x+1,y-1]],
[img[x-1,y],img[x,y],img[x+1,y]],
[img[x-1,y-1],img[x,y-1],img[x+1,y-1]]])
new_img[x,y]=np.sum(pix_array*kernel_array)
#对卷积计算结果直接记录。
return new_img
然后根据两个方向的灰度值计算结果,与阈值进行比较,提取图形边缘。
def grayimg_extracter(img,kernel1,kernel2,threshold=0):
#参数为待处理图像,纵方向梯度算子,横方向梯度算子,阈值
threshold=threshold*threshold#阈值平方,以避免阈值比较时的开方运算
height,width=img.shape#获取图像高度和宽度
grad_img=np.zeros((height,width),dtype='uint8')#创建待输出图像
xgrad=gradient(img,kernel1)#进行卷积运算,获取纵方向梯度
ygrad=gradient(img,kernel2)#进行卷积运算,获取横方向梯度
for x in range(0,height):
for y in range(0,width):#双重循环,遍历像元
grad_value=xgrad[x,y]**2+ygrad[x,y]**2#计算梯度值
if grad_value<threshold:#判断梯度是否超过阈值
grad_img[x,y]=0
grad_img[x,y]=255
return grad_img#返回结果
还需要考虑不同波段结果的合并,所有还需要构造图层合并的方法:
def bands_combine(a:np.array,b:np.array,c:np.array):
height,width=a.shape
new_arr=np.zeros((height,width,3),dtype='uint8')
for x in range(0,height):
for y in range(0,width):
if (a[x,y]==255|b[x,y]==255|c[x,y]==255):
#如果在某一图层梯度值超过域值,则使其显示为白色
new_arr[x,y,0]=255
new_arr[x,y,1]=255
new_arr[x,y,2]=255
return new_arr
通过对前面已经定义的函数的简单调用,就可以实现彩色图像特征提取的方法:
def colorimg_extracter(img,kernel1,kernel2,threshold=0):
a=grayimg_extracter(img[:,:,0],kernel1,kernel2,threshold)
b=grayimg_extracter(img[:,:,1],kernel1,kernel2,threshold)
c=grayimg_extracter(img[:,:,2],kernel1,kernel2,threshold)
new_img=bands_combine(a,b,c)
return new_img
下面演示Sobel算子的运算结果:
img=mpimg.imread('kite.jpg')
xsobel=[[-1,0,1],
[-2,0,2],
[-1,0,1]]
ysobel=[[-1,-2,-1],
[0,0,0],
[1,2,1]]
new_img=colorimg_extracter(img,xsobel,ysobel,99)
mpimg.imsave('sobel_kite.jpg',new_img)
5.多波段特征提取
识别像元在不同波段的灰度值特征,是图像计算机分类的重要手段。
由于python有一个非常方便的特性,即可以将函数作为另一个函数的参数,因此,可以先将指数算法打包成一个函数,例如指数:I=ρGreen-ρBlue:
def index0(red,green,blue):
i=red-green
return i
然后通过遍历像元的方法,获得整体指数的分布:
def bands_calculation(index,img):#将指数计算方法和图像作为参数
height,width,bands=img.shape#获取图片形状
index_array=np.zeros((height,width),dtype=float)
#创建待记录数组
for x in range(0,height):
for y in range(0,width):
#遍历像元
index_array[x,y]=index(img[x,y,0],img[x,y,1],img[x,y,2])
#根据算法记录指数值
return index_array#返回结果数组
需要注意的是,这里返回的是一个记录计算结果数组,并不能直接保存为图片。如果想要以图片方式查看结果,需要将计算计算结果(取值范围不确定的float型)转换为图像灰度值(取值范围为0到255的无符号整型'uint8')。
示例如下,首先设定某个指数的算法,例如以指数i=red-green=50为判断标准,分割图像区域:
def index(red,green,blue):
i=red-green
if i<50:
return 255
else:
return 0
使用bands_calculation
函数,并将算法和图像作为参数传入,并将结果数组转化为uint8
格式,并以图片保存:
img=mpimg.imread('kite.jpg')#读入图片
img_i=bands_calculation(index,img)#调用方法,获得结果数组
img_i=img_i.astype('uint8')#将数组内元素转化为uint8类型
mpimg.imsave('kite_i.jpg',img_i)#保存结果
```