Files
Face_Recognition/face_check_dlib.py

810 lines
31 KiB
Python

# 使用yolov3-face模型人脸识别方法
import cv2
import numpy as np
import os
import glob
import json
from datetime import datetime
from collections import defaultdict
import shutil
import dlib
class UnifiedFaceRecognizer:
def __init__(self):
# 数据源路径
self.data_sources = {
'database': "./data/data_faces/person_mabinhao/",
'labeled': "./labeled_faces/",
'query': "./faces_output/all_faces/"
}
# 输出路径
self.output_folder = "./unified_face_results/"
self.features_folder = "./face_features/"
# 模型路径 - 更新为YOLOv3-face模型
self.model_paths = {
'yolov3_face_cfg': './models/yolov3-face.cfg',
'yolov3_face_weights': './models/yolov3-wider_16000.weights',
'face_recognizer': './models/openface_nn4.small2.v1.t7',
'shape_predictor': './models/shape_predictor_68_face_landmarks.dat'
}
# 人脸检测模型
self.yolov3_face_detector = None
self.face_recognizer = None
self.shape_predictor = None
# 识别阈值
self.recognition_threshold = 0.65
self.face_confidence_threshold = 0.8 # YOLOv3置信度阈值
# 人脸特征数据库
self.face_database = {}
self.feature_vectors = {}
# 创建必要的目录
self.create_directories()
# 加载模型
self.load_models()
def create_directories(self):
"""创建必要的目录"""
for folder in [self.output_folder, self.features_folder]:
os.makedirs(folder, exist_ok=True)
print(f"✅ 创建目录: {folder}")
def load_models(self):
"""加载YOLOv3人脸检测和人脸识别模型"""
print("加载人脸识别模型...")
# 加载YOLOv3-face人脸检测器
try:
if os.path.exists(self.model_paths['yolov3_face_cfg']) and \
os.path.exists(self.model_paths['yolov3_face_weights']):
self.yolov3_face_detector = cv2.dnn.readNetFromDarknet(
self.model_paths['yolov3_face_cfg'],
self.model_paths['yolov3_face_weights']
)
# 使用CPU确保兼容性
self.yolov3_face_detector.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
self.yolov3_face_detector.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
print("✅ YOLOv3-face人脸检测器加载成功")
else:
print("⚠️ YOLOv3-face模型文件不存在,请下载 yolov3-face.cfg 和 yolov3-face.weights")
except Exception as e:
print(f"❌ YOLOv3-face人脸检测器加载失败: {e}")
# 加载dlib关键点检测器
try:
if os.path.exists(self.model_paths['shape_predictor']):
self.shape_predictor = dlib.shape_predictor(self.model_paths['shape_predictor'])
print("✅ dlib关键点检测器加载成功")
else:
print("⚠️ dlib关键点检测器模型文件不存在,请下载 shape_predictor_68_face_landmarks.dat")
except Exception as e:
print(f"❌ dlib关键点检测器加载失败: {e}")
# 加载人脸识别器
try:
if os.path.exists(self.model_paths['face_recognizer']):
self.face_recognizer = cv2.dnn.readNetFromTorch(
self.model_paths['face_recognizer']
)
print("✅ 人脸识别器加载成功")
else:
print("⚠️ 人脸识别器模型文件不存在")
except Exception as e:
print(f"❌ 人脸识别器加载失败: {e}")
def detect_faces_yolov3(self, image):
"""使用YOLOv3检测人脸"""
if image is None or image.size == 0:
return []
faces = []
h, w = image.shape[:2]
try:
# 准备输入blob
blob = cv2.dnn.blobFromImage(image, 1/255.0, (416, 416), (0, 0, 0), swapRB=True, crop=False)
self.yolov3_face_detector.setInput(blob)
# 获取输出层名称
layer_names = self.yolov3_face_detector.getLayerNames()
try:
# OpenCV 4.x
output_layers = [layer_names[i - 1] for i in self.yolov3_face_detector.getUnconnectedOutLayers()]
except:
# OpenCV 3.x
output_layers = [layer_names[i[0] - 1] for i in self.yolov3_face_detector.getUnconnectedOutLayers()]
# 前向传播
outputs = self.yolov3_face_detector.forward(output_layers)
boxes = []
confidences = []
for output in outputs:
for detection in output:
scores = detection[5:]
confidence = scores[0] # 人脸置信度
if confidence > self.face_confidence_threshold:
center_x = int(detection[0] * w)
center_y = int(detection[1] * h)
box_width = int(detection[2] * w)
box_height = int(detection[3] * h)
x = int(center_x - box_width / 2)
y = int(center_y - box_height / 2)
boxes.append([x, y, box_width, box_height])
confidences.append(float(confidence))
# 应用非极大值抑制
indices = cv2.dnn.NMSBoxes(boxes, confidences, self.face_confidence_threshold, 0.4)
if len(indices) > 0:
for i in indices.flatten():
x, y, box_w, box_h = boxes[i]
x1, y1, x2, y2 = x, y, x + box_w, y + box_h
# 确保坐标在图像范围内
x1, y1 = max(0, x1), max(0, y1)
x2, y2 = min(w, x2), min(h, y2)
# 检查人脸区域是否有效
if x2 > x1 and y2 > y1:
face_roi = image[y1:y2, x1:x2]
if face_roi.size > 0 and face_roi.shape[0] > 20 and face_roi.shape[1] > 20:
# 检测关键点
landmarks = self.detect_landmarks_dlib(image, (x1, y1, x2, y2))
# 扩展人脸区域以获得更大的图像
expanded_face = self.expand_face_region(image, (x1, y1, x2, y2))
faces.append({
'roi': expanded_face['image'],
'bbox': expanded_face['bbox'],
'original_bbox': (x1, y1, x2, y2),
'confidence': confidences[i],
'landmarks': landmarks,
'detector': 'yolov3'
})
except Exception as e:
print(f"YOLOv3人脸检测错误: {e}")
return faces
def expand_face_region(self, image, bbox, expansion_ratio=0.3):
"""
扩展人脸区域以获得更大的图像
"""
x1, y1, x2, y2 = bbox
width = x2 - x1
height = y2 - y1
# 计算扩展量
expand_w = int(width * expansion_ratio)
expand_h = int(height * expansion_ratio)
# 计算扩展后的边界框
new_x1 = max(0, x1 - expand_w)
new_y1 = max(0, y1 - expand_h)
new_x2 = min(image.shape[1], x2 + expand_w)
new_y2 = min(image.shape[0], y2 + expand_h)
# 提取扩展后的人脸区域
expanded_face = image[new_y1:new_y2, new_x1:new_x2]
return {
'bbox': (new_x1, new_y1, new_x2, new_y2),
'image': expanded_face
}
def detect_landmarks_dlib(self, image, bbox):
"""使用dlib检测68个人脸关键点"""
try:
# 转换为RGB格式
rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 创建dlib矩形对象
x1, y1, x2, y2 = bbox
dlib_rect = dlib.rectangle(x1, y1, x2, y2)
# 预测关键点
shape = self.shape_predictor(rgb_image, dlib_rect)
# 提取68个关键点坐标
keypoints = []
for i in range(68):
point = shape.part(i)
keypoints.append((point.x, point.y))
# 组织关键点结构
landmarks = {
'jaw': keypoints[0:17],
'right_eyebrow': keypoints[17:22],
'left_eyebrow': keypoints[22:27],
'nose': keypoints[27:36],
'right_eye': keypoints[36:42],
'left_eye': keypoints[42:48],
'mouth': keypoints[48:68],
'all_points': keypoints,
# 重要特征点
'left_eye_center': self.get_eye_center(keypoints[36:42]),
'right_eye_center': self.get_eye_center(keypoints[42:48]),
'nose_tip': keypoints[30],
'mouth_left': keypoints[48],
'mouth_right': keypoints[54],
'mouth_center': keypoints[66]
}
return landmarks
except Exception as e:
print(f"dlib关键点检测错误: {e}")
return self.get_fallback_landmarks(bbox)
def get_eye_center(self, eye_points):
"""计算眼睛中心点"""
if not eye_points:
return (0, 0)
x_coords = [p[0] for p in eye_points]
y_coords = [p[1] for p in eye_points]
center_x = sum(x_coords) // len(x_coords)
center_y = sum(y_coords) // len(y_coords)
return (center_x, center_y)
def get_fallback_landmarks(self, bbox):
"""备用关键点检测(当dlib失败时使用)"""
x1, y1, x2, y2 = bbox
w, h = x2 - x1, y2 - y1
return {
'left_eye_center': (x1 + w//3, y1 + h//3),
'right_eye_center': (x1 + 2*w//3, y1 + h//3),
'nose_tip': (x1 + w//2, y1 + h//2),
'mouth_left': (x1 + w//3, y1 + 2*h//3),
'mouth_right': (x1 + 2*w//3, y1 + 2*h//3),
'mouth_center': (x1 + w//2, y1 + 2*h//3)
}
def detect_faces_ensemble(self, image):
"""多模型融合的人脸检测(主要使用YOLOv3)"""
if image is None or image.size == 0:
return []
all_faces = []
# 1. 使用YOLOv3检测(主要)
if self.yolov3_face_detector is not None:
try:
yolov3_faces = self.detect_faces_yolov3(image)
all_faces.extend(yolov3_faces)
print(f" YOLOv3检测到 {len(yolov3_faces)} 个人脸")
except Exception as e:
print(f"YOLOv3检测失败: {e}")
# 非极大值抑制去除重复检测
if all_faces:
return self.non_max_suppression(all_faces)
else:
return []
def non_max_suppression(self, faces, overlap_threshold=0.5):
"""非极大值抑制去除重复检测"""
if len(faces) == 0:
return []
# 按置信度排序
faces.sort(key=lambda x: x['confidence'], reverse=True)
pick = []
boxes = [face['bbox'] for face in faces]
x1 = np.array([box[0] for box in boxes])
y1 = np.array([box[1] for box in boxes])
x2 = np.array([box[2] for box in boxes])
y2 = np.array([box[3] for box in boxes])
area = (x2 - x1 + 1) * (y2 - y1 + 1)
idxs = np.arange(len(faces))
while len(idxs) > 0:
i = idxs[0]
pick.append(i)
xx1 = np.maximum(x1[i], x1[idxs[1:]])
yy1 = np.maximum(y1[i], y1[idxs[1:]])
xx2 = np.minimum(x2[i], x2[idxs[1:]])
yy2 = np.minimum(y2[i], y2[idxs[1:]])
w = np.maximum(0, xx2 - xx1 + 1)
h = np.maximum(0, yy2 - yy1 + 1)
overlap = (w * h) / area[idxs[1:]]
idxs = np.delete(idxs, np.concatenate(([0], np.where(overlap > overlap_threshold)[0] + 1)))
return [faces[i] for i in pick]
def process_single_image(self, image_path, person_name="unknown", save_aligned=False, save_landmarks=False):
"""处理单张图片:检测→对齐→特征提取"""
results = []
# 读取图片
image = cv2.imread(image_path)
if image is None:
print(f"❌ 无法读取图片: {image_path}")
return results
# 人脸检测(使用YOLOv3)
faces = self.detect_faces_ensemble(image)
if not faces:
print(f"❌ 未检测到人脸: {os.path.basename(image_path)}")
return results
print(f"✅ 检测到 {len(faces)} 个人脸: {os.path.basename(image_path)}")
for i, face in enumerate(faces):
try:
# 人脸对齐
aligned_face = self.align_face_procrustes(face['roi'], face['landmarks'])
# 特征提取
features = self.extract_features(aligned_face)
if features is not None:
result = {
'image_path': image_path,
'person_name': person_name,
'features': features,
'bbox': face['bbox'],
'confidence': face['confidence'],
'detector': face.get('detector', 'unknown'),
'landmarks_count': len(face['landmarks'].get('all_points', [])),
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
results.append(result)
print(f" ✅ 人脸 {i+1}: 对齐和特征提取成功 ({result['landmarks_count']} 个关键点)")
else:
print(f" ❌ 人脸 {i+1}: 特征提取失败")
except Exception as e:
print(f" ❌ 人脸 {i+1} 处理失败: {e}")
return results
def align_face_procrustes(self, face_roi, landmarks):
"""使用Procrustes分析进行精确人脸对齐"""
try:
# 获取眼睛中心点
left_eye = landmarks['left_eye_center']
right_eye = landmarks['right_eye_center']
# 计算眼睛连线角度
dx = right_eye[0] - left_eye[0]
dy = right_eye[1] - left_eye[1]
angle = np.degrees(np.arctan2(dy, dx))
# 计算眼睛中心点
eyes_center = (
(left_eye[0] + right_eye[0]) // 2,
(left_eye[1] + right_eye[1]) // 2
)
# 计算缩放比例(基于眼睛距离)
eye_distance = np.sqrt(dx**2 + dy**2)
desired_eye_distance = 50 # 目标眼睛距离
scale = desired_eye_distance / eye_distance
# 旋转矩阵
rotation_matrix = cv2.getRotationMatrix2D(eyes_center, angle, scale)
# 调整平移分量,使眼睛中心位于图像中心
h, w = face_roi.shape[:2]
rotation_matrix[0, 2] += w * 0.5 - eyes_center[0]
rotation_matrix[1, 2] += h * 0.5 - eyes_center[1]
# 应用仿射变换
aligned_face = cv2.warpAffine(face_roi, rotation_matrix, (w, h),
flags=cv2.INTER_CUBIC)
return aligned_face
except Exception as e:
print(f"Procrustes对齐失败: {e}")
return self.align_face_simple(face_roi, landmarks)
def align_face_simple(self, face_roi, landmarks):
"""简化版人脸对齐"""
try:
left_eye = landmarks['left_eye_center']
right_eye = landmarks['right_eye_center']
# 计算眼睛连线角度
dx = right_eye[0] - left_eye[0]
dy = right_eye[1] - left_eye[1]
angle = np.degrees(np.arctan2(dy, dx))
# 计算旋转中心
h, w = face_roi.shape[:2]
center = (w // 2, h // 2)
# 旋转矩阵
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
# 应用旋转
aligned_face = cv2.warpAffine(face_roi, rotation_matrix, (w, h),
flags=cv2.INTER_CUBIC)
return aligned_face
except Exception as e:
print(f"简化对齐失败: {e}")
return face_roi
def extract_features(self, aligned_face):
"""提取人脸特征"""
if aligned_face is None or aligned_face.size == 0:
return None
try:
# 调整尺寸为模型输入大小
input_size = (96, 96)
resized_face = cv2.resize(aligned_face, input_size)
# 预处理
blob = cv2.dnn.blobFromImage(
resized_face,
1.0 / 255,
input_size,
(0, 0, 0),
swapRB=True,
crop=False
)
# 前向传播获取特征
self.face_recognizer.setInput(blob)
vec = self.face_recognizer.forward()
# 归一化特征向量
embedding = vec.flatten()
norm = np.linalg.norm(embedding)
if norm > 0:
embedding = embedding / norm
return embedding
else:
return None
except Exception as e:
print(f"特征提取失败: {e}")
return None
def draw_landmarks(self, image, landmarks, color=(0, 255, 0), radius=2):
"""在图像上绘制关键点"""
try:
for point in landmarks.get('all_points', []):
x, y = int(point[0]), int(point[1])
cv2.circle(image, (x, y), radius, color, -1)
# 绘制眼睛中心
left_eye = landmarks.get('left_eye_center')
right_eye = landmarks.get('right_eye_center')
if left_eye:
cv2.circle(image, (int(left_eye[0]), int(left_eye[1])), radius, (255, 0, 0), -1)
if right_eye:
cv2.circle(image, (int(right_eye[0]), int(right_eye[1])), radius, (255, 0, 0), -1)
except Exception as e:
print(f"绘制关键点失败: {e}")
def scan_data_source(self, source_name, source_path):
"""扫描数据源并处理所有图片"""
print(f"\n扫描数据源: {source_name} -> {source_path}")
if not os.path.exists(source_path):
print(f"❌ 数据源不存在: {source_path}")
return []
all_results = []
# 根据数据源类型采用不同的处理方式
if source_name == 'labeled':
# labeled_faces: 按人物分目录
person_dirs = [d for d in os.listdir(source_path)
if os.path.isdir(os.path.join(source_path, d))]
for person_dir in person_dirs:
person_path = os.path.join(source_path, person_dir)
image_files = self.find_image_files(person_path)
print(f"处理标注人物: {person_dir} ({len(image_files)} 张图片)")
for image_file in image_files:
results = self.process_single_image(image_file, person_dir, save_aligned=True)
all_results.extend(results)
elif source_name == 'database':
# database: 图片文件名即人物名
image_files = self.find_image_files(source_path)
# 按人物分组
person_images = defaultdict(list)
for image_file in image_files:
person_name = os.path.splitext(os.path.basename(image_file))[0]
person_images[person_name].append(image_file)
for person_name, images in person_images.items():
print(f"处理数据库人物: {person_name} ({len(images)} 张图片)")
for image_file in images:
results = self.process_single_image(image_file, person_name, save_aligned=True)
all_results.extend(results)
elif source_name == 'query':
# query: 未知人物,需要识别
image_files = self.find_image_files(source_path)
print(f"处理查询图片: {len(image_files)}")
for image_file in image_files:
results = self.process_single_image(image_file, "unknown", save_aligned=True)
all_results.extend(results)
print(f"{source_name} 处理完成: {len(all_results)} 个人脸结果")
return all_results
def find_image_files(self, folder_path):
"""查找文件夹中的所有图片文件"""
image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.bmp', '*.tiff']
image_files = []
for extension in image_extensions:
pattern = os.path.join(folder_path, extension)
image_files.extend(glob.glob(pattern))
pattern = os.path.join(folder_path, extension.upper())
image_files.extend(glob.glob(pattern))
return sorted(list(set(image_files)))
def build_face_database(self):
"""构建人脸特征数据库"""
print("\n构建人脸特征数据库...")
# 处理数据库和标注数据来构建数据库
database_results = self.scan_data_source('database', self.data_sources['database'])
labeled_results = self.scan_data_source('labeled', self.data_sources['labeled'])
# 合并结果
known_results = database_results + labeled_results
# 构建特征数据库
self.face_database = {}
self.feature_vectors = {}
for result in known_results:
person_name = result['person_name']
features = result['features']
if person_name not in self.face_database:
self.face_database[person_name] = {
'features_list': [features],
'sample_count': 1,
'image_paths': [result['image_path']]
}
self.feature_vectors[person_name] = features
else:
self.face_database[person_name]['features_list'].append(features)
self.face_database[person_name]['sample_count'] += 1
self.face_database[person_name]['image_paths'].append(result['image_path'])
# 更新特征向量(平均特征)
old_features = self.feature_vectors[person_name]
new_features = (old_features + features) / 2
self.feature_vectors[person_name] = new_features
print(f"✅ 人脸数据库构建完成: {len(self.face_database)} 个已知人物")
return len(self.face_database)
def recognize_face(self, query_features):
"""识别人脸身份"""
if not self.feature_vectors or query_features is None:
return "unknown", 1.0, None
best_name = "unknown"
best_distance = float('inf')
best_similarity = 0.0
for person_name, db_features in self.feature_vectors.items():
# 计算余弦相似度
similarity = np.dot(query_features, db_features)
distance = 1 - similarity
if distance < best_distance:
best_distance = distance
best_similarity = similarity
best_name = person_name
# 判断是否识别成功
if best_similarity > self.recognition_threshold:
return best_name, best_similarity, best_distance
else:
return "unknown", best_similarity, best_distance
def process_all_sources(self):
"""处理所有数据源并进行统一识别"""
print("开始统一处理所有数据源...")
# 1. 构建人脸数据库
if not self.build_face_database():
print("❌ 人脸数据库构建失败")
return None
# 2. 处理查询图片并进行识别
query_results = self.scan_data_source('query', self.data_sources['query'])
# 3. 对查询结果进行识别
recognition_results = []
for result in query_results:
query_image = result['image_path']
query_features = result['features']
recognized_name, similarity, distance = self.recognize_face(query_features)
recognition_result = {
**result,
'recognized_name': recognized_name,
'similarity': similarity,
'distance': distance,
'is_recognized': recognized_name != "unknown"
}
recognition_results.append(recognition_result)
status = "" if recognized_name != "unknown" else ""
print(f"{status} 被识别照片: {query_image} 识别结果: {recognized_name} (相似度: {similarity:.3f})")
# 4. 生成报告
self.generate_comprehensive_report(recognition_results)
return recognition_results
def generate_comprehensive_report(self, results):
"""生成综合报告"""
try:
report_path = os.path.join(self.output_folder, "unified_face_recognition_report.txt")
total_faces = len(results)
recognized_faces = len([r for r in results if r['is_recognized']])
with open(report_path, 'w', encoding='utf-8') as f:
f.write("统一人脸识别系统报告 (使用YOLOv3-face)\n")
f.write("=" * 60 + "\n")
f.write(f"处理时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"识别阈值: {self.recognition_threshold}\n")
f.write(f"检测阈值: {self.face_confidence_threshold}\n\n")
# 数据库信息
f.write("人脸数据库信息:\n")
f.write("-" * 30 + "\n")
f.write(f"已知人物数量: {len(self.face_database)}\n")
for person_name, info in self.face_database.items():
f.write(f" - {person_name}: {info['sample_count']} 个样本\n")
f.write("\n")
# 识别统计
f.write("识别结果统计:\n")
f.write("-" * 30 + "\n")
f.write(f"总检测人脸: {total_faces}\n")
f.write(f"成功识别: {recognized_faces}\n")
f.write(f"识别率: {recognized_faces/max(total_faces,1)*100:.1f}%\n\n")
# 详细结果
f.write("详细识别结果:\n")
f.write("-" * 40 + "\n")
for result in results:
status = "成功" if result['is_recognized'] else "失败"
f.write(f"图片: {os.path.basename(result['image_path'])}\n")
f.write(f" 原始标签: {result['person_name']}\n")
f.write(f" 识别结果: {result['recognized_name']} ({status})\n")
f.write(f" 相似度: {result['similarity']:.3f}\n")
f.write(f" 距离: {result['distance']:.3f}\n")
f.write(f" 检测置信度: {result['confidence']:.3f}\n\n")
print(f"✅ 综合报告已保存: {report_path}")
print(f"识别统计: {recognized_faces}/{total_faces} ({recognized_faces/max(total_faces,1)*100:.1f}%)")
except Exception as e:
print(f"生成报告失败: {e}")
def interactive_mode(self):
while True:
print("\n" + "="*50)
print("统一人脸识别系统 - 交互模式 (使用YOLOv3)")
print("="*50)
print("1. 处理所有数据源并识别")
print("2. 仅构建人脸数据库")
print("3. 仅处理查询图片")
print("4. 查看当前数据库")
print("5. 重新识别查询图片")
print("6. 退出")
print("-"*50)
choice = input("请选择操作 (1-6): ").strip()
if choice == '1':
self.process_all_sources()
elif choice == '2':
self.build_face_database()
self.show_database_info()
elif choice == '3':
query_results = self.scan_data_source('query', self.data_sources['query'])
print(f"处理完成: {len(query_results)} 个人脸")
elif choice == '4':
self.show_database_info()
elif choice == '5':
if not self.face_database:
print("❌ 请先构建人脸数据库")
else:
query_results = self.scan_data_source('query', self.data_sources['query'])
elif choice == '6':
print("再见!")
break
else:
print("无效选择,请重新输入!")
def show_database_info(self):
"""显示数据库信息"""
if not self.face_database:
print("❌ 数据库为空")
return
print(f"\n当前人脸数据库包含 {len(self.face_database)} 个已知人物:")
print("-" * 40)
for i, (person_name, info) in enumerate(self.face_database.items(), 1):
print(f"{i:2d}. {person_name}: {info['sample_count']} 个样本")
print("-" * 40)
def run(self):
"""运行统一人脸识别系统"""
print("=== 统一人脸识别系统启动 (使用YOLOv3-face) ===")
print("将对三个数据源进行统一处理:")
print(" 1. data/data_faces/ - 已知人物数据库")
print(" 2. labeled_faces/ - 手动标注数据")
print(" 3. all_faces/ - 待识别查询数据")
print("="*60)
# 检查模型
if self.yolov3_face_detector is None or self.face_recognizer is None:
print("❌ 模型加载失败,无法继续")
return False
self.interactive_mode()
def main():
# 创建识别器实例
recognizer = UnifiedFaceRecognizer()
# 运行识别系统
success = recognizer.run()
if success:
print("\n🎉 人脸识别任务完成!")
print(f"结果保存在: {recognizer.output_folder}")
else:
print("\n❌ 人脸识别任务失败!")
if __name__ == '__main__':
main()