OpenCV 人脸识别和比对工具
应用场景
在很多张包含人脸的照片中,提取出包含指定人脸相关的照片。适合将图片按人物分类。
功能说明
此Python脚本可以:
- 从一张目标人脸图片中提取人脸特征
- 在指定目录中搜索包含该人脸的所有图片
- 将匹配的图片保存到新目录并在图片上框出人脸位置
- 提供阈值参数以调整识别精度
安装依赖
在运行脚本之前,需要安装以下依赖:
bash
pip install opencv-python numpy requests dlib-bin
使用方法
配置路径
在运行脚本之前,需要在脚本中配置以下路径:
target_image:目标人脸图片路径(默认为:C:\Users\zhangqs\Desktop\TEST\1.jpg)search_directory:要搜索的图片目录(默认为:C:\Users\zhangqs\Desktop\TEST\2)tolerance:人脸识别阈值(默认为:0.4,值越小越严格)
运行脚本
bash
python face_recognition_script.py
脚本执行流程
- 检查模型文件:自动检查并下载所需的dlib模型文件(首次运行时)
- 初始化模型:加载人脸识别模型
- 提取特征:从目标人脸图片中提取特征
- 搜索匹配:在指定目录中搜索包含目标人脸的图片
- 保存结果 :将匹配的图片保存到
C:\Users\zhangqs\Desktop\TEST\3目录,并在图片上框出人脸位置
提高识别准确性的方法
- 使用清晰的目标图片:确保目标人脸图片清晰、正面,光线充足
- 调整tolerance参数:降低阈值(如0.4)可以提高识别精度,但可能会漏掉一些相似的人脸
- 确保搜索目录中的图片质量:搜索目录中的图片也应该清晰可见人脸
- 使用足够的测试数据:搜索目录中应该包含足够多的图片来测试识别效果
注意事项
- 首次运行时间较长:首次运行时需要下载模型文件,可能需要较长时间
- 处理大量图片时可能耗时:这是正常现象,因为人脸识别是计算密集型任务
- 支持的图片格式:jpg、jpeg、png、bmp
- 脚本会遍历子目录:脚本会递归遍历指定目录及其所有子目录中的图片文件
技术原理
-
使用dlib进行人脸检测和特征提取
-
使用OpenCV进行图像处理
-
使用欧氏距离计算人脸特征相似度
-
通过阈值控制识别的严格程度
-
使用ResNet模型进行人脸识别,提高识别准确性
import os
import cv2
import dlib
import numpy as np
import argparse全局变量
detector = None
sp = None
facerec = Nonedef extract_face_features(image_path):
"""
从图片中提取人脸特征
"""
try:
# 读取图片
img = cv2.imread(image_path)
if img is None:
print(f"错误: 无法读取 {image_path}")
return None# 转换为灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 检测人脸 faces = detector(gray) if len(faces) == 0: print(f"错误: 在 {image_path} 中未检测到人脸") return None # 提取第一个人脸的特征 shape = sp(img, faces[0]) face_descriptor = facerec.compute_face_descriptor(img, shape) return np.array(face_descriptor) except Exception as e: print(f"错误: 处理 {image_path} 时出错: {e}") return Nonedef compute_distance(encoding1, encoding2):
"""
计算两个人脸特征之间的欧氏距离
"""
return np.linalg.norm(encoding1 - encoding2)def find_matching_images(target_encoding, search_directory, tolerance=0.6):
"""
在指定目录中查找包含目标人脸的图片
"""
matching_images = []# 创建保存目录 save_directory = "C:\\Users\\zhangqs\\Desktop\\TEST\\3" if not os.path.exists(save_directory): os.makedirs(save_directory) print(f"创建保存目录: {save_directory}") image_count = 1 for root, dirs, files in os.walk(search_directory): for file in files: if file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp')): image_path = os.path.join(root, file) print(f"正在处理: {image_path}") try: # 读取图片 img = cv2.imread(image_path) if img is None: print(f"警告: 无法读取 {image_path}") continue # 转换为灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 检测人脸 faces = detector(gray) # 检查每个人脸 for face in faces: shape = sp(img, face) face_descriptor = facerec.compute_face_descriptor(img, shape) face_encoding = np.array(face_descriptor) # 计算距离 distance = compute_distance(target_encoding, face_encoding) print(f" 距离: {distance:.4f}") # 检查是否匹配 if distance < tolerance: matching_images.append(image_path) # 在图片上框出人脸 x, y, w, h = face.left(), face.top(), face.width(), face.height() cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) # 保存处理后的图片 save_filename = f"{image_count}.jpg" save_path = os.path.join(save_directory, save_filename) cv2.imwrite(save_path, img) print(f" 保存到: {save_path}") image_count += 1 break except Exception as e: print(f"错误: 处理 {image_path} 时出错: {e}") return matching_imagesdef download_required_files():
"""
下载所需的模型文件
"""
import requests
import zipfilefiles = { 'shape_predictor_68_face_landmarks.dat': 'http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2', 'dlib_face_recognition_resnet_model_v1.dat': 'http://dlib.net/files/dlib_face_recognition_resnet_model_v1.dat.bz2' } for filename, url in files.items(): if not os.path.exists(filename): # 检查压缩包是否存在 if os.path.exists(f"{filename}.bz2"): print(f"发现压缩包 {filename}.bz2,正在解压...") try: # 解压文件 import bz2 with bz2.BZ2File(f"{filename}.bz2", 'rb') as fr, open(filename, 'wb') as fw: fw.write(fr.read()) # 删除压缩文件 os.remove(f"{filename}.bz2") print(f"{filename} 解压完成") except Exception as e: print(f"错误: 解压 {filename} 时出错: {e}") return False else: print(f"正在下载 {filename}...") try: # 下载文件 import bz2 response = requests.get(url, stream=True) with open(f"{filename}.bz2", 'wb') as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) # 解压文件 print(f"正在解压 {filename}...") with bz2.BZ2File(f"{filename}.bz2", 'rb') as fr, open(filename, 'wb') as fw: fw.write(fr.read()) # 删除压缩文件 os.remove(f"{filename}.bz2") print(f"{filename} 下载完成") except Exception as e: print(f"错误: 下载 {filename} 时出错: {e}") return False else: print(f"{filename} 已存在") return Truedef main():
global detector, sp, facerec# 硬编码路径 target_image = "C:\\Users\\zhangqs\\Desktop\\TEST\\1.jpg" search_directory = "C:\\Users\\zhangqs\\Desktop\\TEST\\2" tolerance = 0.4 # 降低阈值,提高识别精度 print(f"目标人脸图片: {target_image}") print(f"搜索目录: {search_directory}") print(f"识别阈值: {tolerance}") # 检查模型文件是否存在 print("\n检查所需的模型文件...") model_files = [ 'shape_predictor_68_face_landmarks.dat', 'dlib_face_recognition_resnet_model_v1.dat' ] all_files_exist = True for model_file in model_files: if os.path.exists(model_file): print(f"{model_file} 已存在") else: print(f"错误: {model_file} 不存在") all_files_exist = False if not all_files_exist: print("缺少必要的模型文件,程序退出") return # 初始化dlib的模型 print("初始化人脸识别模型...") detector = dlib.get_frontal_face_detector() sp = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat') facerec = dlib.face_recognition_model_v1('dlib_face_recognition_resnet_model_v1.dat') print(f"\n正在提取目标人脸特征...") target_encoding = extract_face_features(target_image) if target_encoding is None: print("无法提取目标人脸特征,程序退出") return print(f"\n正在搜索包含目标人脸的图片...") matching_images = find_matching_images(target_encoding, search_directory, tolerance) print(f"\n搜索完成!") print(f"找到 {len(matching_images)} 张包含目标人脸的图片:") for image_path in matching_images: print(f"- {image_path}")if name == "main":
main()
资源下载
- 完整人脸检测/识别所需模型及代码,请在文章关联资源下载(文章顶部)。