视觉判断心理压力,从面部微色差分析情绪,非常规识别。

1 阅读14分钟

心理压力视觉感知系统 —— 基于面部微色差的非常规情绪识别

一、实际应用场景描述

在现代社会,心理压力已成为影响工作效率和身心健康的关键因素。传统的心理压力评估依赖于问卷调查(如SCL-90)、生理传感器(心率变异性HRV、皮电反应EDA)或访谈,这些方法存在以下局限性:

  • 侵入性:佩戴传感器给用户带来不适,影响自然状态下的数据采集。
  • 主观性:问卷易受被试者主观意愿和社会期望偏差影响。
  • 滞后性:生理指标通常在压力累积后才显现变化,难以实时预警。
  • 场景受限:实验室环境下的评估难以推广到办公室、客服中心等真实场景。

本系统旨在通过工业机器视觉技术,捕捉面部皮肤的微色差变化(如血氧饱和度波动导致的肤色微妙改变),结合眼部动态特征(眨眼频率、瞳孔变化),实现对个体心理压力状态的非接触式、实时、客观评估。特别适用于:

  • 高危职业人群监控:飞行员、调度员、医护人员等关键岗位的压力预警。
  • 用户体验研究:产品测试中评估用户对特定内容的心理负荷。
  • 心理健康辅助:作为心理咨询的客观辅助诊断工具。

二、引入痛点

  1. 隐性压力难以察觉:心理压力常表现为内隐生理反应,无法通过肉眼直接观察。
  2. 现有视觉方案的局限:主流情绪识别多基于宏表情(如Ekman的6种基本情绪),对“压力”这种复杂、多维的心理状态识别精度低。
  3. 环境光干扰严重:工业现场或日常办公环境的光照变化会淹没微弱的肤色信号。
  4. 个体差异大:不同人种的肤色基线、面部结构差异给普适性模型带来挑战。
  5. 隐私与伦理:传统方案需采集高清人脸图像,存在隐私泄露风险。本系统仅分析皮肤反射光谱特征,不存储或传输原始图像。

三、核心逻辑讲解

本系统的核心假设是:心理压力会引发生理变化,进而通过皮肤微色差和眼动模式体现出来。

技术路径图:

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ 图像采集 │────▶│ 预处理 │────▶│ 特征提取 │ │ (工业相机) │ │ (光照归一化) │ │ (微色差/眼动) │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ 结果输出 │◀────│ 状态判断 │◀────│ 多模态融合 │ │ (压力等级) │ │ (模型推理) │ │ (特征融合) │ └─────────────────┘ └─────────────────┘ └─────────────────┘

核心算法流程:

  1. 图像采集与隐私保护:
    • 使用工业级RGB相机,以60fps以上帧率捕获面部区域。
    • 关键隐私设计:在采集后立即对图像进行人脸模糊化或只保留皮肤区域ROI,仅将处理后的数据送入分析管道,确保原始生物特征不被泄露。
  2. 鲁棒性预处理:
    • 光照归一化 (Illumination Normalization):采用Retinex或自适应直方图均衡化(CLAHE)算法,消除环境光变化对肤色的影响,这是提取微色差的关键前提。
    • 皮肤区域分割:使用YCrCb或HSV颜色空间结合形态学操作,精确分割出前额、脸颊等皮肤区域,排除眼睛、嘴唇、头发等非皮肤部分。
  3. 多维度特征提取:
    • 微色差分析 (Micro-color Difference):
      • 方法:在Lab颜色空间下,计算连续视频帧间皮肤区域的平均色度(a和b)值变化**。
      • 原理:心理压力导致交感神经兴奋,引起外周血管收缩/扩张,影响皮肤血流,从而改变其反射光谱的a(红-绿)和b(黄-蓝)分量。这是一种亚阈值、非显性的生理信号。
    • 眼动动力学特征 (Oculomotor Dynamics):
      • 眨眼频率 (Blink Rate):压力增大通常导致无意识眨眼频率增加。
      • 瞳孔直径变化 (Pupil Dilation):认知负荷和压力会导致瞳孔放大。
      • 注视点稳定性 (Fixation Stability):高压力下,注意力难以持续集中,表现为注视点频繁、小幅度的跳跃。
  4. 多模态融合与状态判断:
    • 将微色差特征、眼动特征、以及可能的头部姿态(如微震颤)整合成一个高维特征向量。
    • 使用一个轻量级机器学习模型(如随机森林或梯度提升树)或预训练的时序模型(如LSTM)进行训练和推理,输出一个量化的心理压力指数 (Psychological Stress Index, PSI)。
  5. 结果输出与反馈:
    • 实时显示压力等级(如:放松、正常、轻度紧张、高度压力)。
    • 当压力水平超过预设阈值时,可触发非侵入式的干预提示(如桌面通知、灯光柔和变化)。

四、代码模块化实现

项目结构

stress_detection/ ├── main.py # 主程序入口,协调各模块运行 ├── config.py # 配置文件:相机参数、模型路径、阈值设置 ├── camera_handler.py # 相机控制模块:负责采集与人脸ROI定位 ├── privacy_engine.py # 隐私引擎模块:模糊处理,确保数据安全 ├── preprocessor.py # 预处理模块:光照归一化、皮肤分割 ├── feature_extractor.py # 特征提取模块:微色差、眼动特征计算 ├── stress_analyzer.py # 压力分析模块:模型推理与状态判断 ├── utils.py # 工具函数:绘图、日志、数学辅助 ├── models/ # 存放训练好的模型文件 (.pkl, .h5) ├── logs/ # 运行时日志 ├── requirements.txt # Python依赖包列表 └── README.md # 项目说明文档

  1. config.py - 配置文件

""" 心理压力视觉感知系统 - 配置文件 包含所有可调参数,无需修改源代码即可适配不同场景。 """

import os

--- 路径配置 ---

BASE_DIR = os.path.dirname(os.path.abspath(file)) MODELS_DIR = os.path.join(BASE_DIR, 'models') LOGS_DIR = os.path.join(BASE_DIR, 'logs')

创建必要目录

for dir_path in [MODELS_DIR, LOGS_DIR]: if not os.path.exists(dir_path): os.makedirs(dir_path)

--- 相机配置 ---

CAMERA_CONFIG = { 'device_id': 0, 'width': 640, 'height': 480, 'fps': 60, 'exposure': -4, 'gain': 2.0 }

--- 人脸检测配置 ---

FACE_DETECTION_CONFIG = { 'scale_factor': 1.1, 'min_neighbors': 5, 'min_size': (100, 100) }

--- 预处理配置 ---

PREPROCESSING_CONFIG = { 'use_clahe': True, 'clahe_clip_limit': 2.0, 'clahe_tile_grid_size': (8, 8) }

--- 特征提取配置 ---

FEATURE_CONFIG = { 'color_difference_window': 5, # 计算色差的时间窗口(帧数) 'blink_threshold': 0.2, # 眨眼检测阈值 'pupil_dilation_sensitivity': 0.1 }

--- 压力分析配置 ---

STRESS_ANALYSIS_CONFIG = { 'model_path': os.path.join(MODELS_DIR, 'stress_model.pkl'), 'psl_thresholds': { # Psychological Stress Level 'low': 0.3, 'medium': 0.6, 'high': 0.8 } }

--- 隐私配置 ---

PRIVACY_CONFIG = { 'enable_blur': True, 'blur_strength': 99, # 高斯模糊核大小,必须为奇数 'save_raw_frames': False }

  1. camera_handler.py - 相机控制模块

""" 心理压力视觉感知系统 - 相机控制模块 负责初始化相机、捕获帧并进行初步的人脸定位。 """

import cv2 import logging from datetime import datetime from config import CAMERA_CONFIG, FACE_DETECTION_CONFIG, BASE_DIR, LOGS_DIR

配置日志

logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(os.path.join(LOGS_DIR, 'camera.log')), logging.StreamHandler() ] ) logger = logging.getLogger('CameraHandler')

加载人脸检测器 (Haar Cascade)

使用OpenCV自带的预训练模型

face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + 'haarcascade_frontalface_default.xml' )

class CameraHandler: """ 工业相机处理器,专注于稳定、高速地提供带有人脸ROI的图像流。 """

def __init__(self, device_id=None):
    self.device_id = device_id or CAMERA_CONFIG['device_id']
    self.camera = None
    self.is_initialized = False
    self.current_frame = None
    self.face_roi = None  # 存储当前帧中人脸的坐标 (x, y, w, h)

def initialize(self):
    """初始化相机并开始视频流。"""
    try:
        self.camera = cv2.VideoCapture(self.device_id, cv2.CAP_DSHOW)
        if not self.camera.isOpened():
            logger.error(f"无法打开相机设备 {self.device_id}")
            return False
        
        # 设置相机参数
        self.camera.set(cv2.CAP_PROP_FRAME_WIDTH, CAMERA_CONFIG['width'])
        self.camera.set(cv2.CAP_PROP_FRAME_HEIGHT, CAMERA_CONFIG['height'])
        self.camera.set(cv2.CAP_PROP_FPS, CAMERA_CONFIG['fps'])
        
        self.is_initialized = True
        logger.info(f"相机 {self.device_id} 初始化成功.")
        return True
    except Exception as e:
        logger.error(f"相机初始化异常: {str(e)}")
        return False

def capture_and_detect_face(self):
    """
    捕获一帧图像并尝试检测人脸。
    
    Returns:
        tuple: (full_frame, face_roi_frame, face_coords)
               - full_frame: 完整的原始帧
               - face_roi_frame: 裁剪后的人脸区域图像,若未检测到则为None
               - face_coords: 人脸坐标元组 (x, y, w, h),若未检测到则为None
    """
    if not self.is_initialized:
        return None, None, None
    
    ret, frame = self.camera.read()
    if not ret:
        logger.warning("图像捕获失败")
        return None, None, None
    
    self.current_frame = frame
    gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    faces = face_cascade.detectMultiScale(
        gray_frame,
        scaleFactor=FACE_DETECTION_CONFIG['scale_factor'],
        minNeighbors=FACE_DETECTION_CONFIG['min_neighbors'],
        minSize=FACE_DETECTION_CONFIG['min_size']
    )
    
    if len(faces) > 0:
        # 选择最大的脸(假设为主要观测对象)
        largest_face = max(faces, key=lambda rect: rect[2] * rect[3])
        x, y, w, h = largest_face
        self.face_roi = (x, y, w, h)
        
        # 稍微扩大ROI,包含更多额头区域,这对压力检测很重要
        padding_w, padding_h = int(w * 0.2), int(h * 0.3)
        x1 = max(0, x - padding_w // 2)
        y1 = max(0, y - padding_h // 2)
        x2 = min(frame.shape[1], x + w + padding_w // 2)
        y2 = min(frame.shape[0], y + h + padding_h // 2)
        
        face_roi_img = frame[y1:y2, x1:x2]
        return frame, face_roi_img, (x1, y1, x2-x1, y2-y1)
    
    self.face_roi = None
    return frame, None, None

def release(self):
    """释放相机资源。"""
    if self.camera:
        self.camera.release()
        self.is_initialized = False
        logger.info("相机资源已释放。")

--- 测试代码 ---

if name == "main": handler = CameraHandler() if handler.initialize(): print("按 'q' 键退出测试") while True: full, roi, coords = handler.capture_and_detect_face() if full is not None: display = full.copy() if coords: x,y,w,h = coords cv2.rectangle(display, (x,y), (x+w, y+h), (0, 255, 0), 2) cv2.imshow("Camera Test", display) if cv2.waitKey(1) & 0xFF == ord('q'): break handler.release() cv2.destroyAllWindows()

  1. privacy_engine.py - 隐私引擎模块

""" 心理压力视觉感知系统 - 隐私引擎模块 确保所有处理的图像数据在进入核心算法前,个人隐私信息已被妥善处理。 """

import cv2 import numpy as np import logging from config import PRIVACY_CONFIG

logger = logging.getLogger('PrivacyEngine')

class PrivacyEngine: """ 隐私保护引擎。 策略:对人脸区域进行不可逆的模糊化处理,使得后续的特征提取无法还原出原始身份信息, 但保留足够的纹理和颜色信息供压力分析使用。 """

def __init__(self):
    self.blur_kernel_size = PRIVACY_CONFIG['blur_strength']
    # 确保核大小为奇数
    if self.blur_kernel_size % 2 == 0:
        self.blur_kernel_size += 1
    logger.info(f"隐私引擎初始化,模糊核大小: {self.blur_kernel_size}")

def process(self, frame, face_coords):
    """
    处理一帧图像,对人脸区域进行模糊。
    
    Args:
        frame: 输入图像帧 (BGR格式)
        face_coords: 人脸坐标 (x, y, w, h)
        
    Returns:
        numpy.ndarray: 处理后的图像帧
    """
    if frame is None or face_coords is None:
        return frame
    
    x, y, w, h = face_coords
    output_frame = frame.copy()
    
    # 提取人脸区域
    face_region = output_frame[y:y+h, x:x+w]
    
    # 应用高斯模糊
    # 使用较大的核进行强模糊,足以破坏身份特征
    blurred_face = cv2.GaussianBlur(face_region, (self.blur_kernel_size, self.blur_kernel_size), 0)
    
    # 将模糊后的区域放回原图
    output_frame[y:y+h, x:x+w] = blurred_face
    
    return output_frame

def get_anonymized_skin_mask(self, skin_mask, face_coords):
    """
    获取经过匿名化处理的皮肤掩码,确保不会泄露未模糊的原始皮肤区域。
    在本系统中,由于整个图像都经过处理,此函数可确保我们只在“安全”的区域内分析。
    """
    if skin_mask is None or face_coords is None:
        return skin_mask
    
    x, y, w, h = face_coords
    anon_mask = skin_mask.copy()
    anon_mask[y:y+h, x:x+w] = 0  # 在模糊的脸上不进行皮肤分析
    
    return anon_mask

4. preprocessor.py - 预处理模块

""" 心理压力视觉感知系统 - 预处理模块 负责对图像进行光照归一化和皮肤区域分割,为后续的特征提取做准备。 """

import cv2 import numpy as np import logging from config import PREPROCESSING_CONFIG

logger = logging.getLogger('Preprocessor')

class Preprocessor: """ 图像预处理流水线。 """

def __init__(self):
    self.clahe = cv2.createCLAHE(
        clipLimit=PREPROCESSING_CONFIG['clahe_clip_limit'],
        tileGridSize=PREPROCESSING_CONFIG['clahe_tile_grid_size']
    )
    logger.info("预处理器初始化完成。")

def normalize_lighting(self, frame):
    """
    光照归一化:使用CLAHE增强图像对比度,减少光照不均的影响。
    
    Args:
        frame: 输入图像帧 (BGR格式)
        
    Returns:
        numpy.ndarray: 光照归一化后的灰度图像
    """
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    normalized = self.clahe.apply(gray)
    return normalized

def segment_skin(self, frame):
    """
    皮肤区域分割:在YCbCr颜色空间中,通过阈值法提取皮肤区域。
    
    Args:
        frame: 输入图像帧 (BGR格式)
        
    Returns:
        numpy.ndarray: 二值化的皮肤掩码 (255表示皮肤,0表示非皮肤)
    """
    # 转换到 YCrCb 颜色空间
    ycrcb = cv2.cvtColor(frame, cv2.COLOR_BGR2YCrCb)
    
    # 定义皮肤在 YCrCb 空间的阈值范围
    # 这些阈值是经验值,可能需要根据实际环境光进行调整
    lower_skin = np.array([0, 133, 77], dtype=np.uint8)
    upper_skin = np.array([255, 173, 127], dtype=np.uint8)
    
    # 创建掩码
    skin_mask = cv2.inRange(ycrcb, lower_skin, upper_skin)
    
    # 形态学操作:去除噪声,填充孔洞
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    skin_mask = cv2.morphologyEx(skin_mask, cv2.MORPH_OPEN, kernel)
    skin_mask = cv2.morphologyEx(skin_mask, cv2.MORPH_CLOSE, kernel)
    
    return skin_mask

def get_skin_pixels(self, frame, skin_mask):
    """
    从原图中提取出被识别为皮肤的所有像素。
    
    Args:
        frame: 输入图像帧 (BGR格式)
        skin_mask: 二值化的皮肤掩码
        
    Returns:
        numpy.ndarray: 包含所有皮肤像素点的BGR图像
    """
    return cv2.bitwise_and(frame, frame, mask=skin_mask)

5. feature_extractor.py - 特征提取模块

""" 心理压力视觉感知系统 - 特征提取模块 从预处理后的图像中提取微色差和眼动特征。 """

import cv2 import numpy as np import dlib import logging from collections import deque from config import FEATURE_CONFIG

logger = logging.getLogger('FeatureExtractor')

尝试加载dlib的面部标志预测器,用于更精确的眼动追踪

try: predictor_path = "shape_predictor_68_face_landmarks.dat" # 需要自行下载 if not os.path.exists(predictor_path): raise FileNotFoundError("Dlib landmark predictor not found.") detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor(predictor_path) DLIB_AVAILABLE = True except Exception as e: logger.warning(f"无法加载dlib面部标志检测器: {e}. 眼动追踪功能将受限。") DLIB_AVAILABLE = False

class FeatureExtractor: """ 特征提取器,负责计算所有与压力相关的视觉特征。 """

def __init__(self):
    self.color_history = deque(maxlen=FEATURE_CONFIG['color_difference_window'])
    self.blink_counter = 0
    self.pupil_history = deque(maxlen=10)
    logger.info("特征提取器初始化完成。")

def _calculate_average_lab(self, bgr_image, mask):
    """
    计算图像在Lab颜色空间下的平均a*和b*值。
    
    Args:
        bgr_image: BGR图像
        mask: 二值化掩码
        
    Returns:
        tuple: (mean_a, mean_b) 或 (None, None) 如果无有效像素
    """
    if mask is None or np.sum(mask) == 0:
        return None, None
    
    lab_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2LAB)
    a_channel = lab_image[:, :, 1].astype(np.float32)
    b_channel = lab_image[:, :, 2].astype(np.float32)
    
    # 使用掩码计算平均值
    masked_a = a_channel[mask == 255]
    masked_b = b_channel[mask == 255]
    
    if len(masked_a) == 0:
        return None, None
    
    mean_a = np.mean(masked_a)
    mean_b = np.mean(masked_b)
    
    return mean_a, mean_b

def extract_micro_color_features(self, skin_pixels, skin_mask):
    """
    提取微色差特征。
    
    Args:
        skin_pixels: 皮肤像素图像
        skin_mask: 皮肤掩码
        
    Returns:
        dict: 包含当前帧和帧间色差的字典
    """
    mean_a, mean_b = self._calculate_average_lab(skin_pixels, skin_mask)
    
    features = {
        'mean_a': mean_a,
        'mean_b': mean_b,
        'delta_a': 0.0,
        'delta_b': 0.0
    }
    
    if mean_a is not None and len(self.color_history) > 0:
        prev_mean_a, prev_mean_b = self.color_history[-1]
        features['delta_a'] = abs(mean_a - prev_mean_a)
        features['delta_b'] = abs(mean_b - prev_mean_b)
    
    self.color_history.append((mean_a, mean_b))
    
    return features

def extract_eye_features(self, frame, face_coords):
    """
    提取眼动特征(眨眼频率和瞳孔变化)。
    这是一个简化的实现。完整的实现需要dlib进行精确的眼部定位和瞳孔检测。
    
    Args:
        frame: 原始帧
        face_coords: 人脸坐标
        
    Returns:
        dict: 包含眼动特征的字典
    """
    features = {
        'blink_detected': False,
        'pupil_change': 0.0
    }
    
    if not DLIB_AVAILABLE or face_coords is None:
        # 返回一个中性特征值
        return features
    
    # ... (此处省略复杂的dlib眼部追踪代码)
    # 在实际产品中,这部分会包含:
    # 1. 使用dlib找到眼睛轮廓。
    # 2. 计算眼睑闭合比率(EAR)来判断眨眼。
    # 3. 使用图像处理(如Dark Pupil Technique)估算瞳孔大小和位置变化。
    
    # 模拟一个简单的眨眼检测逻辑(实际应用中不可用)
    # 这里我们假设有一个外部模块提供了这个信息
    # features['blink_detected'] = check_blink(frame, face_coords)
    
    return features

def get_feature_vector(self, micro_color_features, eye_features):
    """
    将所有特征组合成一个标准化的特征向量。
    
    Args:
        micro_color_features: 微色差特征
        eye_features: 眼动特征
        
    Returns:
        numpy.ndarray: 特征向量
    """
    # 将所有特征值拼接成一个一维数组
    vector = np.array([
        micro_color_features['mean_a'] if micro_color_features['mean_a'] else 0,
        micro_color_features['mean_b'] if micro_color_features['mean_b'] else 0,
        micro_color_features['delta_a'],
        micro_color_features['delta_b'],
        1 if eye_features['blink_detected'] else 0,
        eye_features['pupil_change']
    ], dtype=np.float32)
    
    return vector

6. stress_analyzer.py - 压力分析模块

""" 心理压力视觉感知系统 - 压力分析模块 加载机器学习模型,并根据提取的特征推断心理压力水平。 """

import joblib import numpy as np import logging from config import STRESS_ANALYSIS_CONFIG

logger = logging.getLogger('StressAnalyzer')

class StressAnalyzer: """ 压力状态分析器。 """

def __init__(self):
    self.model = None
    self.psl_thresholds = STRESS_ANALYSIS_CONFIG['psl_thresholds']
    self._load_model()

def _load_model(self):
    """加载预训练的压力检测模型。"""
    model_path = STRESS_ANALYSIS_CONFIG['model_path']
    try:
        self.model = joblib.load(model_path)
        logger.info(f"压力分析模型已从 {model_path} 加载。")
    except FileNotFoundError:
        logger.warning(f"模型文件未找到于 {model_path}。将使用模拟模式。")
        self.model = None
    except Exception as e:
        logger.error(f"加载模型失败: {e}")
        self.model = None

def analyze(self, feature_vector):
    """
    分析特征向量,输出心理压力水平。
    
    Args:
        feature_vector: 从FeatureExtractor获取的特征向量
        
    Returns:
        dict: 包含压力指数(PSI)和分类标签的字典
    """
    if self.model is None:
        # 模拟模式:如果模型不存在,返回一个基于特征值的简单启发式结果
        # 这仅用于演示目的
        psl = self._heuristic_analysis(feature_vector)
    else:
        # 使用模型进行预测
        # 假设模型输出一个0到1之间的压力概率
        psl = self.model.predict_proba([feature_vector])[0][1]  # 取正类的概率
    
    # 根据阈值进行分类
    if psl < self.psl_thresholds['low']:
        label = "Relaxed (放松)"
    elif psl < self.psl_thresholds['medium']:
        label = "Normal (正常)"
    elif psl < self.psl_thresholds['high']:
        label = "Mildly Stressed (轻度紧张)"
    else:
        label = "Highly Stressed (高度压力)"
    
    return {
        'psi': psl,
        'label': label
    }

def _heuristic_analysis(self, fv):
    """
    一个临时的、基于规则的启发式分析器,用于模型未训练时。
    它假设a*和b*的帧间变化(delta)是压力的主要指标。
    """
    # 特征向量: [mean_a, mean_b, delta_a, delta_b, blink, pupil_change]
    delta_a = fv[2]
    delta_b = fv[3]
    
    # 一个简单的加权求和作为模拟的PSI
    simulated_psi = min(1.0, (delta_a * 0.05 + delta_b * 0.05))
    return simulated_psi

7. utils.py - 工具函数

""" 心理压力视觉感知系统 - 工具函数模块 提供绘图、日志格式化等辅助功能。 """

import cv2 import logging from datetime import datetime from config import BASE_DIR, LOGS_DIR

def setup_logger(name, log_file, level=logging.INFO): formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') log_path = LOGS_DIR / log_file handler = logging.FileHandler(log_path) handler.setFormatter(formatter) logger = logging.getLogger(name) logger.setLevel(level) logger.addHandler(handler) return logger

main_logger = setup_logger('StressDetectionSystem', 'system.log')

def draw_status_panel(frame, analysis_result, face_coords): """ 在画面上绘制状态信息面板。

Args:
    frame: 要绘制的图像帧
    analysis_result: 压力分析结果
    face_coords: 人脸坐标
"""
h, w = frame.shape[:2]

# 半透明背景
overlay = frame.copy()
cv2.rectangle(overlay, (10, 10), (350, 130), (0, 0, 0), -1)
cv2.addWeighted(overlay, 0.6, frame, 0.4, 0, frame)

# 绘制边框
cv2.rectangle(frame, (10, 10), (350, 130), (255, 255, 255), 2)

# 绘制文字
psi = analysis_result['psi']
label = analysis_result['label']

cv2.putText(frame, f"Time: {datetime.now().strftime('%H:%M:%S')}", (20, 40),
            cv2

利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!