main012_optimized.py
import os
import cv2
import time
import argparse
import warnings
import datetime
import numpy as np
from threading import Thread, Lock
import sys
import logging
import locale
from flask import Flask, jsonify, request, send_from_directory
import json
import pika
import concurrent.futures
import multiprocessing
import traceback
from sklearn.decomposition import PCA
from sklearn.preprocessing import normalize
from insightface.app import FaceAnalysis
from flask_cors import CORS
warnings.filterwarnings(‘ignore’, category=DeprecationWarning)
warnings.filterwarnings(‘ignore’, category=FutureWarning, module=‘insightface’)
强制 UTF-8 编码
os.environ[“PYTHONIOENCODING”] = “utf-8”
设置OpenCV线程相关环境变量
os.environ[“OPENCV_FFMPEG_CAPTURE_OPTIONS”] = “rtsp_transport;tcp”
os.environ[“OPENCV_FFMPEG_THREADS”] = “1”
配置日志
logging.basicConfig(
level=logging.INFO,
format=‘%(asctime)s - %(levelname)s - %(message)s’,
handlers=[
logging.FileHandler(‘face_recognition.log’, encoding=‘utf-8’),
logging.StreamHandler(sys.stdout)
]
)
Flask应用
app = Flask(name)
优化后的 CORS 配置
CORS(app,
origins=[“*”],
allow_headers=[“Content-Type”, “Authorization”, “Accept”, “X-Requested-With”],
methods=[“GET”, “POST”, “PUT”, “DELETE”, “OPTIONS”],
max_age=3600,
supports_credentials=True,
expose_headers=[“Content-Type”, “Content-Length”, “Access-Control-Allow-Origin”])
---------- 配置参数 ----------
LOCAL_MODEL_ROOT = r"C:\Users\lenovo.insightface"
MODEL_NAME = “buffalo_l”
获取系统默认编码
DEFAULT_ENCODING = locale.getpreferredencoding()
MQ配置
MQ_CONFIG = {
‘host’: ‘mq-host’,
‘port’: ‘mq-port’,
‘username’: ‘mq-username’,
‘password’: ‘mq-password’,
‘exchange’: ‘’,
‘routing_key’: ‘mq-queue’,
‘virtual_host’: ‘/’,
‘heartbeat’: 600,
‘blocked_connection_timeout’: 300
}
目录配置
DEFAULT_FACE_DIR = r"D:\faceData\face"
DEFAULT_DETECTED_DIR = r"D:\faceData\detected_faces"
UNKNOWN_FACES_DIR = r"D:\faceData\unknown_faces"
全局变量
manager_instance = None
mq_connection = None
mq_channel = None
mq_last_heartbeat = time.time()
-------------------------------------------
def imread_chinese(path):
“”“支持中文路径的图像读取函数”“”
try:
path = str(path)
# 路径遍历防护
if ‘…’ in path or path.startswith(‘/’):
logging.warning(f"检测到潜在的路径遍历攻击: {path}")
return None
img_array = np.fromfile(path, dtype=np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
return img
except Exception as e:
logging.error(f"读取图像失败: {path}, 错误: {e}")
logging.debug(f"详细错误信息: {traceback.format_exc()}")
return None
def imwrite_chinese(path, img):
“”“强制使用 imencode + tofile 保存图像”“”
try:
path = str(path)
# 路径遍历防护
if ‘…’ in path or path.startswith(‘/’):
logging.warning(f"检测到潜在的路径遍历攻击: {path}")
return False
ext = os.path.splitext(path)[1].lower()
if ext == '.png':
success, buf = cv2.imencode('.png', img)
else:
success, buf = cv2.imencode('.jpg', img)
if success:
buf.tofile(path)
return True
else:
logging.warning(f"图像编码失败: {path}")
return False
except Exception as e:
logging.error(f"保存图像失败: {path}, 错误: {e}")
logging.debug(f"详细错误信息: {traceback.format_exc()}")
return False
添加静态文件路由
@app.route(‘/detected_faces/path:filename’)
def serve_detected_faces(filename):
try:
# 路径遍历防护
if ‘…’ in filename or filename.startswith(‘/’):
logging.warning(f"检测到潜在的路径遍历攻击: {filename}")
return jsonify({“error”: “无效的文件名”}), 400
full_path = os.path.join(DEFAULT_DETECTED_DIR, filename)
if os.path.exists(full_path):
return send_from_directory(DEFAULT_DETECTED_DIR, filename)
else:
logging.warning(f"请求的文件不存在: {full_path}")
return jsonify({"error": "文件未找到"}), 404
except Exception as e:
logging.error(f"提供图像文件时出错: {e}")
logging.debug(f"详细错误信息: {traceback.format_exc()}")
return jsonify({"error": "文件访问出错"}), 500
@app.route(‘/known_faces/path:filename’)
def serve_known_faces(filename):
try:
# 路径遍历防护
if ‘…’ in filename or filename.startswith(‘/’):
logging.warning(f"检测到潜在的路径遍历攻击: {filename}")
return jsonify({“error”: “无效的文件名”}), 400
full_path = os.path.join(DEFAULT_FACE_DIR, filename)
if os.path.exists(full_path):
return send_from_directory(DEFAULT_FACE_DIR, filename)
else:
logging.warning(f"请求的原始图像文件不存在: {full_path}")
return jsonify({"error": "文件未找到"}), 404
except Exception as e:
logging.error(f"提供原始图像文件时出错: {e}")
logging.debug(f"详细错误信息: {traceback.format_exc()}")
return jsonify({"error": "文件访问出错"}), 500
@app.route(‘/unknown_faces/path:filename’)
def serve_unknown_faces(filename):
try:
# 路径遍历防护
if ‘…’ in filename or filename.startswith(‘/’):
logging.warning(f"检测到潜在的路径遍历攻击: {filename}")
return jsonify({“error”: “无效的文件名”}), 400
full_path = os.path.join(UNKNOWN_FACES_DIR, filename)
if os.path.exists(full_path):
return send_from_directory(UNKNOWN_FACES_DIR, filename)
else:
logging.warning(f"请求的陌生人文件不存在: {full_path}")
return jsonify({"error": "文件未找到"}), 404
except Exception as e:
logging.error(f"提供陌生人图像文件时出错: {e}")
logging.debug(f"详细错误信息: {traceback.format_exc()}")
return jsonify({"error": "文件访问出错"}), 500
class DataAugmentation:
“”“数据增强类”“”
@staticmethod
def augment_image(image):
"""对图像进行多种数据增强"""
augmented_images = [image] # 原始图像
# 亮度调整
brightness_factor = np.random.uniform(0.7, 1.3)
bright_img = np.clip(image * brightness_factor, 0, 255).astype(np.uint8)
augmented_images.append(bright_img)
# 对比度调整
contrast_factor = np.random.uniform(0.7, 1.3)
contrast_img = np.clip(128 + contrast_factor * (image - 128), 0, 255).astype(np.uint8)
augmented_images.append(contrast_img)
# 水平翻转
flipped_img = cv2.flip(image, 1)
augmented_images.append(flipped_img)
# 轻微旋转
angle = np.random.uniform(-15, 15)
center = (image.shape[1] // 2, image.shape[0] // 2)
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated_img = cv2.warpAffine(image, rotation_matrix, (image.shape[1], image.shape[0]))
augmented_images.append(rotated_img)
return augmented_images
class AngularMargin:
“”“角度间隔损失函数实现,增强数值稳定性”“”
@staticmethod
def arcface(cos_theta, label_one_hot, margin=0.5, eps=1e-7):
"""ArcFace: cos(theta + m)"""
# 增强数值稳定性
cos_theta = np.clip(cos_theta, -1.0 + eps, 1.0 - eps)
theta = np.arccos(cos_theta)
# 限制角度范围以增强稳定性
theta = np.clip(theta, 0, np.pi)
marginal_cos = np.cos(theta + margin)
# 确保结果在有效范围内
marginal_cos = np.clip(marginal_cos, -1.0, 1.0)
return label_one_hot * marginal_cos + (1 - label_one_hot) * cos_theta
@staticmethod
def cosface(cos_theta, label_one_hot, margin=0.35, eps=1e-7):
"""CosFace: cos(theta) - m"""
# 增强数值稳定性
cos_theta = np.clip(cos_theta, -1.0 + eps, 1.0 - eps)
marginal_cos = cos_theta - margin * label_one_hot
return np.clip(marginal_cos, -1.0, 1.0)
@staticmethod
def sphereface(cos_theta, label_one_hot, margin=4.0, eps=1e-7):
"""SphereFace: cos(m * theta)"""
# 增强数值稳定性
cos_theta = np.clip(cos_theta, -1.0 + eps, 1.0 - eps)
theta = np.arccos(cos_theta)
# 限制角度范围
theta = np.clip(theta, 0, np.pi)
marginal_cos = np.cos(margin * theta)
# 确保结果在有效范围内
marginal_cos = np.clip(marginal_cos, -1.0, 1.0)
return label_one_hot * marginal_cos + (1 - label_one_hot) * cos_theta
class EnhancedFaceRecognizer:
# 类变量,用于共享模型实例
_shared_app = None
_shared_app_lock = Lock()
def __init__(self, known_faces_dir, tolerance=None, unknown_threshold=None, base_url="",
loss_type=None, margin=None, scale=64, use_pca=False, pca_components=128,
min_detection_size=640, gpu_id=0):
# 使用传入的参数,如果没有则使用硬编码的默认值
self.tolerance = tolerance if tolerance is not None else 0.3
self.unknown_threshold = unknown_threshold if unknown_threshold is not None else 0.4
self.loss_type = loss_type if loss_type is not None else "cosface"
self.margin = margin if margin is not None else 0.35
self.prev_gray = {}
self.motion_threshold = 0.01
self.lock = Lock()
self.unknown_faces = []
self.unknown_counter = 0
self.known_faces_dir = known_faces_dir
self.last_load_time = 0
self.unknown_last_seen = {}
self.last_saved_faces = {}
self.base_url = base_url.rstrip('/') if base_url else ""
self.similarity_cache = {} # 添加相似度缓存
self.cache_ttl = 5 # 缓存5秒
# 新增参数
self.scale = scale
self.use_pca = use_pca
self.pca_components = pca_components
self.pca = None
self.min_detection_size = min_detection_size
self.gpu_id = gpu_id # GPU ID
# 获取或创建共享的模型实例
with EnhancedFaceRecognizer._shared_app_lock:
if EnhancedFaceRecognizer._shared_app is None:
model_dir = os.path.join(LOCAL_MODEL_ROOT, "models", MODEL_NAME)
if not os.path.isdir(model_dir):
logging.error(f"本地模型目录不存在: {model_dir}")
sys.exit(1)
EnhancedFaceRecognizer._shared_app = FaceAnalysis(name=MODEL_NAME, root=LOCAL_MODEL_ROOT)
EnhancedFaceRecognizer._shared_app.prepare(ctx_id=self.gpu_id, det_size=(640, 640))
logging.info(f"InsightFace-GPU 已加载,使用GPU ID: {self.gpu_id}")
self.app = EnhancedFaceRecognizer._shared_app
# 加载人脸库
self.known_encodings, self.known_names, self.known_ids = [], [], []
self._load_known_faces(known_faces_dir)
logging.info(f"已加载 {len(self.known_names)} 个人脸数据")
def _normalize_features(self, features):
"""特征归一化"""
return normalize(features.reshape(1, -1), norm='l2').flatten()
def _apply_pca(self, features):
"""应用PCA降维"""
if self.pca is None or not self.use_pca:
return features
return self.pca.transform(features.reshape(1, -1))[0]
def _train_pca(self):
"""训练PCA模型,智能选择组件数量"""
if not self.use_pca or len(self.known_encodings) < 2:
return
try:
# 归一化特征
normalized_features = np.array([
self._normalize_features(f) for f in self.known_encodings
])
# 智能选择PCA组件数量
n_samples, n_features = normalized_features.shape
# 根据样本数量和特征维度智能确定组件数
if self.pca_components and self.pca_components > 0:
# 使用指定的组件数
n_components = min(self.pca_components, n_samples - 1, n_features)
else:
# 自动确定组件数,保留95%的方差
# 先用全部组件拟合,计算方差解释比例
temp_pca = PCA()
temp_pca.fit(normalized_features)
# 计算累积方差解释比例
cumsum_ratio = np.cumsum(temp_pca.explained_variance_ratio_)
# 找到解释95%方差所需的组件数
n_components = np.argmax(cumsum_ratio >= 0.95) + 1
n_components = min(n_components, self.pca_components or 128, n_samples - 1, n_features)
# 训练PCA
self.pca = PCA(n_components=n_components)
self.pca.fit(normalized_features)
# 记录PCA信息
if hasattr(self.pca, 'explained_variance_ratio_'):
total_variance = np.sum(self.pca.explained_variance_ratio_)
logging.info(
f"PCA训练完成,从{len(self.known_encodings[0])}维降至{n_components}维,保留{total_variance:.2%}方差")
else:
logging.info(f"PCA训练完成,从{len(self.known_encodings[0])}维降至{n_components}维")
except Exception as e:
logging.error(f"PCA训练失败: {e}")
logging.debug(f"详细错误信息: {traceback.format_exc()}")
self.pca = None
def _load_known_faces(self, directory):
if not os.path.exists(directory):
logging.warning(f"人脸库目录不存在,将创建目录: {directory}")
os.makedirs(directory, exist_ok=True)
return
logging.info(f"正在从目录加载人脸库: {os.path.abspath(directory)}")
self.known_encodings, self.known_names, self.known_ids = [], [], []
try:
files = [f for f in os.listdir(directory) if f.lower().endswith(('.jpg', '.png'))]
except Exception as e:
logging.error(f"无法读取人脸库目录: {e}")
logging.debug(f"详细错误信息: {traceback.format_exc()}")
return
loaded_count = 0
for fn in files:
path = os.path.join(directory, fn)
if not os.path.exists(path):
continue
try:
# 路径遍历防护
if

最低0.47元/天 解锁文章
648

被折叠的 条评论
为什么被折叠?



