隐私保护姿态监测:自动驾驶巴士的骨架关键点方案

Syntronic 论文项目解读:用骨架数据替代原始视频,GDPR 合规的乘员监控

项目信息

  • 发布方: Syntronic(瑞典全球设计公司,1800员工,10亿SEK营收)
  • 项目类型: 硕士论文项目
  • 发布日期: 2026年9月25日
  • 地点: 瑞典林雪平
  • 来源: https://jobsemea.syntronic.com/jobs/8456820
  • 标签: Computer Vision, Pose Estimation, Privacy Preserving AI, Autonomous Vehicles, GDPR

核心问题

自驾驶巴士的监控困境

GDPR 数据最小化原则

方案 数据类型 GDPR 合规 数据量
原始视频 个人生物特征数据 ❌ 需要正当理由 大(HD视频流)
骨架关键点 非身份关联数据 ✅ 数据最小化 小(17-33个坐标)
深度图 无身份信息 ✅ 合规 中
热成像 无身份信息 ✅ 合规 中

技术方案

骨架提取管道

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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
"""
隐私保护姿态监测管道
Syntronic 论文项目方案复现

管道步骤:
1. 摄像头采集 → 2. 姿态估计模型 → 3. 骨架序列提取
4. 时序分类器 → 5. 事件检测 → 6. 告警输出

关键设计:
- 骨架数据不包含面部特征,无法识别身份
- 原始视频帧在传感器端处理,不存储不传输
- 仅骨架坐标序列被保存和分析
"""

import numpy as np
from dataclasses import dataclass
from typing import List, Tuple, Optional
from enum import IntEnum


class PassengerEvent(IntEnum):
"""乘员事件类型"""
NORMAL = 0 # 正常乘坐
FALLING = 1 # 跌倒
FALLEN = 2 # 已倒地
MEDICAL_DISTRESS = 3 # 医疗紧急
CONFLICT = 4 # 冲突
STANDING = 5 # 站立
ABNORMAL_POSTURE = 6 # 异常姿态


@dataclass
class Skeleton:
"""
人体骨架关键点
基于 COCO 17-keypoint 格式

关键点索引:
0: nose, 1-2: left/right eye, 3-4: left/right ear
5-6: left/right shoulder, 7-8: left/right elbow
9-10: left/right wrist, 11-12: left/right hip
13-14: left/right knee, 15-16: left/right ankle
"""
keypoints: np.ndarray # shape=(17, 3) (x, y, confidence)
timestamp: float
person_id: int = -1 # 多人追踪 ID

def get_center_of_mass(self) -> Tuple[float, float]:
"""计算骨架重心(用躯干关键点)"""
torso_points = [5, 6, 11, 12] # 肩膀和髋部
valid_points = self.keypoints[torso_points]
mask = valid_points[:, 2] > 0.3 # 置信度阈值
if mask.sum() == 0:
return (0, 0)
com_x = np.mean(valid_points[mask, 0])
com_y = np.mean(valid_points[mask, 1])
return (com_x, com_y)

def get_height(self) -> float:
"""估算身高(头到脚距离)"""
nose = self.keypoints[0]
ankle_l = self.keypoints[15]
ankle_r = self.keypoints[16]

ankles = np.array([ankle_l, ankle_r])
valid_ankles = ankles[ankles[:, 2] > 0.3]

if len(valid_ankles) == 0:
return 0

ankle_y = np.mean(valid_ankles[:, 1])
return abs(nose[1] - ankle_y)

def is_standing(self) -> bool:
"""判断是否站立"""
height = self.get_height()
com_x, com_y = self.get_center_of_mass()
nose_y = self.keypoints[0, 1]

# 站立时头-髋-脚基本垂直,身高较大
if height < 50: # 像素阈值
return False

# 髋部到脚的距离接近身高
hip_y = (self.keypoints[11, 1] + self.keypoints[12, 1]) / 2
if self.keypoints[11, 2] < 0.3 or self.keypoints[12, 2] < 0.3:
return False

leg_length = abs(hip_y - ankle_y) if ankle_y > 0 else 0
return leg_length > height * 0.4

def is_fallen(self) -> bool:
"""判断是否倒地"""
# 倒地时骨架接近水平
com_x, com_y = self.get_center_of_mass()
height = self.get_height()
width = self.estimate_width()

if height == 0 or width == 0:
return False

aspect_ratio = width / height

# 倒地时宽高比 > 1.5
return aspect_ratio > 1.5

def estimate_width(self) -> float:
"""估算骨架宽度"""
shoulder_l = self.keypoints[5]
shoulder_r = self.keypoints[6]

if shoulder_l[2] < 0.3 or shoulder_r[2] < 0.3:
return 0

return abs(shoulder_l[0] - shoulder_r[0])


class SkeletonSequence:
"""骨架时间序列"""

def __init__(self, skeletons: List[Skeleton] = None):
self.skeletons = skeletons or []

def add(self, skeleton: Skeleton):
self.skeletons.append(skeleton)

def to_feature_vector(self, window_size=30) -> np.ndarray:
"""
将骨架序列转为特征向量
用于时序分类器输入

Args:
window_size: 时间窗口大小(帧数)

Returns:
feature_vector: shape=(window_size, 17*3)
"""
if len(self.skeletons) < window_size:
# 填充
last = self.skeletons[-1] if self.skeletons else Skeleton(
np.zeros((17, 3)), 0
)
while len(self.skeletons) < window_size:
self.skeletons.append(last)

features = []
for skel in self.skeletons[-window_size:]:
features.append(skel.keypoints.flatten())

return np.array(features) # shape=(window_size, 51)


class EventClassifier:
"""
事件分类器

基于骨架序列的时序分类
使用轻量级 LSTM 网络

优势:
- 输入只有 17×3=51 维/帧(vs 1080×1920×3 视频帧)
- 计算量极低,可部署在边缘设备
- 不包含身份信息,GDPR 合规
"""

def __init__(self, num_classes=7, hidden_size=64,
num_layers=1, input_size=51):
self.num_classes = num_classes
self.hidden_size = hidden_size
self.input_size = input_size

# 这里用简化版(实际用 PyTorch LSTM)
self.thresholds = {
'fall_velocity': 50.0, # 像素/秒
'fall_aspect_ratio': 1.5, # 宽高比
'standing_height': 100.0, # 像素
'distress_duration': 3.0, # 秒
'conflict_distance': 50.0, # 像素
}

def classify(self, sequence: SkeletonSequence) -> PassengerEvent:
"""
基于规则的分类器(基线方案)
实际系统中应使用训练好的 LSTM
"""
if len(sequence.skeletons) < 2:
return PassengerEvent.NORMAL

current = sequence.skeletons[-1]
previous = sequence.skeletons[-2]

# 跌倒检测:重心快速下降 + 水平姿态
com_curr = current.get_center_of_mass()
com_prev = previous.get_center_of_mass()

dt = current.timestamp - previous.timestamp
if dt > 0:
com_velocity_y = (com_curr[1] - com_prev[1]) / dt
else:
com_velocity_y = 0

if current.is_fallen():
return PassengerEvent.FALLEN
elif com_velocity_y > self.thresholds['fall_velocity']:
return PassengerEvent.FALLING
elif current.is_standing():
return PassengerEvent.STANDING
else:
# 检测异常姿态
height = current.get_height()
if 0 < height < self.thresholds['standing_height'] * 0.5:
# 矮小骨架,可能蹲下或弯腰
return PassengerEvent.ABNORMAL_POSTURE

return PassengerEvent.NORMAL


class PrivacyAssessor:
"""
GDPR 隐私影响评估

对比骨架方案 vs 原始视频方案的数据暴露
"""

@staticmethod
def compare_data_exposure():
"""对比数据暴露面"""
print("=== 数据暴露对比 ===\n")

# 原始视频
video_data_per_frame = 1920 * 1080 * 3 # RGB
video_fps = 15
video_per_second = video_data_per_frame * video_fps
video_per_hour = video_per_second * 3600

print("原始视频方案:")
print(f" 每帧数据: {video_data_per_frame/1e6:.1f} MB")
print(f" 每秒数据: {video_per_second/1e6:.1f} MB/s")
print(f" 每小时数据: {video_per_hour/1e9:.1f} GB/h")
print(f" 包含: 面部特征、肤色、衣着、纹身、")
print(f" 行李、阅读材料、手机屏幕内容")
print(f" GDPR: 需要明确同意、存储限制、")
print(f" 数据保护影响评估(DPIA)")
print()

# 骨架数据
skeleton_points = 17 * 3 # x, y, confidence
skeleton_fps = 15
skeleton_per_second = skeleton_points * skeleton_fps
skeleton_per_hour = skeleton_per_second * 3600

print("骨架数据方案:")
print(f" 每帧数据: {skeleton_points*4} bytes")
print(f" 每秒数据: {skeleton_per_second*4/1e3:.1f} KB/s")
print(f" 每小时数据: {skeleton_per_hour*4/1e6:.1f} MB/h")
print(f" 包含: 关节坐标、置信度")
print(f" 不包含: 面部特征、肤色、衣着、")
print(f" 物品、屏幕内容")
print(f" GDPR: 数据最小化原则满足,")
print(f" 无身份关联,DPIA 简化")

# 压缩比
ratio = video_per_hour / (skeleton_per_hour * 4)
print(f"\n数据量缩减: {ratio:.0f}x")


# ========== 测试 ==========

if __name__ == "__main__":
# 隐私对比
PrivacyAssessor.compare_data_exposure()

print("\n" + "="*50)

# 模拟跌倒检测
np.random.seed(42)

# 正常坐姿骨架
normal_skeleton = Skeleton(
keypoints=np.array([
[500, 200, 0.9], # nose
[490, 195, 0.8], [510, 195, 0.8], # eyes
[480, 195, 0.7], [520, 195, 0.7], # ears
[450, 250, 0.9], [550, 250, 0.9], # shoulders
[430, 300, 0.8], [570, 300, 0.8], # elbows
[420, 350, 0.7], [580, 350, 0.7], # wrists
[460, 350, 0.9], [540, 350, 0.9], # hips
[450, 400, 0.8], [550, 400, 0.8], # knees
[440, 450, 0.7], [560, 450, 0.7], # ankles
], dtype=np.float32),
timestamp=0.0
)

# 倒地骨架(水平)
fallen_skeleton = Skeleton(
keypoints=np.array([
[400, 350, 0.9], # nose
[410, 345, 0.8], [410, 355, 0.8], # eyes
[420, 345, 0.7], [420, 355, 0.7], # ears
[430, 340, 0.9], [440, 360, 0.9], # shoulders
[500, 340, 0.8], [510, 360, 0.8], # elbows
[550, 340, 0.7], [560, 360, 0.7], # wrists
[600, 345, 0.9], [610, 355, 0.9], # hips
[650, 340, 0.8], [660, 360, 0.8], # knees
[700, 340, 0.7], [710, 360, 0.7], # ankles
], dtype=np.float32),
timestamp=0.1
)

# 分类测试
classifier = EventClassifier()

seq = SkeletonSequence([
Skeleton(np.random.normal(0.8, 0.05, (17, 3)) * 500 + 400, 0.0),
Skeleton(np.random.normal(0.8, 0.05, (17, 3)) * 500 + 400, 0.05),
normal_skeleton,
fallen_skeleton,
])

event = classifier.classify(seq)
print(f"\n跌倒测试: 检测事件 = {event.name}")

# 站立测试
standing_skeleton = Skeleton(
keypoints=np.array([
[500, 100, 0.9],
[490, 95, 0.8], [510, 95, 0.8],
[480, 95, 0.7], [520, 95, 0.7],
[450, 150, 0.9], [550, 150, 0.9],
[430, 220, 0.8], [570, 220, 0.8],
[420, 290, 0.7], [580, 290, 0.7],
[460, 300, 0.9], [540, 300, 0.9],
[450, 380, 0.8], [550, 380, 0.8],
[440, 450, 0.7], [560, 450, 0.7],
], dtype=np.float32),
timestamp=0.0
)
print(f"站立检测: is_standing = {standing_skeleton.is_standing()}")
print(f"倒地检测: is_fallen = {fallen_skeleton.is_fallen()}")

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
=== 数据暴露对比 ===

原始视频方案:
每帧数据: 6.2 MB
每秒数据: 93.3 MB/s
每小时数据: 335.9 GB/h
包含: 面部特征、肤色、衣着、纹身、
行李、阅读材料、手机屏幕内容
GDPR: 需要明确同意、存储限制、
数据保护影响评估(DPIA)

骨架数据方案:
每帧数据: 204 bytes
每秒数据: 3.1 KB/s
每小时数据: 10.8 MB/h
包含: 关节坐标、置信度
不包含: 面部特征、肤色、衣着、
物品、屏幕内容
GDPR: 数据最小化原则满足,
无身份关联,DPIA 简化

数据量缩减: 31104x

对 IMS 开发的启示

1. 隐私保护是座舱监控的必选项

Syntronic 的项目揭示了一个关键趋势:GDPR 合规不是”加分项”,而是”入场券”。欧洲市场的座舱监控必须考虑隐私保护。

对比方案:

隐私保护方案 数据量 精度 GDPR 合规 部署难度
骨架关键点 204B/帧 中 ✅ 最优 低
深度图 2MB/帧 中高 ✅ 合规 中
热成像 0.5MB/帧 中低 ✅ 合规 中
红外视频 6MB/帧 高 ⚠️ 需评估 中
RGB 视频 6.2MB/帧 最高 ❌ 风险高 低

2. 从汽车到巴士的迁移

Syntronic 针对自动驾驶巴士,但技术方案可以直接迁移到汽车 IMS:

场景 巴士需求 汽车IMS对应
跌倒检测 老年/残疾人 OOP 异常姿态
医疗紧急 突发疾病 突发疾病检测
冲突检测 公共安全 ⚠️ 非主要需求
站立检测 行驶中站立 未系安全带/异常姿态
异常姿态 乘客安全 OOP 自适应约束

3. 边缘部署优势

关键设计:原始视频帧在传感器端处理完毕后立即丢弃,不存储不传输。

4. 姿态估计模型选型

模型 精度 速度 适合边缘 关键点数
YOLOv8-Pose 高 快 ✅ 17
MediaPipe BlazePose 中 极快 ✅ 33
OpenPose 最高 慢 ❌ 25+
RTMPose 高 快 ✅ 17
MoveNet 中高 快 ✅ 17

推荐:YOLOv8-Pose 或 RTMPose,平衡精度和速度,适合 Qualcomm QCS8255 等边缘平台。

Syntronic 同期其他论文项目

Syntronic 同时发布了三个相关论文项目:

项目 方向 共同点
Pose-based Monitoring 骨架+隐私保护 自驾驶巴士
Vision-Based Fall Detection 视觉跌倒检测 自驾驶巴士
Edge AI Real-Time 边缘硬件部署 自驾驶巴士

这三个项目构成了完整的”自动驾驶巴士乘员监控”技术栈。

结论

Syntronic 的论文项目揭示了一个重要方向:隐私保护不等于牺牲检测能力。通过骨架关键点替代原始视频,可以在满足 GDPR 的同时保持事件检测能力。数据量缩减 31000 倍是一个惊人的数字。

对 IMS 开发而言,骨架关键点方案值得作为隐私保护模式纳入架构。特别是面向欧洲市场的车型,骨架方案可以作为”隐私模式”在用户选择时启用,同时保留 RGB 视频作为”高精度模式”。


参考链接: