PixExpo:像素级曝光解决车载rPPG暗光心率检测难题(arXiv 2609.36607 论文解读+代码复现)

PixExpo:像素级曝光解决车载rPPG暗光心率检测难题

论文信息

项目 内容
标题 Pixel-wise Exposure for Highly Robust In-Vehicle Remote-PPG
作者 Jieying Wang 等
年份 2026
arXiv 2609.36607
链接 https://arxiv.org/abs/2609.36607
领域 cs.CV
数据集 MEX-Drive(48名被试,真实驾驶条件)

1. 核心创新

1.1 问题定义

rPPG(远程光电容积描记)通过摄像头捕捉面部皮肤颜色的微小变化来测量心率,但车载场景面临根本性硬件限制:

1
2
3
4
5
6
7
车载光照挑战:
┌─────────────────────────────────────────────┐
│ 强阳光直射 → 面部局部过曝 → 信号饱和 │
│ 隧道入口/出口 → 高动态范围 → 曝光不足 │
│ 夜间行驶 → 极暗 → 噪声主导 │
│ 树荫光斑 → 空间不均匀 → 部分区域不可用 │
└─────────────────────────────────────────────┘

核心矛盾:传统摄像头一帧内所有像素使用相同曝光时间,无法同时满足亮区和暗区的曝光需求。

1.2 PixExpo 方案

提出”以时间换空间”框架:

特性 描述
核心思路 序列捕获不同曝光时间的帧,在像素级选择最佳观测
不需要改传感器 仅需可编程帧级曝光控制
选择准则 选择最接近 rPPG 目标强度的观测值
目标 减少局部过曝和严重欠曝

1.3 关键结果

指标 传统自动曝光 PixExpo 提升
MAE 13.94 BPM 6.73 BPM ↓ 7.21 BPM
成功率 25.95% 63.24% ↑ 37.29pp
被试数 48 48 —
场景 真实驾驶 真实驾驶 —

2. 方法详解

2.1 像素级曝光融合原理

graph TD
    A[预定义循环曝光时间表<br/>T1, T2, T3, T4] --> B[序列捕获帧]
    
    B --> B1[帧1: 曝光 T1]
    B --> B2[帧2: 曝光 T2]
    B --> B3[帧3: 曝光 T3]
    B --> B4[帧4: 曝光 T4]
    
    B1 --> C[像素级融合]
    B2 --> C
    B3 --> C
    B4 --> C
    
    C --> D{每个像素选择<br/>最接近目标强度的观测}
    D --> E[融合图像<br/>rPPG 信号提取]

2.2 像素级融合算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""
PixExpo 像素级曝光融合算法
"""

import numpy as np
from typing import List

class PixExpoFusion:
"""
像素级曝光融合

输入:多帧不同曝光时间的图像
输出:融合后的单帧图像,每个像素选择最佳曝光
"""

def __init__(self, config: dict):
# rPPG 目标强度(皮肤区域的中值)
self.target_intensity = config.get('target_intensity', 128) # 0-255

# 曝光时间表(循环)
self.exposure_schedule = config.get(
'exposure_schedule',
[5e-3, 10e-3, 20e-3, 40e-3] # 5ms, 10ms, 20ms, 40ms
)

# 过曝/欠曝阈值
self.overexposure = config.get('overexposure', 240)
self.underexposure = config.get('underexposure', 15)

def fuse(self, frames: List[np.ndarray]) -> tuple:
"""
像素级融合

Args:
frames: 不同曝光时间的图像列表, 每帧 (H, W, C)

Returns:
fused_image: 融合后图像 (H, W, C)
selection_map: 每像素选择的曝光索引 (H, W)
"""
n_frames = len(frames)
if n_frames == 0:
return None, None

H, W, C = frames[0].shape

# 计算每帧每个像素与目标强度的距离
distances = np.zeros((n_frames, H, W))

for i, frame in enumerate(frames):
# 转为灰度计算距离
gray = np.mean(frame, axis=2) # (H, W)
distances[i] = np.abs(gray - self.target_intensity)

# 过曝/欠曝惩罚
over_mask = gray > self.overexposure
under_mask = gray < self.underexposure
distances[i][over_mask] += 100 # 强惩罚
distances[i][under_mask] += 100

# 每像素选择距离最小的帧
selection_map = np.argmin(distances, axis=0) # (H, W)

# 构建融合图像
fused_image = np.zeros_like(frames[0])
for i in range(n_frames):
mask = selection_map == i
fused_image[mask] = frames[i][mask]

return fused_image, selection_map

def extract_rppg_signal(self, fused_sequence: List[np.ndarray],
skin_mask: np.ndarray) -> np.ndarray:
"""
从融合图像序列提取 rPPG 信号

Args:
fused_sequence: 融合后的图像序列
skin_mask: 皮肤区域mask

Returns:
rppg_signal: 心率信号 (T,)
"""
signals = []
for frame in fused_sequence:
# 提取皮肤区域绿色通道
green = frame[:, :, 1] # G channel
skin_pixels = green[skin_mask > 0]
if len(skin_pixels) > 0:
signals.append(np.mean(skin_pixels))
else:
signals.append(0)

signal = np.array(signals)

# 去趋势
signal = signal - np.mean(signal)

# 带通滤波 0.7-3.5 Hz (42-210 BPM)
from scipy.signal import butter, filtfilt
fs = len(signal) / (len(signal) * 0.05) # 假设 20fps
nyq = fs / 2
b, a = butter(4, [0.7/nyq, 3.5/nyq], btype='band')
signal = filtfilt(b, a, signal)

return signal


# 测试
if __name__ == "__main__":
fusion = PixExpoFusion({
'target_intensity': 128,
'exposure_schedule': [5e-3, 10e-3, 20e-3, 40e-3],
'overexposure': 240,
'underexposure': 15
})

# 模拟4帧不同曝光(64x64x3)
np.random.seed(42)
frames = [
(np.random.rand(64, 64, 3) * 50 + 10).astype(np.uint8), # 短曝光:暗
(np.random.rand(64, 64, 3) * 80 + 50).astype(np.uint8), # 中曝光
(np.random.rand(64, 64, 3) * 100 + 100).astype(np.uint8), # 中长曝光
(np.random.rand(64, 64, 3) * 50 + 200).astype(np.uint8), # 长曝光:亮
]

fused, selection = fusion.fuse(frames)

print(f"融合图像 shape: {fused.shape}")
print(f"选择图分布:")
for i in range(4):
ratio = np.sum(selection == i) / selection.size * 100
print(f" 曝光{i+1}: {ratio:.1f}%")

# 模拟皮肤mask
skin_mask = np.zeros((64, 64), dtype=np.uint8)
skin_mask[20:40, 20:40] = 1

# 提取rPPG信号
signal = fusion.extract_rppg_signal([fused] * 30, skin_mask)
print(f"rPPG信号长度: {len(signal)}")
print(f"信号标准差: {np.std(signal):.4f}")

2.3 MEX-Drive 数据集

属性 数值
被试数 48人
场景 真实驾驶条件
挑战 强阳光、隧道、夜间、树荫
参考心率 接触式 PPG 传感器
曝光策略 循环曝光(4个曝光时间)

3. 对 IMS 开发的启示

3.1 rPPG 在座舱中的应用价值

应用 价值 难度
驾驶员健康监测 心血管疾病早期预警 🔴 高
压力评估 HRV 分析驾驶压力 🟡 中
疲劳辅助指标 心率变化辅助疲劳判断 🟡 中
酒驾辅助 酒精影响心率变异性 🟢 低
身份认证 心率信号作为生物特征 🟢 低

3.2 落地建议

优先级 建议
🔴 P0 摄像头选型需支持可编程曝光控制
🔴 P0 实现循环曝光策略(4个曝光时间)
🟡 P1 像素级融合算法部署在 ISP 或 NPU
🟡 P1 皮肤区域分割模型(支持不同光照)
🟢 P2 与 mmWave 心率检测做冗余验证

3.3 硬件要求

组件 要求 推荐
摄像头 可编程曝光控制 OmniVision OX05C1S
帧率 ≥ 120fps(4次曝光×30fps有效输出) 120fps 传感器
ISP 支持像素级融合 Qualcomm ISP
算力 轻量(融合+滤波) < 1 TOPS

4. 与 mmWave 心率检测的互补分析

4.1 rPPG vs mmWave 心率检测对比

维度 rPPG (视觉) mmWave (雷达)
接触方式 非接触 非接触
光照依赖 ❌ 强依赖 ✅ 不依赖
遮挡鲁棒性 ❌ 面部必须可见 ✅ 可穿透遮挡
运动鲁棒性 ❌ 运动伪影严重 ⚠️ 中等
精度(理想条件) ✅ 高(MAE < 5 BPM) ⚠️ 中(MAE 2-20 BPM)
成本 低(已有摄像头) 中(需雷达模块)
隐私 ❌ 面部图像 ✅ 无图像

4.2 多模态融合策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""
rPPG + mmWave 心率检测融合框架
"""

import numpy as np
from typing import Optional

class MultiModalHRFusion:
"""
rPPG + mmWave 心率检测融合

策略:
- 光照良好时 rPPG 为主
- 暗光/遮挡时 mmWave 为主
- 两者都可用时加权融合
"""

def __init__(self):
self.rppg_weight = 0.5
self.mmwave_weight = 0.5

def fuse(self, rppg_hr: Optional[float], rppg_confidence: float,
mmwave_hr: Optional[float], mmwave_observability: float) -> dict:
"""
融合两种心率估计

Args:
rppg_hr: rPPG 心率估计
rppg_confidence: rPPG 置信度(基于皮肤检测质量)
mmwave_hr: mmWave 心率估计
mmwave_observability: mmWave 可观测性分数

Returns:
{'heart_rate': float, 'source': str, 'confidence': float}
"""
if rppg_hr is None and mmwave_hr is None:
return {'heart_rate': 0, 'source': 'none', 'confidence': 0}

if rppg_hr is None:
return {'heart_rate': mmwave_hr, 'source': 'mmwave',
'confidence': mmwave_observability}

if mmwave_hr is None:
return {'heart_rate': rppg_hr, 'source': 'rppg',
'confidence': rppg_confidence}

# 两者都可用:加权融合
w_rppg = rppg_confidence
w_mmwave = mmwave_observability
total_w = w_rppg + w_mmwave

if total_w < 0.1:
return {'heart_rate': 0, 'source': 'none', 'confidence': 0}

fused_hr = (rppg_hr * w_rppg + mmwave_hr * w_mmwave) / total_w
confidence = total_w / 2

# 一致性检查
if abs(rppg_hr - mmwave_hr) > 20: # BPM 差异大
# 信任可观测性更高的源
if w_rppg > w_mmwave:
return {'heart_rate': rppg_hr, 'source': 'rppg',
'confidence': rppg_confidence * 0.7}
else:
return {'heart_rate': mmwave_hr, 'source': 'mmwave',
'confidence': mmwave_observability * 0.7}

return {'heart_rate': fused_hr, 'source': 'fusion', 'confidence': confidence}


# 测试
if __name__ == "__main__":
fusion = MultiModalHRFusion()

# 场景1:白天,两者都可用
r = fusion.fuse(rppg_hr=72, rppg_confidence=0.9,
mmwave_hr=74, mmwave_observability=0.7)
print(f"白天: HR={r['heart_rate']:.1f}, 源={r['source']}, 置信={r['confidence']:.2f}")

# 场景2:夜间,rPPG失败
r = fusion.fuse(rppg_hr=None, rppg_confidence=0.1,
mmwave_hr=68, mmwave_observability=0.8)
print(f"夜间: HR={r['heart_rate']:.1f}, 源={r['source']}, 置信={r['confidence']:.2f}")

# 场景3:两者不一致
r = fusion.fuse(rppg_hr=60, rppg_confidence=0.8,
mmwave_hr=90, mmwave_observability=0.5)
print(f"不一致: HR={r['heart_rate']:.1f}, 源={r['source']}, 置信={r['confidence']:.2f}")

5. 参考路线图

5.1 rPPG 技术成熟度

阶段 技术状态 当前位置
实验室 固定光照+固定姿势 ✅ 已超越
车辆静态 停车+可控光照 ✅ 已超越
车辆动态 真实驾驶+PixExpo 🔵 当前
量产部署 ISP集成+实时处理 ⏳ 2027+

5.2 下一步建议

1
2
3
4
Q1 2026: 采购支持可编程曝光的摄像头(OX05C1S)
Q2 2026: 实现 PixExpo 循环曝光策略
Q3 2026: 部署像素级融合 + rPPG 信号提取
Q4 2026: 与 mmWave 心率检测做融合验证

6. 参考


PixExpo:像素级曝光解决车载rPPG暗光心率检测难题(arXiv 2609.36607 论文解读+代码复现)
https://dapalm.com/2026/10/06/2026-10-06-012-pixexpo-pixel-wise-exposure-rppg-vehicle-arxiv2026/
作者
Mars
发布于
2026年10月6日
许可协议