使用 face_recognition 和 pyside2,开发了一个小工具,识别指定的人脸照片,保存到指定的文件夹。
源码如下:
import sys
import os
import shutil
import face_recognition
import logging
from PySide2.QtWidgets import QApplication, QMainWindow, QFileDialog, QPushButton, QLabel, QLineEdit, QVBoxLayout, \
QWidget, QComboBox, QProgressBar
from PySide2.QtCore import Qt, QThread, Signal
# 配置 logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 定义模型路径获取函数
def get_model_path():
if getattr(sys, 'frozen', False): # 检测是否为打包后的环境
base_path = sys._MEIPASS
else:
base_path = os.path.dirname(__file__)
return os.path.join(base_path, 'shape_predictor_68_face_landmarks.dat')
class WorkerThread(QThread):
progress = Signal(int)
status = Signal(str)
finished = Signal(list)
def __init__(self, directory_path, known_face_encodings, output_directory, model, threshold):
super().__init__()
self.directory_path = directory_path
self.known_face_encodings = known_face_encodings
self.output_directory = output_directory
self.model = model
self.threshold = threshold
def run(self):
matched_images = []
total_images = len([f for f in os.listdir(self.directory_path) if f.endswith(('.png', '.jpg', '.jpeg'))])
logging.info(f"总共有 {total_images} 张图片需要处理。")
# 获取模型文件路径
model_path = get_model_path()
for idx, filename in enumerate(os.listdir(self.directory_path)):
if filename.endswith(('.png', '.jpg', '.jpeg')):
image_path = os.path.join(self.directory_path, filename)
logging.info(f"正在处理图片: {image_path}")
image = face_recognition.load_image_file(image_path)
# 使用模型文件路径
face_locations = face_recognition.face_locations(image, model=self.model)
face_encodings = face_recognition.face_encodings(image, face_locations)
matched = False # 用于记录是否有匹配
for face_encoding in face_encodings:
face_distances = face_recognition.face_distance(self.known_face_encodings, face_encoding)
best_match_index = face_distances.argmin()
# 记录匹配度信息
logging.info(f"图片 {filename} 的相似度: {(1 - face_distances[best_match_index]) * 100:.2f} %")
if face_distances[best_match_index] < self.threshold:
matched_images.append(filename)
shutil.move(image_path, os.path.join(self.output_directory, filename))
logging.info(f"匹配并移动: {filename}")
matched = True # 标记为匹配成功
break # 只要有一个匹配成功就跳出
if not matched:
logging.info(f"图片 {filename} 未找到匹配 (所有匹配度均大于 {self.threshold})")
# 发射进度信号
self.progress.emit(int((idx + 1) / total_images * 100))
# 处理完成后发射信号
self.finished.emit(matched_images)
self.status.emit(f"处理完成,共匹配到 {len(matched_images)} 张图片。")
class FaceRecognitionApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("人脸识别工具")
self.setGeometry(100, 100, 100, 400)
layout = QVBoxLayout()
# 已知图片文件夹选择
self.known_images_label = QLabel("已知人脸文件夹:")
layout.addWidget(self.known_images_label)
self.known_images_button = QPushButton("选择已知人脸文件夹")
self.known_images_button.clicked.connect(self.select_known_images_directory)
layout.addWidget(self.known_images_button)
# 待处理文件夹选择
self.directory_label = QLabel("待处理的文件夹:")
layout.addWidget(self.directory_label)
self.directory_button = QPushButton("选择待处理的文件夹")
self.directory_button.clicked.connect(self.select_directory)
layout.addWidget(self.directory_button)
# 输出文件夹选择
self.output_directory_label = QLabel("输出文件夹:")
layout.addWidget(self.output_directory_label)
self.output_directory_button = QPushButton("选择输出文件夹")
self.output_directory_button.clicked.connect(self.select_output_directory)
layout.addWidget(self.output_directory_button)
# 阈值输入
self.threshold_label = QLabel("阈值 (0 到 1): 数值越大通过率越高")
layout.addWidget(self.threshold_label)
self.threshold_input = QLineEdit()
self.threshold_input.setText("0.6") # 默认值
layout.addWidget(self.threshold_input)
# 模型选择下拉框
self.model_label = QLabel("选择模型 (HOG 或 CNN): hog速度快,cnn精准但速度巨慢")
layout.addWidget(self.model_label)
self.model_selector = QComboBox()
self.model_selector.addItems(["hog", "cnn"])
layout.addWidget(self.model_selector)
# 开始处理按钮
self.process_button = QPushButton("开始处理")
self.process_button.clicked.connect(self.start_processing)
layout.addWidget(self.process_button)
# 进度条
self.progress_bar = QProgressBar()
layout.addWidget(self.progress_bar)
# 状态标签
self.status_label = QLabel("")
layout.addWidget(self.status_label)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def select_known_images_directory(self):
self.known_images_directory = QFileDialog.getExistingDirectory(self, "选择已知图片文件夹")
self.known_images_label.setText(f"已知图片文件夹: {self.known_images_directory}")
def select_directory(self):
self.directory_path = QFileDialog.getExistingDirectory(self, "选择待处理的文件夹")
self.directory_label.setText(f"待处理的文件夹: {self.directory_path}")
def select_output_directory(self):
self.output_directory = QFileDialog.getExistingDirectory(self, "选择输出文件夹")
self.output_directory_label.setText(f"输出文件夹: {self.output_directory}")
def load_known_faces(self, known_image_paths):
known_face_encodings = []
for path in known_image_paths:
image = face_recognition.load_image_file(path)
encodings = face_recognition.face_encodings(image)
if len(encodings) > 0:
known_face_encodings.append(encodings[0])
logging.info(f"已加载并编码: {path}")
else:
logging.warning(f"在 {path} 中未找到人脸")
return known_face_encodings
def start_processing(self):
try:
known_image_paths = [os.path.join(self.known_images_directory, f) for f in
os.listdir(self.known_images_directory) if f.endswith(('.png', '.jpg', '.jpeg'))]
threshold = float(self.threshold_input.text())
model = self.model_selector.currentText()
self.status_label.setText("处理中...")
self.status_label.repaint()
known_face_encodings = self.load_known_faces(known_image_paths)
# 创建并启动工作线程
self.worker = WorkerThread(self.directory_path, known_face_encodings, self.output_directory, model,
threshold)
self.worker.progress.connect(self.update_progress)
self.worker.status.connect(self.update_status)
self.worker.finished.connect(self.processing_finished)
self.worker.start()
except Exception as e:
self.status_label.setText(f"错误: {e}")
logging.error(f"错误: {e}", exc_info=True)
def update_progress(self, value):
self.progress_bar.setValue(value)
def update_status(self, message):
self.status_label.setText(message)
def processing_finished(self, matched_images):
self.status_label.setText(f"处理完成,共匹配到 {len(matched_images)} 张图片。")
if __name__ == "__main__":
# 获取模型文件路径
model_path = get_model_path()
app = QApplication(sys.argv)
window = FaceRecognitionApp()
window.show()
sys.exit(app.exec_())
打包exe时,遇到的问题,缺少shape_predictor_68_face_landmarks.dat,
解决步骤:
1、先生成demo.spec文件
pyinstaller 你要打包的文件.py
2、找到 face_recognition_models 文件复制到打包py文件的根目录下
3、然后更改demo.spec文件
# -*- mode: python -*-
block_cipher = None
# 最重要的是这里,缺少的文件
face_models = [
('.\\face_recognition_models\\models\\dlib_face_recognition_resnet_model_v1.dat', './face_recognition_models/models'),
('.\\face_recognition_models\\models\\mmod_human_face_detector.dat', './face_recognition_models/models'),
('.\\face_recognition_models\\models\\shape_predictor_5_face_landmarks.dat', './face_recognition_models/models'),
('.\\face_recognition_models\\models\\shape_predictor_68_face_landmarks.dat', './face_recognition_models/models'),
]
# demourl.py 改为你自己的打包文件
a = Analysis(['demourl.py'],
# 打包exe文件路径
pathex=[r'D:\Google\png\dist'],
binaries=face_models,
datas=[],
hiddenimports=['scipy._lib.messagestream', 'scipy', 'scipy.signal', 'scipy.signal.bsplines', 'scipy.special', 'scipy.special._ufuncs_cxx',
'scipy.linalg.cython_blas',
'scipy.linalg.cython_lapack',
'scipy.integrate',
'scipy.integrate.quadrature',
'scipy.integrate.odepack',
'scipy.integrate._odepack',
'scipy.integrate.quadpack',
'scipy.integrate._quadpack',
'scipy.integrate._ode',
'scipy.integrate.vode',
'scipy.integrate._dop', 'scipy._lib', 'scipy._build_utils','scipy.__config__',
'scipy.integrate.lsoda', 'scipy.cluster', 'scipy.constants','scipy.fftpack','scipy.interpolate','scipy.io','scipy.linalg','scipy.misc','scipy.ndimage','scipy.odr','scipy.optimize','scipy.setup','scipy.sparse','scipy.spatial','scipy.special','scipy.stats','scipy.version'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='test',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )
4、执行打包命令
pyinstaller demo.spec
打包完了,去dist运行exe文件,就可以运行了。