本节为图像空域增强的第四篇,介绍重要的图像空域增强技术:邻域处理,本节是下篇,介绍邻域平滑处理中的非线性滤波技术,以及邻域锐化处理。重点介绍中值滤波、以及Laplace算子和非锐化遮蔽技术以及在锐化处理中的应用。本节除了用到Skimage中filter子模块的部分函数外,还会用到SciPy中ndimage模块的部分函数。
目录
5.4 邻域处理
5.4.3 非线性平滑滤波器
除了线性平滑滤波器之外,还有一类非线性滤波器也能在一定程度上满足平滑功能,称为非线性平滑滤波器。其中,最常用的一类非线性滤波器是统计排序滤波器(Statistic order filter),中值滤波 (Median filter)统计排序滤波器的典型代表 。
中值滤波法将像素点的邻域内的所有像素点灰度值按照从小到大顺序排列,并选取位于中间位置的灰度值(称为中值)作为输出值。以
Skimage中有两个函数都可以完成中值滤波功能,分别是skimage.filters.rank.median和skimage.filters.median,再加上SciPy中scipy.ndimage.median_filter提供的函数,一下子出现了三个中值滤波函数。它们功能上有何区别呢?在此简要给出比较。
- scipy.ndimage.median:实现多维数据的中值滤波器。
- skimage.filters.median:返回一幅图像的局部均值。
- skimage.filters.rank.median:返回一幅图像的局部均值。
显然,三个函数功能相似。
看三个函数各自的声明:
- scipy.ndimage.median_filter(input, size, footprint, output, mode, cval, origin, axes)
- skimage.filters.rank.median(image, footprint, out, mask, shift_x, shift_y, shift_z)
- skimage.filters.median(image, footprint, out, mode, cval, behavior)
部分参数说明:
- input:输入图像。
- output:处理结果。
- footprint:用来设定中滤波器的模板,包括形状和大小。
- size:用来设定中值滤波器模板的尺寸,和footprint不能同时使用。
- mode:边界延拓方式。
- cval:如果边界延拓方式设置为参数值,则用cval设定数值,默认值为0。
- footprint:用于计算直方图的bin的数目。
- origin:用于指定模板中参考点的位置,默认值为中心点。
- mask:图像模板,只有取值>0的点才进行滤波后处理,如果设置为None(默认值),则表示对整幅图像都进行滤波。
- shift_x/shift_y/shift_z:在x/y/z三方向上相对于参考点的偏移量。
- origin:用于指定模板中参考点的位置,默认值为中心点。
- behavior:滤波模式标志,一共两种模式:{‘ndimage’, ‘rank’} ,如果设为‘rank‘,则调用skimage.filters.rank.median;如果设为'ndimage',则调用scipy.ndimage.median_filter。默认值为‘ndimage’。
- channel_axis:待补充。
- axes:标志位,如果设为None,则在所有轴(方向上)进行滤波;否则,沿着特定方向进行滤波。
几点说明:
- 对于skimage.filters.rank.median和skimage.filters.median,默认的滤波器形状都是3*3*N,其中N为图像数据的通道数。
- skimage.filters.median函数并没有提供新功能,而是通过设置标志位,选择是调用skimage.filters.rank.median还是scipy.ndimage.median。
- scipy.ndimage.median和skimage.filters.rank.median这两个函数相比,官网文档给出的说明是,scipy.ndimage.median比skimage.filters.rank.median参数更多,使用起来也更灵活,但它要求图像数据为无符号整型。
基于上述分析,最终推荐优先使用skimage.filters.median函数完成中值滤波功能,且滤波模式采用默认设置,实际上调用的是scipy.ndimage.median。当然也可以直接调用后者。
from scipy import ndimage, datasets
import matplotlib.pyplot as plt
ascent = datasets.ascent()
result = ndimage.median_filter(ascent, size=20)
fig = plt.figure()
plt.gray() # show the filtered result in grayscale
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
ax1.imshow(ascent)
ax2.imshow(result)
plt.show()
以下是处理结果。
5.4.4 锐化滤波器
图像锐化(Sharpen)的目的是增强图像的灰度跳变部分,使模糊的图像变得清晰。图像锐化也称为高通滤波,通过和增强高频,衰减和抑制低频。图像锐化常用于电子印刷、医学成像和工业检测。
图像锐化可通过微分运算(有限差分)实现,使用一阶微分(差分)或二阶微分(差分)都可以得到图像灰度的变化值。
- 恒定灰度区域:一阶差分为零,二阶差分为零;
- 灰度台阶或斜坡起点区域:一阶差分非零,二阶差分非零;
- 灰度斜坡区域:一阶差分非零,二阶差分为零。
两类技术相比,二阶差分算子对变化更敏感,因此常用语锐化处理;一阶差分算子虽然对细节变化的敏感程度不如二阶差分,但同样对噪声也不敏感,且计算量更少,因此常用于边缘检测领域,我们将在后面的章节中详细介绍。
(1)Laplace算子
是拉普拉斯算子(Laplacian Operator)是一种最常用的二阶差分算子。它定义如下:
由此可以得到4方向拉普拉斯核K1和K3。类似地,添加上对角项后可以得到8方向拉普拉斯核K2和K4。
由定义式不难发现,拉普拉斯算子是一种各向同性差分算子。由于Laplace 是差分算子,会突出图像中的急剧灰度变化,抑制灰度缓慢变化区域,往往会产生暗色背景下的灰色边缘和不连续图像。将拉普拉斯图像与原图叠加,可以得到保留锐化效果的图像。
Skimage使用skimage.filters.laplace函数实现Laplace算子。
laplace函数完成相关处理。
laplace函数的声明:
skimage.filters.laplace(image, ksize, mask)
部分参数说明:
- image:输入图像,可以是任意维数。
- ksize:模板大小,可以是高维数据。
- mask:用于设置进行处理图像区域的模板。
返回值:
- output:Laplace滤波结果图像,与输入图像维数相同。
在上述函数中,并没有显式指名所用Laplace模板的类型,即没有说明是4-方向还是8-方向模板。
但在Skimage官方文档中提到,skimage.filters.laplace函数实际上是调用了skimage.restoration.uft内部的laplacian函数。通过阅读skimage.restoration.uft.laplacian的源代码发现,原函数使用的是形如上图K3的模板完成的Laplace滤波操作。
需要说明的一点是,使用Laplace算子得到的结果只是图像的细节图像,如果想得到锐化图像,还需要将细节图像叠加到原图像上。
后面给出一段基于Laplace算子的图像锐化增强。
(2)非锐化遮蔽
除了直接使用梯度算子之外,从原始图像中减去一幅平滑处理的钝化图像,也可以实现图像锐化效果,称为非钝化掩蔽(又称钝化遮蔽,Unsharpen Mask)。
钝化掩蔽的实现过程是:
- 对原始图像进行平滑处理,得到平滑图像;
- 从原始图像中减去平滑图像,产生掩蔽模板;
- 将原始图像与掩蔽模板加权相加,得到钝化掩蔽。
用和分别表示原图、对应的平滑图像和钝化遮蔽处理图像,上述过程可用下式表示:
- 当 k>1 时,实现高提升滤波;
- 当 k=1 时,实现非锐化掩蔽;
- 当 k<1时,减弱型非锐化掩蔽。
Skimage使用skimage.filters.unsharp_mask函数完成非锐化遮蔽功能。
unsharp_mask函数完成相关处理。
unsharp_mask函数的声明:
skimage.filters.unsharp_mask(image, radius, amount, preserve_range, channel_axis)
部分参数说明:
- image:输入图像,可以是任意维数。
- radius:各个维度上的模糊度参数,非负值,如果设为0,表示不进行模糊;如果设为标量,表示各个维度采用相同的模糊度处理。
- amount:细节方法倍数,默认值是1.0,值越大,细节增强效果越明显。
- preserve_range:用于设置进行处理图像区域的模板。
- channel_axis:用于设置进行处理图像区域的模板。
返回值:
- output:反锐化遮蔽处理结果图像,与输入图像维数相同。
以下是一段图像锐化处理的示例代码,包括了前面介绍的两种代表性方法,其中反锐化遮蔽用到了两个参数。(需修订)
from skimage import data
from skimage.filters import unsharp_mask
import matplotlib.pyplot as plt
image = data.moon()
result_1 = unsharp_mask(image, radius=1, amount=1)
result_2 = unsharp_mask(image, radius=5, amount=2)
result_3 = unsharp_mask(image, radius=20, amount=1)
fig, axes = plt.subplots(nrows=2, ncols=2,
sharex=True, sharey=True, figsize=(10, 10))
ax = axes.ravel()
ax[0].imshow(image, cmap=plt.cm.gray)
ax[0].set_title('Original image')
ax[1].imshow(result_1, cmap=plt.cm.gray)
ax[1].set_title('Enhanced image, radius=1, amount=1.0')
ax[2].imshow(result_2, cmap=plt.cm.gray)
ax[2].set_title('Enhanced image, radius=5, amount=2.0')
ax[3].imshow(result_3, cmap=plt.cm.gray)
ax[3].set_title('Enhanced image, radius=20, amount=1.0')
for a in ax:
a.axis('off')
fig.tight_layout()
plt.show()
处理结果如下所示:
(本节初稿完成时间:2024-02-18)
(欢迎对DIP+python算法开发感兴趣的初学者,尤其是相关专业本科和低年级研究生关注,本专栏完将持续更新,总篇数不会少于50篇,每篇不会少于5000字,专栏完成之前(差不多到2024年5月份)完全免费阅读,敬请关注)