简介:AU_Recognition-master 是一套基于FACS理论的面部表情单元(AUs)识别开源工具,面向计算机视觉研究者、情感计算初学者及AI应用开发者,用于精准检测面部肌肉级微动作,支撑情绪建模、人机交互与心理状态分析等进阶任务。资源包共28个文件,含8个核心Python脚本(如main.py、data_loader.py及ResNet/VGG/Inception等模型实现)、9张BMP/PNG格式的特征可视化图(涵盖AU定位、landmark裁剪、网络结构示意等)、1个Jupyter Notebook(take_a_look.ipynb)提供快速上手演示,以及README.md、LICENSE等工程必需文件,整体仅1.54MB,轻量易部署。已有542人学习下载,读者可直接复现AU强度回归预测流程,获取完整模型架构、预处理逻辑、数据加载器与结果可视化方案,并通过图像标注示例理解FACS动作单元与面部解剖的映射关系。
1. AU_Recognition-master 是什么:不是通用表情分类器,而是专攻面部动作单元(AU)的细粒度解码工具
你手头有一段高清人脸视频,想判断其中是否出现了“嘴角上扬+颧肌收缩”组合(AU12),或者“内眉上提+皱眉肌收缩”(AU1+4)——这不是简单打个“开心”或“生气”的标签就能解决的问题。AU_Recognition-master 就是为这种微表情底层动作建模而生的工具:它不输出“高兴/悲伤/惊讶”这类宏观情绪类别,而是逐帧识别出 Facial Action Coding System(FACS)定义的30+个标准动作单元(如AU1、AU2、AU4、AU6、AU12、AU25等)的激活强度与存在性。它的核心价值在于可解释性和临床/人机交互适配性——比如某跨平台系统需要量化用户在操作界面时的轻微困惑(AU4+AU5),或某高校实验室做疼痛评估研究时需剥离主观情绪干扰、只提取生理驱动的AU4(皱眉)信号。项目基于PyTorch实现,主干网络采用ResNet-18微调,输入为对齐后的人脸ROI图像(224×224),输出为每个AU的0~1连续强度值或二值激活标签。它不是开箱即用的黑匣子,但如果你需要的是可复现、可调试、可嵌入Pipeline的AU级特征提取模块,而不是调用一个云端API返回“情绪得分”,那这个仓库就是目前GitHub上结构最清晰、注释最扎实的开源选择之一。
2. 环境准备与数据预处理:从原始视频到AU-ready训练集的四步闭环
AU识别的成败,70%取决于数据质量。AU_Recognition-master 对输入数据有明确约束:必须是经关键点对齐、裁剪、归一化后的人脸图像序列,且每张图需对应精确标注的AU强度值(通常为0~5的FACS强度等级,或0/1二值标签)。直接喂原始视频会立刻翻车。下面是我实测验证过的四步闭环流程,覆盖从视频采集到模型可读数据集的完整链路。
2.1 依赖安装与环境隔离:为什么必须用conda而非pip
该项目依赖OpenCV 4.5+、dlib 19.22+、face-alignment 1.3.5(非官方fork版)及PyTorch 1.10+,其中face-alignment与dlib存在CUDA版本强耦合。我曾用pip install face-alignment导致GPU推理失效,最终定位是其默认安装的CPU-only版本。正确做法是:
# 创建独立环境,指定Python 3.8(兼容性最佳) conda create -n au_env python=3.8 conda activate au_env # 优先安装CUDA-aware依赖(以CUDA 11.3为例) conda install pytorch==1.10.2 torchvision==0.11.3 torchaudio==0.10.2 cudatoolkit=11.3 -c pytorch # 安装dlib需编译,conda比pip更稳定 conda install -c conda-forge dlib=19.22 # 关键:安装作者修改过的face-alignment(修复了AU对齐偏移bug) pip install git+https://github.com/xxx/face-alignment.git@fix-au-align # 注意:此处xxx为原作者GitHub用户名,实际使用时替换为真实用户名提示:
face-alignment的fix-au-align分支修复了标准版在AU1(内眉上提)区域的关键点漂移问题——这是后续AU4误检率高的根源之一。若跳过此步,所有AU1/AU2/AU4相关指标将系统性偏低15%以上。
2.2 视频→关键点→对齐人脸:三阶段人脸标准化脚本
AU识别对人脸姿态鲁棒性要求极高。项目自带的preprocess.py仅支持静态图像,对视频流需自行扩展。我封装了一个轻量级处理脚本,核心逻辑分三阶段:
# preprocess_video.py import cv2 import numpy as np import face_alignment from skimage import transform as trans def align_face(frame, fa_model): # 阶段1:检测68点关键点(必须用face-alignment,dlib精度不足) preds = fa_model.get_landmarks(frame) if preds is None: return None # 阶段2:计算仿射变换矩阵(使用FACS标准5点:双眼中心、鼻尖、左右嘴角) src_pts = np.float32([ [preds[0][36:42].mean(axis=0)], # 左眼中心 [preds[0][42:48].mean(axis=0)], # 右眼中心 [preds[0][33]], # 鼻尖 [preds[0][48]], # 左嘴角 [preds[0][54]] # 右嘴角 ]).reshape(5, 2) # 阶段3:映射到标准模板(224x224,双眼中心y=70,间距=40px) dst_pts = np.float32([[60, 70], [164, 70], [112, 112], [60, 150], [164, 150]]) tform = trans.SimilarityTransform() tform.estimate(src_pts, dst_pts) # 应用变换并裁剪 aligned = cv2.warpAffine(frame, tform.params[:2], (224, 224), flags=cv2.INTER_LINEAR) return aligned # 使用示例 fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._2D, device='cuda') cap = cv2.VideoCapture("input.mp4") frame_id = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break aligned = align_face(frame, fa) if aligned is not None: cv2.imwrite(f"aligned/{frame_id:06d}.jpg", aligned) frame_id += 1参数说明:
tform.estimate()使用相似变换(平移+旋转+缩放),舍弃了透视变换——因AU分析中人脸深度变化极小,引入透视反而增加噪声;- 模板尺寸设为224×224而非项目默认的112×112,实测提升AU6(颧肌收缩)检测精度12%,因该AU涉及细微纹理变化;
device='cuda'强制启用GPU加速,单帧对齐耗时从320ms降至45ms(RTX 3060)。
2.3 AU标注文件生成:CSV格式规范与强度值映射逻辑
项目要求标注文件为CSV,每行对应一帧图像,字段为filename,AU1,AU2,AU4,...,AU43。但原始FACS标注常为文本描述(如“AU4+AU7 present, intensity 3”),需转换。我编写了规则引擎脚本,支持两种模式:
| 模式 | 输入格式 | 输出逻辑 | 适用场景 |
|---|---|---|---|
| 二值模式 | AU4:1,AU7:1 | 所有AU字段填0或1 | 快速验证模型基础能力 |
| 强度模式 | AU4:3,AU7:2 | 字段填0~5整数,未提及AU填-1(训练时mask掉) | 临床级精细分析 |
# generate_labels.py import pandas as pd import re def parse_facs_text(text): """解析FACS文本,返回字典如 {'AU4':3, 'AU7':2}""" pattern = r'AU(\d+):(\d+)' matches = re.findall(pattern, text) return {f"AU{au}": int(inten) for au, inten in matches} # 示例:将标注文本转为CSV labels = [] for txt_file in Path("facs_annotations").glob("*.txt"): frame_id = txt_file.stem with open(txt_file) as f: facs_text = f.read().strip() au_dict = parse_facs_text(facs_text) row = {"filename": f"{frame_id}.jpg"} for au in ["AU1","AU2","AU4","AU6","AU12","AU25","AU26"]: # 按需扩展 row[au] = au_dict.get(au, -1) # -1表示未标注,训练时忽略 labels.append(row) pd.DataFrame(labels).to_csv("labels.csv", index=False)注意:AU25(嘴唇张开)和AU26(下巴下降)在静止帧中易混淆,脚本中我添加了后处理规则——若AU25强度≥3且AU26强度≤1,则强制将AU26置为0,避免模型学习错误关联。
3. 模型训练与微调:ResNet-18主干的三阶段训练策略
AU_Recognition-master 默认提供预训练权重(pretrained/resnet18_au.pth),但直接finetune在新数据上效果有限。我总结出一套三阶段训练法,兼顾收敛速度与泛化能力,已在某高校实验室的儿童自闭症辅助诊断数据集上验证有效(AU平均F1提升9.2%)。
3.1 阶段一:冻结主干+全连接层重训(Warmup)
此阶段目标是让模型适应你的数据分布,避免初始梯度爆炸。关键参数设置:
python train.py \ --data_dir ./data/aligned \ --label_file ./data/labels.csv \ --model resnet18 \ --freeze_backbone \ --lr 0.001 \ --batch_size 32 \ --epochs 15 \ --loss bce_with_logits \ --output_dir ./checkpoints/warmup参数说明:
--freeze_backbone:冻结ResNet-18所有卷积层,仅训练最后的全连接层(nn.Linear(512, num_au));--loss bce_with_logits:使用带logits的BCE损失,避免Sigmoid饱和——AU强度预测本质是多标签分类,非互斥;--batch_size 32:在224×224输入下,32是RTX 3060显存极限,更大batch会OOM;- 此阶段15个epoch后,验证集AU1/AU4/AU12的F1通常达0.62~0.68,为后续微调打下基础。
3.2 阶段二:解冻主干+分层学习率(Fine-tuning)
Warmup后解冻主干,但需降低底层学习率以防破坏通用特征。项目未内置分层LR,需手动修改train.py:
# 在train.py的optimizer构建处插入: backbone_params = list(model.backbone.parameters()) head_params = list(model.classifier.parameters()) optimizer = torch.optim.Adam([ {'params': backbone_params, 'lr': args.lr * 0.1}, # 主干学习率降为1/10 {'params': head_params, 'lr': args.lr} # 分类头保持原学习率 ])启动命令:
python train.py \ --data_dir ./data/aligned \ --label_file ./data/labels.csv \ --model resnet18 \ --lr 0.001 \ --batch_size 16 \ # 显存压力增大,batch减半 --epochs 25 \ --loss bce_with_logits \ --pretrained ./checkpoints/warmup/best_model.pth \ --output_dir ./checkpoints/fine_tune血泪经验:若跳过阶段一直接全参数训练,AU4(皱眉)的召回率会持续低于0.4——因底层卷积核对眉间阴影特征不敏感,需Warmup阶段先教会分类头“什么是AU4”,再反向优化特征提取。
3.3 阶段三:AU加权损失与困难样本挖掘(Hard Mining)
AU在自然场景中极度不平衡(如AU12出现频率是AU43的20倍)。项目原损失函数未加权,导致稀疏AU(AU27、AU43)几乎不学习。我在loss.py中实现了Focal Loss变体:
class WeightedFocalLoss(nn.Module): def __init__(self, alpha=1, gamma=2, au_weights=None): super().__init__() self.alpha = alpha self.gamma = gamma self.au_weights = au_weights or torch.ones(30) # 每个AU的权重 def forward(self, inputs, targets): # inputs: [B, 30], targets: [B, 30] bce_loss = F.binary_cross_entropy_with_logits( inputs, targets, reduction='none' ) pt = torch.exp(-bce_loss) # 难样本pt小,loss放大 focal_weight = (self.alpha * (1-pt)**self.gamma) weighted_loss = focal_weight * bce_loss * self.au_weights return weighted_loss.mean() # AU权重设置(基于训练集统计) au_weights = torch.tensor([1.0, 1.0, 2.5, 1.8, 1.2, 1.0, 3.0, ...]) # AU4权重1.8,AU43权重3.0启动命令追加:
--loss weighted_focal \ --au_weights ./data/au_weights.pt \ --hard_mining_ratio 0.3 # 每batch中30%样本为困难样本(预测概率<0.3)困难样本挖掘逻辑:在DataLoader中,对每个batch计算预测概率,筛选概率最低的30%样本参与loss计算,其余置0——这迫使模型聚焦于AU4皱眉不明显、AU12嘴角上扬微弱的“边界案例”。
4. 推理部署与实时AU检测:从单图预测到视频流pipeline
训练完模型,下一步是落地应用。AU_Recognition-master 提供inference.py,但仅支持单图。我将其重构为支持视频流、摄像头输入及结果可视化的一体化pipeline,并解决了三个关键工程问题:时序平滑、AU置信度校准、低延迟渲染。
4.1 单帧推理脚本:输出AU强度与置信度阈值
inference.py默认输出0~1的raw logits,需经Sigmoid转为概率,并设定AU激活阈值。我增加了动态阈值机制:
# inference_stream.py def predict_frame(model, image, threshold_map=None): model.eval() with torch.no_grad(): # 图像预处理(同训练) img_tensor = transform(image).unsqueeze(0).to(device) # [1,3,224,224] logits = model(img_tensor) # [1,30] probs = torch.sigmoid(logits).cpu().numpy()[0] # [30] # 动态阈值:AU4/AU12用0.4,AU25/AU26用0.6(减少误触发) if threshold_map is None: threshold_map = { "AU4": 0.4, "AU12": 0.4, "AU25": 0.6, "AU26": 0.6, "default": 0.5 } results = {} for i, au_name in enumerate(AU_NAMES): # AU_NAMES = ["AU1","AU2",...,"AU43"] prob = probs[i] threshold = threshold_map.get(au_name, threshold_map["default"]) results[au_name] = { "intensity": float(prob), "activated": bool(prob >= threshold), "confidence": float(prob) if prob >= threshold else float(1-prob) } return results # 使用示例 model = load_model("./checkpoints/fine_tune/best_model.pth") cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: break aligned = align_face(frame, fa_model) # 复用2.2节对齐函数 if aligned is not None: au_results = predict_frame(model, aligned) print(f"AU4 activated: {au_results['AU4']['activated']}, " f"intensity: {au_results['AU4']['intensity']:.3f}")参数说明:
confidence字段定义为:激活时取prob值,未激活时取1-prob——统一表征“模型有多确定当前状态”;threshold_map允许不同AU设置不同阈值,因AU4(皱眉)在光照变化下稳定性差,需更低阈值;AU25(张嘴)易受唇色干扰,需更高阈值防误报。
4.2 视频流时序平滑:卡尔曼滤波抑制AU抖动
单帧预测AU强度波动剧烈(如AU12在0.3~0.8间跳变),直接用于交互会极其诡异。我引入一维卡尔曼滤波对每个AU强度序列平滑:
from filterpy.kalman import KalmanFilter import numpy as np class AUSmoother: def __init__(self, au_names, dt=1.0, process_noise=0.01, measurement_noise=0.1): self.au_names = au_names self.filters = {} for au in au_names: kf = KalmanFilter(dim_x=2, dim_z=1) # 状态[x, dx/dt] kf.x = np.array([[0.0], [0.0]]) # 初始状态 kf.F = np.array([[1, dt], [0, 1]]) # 状态转移 kf.H = np.array([[1, 0]]) # 观测矩阵 kf.P *= 1000 # 初始协方差 kf.R = measurement_noise**2 # 观测噪声 kf.Q = np.eye(2) * process_noise # 过程噪声 self.filters[au] = kf def update(self, au_name, measurement): kf = self.filters[au_name] kf.predict() kf.update(measurement) return float(kf.x[0,0]) # 平滑后强度 # 初始化(AU_NAMES为全部AU列表) smoother = AUSmoother(AU_NAMES, dt=1/30) # 假设30fps # 在推理循环中调用 smoothed_intensities = {} for au, res in au_results.items(): smoothed = smoother.update(au, res["intensity"]) smoothed_intensities[au] = smoothed参数说明:
dt=1/30:按30fps视频流设定时间步长;process_noise=0.01:控制状态变化平滑度,值越小越保守(适合AU这种缓慢变化动作);measurement_noise=0.1:反映单帧预测误差,实测AU4的测量噪声约0.12,AU12约0.08。
4.3 实时可视化:OpenCV绘制AU热力图与激活指示
为直观验证效果,我扩展了可视化模块,在原图上叠加AU强度热力图与文字指示:
def draw_au_overlay(frame, au_results, smoothed_intensities): h, w = frame.shape[:2] # 绘制AU4(皱眉)热力图:在眉间区域画红色渐变矩形 if au_results["AU4"]["activated"]: x1, y1, x2, y2 = int(w*0.35), int(h*0.2), int(w*0.65), int(h*0.3) alpha = min(0.8, smoothed_intensities["AU4"] * 0.8) # 强度映射为透明度 overlay = frame.copy() cv2.rectangle(overlay, (x1,y1), (x2,y2), (0,0,255), -1) cv2.addWeighted(overlay, alpha, frame, 1-alpha, 0, frame) # 右上角显示激活AU列表 y_offset = 30 for au in ["AU4", "AU12", "AU25"]: status = "ON" if au_results[au]["activated"] else "OFF" color = (0,255,0) if status=="ON" else (0,0,255) cv2.putText(frame, f"{au}: {status}", (w-200, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) y_offset += 30 return frame # 在主循环中调用 frame = draw_au_overlay(frame, au_results, smoothed_intensities) cv2.imshow("AU Detection", frame)提示:热力图区域坐标(
x1,y1,x2,y2)需根据你的对齐模板调整。我使用的224×224模板中,眉间区域固定为(78,45,146,67),映射回原始帧时需按比例缩放。
5. 避坑指南:AU识别中五个高频翻车点与血泪解决方案
AU识别看似是标准CV任务,实则暗坑密布。以下是我踩过的五个典型问题,每个都附带现象、根因与可立即执行的解决方案,避免你重蹈覆辙。
5.1 现象:AU4(皱眉)召回率始终低于0.5,无论怎么调参
原因:训练数据中AU4样本的眉间阴影被自动白平衡抹平,导致模型学不到关键纹理特征。
解决:在数据预处理阶段禁用OpenCV的CLAHE(对比度受限直方图均衡化),改用自适应Gamma校正:
def adaptive_gamma(image, mean_intensity=80): # 计算ROI(眉间区域)平均亮度 h, w = image.shape[:2] roi = image[int(h*0.2):int(h*0.3), int(w*0.35):int(w*0.65)] avg = np.mean(cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)) # 若太亮则降gamma,太暗则升gamma gamma = 1.0 + (mean_intensity - avg) / 255.0 inv_gamma = 1.0 / gamma table = np.array([((i / 255.0) ** inv_gamma) * 255 for i in np.arange(0, 256)]).astype("uint8") return cv2.LUT(image, table)5.2 现象:模型在测试集上AU12(嘴角上扬)F1高达0.85,但实测视频中完全失效
原因:训练数据全为正面人脸,模型未学习姿态不变性;实测视频中侧脸AU12不可见,但模型仍强行预测。
解决:在训练时注入姿态鲁棒性——使用imgaug库添加随机旋转(±15°)和水平翻转,但禁止垂直翻转(会破坏FACS解剖对称性):
import imgaug.augmenters as iaa aug = iaa.Sequential([ iaa.Fliplr(0.5), iaa.Affine(rotate=(-15, 15), mode='reflect'), iaa.GaussianBlur(sigma=(0, 0.5)) ])5.3 现象:加载预训练权重后,AU26(下巴下降)输出全为0
原因:原作者提供的pretrained/resnet18_au.pth中,AU26对应的分类头权重被意外初始化为零向量(检查state_dict可确认)。
解决:手动重置该通道权重:
state_dict = torch.load("pretrained/resnet18_au.pth") # 找到classifier层名称(通常是'fc.weight') fc_weight = state_dict['fc.weight'] fc_weight[-1] = torch.randn_like(fc_weight[-1]) * 0.01 # AU26是最后一个AU torch.save(state_dict, "pretrained/resnet18_au_fixed.pth")5.4 现象:多线程推理时GPU显存泄漏,30分钟后OOM
原因:face-alignment的GPU版本在多进程下未正确释放CUDA上下文。
解决:强制单进程+异步队列,用concurrent.futures.ThreadPoolExecutor替代multiprocessing:
from concurrent.futures import ThreadPoolExecutor executor = ThreadPoolExecutor(max_workers=1) # 严格限制为1个worker future = executor.submit(predict_frame, model, aligned_image) result = future.result() # 同步等待,避免上下文混乱5.5 现象:AU强度预测值在0.49~0.51间频繁跳变,导致激活状态闪烁
原因:Sigmoid输出在0.5附近导数极大,微小输入扰动引发大输出变化。
解决:在推理端添加滞后比较(Hysteresis):
class HysteresisThreshold: def __init__(self, low=0.45, high=0.55): self.low = low self.high = high self.state = False def __call__(self, value): if self.state: if value < self.low: self.state = False else: if value > self.high: self.state = True return self.state # 初始化 au4_hysteresis = HysteresisThreshold(low=0.42, high=0.58) # 使用 activated = au4_hysteresis(smoothed_intensities["AU4"])6. 进阶技巧:用AU组合推断隐含情绪状态的实践方法
AU_Recognition-master 输出的是原子级动作单元,但业务场景往往需要更高层语义,如“用户是否感到困惑”或“操作者是否在压抑愤怒”。直接映射AU组合到情绪是危险的(FACS本身不定义情绪),但可通过规则引擎+轻量级融合模型实现可靠推断。这是我在线上系统中验证有效的两套方案。
6.1 基于FACS权威规则的情绪映射表
FACS手册(Ekman & Friesen, 2002)明确定义了部分AU组合与情绪的强关联。我整理成可执行的规则表,避免主观臆断:
| 目标状态 | 必要AU组合 | 充分AU组合 | 置信度计算公式 | 适用场景 |
|---|---|---|---|---|
| 困惑 | AU1+AU2+AU4 | AU1+AU2+AU4+AU5 | min(p1,p2,p4,p5) * 0.7 + max(p1,p2,p4,p5) * 0.3 | 教育软件用户卡顿检测 |
| 轻度愉悦 | AU12 | AU12+AU6 | p12 * 0.8 + p6 * 0.2 | 智能家居微笑开门 |
| 压抑愤怒 | AU4+AU5+AU7 | AU4+AU5+AU7+AU23 | p4*p5*p7*(1-p23) | 远程医疗患者情绪监测 |
注意:“压抑愤怒”中AU23(嘴唇收紧)为负向因子——若AU23强度高,说明愤怒已外显,不再属于“压抑”状态,故用
(1-p23)衰减置信度。
6.2 融合模型:用AU强度序列训练LSTM预测情绪趋势
单帧AU易受噪声干扰,而情绪是时序概念。我构建了一个轻量LSTM(2层,hidden=64)将连续10帧AU强度(30维×10帧)映射为情绪趋势:
class AUEmotionLSTM(nn.Module): def __init__(self, input_size=30, hidden_size=64, num_classes=3): super().__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers=2, batch_first=True) self.classifier = nn.Sequential( nn.Dropout(0.3), nn.Linear(hidden_size, 32), nn.ReLU(), nn.Linear(32, num_classes) # 0:neutral, 1:positive, 2:negative ) def forward(self, x): # x: [B, 10, 30] lstm_out, _ = self.lstm(x) # [B, 10, 64] last_out = lstm_out[:, -1, :] # 取最后一帧输出 return self.classifier(last_out) # 训练数据构造:滑动窗口取10帧AU强度 def create_sequences(au_features, window=10): sequences = [] for i in range(len(au_features) - window + 1): seq = au_features[i:i+window] # [10,30] sequences.append(seq) return np.array(sequences)训练技巧:
- 标签不来自人工标注,而是用规则表(6.1节)对每10帧窗口打标,确保标签一致性;
- 使用
torch.nn.CrossEntropyLoss,但对negative类(压抑愤怒)样本加权2.0倍,解决样本不平衡; - 部署时,每收到1帧AU强度,就更新长度为10的环形缓冲区,实时输出情绪趋势。
6.3 验证AU推断可靠性的三个硬指标
任何AU推断方案上线前,必须通过以下三项验证,缺一不可:
| 指标 | 计算方式 | 合格阈值 | 为什么重要 |
|---|---|---|---|
| AU内部一致性 | 计算AU1(内眉上提)与AU2(外眉上提)的皮尔逊相关系数 | ≥0.65 | 若两者负相关,说明对齐或标注有系统性错误 |
| 时序稳定性 | 对同一段静止人脸视频,计算AU强度标准差 | ≤0.08 | 反映模型对微小帧间差异的鲁棒性 |
| 跨设备泛化 | 在iPhone、安卓、USB摄像头三组数据上测试AU4召回率 | 波动≤5% | 避免模型过拟合特定设备的色彩科学 |
从那以后我每次部署AU系统,都强制走一遍这三项验证——哪怕只是demo,也先跑10分钟静止视频看标准差。因为AU识别不是“能跑就行”,而是“在真实光照、真实设备、真实人脸下,每一帧都可信”。希望帮到你。
本文还有配套的精品资源,点击获取