车内人体姿态数据集生成:MakeHuman+Blender合成数据管道

研究背景

车内人体姿态估计需要大量标注数据,但实际采集和标注成本高昂。

数据采集挑战

挑战 具体表现 影响
场景多样性 不同车型、座椅、光照 需要大量场景
姿态多样性 10+种驾驶行为 标注工作量大
隐私问题 面部信息采集 合规限制
标注成本 3D关键点标注 人力成本高

解决方案: 合成数据生成


数据生成管道

MakeHuman + Blender架构

graph LR
    A[MakeHuman] --> B[生成人体模型]
    B --> C[导入Blender]
    C --> D[构建车内场景]
    D --> E[设置姿态动作]
    E --> F[渲染图像]
    F --> G[自动生成标注]
    G --> H[输出数据集]

核心流程代码

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
import bpy
import mathutils
import numpy as np

class InCabinPoseDataGenerator:
"""
车内姿态数据生成器

工具链:
- MakeHuman: 生成人体模型
- Blender: 场景构建、姿态设置、渲染
"""

def __init__(self, output_dir):
self.output_dir = output_dir
self.setup_car_cabin()

def setup_car_cabin(self):
"""
构建车内场景

包含:
- 座椅
- 方向盘
- 仪表板
- 车窗
"""

# 导入车内模型(从CAD或预建模)
cabin_path = "assets/car_cabin.blend"
bpy.ops.wm.append(
filepath=f"{cabin_path}/Object/"
)

# 设置光照
self.setup_lighting()

# 设置相机
self.setup_camera()

def setup_lighting(self):
"""设置车内光照"""

# 日间光照
sun = bpy.data.lights.new("Sun", type='SUN')
sun.energy = 2.0
sun.angle = 0.5

# 环境光
env_light = bpy.data.lights.new("EnvLight", type='AREA')
env_light.energy = 500
env_light.size = 2.0

def setup_camera(self):
"""设置相机位置"""

# DMS摄像头位置:仪表板上方
camera = bpy.data.objects['Camera']
camera.location = (0.3, -0.5, 1.2) # 相对于驾驶员
camera.rotation_euler = (80, 0, 0) # 俯视角

def import_human_model(self, variation_seed):
"""
从MakeHuman导入人体模型

变化维度:
- 性别
- 年龄
- 体型
- 服装
"""

import random

random.seed(variation_seed)

# 参数变化
gender = random.choice(['male', 'female'])
age = random.randint(20, 60)
height = random.gauss(170, 15) # cm
weight = random.gauss(70, 15) # kg

# 构建MakeHuman命令
mh_command = f"""
import makehuman
human = makehuman.getHuman()
human.setGender({1 if gender == 'male' else 0})
human.setAge({age / 100.0})
human.setHeight({height / 170.0})
human.setWeight({weight / 70.0})
"""

# 导出为Blender格式
# (实际实现需要调用MakeHuman API)

return {
'gender': gender,
'age': age,
'height': height,
'weight': weight
}

def set_driving_posture(self, behavior_type):
"""
设置驾驶姿态

Args:
behavior_type: 行为类型
- 'normal': 正常驾驶
- 'forward_lean': 前倾
- 'lateral_lean': 侧倾
- 'recline': 后仰
- 'phone_use': 手机使用
- 'drinking': 喝水
"""

human = bpy.data.objects['Human']

# 骨骼姿态设置
pose_data = self.get_pose_data(behavior_type)

for bone_name, rotation in pose_data.items():
bone = human.pose.bones[bone_name]
bone.rotation_euler = rotation

# IK约束调整(手部位置)
self.adjust_hand_ik(behavior_type)

def get_pose_data(self, behavior_type):
"""获取姿态数据"""

# 预定义姿态(简化示例)
poses = {
'normal': {
'spine': (0, 0, 0),
'spine_01': (0, 0, 0),
'spine_02': (0, 0, 0),
},
'forward_lean': {
'spine': (25, 0, 0), # 前倾25度
'spine_01': (15, 0, 0),
'spine_02': (10, 0, 0),
},
'lateral_lean': {
'spine': (0, 20, 0), # 侧倾20度
'spine_01': (0, 15, 0),
},
'phone_use': {
'spine': (10, 0, 0),
'shoulder_R': (0, 0, -30), # 右手抬起
'upperarm_R': (0, 0, -45),
'forearm_R': (0, 0, -90),
}
}

return poses.get(behavior_type, poses['normal'])

def render_image(self, frame_id):
"""
渲染图像

输出:
- RGB图像
- 深度图
- 分割图
"""

# 设置渲染参数
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.render.resolution_x = 1920
bpy.context.scene.render.resolution_y = 1080

# 启用深度输出
bpy.context.scene.render.use_compositing = True
bpy.context.scene.use_nodes = True

# 渲染
bpy.context.scene.render.filepath = f"{self.output_dir}/rgb_{frame_id:04d}.png"
bpy.ops.render.render(write_still=True)

# 输出深度图
self.render_depth(frame_id)

# 输出分割图
self.render_segmentation(frame_id)

def generate_annotations(self, frame_id):
"""
自动生成标注

优势:合成数据可自动生成精确标注
"""

human = bpy.data.objects['Human']

# 提取3D关键点
keypoints_3d = self.extract_3d_keypoints(human)

# 投影到2D
keypoints_2d = self.project_to_2d(keypoints_3d)

# 保存标注
annotation = {
'frame_id': frame_id,
'keypoints_3d': keypoints_3d.tolist(),
'keypoints_2d': keypoints_2d.tolist(),
'behavior': self.current_behavior,
'camera_intrinsics': self.get_camera_intrinsics()
}

return annotation

def extract_3d_keypoints(self, human):
"""提取3D关键点(16点)"""

keypoint_names = [
'head_top', 'neck', 'spine_shoulder', 'spine_mid', 'spine_base',
'shoulder_L', 'elbow_L', 'wrist_L',
'shoulder_R', 'elbow_R', 'wrist_R',
'hip_L', 'knee_L', 'ankle_L',
'hip_R', 'knee_R', 'ankle_R'
]

keypoints_3d = []

for bone_name in keypoint_names:
bone = human.pose.bones.get(bone_name)
if bone:
# 获取世界坐标
world_pos = bone.head
keypoints_3d.append([world_pos.x, world_pos.y, world_pos.z])
else:
keypoints_3d.append([0, 0, 0]) # 缺失点

return np.array(keypoints_3d)

def generate_dataset(self, num_samples=1000):
"""
生成完整数据集

Args:
num_samples: 样本数量
"""

behaviors = ['normal', 'forward_lean', 'lateral_lean',
'recline', 'phone_use', 'drinking']

for i in range(num_samples):
# 随机变化人体模型
self.import_human_model(i)

# 随机选择行为
behavior = np.random.choice(behaviors)
self.set_driving_posture(behavior)

# 随机变化参数
self.randomize_parameters()

# 渲染
self.render_image(i)

# 生成标注
annotation = self.generate_annotations(i)

# 保存
self.save_annotation(annotation, i)

print(f"生成完成:{num_samples} 个样本")

def randomize_parameters(self):
"""随机化场景参数"""

# 光照变化
self.randomize_lighting()

# 服装变化
self.randomize_clothing()

# 相机微小抖动
self.randomize_camera_pose()


# 使用示例
if __name__ == "__main__":
generator = InCabinPoseDataGenerator("output/cabin_pose_dataset")
generator.generate_dataset(num_samples=10000)

数据增强策略

几何变化

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
def apply_geometric_augmentation(image, keypoints_3d):
"""
几何增强

包括:
- 尺度变化
- 旋转
- 噪声
- 模糊
"""

augmented_images = []
augmented_keypoints = []

# 尺度变化
for scale in [0.8, 0.9, 1.1, 1.2]:
scaled_img = cv2.resize(image, None, fx=scale, fy=scale)
scaled_kp = keypoints_3d * scale
augmented_images.append(scaled_img)
augmented_keypoints.append(scaled_kp)

# 亮度变化
for brightness in [-30, -15, 15, 30]:
bright_img = cv2.convertScaleAbs(image, beta=brightness)
augmented_images.append(bright_img)
augmented_keypoints.append(keypoints_3d)

# 模糊
for ksize in [3, 5, 7]:
blurred = cv2.GaussianBlur(image, (ksize, ksize), 0)
augmented_images.append(blurred)
augmented_keypoints.append(keypoints_3d)

return augmented_images, augmented_keypoints

领域随机化

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
def domain_randomization():
"""
领域随机化

缩小合成数据与真实数据的差距
"""

randomization_config = {
'lighting': {
'intensity_range': (0.5, 2.0),
'color_temp_range': (3000, 7000), # K
'shadow_probability': 0.3
},
'texture': {
'seat_color_variation': True,
'clothing_randomization': True,
'skin_tone_variation': True
},
'camera': {
'focal_length_range': (25, 35), # mm
'exposure_variation': True,
'noise_injection': True
},
'environment': {
'window_reflection': True,
'dashboard_shadows': True,
'steering_wheel_occlusion': True
}
}

return randomization_config

数据集统计

生成数据示例

属性 数值
总样本数 10,000
行为类型 10种
人体变化 性别、年龄、体型
光照场景 日间、夜间、逆光
标注类型 2D关键点 + 3D关键点
分辨率 1920×1080

标注质量

1
2
3
4
5
6
7
# 标注精度验证
annotation_accuracy = {
'keypoint_precision': 'pixel-perfect', # 像素级精确
'3d_accuracy': '< 1mm', # 毫米级精度
'occlusion_handling': 'explicit', # 显式遮挡标注
'temporal_consistency': 'guaranteed' # 时序一致性保证
}

IMS开发启示

1. 数据生成配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
### DAT-01 合成数据生成配置

**数据需求:**
| 维度 | 需求量 | 合成数据占比 |
|------|--------|-------------|
| 训练集 | 50,000 | 80%合成 |
| 验证集 | 5,000 | 50%合成 |
| 测试集 | 2,000 | 0%合成(真实) |

**生成参数:**
- 人体变化:性别×年龄×体型 = 2×5×5 = 50种
- 行为类型:10种
- 光照场景:5种
- 总样本:50 × 10 × 5 × 20 = 50,000

**质量要求:**
- 关键点标注精度:< 1px
- 3D误差:< 5mm
- 遮挡标注完整率:100%

2. 合成数据验证

1
2
3
4
5
6
7
8
9
10
11
12
13
# 合成数据有效性验证
def validate_synthetic_data(synthetic_model, real_test_set):
"""
验证合成数据训练的模型在真实数据上的性能
"""

# 在合成数据训练
# 在真实数据测试

performance_gap = synthetic_perf - real_perf

# 可接受差距:< 5%
return performance_gap < 0.05

参考文献

  • 论文:In-Cabin vehicle synthetic data to test Deep Learning based human pose estimation models, IEEE 2021
  • 论文:A system for the generation of in-car human body pose datasets, Springer 2020
  • 工具:MakeHuman, Blender

总结

车内人体姿态合成数据生成的核心优势:

  1. 零标注成本:自动生成精确标注
  2. 场景多样:可控变化人体、姿态、光照
  3. 隐私友好:无需真实人脸数据
  4. 3D标注:同时提供2D和3D标注
  5. 快速迭代:算法改进后快速重新生成

IMS开发优先级: 🟡 中优先级(补充真实数据,降低采集成本)


车内人体姿态数据集生成:MakeHuman+Blender合成数据管道
https://dapalm.com/2026/07/31/2026-07-31-in-cabin-human-pose-dataset-generation-makehuman-blender/
作者
Mars
发布于
2026年7月31日
许可协议