论文简介
论文数据是根据接触式掌纹图片进行掌纹识别。主要分为三大步骤:
- 预处理:从整个掌纹图片中获取感兴趣的区域ROI
- 特征提取:对ROI使用Gabor滤波器提取出特征图
- 对比:对特征进行逐帧对比得到汉明距离
掌纹图使用香港中文大学公开数据集
代码复现
预处理
根据论文需要获取手指指缝的两个谷点,并构建直角坐标系,截取ROI 该部分方法参考了 Opencv-Python提取掌纹图片ROI 基于图像相位及方向特征的掌纹识别的c++实现(二)
获取手掌轮廓
先二值化整个图像,找到所有轮廓中的最大面积部分,一般该部分为手掌
def binarize_image(img, blur_kernel_size=(15, 15), blur_sigma=2, threshold=20):
"""二值化图像"""
# 使用高斯滤波进行平滑处理,去除噪声
blur = cv2.GaussianBlur(img, blur_kernel_size, blur_sigma)
# 使用阈值二值化图像
_, thresh = cv2.threshold(blur, threshold, 255, cv2.THRESH_BINARY)
return thresh
def extract_contour(img, thresh, output_path=None):
"""提取并填充最大轮廓"""
# 寻找所有轮廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# 计算每个轮廓的面积,找到最大的
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
max_cnt = contours[max_index]
# 创建结果图
contour_img = np.zeros(img.shape)
# 填充轮廓
cv2.fillPoly(contour_img, pts=[max_cnt], color=(255, 255, 255))
# 保存结果
if output_path:
cv2.imwrite(output_path, contour_img)
return contour_img, max_cnt
最后得到的图片如图所示
获取关键点
根据该手掌轮廓获取文中小拇指与无名指、食指和中指直接的谷点
def find_key_points(contour_img, img_height, img_width):
"""查找关键点"""
# 初始化点
point_in_top = [0, 0]
point_out_bottom = [0, 0]
# 从上到下查找第一个白点
for row in range(img_height):
for col in range(img_width):
if contour_img[row][col] == 255.0:
point_in_top = [col, row]
break
if point_in_top != [0, 0]:
break
# 从下到上查找第一个白点
for row in range(img_height-1, -1, -1):
for col in range(img_width):
if contour_img[row][col] == 255.0:
point_out_bottom = [col, row]
break
if point_out_bottom != [0, 0]:
break
# 查找中间的缝隙
gap_x = 0
for col in range(img_width):
gap_width = 0
for row in range(img_height):
if contour_img[row][col] == 0:
gap_width += 1
if gap_width < 200:
gap_x = col
break
# 查找上下点
point_Out_top = (gap_x, 0)
point_In_bottom = (gap_x, 0)
center_y = img_height // 2
for row in range(center_y, -1, -1):
if contour_img[row][gap_x] == 255:
point_Out_top = (gap_x, row)
break
for row in range(center_y, img_height):
if contour_img[row][gap_x] == 255:
point_In_bottom = (gap_x, row)
break
# 查找边缘点
Top_x = Bottom_x = 0.0
Top_y_vector = []
Bottom_y_vector = []
# 查找上边缘最右点
for i in range(point_in_top[1], point_Out_top[1]+1):
for j in range(img_width):
if contour_img[i][j] == 255:
if j > Top_x:
Top_x = j
break
# 查找下边缘最右点
for i in range(point_In_bottom[1], point_out_bottom[1]+1):
for j in range(img_width):
if contour_img[i][j] == 255:
if j > Bottom_x:
Bottom_x = j
break
# 收集上边缘点的y坐标
for i in range(point_in_top[1], point_Out_top[1]+1):
for j in range(img_width):
if contour_img[i][j] == 255:
if j == Top_x:
Top_y_vector.append(i)
break
# 收集下边缘点的y坐标
for i in range(point_In_bottom[1], point_out_bottom[1]+1):
for j in range(img_width):
if contour_img[i][j] == 255:
if j == Bottom_x:
Bottom_y_vector.append(i)
break
# 计算平均y坐标
Top_y = sum(Top_y_vector) / float(len(Top_y_vector))
Bottom_y = sum(Bottom_y_vector) / float(len(Bottom_y_vector))
point_a = (Top_x, Top_y)
point_b = (Bottom_x, Bottom_y)
return point_a, point_b
最后大致获得的关键点如图所示,图源于基于图像相位及方向特征的掌纹识别的c++实现(二)
裁剪ROI
使用Top和Bottom点进行构建直角坐标系,并截取出ROI区域
def extract_roi(point_a, point_b, img, output_path=None):
"""提取ROI区域"""
Top = (point_a[0], point_a[1])
Bottom = (point_b[0], point_b[1])
Origin_X = (Top[0] + Bottom[0]) / 2.0
Origin_Y = (Top[1] + Bottom[1]) / 2.0
Origin = (Origin_X, Origin_Y)
# 计算旋转角度
if Top[0] == Bottom[0]:
angle = 0
else:
Slope_y_axis = (Top[1] - Bottom[1]) / (Top[0] - Bottom[0])
angle = -1 * math.atan(1 / Slope_y_axis) * (180 / math.pi)
# 旋转图像
center = (Origin_X, Origin_Y)
rot_mat = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated_img = cv2.warpAffine(img, rot_mat, (img.shape[1], img.shape[0]), flags=cv2.INTER_LINEAR)
# 裁剪ROI
Uleft = (int(Origin_X + 36), int(Origin_Y - 128 / 2))
roi = rotated_img[Uleft[1]:Uleft[1]+128, Uleft[0]:Uleft[0]+128]
# 保存结果
if output_path:
cv2.imwrite(output_path, roi_blur)
return roi_blur
最后得到的ROI图片
特征提取
使用论文提到的Gabor滤波器对ROI区域进行卷积,提取特征
创建论文需求的Gabor滤波器,角度theta设置为π/4
def create_gabor_filter(theta, u, sigma, size):
"""创建Gabor滤波器"""
g = np.zeros((2 * size + 1, 2 * size + 1), dtype=complex)
for x in range(-size, size + 1):
for y in range(-size, size + 1):
g[x + size, y + size] = ((1 / (2 * np.pi * sigma ** 2)) *
np.exp(-0.5 * ((x / sigma) ** 2 + (y / sigma) ** 2) +
2 * np.pi * 1j * (u * np.cos(theta) * x + u * np.sin(theta) * y)))
# 零均值化
g = g - np.mean(g)
return g
应用滤波器,并分离实部虚部
def apply_gabor_filter(img, theta=GABOR_THETA, u=GABOR_U, sigma=GABOR_SIGMA, size=GABOR_SIZE):
"""应用Gabor滤波器"""
gabor_filter = create_gabor_filter(theta, u, sigma, size)
features = signal.convolve2d(img, gabor_filter, mode='same')
return features
def separate_complex_features(features):
"""分离复数特征为实部和虚部"""
real_part = np.real(features)
imag_part = np.imag(features)
return real_part, imag_part
def binarize_features(features, threshold=0):
"""二值化特征"""
binary_features = np.zeros_like(features)
binary_features[features >= threshold] = 255
binary_features[features < threshold] = 0
return binary_features
def downsample_features(features, block_size=(4, 4), threshold=128):
"""下采样特征"""
downsampled = skimage.measure.block_reduce(features, block_size, np.mean)
binary = np.where(downsampled >= threshold, 255, 0)
return binary
最后的得到的实部虚部类似于
对比
对不同的手掌使用二值化后的实部虚部进行对比,相减得到汉明距离
创建掩膜,主要为了去除背景
def create_palm_mask(roi_path):
"""创建掌纹掩膜"""
img = read_grayscale_image(roi_path)
return create_binary_mask(img, BINARY_THRESHOLD_FEATURE)
使用实部虚部以及掩膜计算汉明距离
def calculate_hamming_distance(img1_real, img1_imag, img1_mask,
img2_real, img2_imag, img2_mask,
offset_x, offset_y):
"""计算汉明距离"""
# 裁剪图像以匹配偏移后的区域
img1_real_crop = crop_image(img1_real, offset_x, offset_y)
img1_imag_crop = crop_image(img1_imag, offset_x, offset_y)
img1_mask_crop = crop_image(img1_mask, offset_x, offset_y)
img2_real_crop = crop_image(img2_real, offset_x, offset_y)
img2_imag_crop = crop_image(img2_imag, offset_x, offset_y)
img2_mask_crop = crop_image(img2_mask, offset_x, offset_y)
# 确保掩膜与图像尺寸一致
if img1_real_crop.shape != img1_mask_crop.shape or img2_real_crop.shape != img2_mask_crop.shape:
img1_mask_crop = cv2.resize(img1_mask_crop, (img1_real_crop.shape[1], img1_real_crop.shape[0]))
img2_mask_crop = cv2.resize(img2_mask_crop, (img2_real_crop.shape[1], img2_real_crop.shape[0]))
# 计算掩膜交集
mask_intersection = img1_mask_crop & img2_mask_crop
# 计算特征异或
real_xor = np.logical_xor(img1_real_crop, img2_real_crop).astype(np.uint8)
imag_xor = np.logical_xor(img1_imag_crop, img2_imag_crop).astype(np.uint8)
# 计算分子
numerator_real = np.sum(mask_intersection & real_xor)
numerator_imag = np.sum(mask_intersection & imag_xor)
numerator = numerator_real + numerator_imag
# 计算分母
denominator = 2 * np.sum(mask_intersection)
# 避免除零错误
if denominator == 0:
return 1.0
# 计算汉明距离
hamming_dist = numerator / denominator
return hamming_dist
使用偏移量减小图片中手掌位置的影响
img1_mask = create_palm_mask(roi_path1)
img2_mask = create_palm_mask(roi_path2)
# 尝试不同的偏移量,找到最小距离
min_distance = float('inf')
for x in range(*TRANSLATION_RANGE):
for y in range(*TRANSLATION_RANGE):
# 移动第一个图像
img1_real_moved = move_image(img1_real, x, y)
img1_imag_moved = move_image(img1_imag, x, y)
# 计算距离
distance = calculate_hamming_distance(
img1_real_moved, img1_imag_moved, img1_mask,
img2_real, img2_imag, img2_mask,
x, y
)
# 更新最小距离
min_distance = min(min_distance, distance)
最后得到两个掌纹间的汉明距离
感谢大家的支持,希望大家能够关注公众号
Prune的开发小记
我会不断在公众号上更新新内容,谢谢大家!