赞
踩
首先加载一个指纹图片看看,使用numpy去存储:
fingerprint = cv.imread('samples/sample_1_1.png', cv.IMREAD_GRAYSCALE)
show(fingerprint, f'Fingerprint with size (w,h): {fingerprint.shape[::-1]}')
分割的目标主要是将指纹(前景)从背景中分割出来。图片的前景通过观察可知是由条状或者圆形的一些组成,而背景就只是一个均匀的底色而已。我们使用非常简单的手段,基于局部梯度就可以很容易实现我们的目标。
那么使用sobel滤波是一个非常好的选择。Sobel 算子是一个离散微分算子 (discrete differentiation operator)。 它结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像边缘,相素值会发生显著的变化了。表示这一改变的一个方法是使用 导数 。 梯度值的大变预示着图像中内容的显著变化。用更加形象的图像来解释,假设我们有一张一维图形。下图1中灰度值的”跃升”表示边缘的存在,图2中使用一阶微分求导我们可以更加清晰的看到边缘”跃升”的存在。
具体的计算算法为:
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
我们使用sobel在指纹上:
# Calculate the local gradient (using Sobel filters)
gx, gy = cv.Sobel(fingerprint, cv.CV_32F, 1, 0), cv.Sobel(fingerprint, cv.CV_32F, 0, 1)
show((gx, 'Gx'), (gy, 'Gy'))
可以看到两个方向的梯度在指纹的条纹上展现了很明显的特征,横向Gx可以看到左右部分明显的轮廓。纵向Gy可以看到上下部分明显的轮廓。我们可以计算一下梯度的平方来看一下效果,平方就不管是正负了。
# Calculate the magnitude of the gradient for each pixel
gx2, gy2 = gx**2, gy**2
gm = np.sqrt(gx2 + gy2)
show((gx2, 'Gx**2'), (gy2, 'Gy**2'), (gm, 'Gradient magnitude'))
这里,gm是两个相加的结果。可以看到相加之后,主要的纹理特征已经可以很明显的提取出来了。Perfect!
接下来,使用一个积分模块,在一个方形区域进行积分:
# Integral over a square window
sum_gm = cv.boxFilter(gm, -1, (25, 25), normalize = False)
show(sum_gm, 'Integral of the gradient magnitude')
可以看到大部分指纹区域都变白色,而没有指纹区域还是黑色。我们可以使用一个简单的阈值来把背景和前景区分开:
# Use a simple threshold for segmenting the fingerprint pattern
thr = sum_gm.max() * 0.2
mask = cv.threshold(sum_gm, thr, 255, cv.THRESH_BINARY)[1].astype(np.uint8)
show(fingerprint, mask, cv.merge((mask, fingerprint, fingerprint)))
左边是原始的指纹图像,中间是mask,右边则是将指纹和mask合并之后得到的图像。
位置(x,y)的局部脊线的方向在【0-180】之间,是脊线和水平轴形成的。对于每一个像素点,我们计算他的梯度Gx,Gy,也就是上面我们计算的。那么某一个像素点的方向就可以计算为这一点的梯度方向的正交方向,这个角度在一个预定的窗口中进行平均。
G x x = ∑ W G x 2 G_{xx}=\sum_W{G_x^2} Gxx=∑WGx2, G y y = ∑ W G y 2 G_{yy}=\sum_W{G_y^2} Gyy=∑WGy2, G x y = ∑ W G x G y G_{xy}=\sum_W{G_xG_y} Gxy=∑WGxGy
θ = π 2 + p h a s e ( G x x − G y y , 2 G x y ) 2 \theta=\frac{\pi}{2} + \frac{phase(G_{xx}-G_{yy}, 2G_{xy})}{2} θ=2π+2phase(Gxx−Gyy,2Gxy)
对于每一个方向,我们也会去计算他的置信度,也就是计算在W 中有多少梯度有同样的方向,自然数量越多,计算的结果越可靠:
s
t
r
e
n
g
t
h
=
(
G
x
x
−
G
y
y
)
2
+
(
2
G
x
y
)
2
G
x
x
+
G
y
y
strength = \frac{\sqrt{(G_{xx}-G_{yy})^2+(2G_{xy})^2}}{G_{xx}+G_{yy}}
strength=Gxx+Gyy(Gxx−Gyy)2+(2Gxy)2
phase函数根据函数:
来计算角度,计算精度大约为0.3弧度,当x,y相等时,angle为0。
数学上函数atan2为:
该函数的值域为(-pi,pi],可以通过对负数结果加2pi的方法,将函数的结果映射到[0,2pi)范围内。
参考:
W = (23, 23)#我们定义一个23x23的窗口
gxx = cv.boxFilter(gx2, -1, W, normalize = False)# 在给定的滑动窗口大小下,对每个窗口内的像素值进行快速相加求和
gyy = cv.boxFilter(gy2, -1, W, normalize = False)
gxy = cv.boxFilter(gx * gy, -1, W, normalize = False) # gx * gy
gxx_gyy = gxx - gyy
gxy2 = 2 * gxy
orientations = (cv.phase(gxx_gyy, -gxy2) + np.pi) / 2 # '-' to adjust for y axis direction phase函数计算方向场
sum_gxx_gyy = gxx + gyy
strengths = np.divide(cv.sqrt((gxx_gyy**2 + gxy2**2)), sum_gxx_gyy, out=np.zeros_like(gxx), where=sum_gxx_gyy!=0)# 计算置信度,也就是计算在W 中有多少梯度有同样的方向,自然数量越多,计算的结果越可靠
show(draw_orientations(fingerprint, orientations, strengths, mask, 1, 16), 'Orientation image')
可以看到脊线方向被正确识别出来。
Charles@Shenzhen
分割:
local gradient (Sobel filters)
[1] Mehtre, Babu M., and B. Chatterjee. “Segmentation of fingerprint images—a composite method.” Pattern recognition 22.4 (1989): 381-385.
[3] https://docs.opencv.org/3.4/d2/d2c/tutorial_sobel_derivatives.html
local ridge orientation
[4] A.M. Bazen and S.H. Gerez, “Systematic methods for the computation of the directional fields and singular points of fingerprints,” in IEEE tPAMI, July 2002
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。