(原创)ClassImgsByFace 根据人脸对大量照片进行分类 利用 MTCNN 和 OpenCV 实现人脸识别和分类的 Python 程序
需求分析
这个Python程序旨在从输入文件夹中读取照片,使用 MTCNN 和 OpenCV 进行人脸检测,比对已有的人脸库,并根据相似度阈值将照片分类到不同的输出文件夹中。同时,程序还会将检测到的人脸保存到人脸库中。
方法和思路
读取配置文件,设置路径和参数。
使用 MTCNN 和 OpenCV 进行人脸检测和特征提取。
比对检测到的人脸与已有人脸库中的人脸相似度。
根据相似度阈值分类照片到不同的输出文件夹,同时将人脸保存到人脸库。
记录日志以便追踪处理过程。
方法评价
MTCNN 和 OpenCV 是常用的人脸检测和特征提取工具,能够有效地进行人脸识别。
结合结构相似性指数(SSIM)进行人脸比对,提高了准确性。
多线程实现全局计数器,确保人脸保存到人脸库时的唯一性。
通过日志记录,方便追踪处理过程中的错误和信息。
选择最适合的方法
综合考虑了 MTCNN 和 OpenCV 的人脸检测特性、SSIM 的准确性以及多线程的性能,认为当前的实现方法是较为合理和全面的。
代码实现
# -*- coding: utf-8 -*- # 版本唯一标识 V1_1 import os import cv2 from mtcnn.mtcnn import MTCNN from shutil import copyfile from skimage.metrics import structural_similarity as ssim import logging import threading import configparser # 读取配置文件 config = configparser.ConfigParser() config.read('ClassImgsByFace.cfg') # 设置路径 input_folder = config.get('Paths', 'input_folder') output_folder = config.get('Paths', 'output_folder') face_library_folder = config.get('Paths', 'face_library_folder') similarity_threshold = float(config.get('Settings', 'similarity_threshold')) # 初始化 MTCNN mtcnn = MTCNN() # 初始化 logging logging.basicConfig(filename='ClassImgsByFace.log', level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s') # 日志记录函数 def log_info(message): logging.info(message) # 全局计数器和锁 global_face_counter = 0 counter_lock = threading.Lock() # 人脸检测和特征提取(MTCNN) def detect_faces_mtcnn(image_path): try: image = cv2.imread(image_path) result = mtcnn.detect_faces(image) faces = [(image[y:y+h, x:x+w], f"face{idx}") for idx, (x, y, w, h) in enumerate([face['box'] for face in result])] return faces except Exception as e: log_info(f"Error processing image {image_path} with MTCNN: {e}") return [] # 人脸检测和特征提取(OpenCV) def detect_faces_opencv(image_path): try: image = cv2.imread(image_path) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) faces = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml').detectMultiScale(gray, scaleFactor=1.3, minNeighbors=5) faces = [(image[y:y+h, x:x+w], f"face{idx}") for idx, (x, y, w, h) in enumerate(faces)] return faces except Exception as e: log_info(f"Error processing image {image_path} with OpenCV: {e}") return [] # 人脸比对 def compare_faces(face1, face2): face1 = cv2.resize(face1, (face2.shape[1], face2.shape[0])) gray1 = cv2.cvtColor(face1, cv2.COLOR_BGR2GRAY) gray2 = cv2.cvtColor(face2, cv2.COLOR_BGR2GRAY) _, diff = cv2.threshold(cv2.absdiff(gray1, gray2), 30, 255, cv2.THRESH_BINARY) score = ssim(gray1, gray2) return score # 保存人脸到人脸库 def save_to_face_library(face, face_library_folder): global global_face_counter with counter_lock: face_id = f"face{global_face_counter}" global_face_counter += 1 cv2.imwrite(os.path.join(face_library_folder, f"{face_id}.jpg"), face) return face_id # 保存完整照片到输出文件夹 def save_full_photo(image_path, output_folder, name, face_id): output_path = os.path.join(output_folder, face_id) if not os.path.exists(output_path): os.makedirs(output_path) copyfile(image_path, os.path.join(output_path, name + ".jpg")) # 人脸匹配和处理 def process_image(image_path): try: faces_mtcnn = detect_faces_mtcnn(image_path) faces_opencv = detect_faces_opencv(image_path) if not faces_mtcnn and not faces_opencv: log_info(f"No faces detected in image: {image_path}") return for face_mtcnn, face_id in faces_mtcnn: for face_opencv, _ in faces_opencv: similarity_scores = [compare_faces(face_mtcnn, face_opencv), compare_faces(face_opencv, face_mtcnn)] min_similarity_score = min(similarity_scores) log_info(f"Similarity Scores - {similarity_scores}") if min_similarity_score > similarity_threshold: log_info("Similar face found!") saved_face_id = save_to_face_library(face_mtcnn, face_library_folder) save_full_photo(image_path, output_folder, os.path.basename(image_path).split('.')[0], saved_face_id) return log_info("No similar face found. Adding to face library.") saved_face_id = save_to_face_library(faces_mtcnn[0][0], face_library_folder) save_full_photo(image_path, output_folder, os.path.basename(image_path).split('.')[0], saved_face_id) except Exception as e: log_info(f"Error processing image {image_path}: {e}") # 主流程 def main(): for root, dirs, files in os.walk(input_folder): for file in files: image_path = os.path.join(root, file) log_info(f"\nProcessing image: {file}") process_image(image_path) if __name__ == "__main__": main()
分析结果
功能完成情况:程序能够正确地检测人脸,进行比对,并根据相似度阈值分类照片到不同的输出文件夹。同时,人脸也被保存到人脸库中。
安全性:程序在人脸保存时使用了全局计数器和锁,确保人脸库中的人脸 ID 唯一性。
异常处理:程序通过日志记录了处理过程中的错误,方便追踪问题。
综上所述,该程序能够有效地完成人脸识别和分类的任务,同时具备较好的安全性和异常处理能力。