使用 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 你要打包的文件.py2、找到 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文件,就可以运行了。
  


















