facerecognition/face_utils.py
2025-04-07 08:08:39 +08:00

225 lines
7.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import cv2
import numpy as np
import face_recognition
from pathlib import Path
import time
class FaceDetector:
def __init__(self, conf_thres=0.5):
"""初始化人脸检测器使用face_recognition库
Args:
conf_thres: 置信度阈值
"""
self.conf_thres = conf_thres
def _load_model(self):
"""不再需要加载模型使用face_recognition库"""
print("使用face_recognition库进行人脸检测无需加载模型")
pass
def detect_faces(self, image):
"""使用face_recognition库检测图像中的人脸
Args:
image: 输入图像 (numpy数组BGR格式)
Returns:
list: 检测到的人脸边界框列表 [x1, y1, x2, y2, confidence]
"""
boxes = []
# 使用face_recognition库检测人脸位置
# 将BGR图像转换为RGB格式face_recognition需要RGB格式
rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 使用face_recognition检测人脸位置
face_locations = face_recognition.face_locations(rgb_image)
# 将检测结果转换为所需的格式 [x1, y1, x2, y2, confidence]
for face_location in face_locations:
# face_location格式为(top, right, bottom, left)
top, right, bottom, left = face_location
# 转换为[x1, y1, x2, y2, confidence]格式
# 由于face_recognition不提供置信度我们使用1.0作为默认值
boxes.append([left, top, right, bottom, 1.0])
print(f"检测到 {len(boxes)} 个人脸")
# 如果未检测到人脸尝试使用OpenCV的人脸检测器作为备份
if len(boxes) == 0:
try:
print("使用OpenCV备用人脸检测...")
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.1, 5)
for (x, y, w, h) in faces:
boxes.append([x, y, x+w, y+h, 0.8]) # 使用默认置信度0.8
print(f"OpenCV检测到 {len(faces)} 个人脸")
except Exception as e:
print(f"OpenCV备用人脸检测失败: {e}")
return boxes
class FaceRecognizer:
def __init__(self, detector=None, similarity_threshold=0.6):
"""初始化人脸识别器
Args:
detector: 人脸检测器实例如果为None则创建默认检测器
similarity_threshold: 人脸相似度阈值
"""
self.detector = detector if detector else FaceDetector()
self.similarity_threshold = similarity_threshold
# 使用OpenCV内置的人脸识别器
self.face_recognizer = cv2.face.LBPHFaceRecognizer_create()
self.face_size = (128, 128) # 标准化人脸大小
def extract_face_features(self, image, box=None):
"""提取人脸特征
Args:
image: 输入图像
box: 可选的人脸边界框 [x1, y1, x2, y2],如果未提供则自动检测
Returns:
numpy.ndarray: 人脸特征向量
"""
# 如果没有提供边界框,使用检测器检测人脸
if box is None:
boxes = self.detector.detect_faces(image)
if not boxes:
return None
box = boxes[0][:4] # 使用第一个检测到的人脸
# 裁剪人脸区域
x1, y1, x2, y2 = box
face_img = image[y1:y2, x1:x2]
# 转换为灰度图像
face_gray = cv2.cvtColor(face_img, cv2.COLOR_BGR2GRAY)
# 调整大小为标准尺寸
face_resized = cv2.resize(face_gray, self.face_size)
# 使用直方图均衡化增强对比度
face_normalized = cv2.equalizeHist(face_resized)
# 简单特征提取:将图像展平为向量
# 在实际应用中可以使用更复杂的特征提取方法如HOG或深度特征
feature_vector = face_normalized.flatten().astype(np.float32)
# 对特征向量进行标准化
if np.linalg.norm(feature_vector) > 0:
feature_vector = feature_vector / np.linalg.norm(feature_vector)
return feature_vector
def compare_faces(self, known_feature, unknown_feature):
"""比较两个人脸特征的相似度
Args:
known_feature: 已知人脸特征
unknown_feature: 待比对人脸特征
Returns:
float: 相似度 (0-1之间越大表示越相似)
"""
if known_feature is None or unknown_feature is None:
return 0.0
# 计算欧氏距离
face_distance = np.linalg.norm(known_feature - unknown_feature)
# 将距离转换为相似度
similarity = 1.0 / (1.0 + face_distance)
return similarity
def identify_face(self, image, known_faces, box=None):
"""识别人脸
Args:
image: 输入图像
known_faces: 已知人脸特征列表 [(person_id, name, feature_vector), ...]
box: 可选的人脸边界框
Returns:
tuple or None: (person_id, name, similarity) 或 None如果未匹配
"""
# 提取人脸特征
face_feature = self.extract_face_features(image, box)
if face_feature is None:
return None
# 寻找最佳匹配
best_match = None
best_similarity = 0
for person_id, name, known_feature in known_faces:
similarity = self.compare_faces(known_feature, face_feature)
if similarity > best_similarity:
best_similarity = similarity
best_match = (person_id, name, similarity)
# 如果最佳匹配的相似度低于阈值返回None
if best_match and best_match[2] >= self.similarity_threshold:
return best_match
return None
def draw_face_box(image, box, name=None, similarity=None, color=(0, 255, 0), thickness=2):
"""在图像上绘制人脸边界框和标签
Args:
image: 输入图像
box: 人脸边界框 [x1, y1, x2, y2, ...]
name: 可选的名称标签
similarity: 可选的相似度标签
color: 边界框颜色 (BGR格式)
thickness: 边界框线条粗细
Returns:
numpy.ndarray: 绘制了边界框的图像
"""
x1, y1, x2, y2 = box[:4]
# 绘制边界框
cv2.rectangle(image, (x1, y1), (x2, y2), color, thickness)
# 如果提供了名称,则绘制标签
if name or similarity is not None:
label = ""
if name:
label += name
if similarity is not None:
label += f" ({similarity:.2f})"
# 设置标签背景
(label_width, label_height), baseline = cv2.getTextSize(
label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1
)
cv2.rectangle(
image,
(x1, y1 - label_height - 5),
(x1 + label_width, y1),
color,
-1 # 填充矩形
)
# 绘制标签文本
cv2.putText(
image,
label,
(x1, y1 - 5),
cv2.FONT_HERSHEY_SIMPLEX,
0.5,
(0, 0, 0), # 黑色文本
1
)
return image