1. 项目背景与核心思路
疲劳检测这个需求在工业安全、交通运输、医疗健康等领域一直是个刚需。传统方案主要依赖人脸关键点检测——通过追踪眼睛开合度、嘴巴张合度等面部特征来判断疲劳状态。但我在实际项目中发现了几个痛点:
- 光照条件差时关键点检测容易失效
- 戴口罩场景下嘴部特征完全不可用
- 头部偏转超过30°时检测精度断崖式下降
去年参与某物流公司司机疲劳监测项目时,偶然发现一个现象:人在疲劳状态下,即使面部表情不变,肤色纹理也会发生微妙变化。比如眼周皮肤会出现轻微浮肿、脸颊血色分布改变等。这让我开始思考:能否绕过传统关键点检测,直接从皮肤纹理特征入手做疲劳判断?
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术方案选型
2.1 为什么放弃关键点检测
传统方案常用Dlib或MediaPipe的面部landmark检测,但存在几个硬伤:
- 依赖68/468个关键点的准确定位
- 需要完整的正脸图像
- 对图像分辨率要求高(至少300x300像素)
- 计算量大(平均每帧150ms+)
相比之下,肤色纹理分析只需要:
- 面部区域的大致定位(可以用轻量级检测器)
- 中等分辨率(200x200像素足够)
- 对头部偏转容忍度更高(可达60°)
2.2 肤色纹理的特征提取
经过对比测试,最终选择Local Binary Patterns (LBP) + Color Histogram的混合特征:
python复制def extract_features(img):
# 转换到LAB颜色空间
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# LBP纹理特征
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
lbp = local_binary_pattern(gray, 8, 1, method='uniform')
lbp_hist = np.histogram(lbp, bins=59)[0]
# 颜色直方图(a/b通道)
a_hist = cv2.calcHist([lab], [1], None, [32], [0,256])
b_hist = cv2.calcHist([lab], [2], None, [32], [0,256])
return np.concatenate([lbp_hist, a_hist.flatten(), b_hist.flatten()])
选择LAB空间的原因:
- L通道:亮度信息(受光照影响大)
- A通道:红绿色度(反映血氧变化)
- B通道:黄蓝色度(反映皮肤黄疸程度)
2.3 分类模型构建
使用简单的SVM分类器就能达到不错效果:
| 特征组合 | 准确率 | 召回率 | 推理速度 |
|---|---|---|---|
| 纯LBP | 72.3% | 68.5% | 8ms |
| 纯颜色 | 65.1% | 61.2% | 5ms |
| LBP+颜色 | 83.7% | 79.4% | 12ms |
实测发现:疲劳状态下a通道直方图会向左偏移(血色减少),b通道向右偏移(皮肤暗沉)
3. 完整实现流程
3.1 数据采集与标注
自制数据集时要注意:
- 采集不同时段的面部图像(晨起/午休/下班后)
- 包含多种光照条件(自然光/室内光/逆光)
- 标注标准参考:
- 0:清醒(PERCLOS<0.15)
- 1:轻度疲劳(0.15≤PERCLOS<0.35)
- 2:重度疲劳(PERCLOS≥0.35)
3.2 预处理关键步骤
python复制def preprocess(img):
# 人脸检测(轻量级)
faces = face_detector.detect(img)
if len(faces)==0:
return None
# 提取ROI并标准化
x,y,w,h = faces[0]
face_roi = img[y:y+h, x:x+w]
# 光照补偿
face_roi = exposure.equalize_adapthist(face_roi)
# 尺寸归一化
return cv2.resize(face_roi, (200,200))
3.3 实时检测逻辑
python复制while True:
frame = camera.read()
preprocessed = preprocess(frame)
if preprocessed is not None:
features = extract_features(preprocessed)
prob = model.predict_proba([features])[0]
if prob[1]+prob[2] > 0.7: # 疲劳概率阈值
alert()
4. 避坑指南
-
肤色偏差问题:
- 不同人种肤色差异大,建议:
- 训练数据覆盖目标人群
- 在LAB空间做z-score标准化
- 不同人种肤色差异大,建议:
-
光照干扰对策:
- 加入灰度世界白平衡
- 使用Retinex算法增强
-
实时性优化:
- 人脸检测改用UltraLight-fast模型
- 特征提取改用GPU加速
-
误报场景处理:
- 加入连续帧验证(5帧中有3帧报警才触发)
- 排除剧烈运动场景(用光流法检测)
5. 效果对比实测
在物流公司实地测试结果:
| 检测方式 | 白天准确率 | 夜间准确率 | 戴口罩场景 |
|---|---|---|---|
| 传统关键点法 | 81.2% | 52.3% | 不可用 |
| 本方案 | 85.7% | 76.8% | 79.4% |
特别在司机戴N95口罩的场景下,传统方案完全失效,而本方案仍能通过眼周和额头的皮肤状态保持79%的检测准确率。
这个项目给我的最大启示是:有时候跳出主流技术路线,从更本质的生理特征入手,反而能解决传统方案无法突破的瓶颈。现在团队正在探索将类似思路应用到其他体征检测场景,比如通过手部皮肤纹理判断脱水程度。
