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
| """ ELA 驱动的 Blender 3D 合成数据管道
核心思路: 1. 使用 ELA 信号驱动 rigged 3D 角色模型的眼睑动画 2. 通过 Blender Python API (bpy) 控制渲染 3. 生成多样化数据集:不同相机角度、光照、噪声
优势:解决真实疲劳数据采集的伦理和安全问题 """
import bpy import numpy as np import json
class SyntheticBlinkGenerator: """ 基于 ELA 信号的合成眨眼数据生成器 在 Blender 中驱动 rigged 角色模型 """ def __init__(self, blend_file: str, output_dir: str): """ Args: blend_file: Blender 角色模型文件路径 output_dir: 渲染图像输出目录 """ self.output_dir = output_dir bpy.ops.wm.open_mainfile(filepath=blend_file) self.armature = bpy.data.objects.get("Armature") self.left_eyelid_bones = ["eyelid_top_L", "eyelid_bot_L"] self.right_eyelid_bones = ["eyelid_top_R", "eyelid_bot_R"] self.camera = bpy.data.objects.get("Camera") self.render_engine = "CYCLES" def generate_blink_sequence(self, ela_signal: np.ndarray, fps: int = 30, camera_angles: list = None) -> int: """ 根据 ELA 信号生成一个眨眼序列 Args: ela_signal: ELA角度序列 (N,),每个值代表该帧的眼睑角度 fps: 帧率 camera_angles: 相机角度列表 [(yaw, pitch, roll), ...] Returns: num_frames: 生成的帧数 """ if camera_angles is None: camera_angles = [(0, 0, 0), (15, 0, 0), (-15, 0, 0), (0, 15, 0), (0, -15, 0)] num_frames = len(ela_signal) total_rendered = 0 for angle_idx, (yaw, pitch, roll) in enumerate(camera_angles): self._set_camera_angle(yaw, pitch, roll) for frame_idx, ela_value in enumerate(ela_signal): rotation = self._ela_to_rotation(ela_value) self._set_eyelid_rotation(rotation) bpy.context.scene.frame_set(frame_idx) filepath = f"{self.output_dir}/seq_{angle_idx}_frame_{frame_idx:04d}.png" bpy.context.scene.render.filepath = filepath bpy.ops.render.render(write_still=True) total_rendered += 1 return total_rendered def _ela_to_rotation(self, ela_degrees: float) -> float: """ 将 ELA 角度转换为骨骼旋转角度 Args: ela_degrees: ELA值(0=闭眼, 90=睁眼) Returns: rotation: 骨骼旋转角度(弧度) """ max_rotation = np.radians(45) rotation = max_rotation * (1 - ela_degrees / 90.0) return rotation def _set_eyelid_rotation(self, rotation: float): """设置眼睑骨骼旋转""" for bone_name in self.left_eyelid_bones + self.right_eyelid_bones: bone = self.armature.pose.bones.get(bone_name) if bone: bone.rotation_euler = (rotation, 0, 0) def _set_camera_angle(self, yaw: float, pitch: float, roll: float): """设置相机角度""" import math self.camera.rotation_euler = ( math.radians(pitch), math.radians(roll), math.radians(yaw) )
SYNTHETIC_DATASET_CONFIG = { 'fps': 30, 'duration_per_clip': '3-5秒', 'camera_angles': [ (0, 0, 0), (15, 0, 0), (-15, 0, 0), (0, 15, 0), (0, -15, 0), (30, 0, 0), (-30, 0, 0), ], 'lighting_conditions': [ {'type': 'daylight', 'intensity': 500}, {'type': 'night_ir', 'intensity': 50, 'wavelength': 940}, {'type': 'tunnel', 'intensity': 200}, ], 'noise_levels': [0, 5, 10, 20], 'blink_types': ['normal', 'fatigue', 'microsleep'], 'target_samples': 10000, }
|