前言:为什么需要掌握 pbtxt?
5.1 pbtxt 的核心作用
Graph 配置文件(pbtxt)是 MediaPipe 的核心配置机制:
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
| ┌─────────────────────────────────────────────────────────────────────────┐ │ pbtxt 的核心作用 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 问题:如何定义复杂的感知流水线? │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 挑战: │ │ │ │ │ │ │ │ • 如何定义输入输出? │ │ │ │ • 如何连接 Calculator? │ │ │ │ • 如何配置参数? │ │ │ │ • 如何管理执行线程? │ │ │ │ • 如何实现条件分支? │ │ │ │ • 如何实现循环结构? │ │ │ │ • 如何调试流水线? │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ 解决方案:pbtxt 配置文件 │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ pbtxt = Protobuf 文本格式 │ │ │ │ │ │ │ │ • 声明式配置 │ │ │ │ • 可读性强 │ │ │ │ • 可视化支持 │ │ │ │ • 热重载(开发阶段) │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
5.2 pbtxt vs 代码配置
| 维度 |
pbtxt 配置 |
代码配置 |
| 可读性 |
高(声明式) |
中(命令式) |
| 可维护性 |
高(独立文件) |
低(嵌入代码) |
| 可视化 |
支持(官方工具) |
不支持 |
| 热重载 |
支持(开发阶段) |
不支持 |
| 版本控制 |
友好(文本文件) |
不友好(嵌入代码) |
| 调试 |
方便(可视化) |
困难(日志) |
| 灵活性 |
中(配置时决定) |
高(运行时决定) |
六、pbtxt 语法基础
6.1 Protobuf 文本格式
pbtxt 是 Protocol Buffers 的文本表示:
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
| ┌─────────────────────────────────────────────────────────────┐ │ pbtxt 基本语法 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. 注释 │ │ │ │ │ │ 2. 字段赋值 │ │ field_name: "value" │ field_name: 123 │ field_name: 3.14 │ field_name: true │ │ │ 3. 嵌套消息 │ │ nested_field { │ │ inner_field: "value" │ │ inner_field_2: 456 │ │ } │ │ │ │ 4. 重复字段 │ │ repeated_field: "value1" │ │ repeated_field: "value2" │ │ repeated_field: "value3" │ │ │ │ 5. 数组/列表 │ │ array_field: [1, 2, 3, 4, 5] │ │ │ └─────────────────────────────────────────────────────────────┘
|
6.2 Graph 配置结构
完整的 Graph 配置文件结构:
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
| # ========== 1. Graph 元信息(可选)========== # Graph 名称、描述等
# ========== 2. 输入流定义 ========== input_stream: "INPUT_VIDEO:input_video" input_stream: "INPUT_AUDIO:input_audio" input_stream: "SENSOR_DATA:sensor_data"
# ========== 3. 输出流定义 ========== output_stream: "OUTPUT_VIDEO:output_video" output_stream: "DETECTIONS:detections" output_stream: "METRICS:metrics"
# ========== 4. Side Packet 定义 ========== input_side_packet: "MODEL_PATH:model_path" input_side_packet: "CONFIG:config" input_side_packet: "THRESHOLD:threshold"
output_side_packet: "STATUS:status" output_side_packet: "STATISTICS:statistics"
# ========== 5. Calculator 节点定义 ========== node { calculator: "CalculatorName" input_stream: "TAG:stream_name" output_stream: "TAG:stream_name" input_side_packet: "TAG:packet_name" options { # Calculator 特定配置 } }
# ========== 6. 执行器配置(可选)========== executor { name: "gpu_executor" type: "ThreadPool" options { num_threads: 4 } }
# ========== 7. 全局配置(可选)========== options { [mediapipe.GraphOptions.ext] { max_queue_size: 100 report_deadlock: true } }
# ========== 8. 子图定义(可选)========== subgraph { name: "MySubgraph" input_stream: "INPUT:input" output_stream: "OUTPUT:output" node { calculator: "InternalCalculator" input_stream: "INPUT:input" output_stream: "OUTPUT:output" } }
|
6.3 语法详细说明
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
| # ========== 字段名和值 ========== # 字段名:使用下划线分隔的小写字母 # 值类型:字符串、整数、浮点数、布尔值
calculator: "FaceDetectionCalculator" # 字符串 min_score_threshold: 0.5 # 浮点数 max_results: 10 # 整数 use_gpu: true # 布尔值
# ========== 字符串 ========== # 可以使用单引号或双引号 model_path: "/models/face.tflite" model_path: '/models/face.tflite'
# 多行字符串 description: "This is a long description that spans multiple lines for better readability."
# ========== 枚举值 ========== # 枚举值不需要引号 scale_mode: FIT # 枚举值 backend: QNN # 枚举值
# ========== 嵌套消息 ========== options { [mediapipe.FaceDetectionOptions.ext] { min_score_threshold: 0.5 max_num_detections: 10 # 更深层嵌套 model_config { model_path: "/models/face.tflite" input_size: 320 } } }
# ========== 重复字段 ========== # 方式 1:多次使用同一字段名 labels: "face" labels: "person" labels: "car"
# 方式 2:使用数组语法 labels: ["face", "person", "car"]
# ========== Map 字段 ========== # Map 字段使用 key: value 格式 params { key: "threshold" value: "0.5" }
params { key: "num_threads" value: "4" }
# ========== 扩展字段 ========== # 使用 [package.MessageType.ext] 语法 options { [mediapipe.MyCalculatorOptions.ext] { my_param: 123 } }
|
七、节点定义详解
7.1 基本结构
Calculator 节点的完整定义:
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
| node { # ========== Calculator 类型(必需)========== calculator: "FaceDetectionCalculator" # ========== 输入流 ========== input_stream: "IMAGE:image_stream" input_stream: "ROI:region_of_interest" input_stream: "CONFIG:runtime_config" # ========== 输出流 ========== output_stream: "DETECTIONS:detections" output_stream: "SCORES:scores" output_stream: "LANDMARKS:landmarks" # ========== Side Packet ========== input_side_packet: "MODEL:model_path" input_side_packet: "CONFIG:static_config" output_side_packet: "STATUS:status" # ========== 配置选项 ========== options { [mediapipe.FaceDetectionOptions.ext] { min_score_threshold: 0.5 max_num_detections: 10 # 嵌套配置 nms_config { max_output_size: 10 iou_threshold: 0.45 score_threshold: 0.5 } } } # ========== 执行器(可选)========== executor: "gpu_executor" # ========== 输入流处理器(可选)========== input_stream_handler { input_stream_handler: "SyncSetInputStreamHandler" options { [mediapipe.SyncSetInputStreamHandlerOptions.ext] { # 同步配置 } } } }
|
7.2 输入输出标签(Tag)
使用 Tag 区分多个输入输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ┌─────────────────────────────────────────────────────────────┐ │ Tag 命名规范 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 格式:TAG:stream_name │ │ │ │ 示例: │ │ input_stream: "IMAGE:input_video" │ │ │ │ │ │ │ └── Stream 名称(用于连接) │ │ └── Tag 标签(用于 Calculator 识别) │ │ │ │ Tag 的作用: │ │ 1. Calculator 内部识别输入输出端口 │ │ 2. 与 Stream 名称解耦(可以不同) │ │ 3. 提高可读性 │ │ │ │ Stream 名称的作用: │ │ 1. 用于 Calculator 之间的连接 │ │ 2. 相同 Stream 名称自动连接 │ │ 3. Graph 级别的唯一标识 │ │ │ └─────────────────────────────────────────────────────────────┘
|
Tag 使用示例:
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
| # ========== Calculator A:输出 ========== node { calculator: "FaceDetector" output_stream: "FACES:detected_faces" # Tag: FACES, Stream: detected_faces }
# ========== Calculator B:输入 ========== node { calculator: "FaceProcessor" input_stream: "INPUT:detected_faces" # Tag: INPUT, Stream: detected_faces # Stream 名称相同(detected_faces),自动连接 }
# ========== 多输出示例 ========== node { calculator: "MultiOutputCalculator" output_stream: "OUTPUT_A:result_a" # Tag: OUTPUT_A output_stream: "OUTPUT_B:result_b" # Tag: OUTPUT_B output_stream: "OUTPUT_C:result_c" # Tag: OUTPUT_C }
# ========== 多输入示例 ========== node { calculator: "MultiInputCalculator" input_stream: "IMAGE:video_stream" # Tag: IMAGE input_stream: "AUDIO:audio_stream" # Tag: AUDIO input_stream: "SENSOR:sensor_stream" # Tag: SENSOR }
|
7.3 索引访问
使用索引而非标签:
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
| # ========== 使用索引 ========== # 索引从 0 开始,按顺序对应
# 定义 node { calculator: "MyCalculator" input_stream: "input_stream_0" # Index 0 input_stream: "input_stream_1" # Index 1 output_stream: "output_stream_0" # Index 0 }
# C++ 访问 static absl::Status GetContract(CalculatorContract* cc) { cc->Inputs().Index(0).Set<cv::Mat>(); cc->Inputs().Index(1).Set<AudioData>(); cc->Outputs().Index(0).Set<Result>(); return absl::OkStatus(); }
# ========== Tag vs Index ========== # 推荐:使用 Tag(可读性更好) input_stream: "IMAGE:video" input_stream: "AUDIO:audio"
# 不推荐:使用 Index(可读性差) input_stream: "input_0" input_stream: "input_1"
|
八、Options 配置详解
8.1 Calculator Options
每个 Calculator 可以定义自己的 Options:
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
| # ========== Proto 定义 ========== # my_calculator_options.proto syntax = "proto3";
package mediapipe;
import "mediapipe/framework/calculator_options.proto";
message MyCalculatorOptions { extend CalculatorOptions { optional MyCalculatorOptions ext = 1000000; } optional float threshold = 1 [default = 0.5]; optional int32 max_results = 2 [default = 10]; optional bool use_gpu = 3 [default = false]; optional string model_path = 4; enum Backend { CPU = 0; GPU = 1; DSP = 2; } optional Backend backend = 5 [default = CPU]; repeated string labels = 6; repeated float thresholds = 7; message ModelConfig { optional string path = 1; optional int32 input_size = 2; optional int32 output_size = 3; } optional ModelConfig model_config = 8; map<string, float> params = 9; }
# ========== Graph 配置 ========== # graph.pbtxt node { calculator: "MyCalculator" input_stream: "input" output_stream: "output" options { [mediapipe.MyCalculatorOptions.ext] { threshold: 0.75 max_results: 10 use_gpu: true model_path: "/models/face.tflite" backend: GPU labels: "face" labels: "person" labels: "car" thresholds: [0.5, 0.6, 0.7] model_config { path: "/models/face.tflite" input_size: 320 output_size: 10 } params { key: "learning_rate" value: 0.001 } params { key: "batch_size" value: 32.0 } } } }
|
8.2 Graph Options
全局 Graph 配置:
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
| # ========== Graph Options ========== options { [mediapipe.GraphOptions.ext] { # 队列大小 max_queue_size: 100 # 死锁报告 report_deadlock: true # 性能分析 enable_profiling: true # 日志级别 log_level: INFO # 超时配置 timeout_seconds: 10.0 } }
# ========== 执行器 Options ========== executor { name: "default_executor" type: "ThreadPool" options { [mediapipe.ThreadPoolExecutorOptions.ext] { # 线程数 num_threads: 4 # 栈大小 stack_size: 32768 # 线程优先级 thread_priority: HIGH # CPU 亲和性 cpu_affinity: [0, 1, 2, 3] } } }
|
8.3 默认值处理
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
| # ========== 默认值 ========== # Proto 中可以定义默认值 optional float threshold = 1 [default = 0.5]; optional int32 max_results = 2 [default = 10]; optional bool use_gpu = 3 [default = false];
# ========== Graph 中覆盖默认值 ========== node { calculator: "MyCalculator" options { [mediapipe.MyCalculatorOptions.ext] { # 只覆盖需要修改的字段 threshold: 0.75 # max_results 使用默认值 10 # use_gpu 使用默认值 false } } }
# ========== C++ 中读取 ========== absl::Status Open(CalculatorContext* cc) override { const auto& options = cc->Options<MyCalculatorOptions>(); float threshold = options.threshold(); int max_results = options.max_results(); bool use_gpu = options.use_gpu(); return absl::OkStatus(); }
|
九、执行器配置详解
9.1 默认执行器
MediaPipe 默认使用线程池执行器:
1 2 3
| # 默认配置,无需显式定义 # 默认线程数 = CPU 核心数 # 所有 Calculator 使用默认执行器
|
9.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
| # ========== 定义多个执行器 ==========
# GPU 执行器(用于推理) executor { name: "gpu_executor" type: "ThreadPool" options { [mediapipe.ThreadPoolExecutorOptions.ext] { num_threads: 2 stack_size: 65536 thread_priority: HIGH } } }
# CPU 执行器(用于后处理) executor { name: "cpu_executor" type: "ThreadPool" options { [mediapipe.ThreadPoolExecutorOptions.ext] { num_threads: 4 stack_size: 32768 thread_priority: NORMAL } } }
# IO 执行器(用于文件读写) executor { name: "io_executor" type: "ThreadPool" options { [mediapipe.ThreadPoolExecutorOptions.ext] { num_threads: 2 stack_size: 16384 thread_priority: LOW } } }
# ========== Calculator 指定执行器 ========== node { calculator: "GPUCalculator" input_stream: "input" output_stream: "output" executor: "gpu_executor" # 使用 GPU 执行器 }
node { calculator: "CPUCalculator" input_stream: "input" output_stream: "output" executor: "cpu_executor" # 使用 CPU 执行器 }
node { calculator: "IOCalculator" input_stream: "input" output_stream: "output" executor: "io_executor" # 使用 IO 执行器 }
|
9.3 执行器类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ┌─────────────────────────────────────────────────────────────┐ │ 执行器类型 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. ThreadPool(线程池) │ │ • 默认执行器 │ │ • 多线程并发执行 │ │ • 适用于 CPU 密集型任务 │ │ │ │ 2. ApplicationThread(应用线程) │ │ • 单线程执行 │ │ • 与应用主线程同步 │ │ • 适用于 UI 更新任务 │ │ │ │ 3. CustomExecutor(自定义执行器) │ │ • 用户自定义执行器 │ │ • 可实现特定调度策略 │ │ • 适用于特殊硬件(DSP, NPU) │ │ │ └─────────────────────────────────────────────────────────────┘
|
十、高级配置
10.1 条件执行
使用 GateCalculator 实现条件分支:
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
| # ========== 条件执行示例 ========== input_stream: "input" input_stream: "condition" # true = 通过,false = 阻塞 output_stream: "output"
# Gate Calculator node { calculator: "GateCalculator" input_stream: "input" input_stream: "ALLOW:condition" output_stream: "output" options { [mediapipe.GateCalculatorOptions.ext] { # 允许通过的初始状态 initial_state: true } } }
# ========== 多路分支 ========== node { calculator: "MuxCalculator" input_stream: "INPUT:0:branch_a" input_stream: "INPUT:1:branch_b" input_stream: "INPUT:2:branch_c" output_stream: "OUTPUT:output" input_stream_handler { input_stream_handler: "MuxInputStreamHandler" } }
# ========== Switch Calculator ========== node { calculator: "SwitchCalculator" input_stream: "INPUT:input" input_stream: "SELECT:selector" # 选择哪个分支 output_stream: "OUTPUT:0:branch_a" output_stream: "OUTPUT:1:branch_b" options { [mediapipe.SwitchCalculatorOptions.ext] { num_outputs: 2 } } }
|
10.2 循环结构
使用 BackEdge 实现循环:
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
| # ========== 循环结构示例 ========== input_stream: "INIT:init_value" output_stream: "RESULT:result"
# Loop Calculator node { calculator: "LoopCalculator" input_stream: "INIT:init_value" input_stream: "PREV:loop_back" # 来自上一轮输出 output_stream: "NEXT:loop_value" input_stream_info: { tag_index: "PREV" back_edge: true # 标记为反向边(循环) } options { [mediapipe.LoopCalculatorOptions.ext] { max_iterations: 10 convergence_threshold: 0.001 } } }
# 处理节点 node { calculator: "ProcessCalculator" input_stream: "loop_value" output_stream: "loop_back" output_stream: "result" }
|
10.3 子图(Subgraph)
定义可复用的子图:
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
| # ========== 子图定义 ========== # face_detection_subgraph.pbtxt subgraph { name: "FaceDetectionSubgraph" # 子图接口 input_stream: "IMAGE:image" input_side_packet: "MODEL:model_path" output_stream: "DETECTIONS:detections" # 内部节点 node { calculator: "ImagePreprocessCalculator" input_stream: "IMAGE:image" output_stream: "PREPROCESSED:preprocessed_image" } node { calculator: "FaceDetector" input_stream: "IMAGE:preprocessed_image" input_side_packet: "MODEL:model_path" output_stream: "RAW:raw_detections" } node { calculator: "DetectionPostprocessCalculator" input_stream: "DETECTIONS:raw_detections" output_stream: "DETECTIONS:detections" } }
# ========== 使用子图 ========== # main_graph.pbtxt input_stream: "IMAGE:input_image" output_stream: "DETECTIONS:output_detections"
node { calculator: "FaceDetectionSubgraph" input_stream: "IMAGE:input_image" input_side_packet: "MODEL:model_path" output_stream: "DETECTIONS:output_detections" }
|
十一、完整示例:IMS DMS Graph
11.1 完整配置
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
| # dms_graph.pbtxt # IMS DMS 完整感知流水线配置
# ========== 输入输出定义 ========== input_stream: "IR_IMAGE:ir_frame" input_stream: "VEHICLE_SPEED:speed" input_stream: "CAN_SIGNAL:can_data" input_stream: "TIMESTAMP:timestamp"
output_stream: "DMS_RESULT:dms_output" output_stream: "ALERT:alert" output_stream: "METRICS:metrics"
# ========== Side Packet 定义 ========== input_side_packet: "FACE_MODEL:face_model_path" input_side_packet: "LANDMARK_MODEL:landmark_model_path" input_side_packet: "IRIS_MODEL:iris_model_path" input_side_packet: "POSE_MODEL:pose_model_path" input_side_packet: "FATIGUE_CONFIG:fatigue_config" input_side_packet: "DISTRACTION_CONFIG:distraction_config"
# ========== 流量限制 ========== node { calculator: "FlowLimiterCalculator" input_stream: "ir_frame" input_stream: "dms_output" input_stream_info: { tag_index: "dms_output" back_edge: true } output_stream: "throttled_ir_frame" options { [mediapipe.FlowLimiterCalculatorOptions.ext] { max_in_flight: 1 max_in_queue: 1 } } }
# ========== 人脸检测 ========== node { calculator: "FaceDetectionCalculator" input_stream: "IMAGE:throttled_ir_frame" input_side_packet: "MODEL:face_model_path" output_stream: "DETECTIONS:face_detections" options { [mediapipe.FaceDetectionOptions.ext] { min_score_threshold: 0.7 max_num_detections: 1 } } executor: "gpu_executor" }
# ========== 人脸关键点 ========== node { calculator: "FaceMeshCalculator" input_stream: "IMAGE:throttled_ir_frame" input_stream: "DETECTIONS:face_detections" input_side_packet: "MODEL:landmark_model_path" output_stream: "LANDMARKS:face_landmarks" options { [mediapipe.FaceMeshOptions.ext] { num_faces: 1 enable_iris: true } } executor: "gpu_executor" }
# ========== 虹膜检测 ========== node { calculator: "IrisDetectionCalculator" input_stream: "LANDMARKS:face_landmarks" input_stream: "IMAGE:throttled_ir_frame" input_side_packet: "MODEL:iris_model_path" output_stream: "IRIS:iris_landmarks" output_stream: "GAZE:gaze_vector" executor: "gpu_executor" }
# ========== 头部姿态估计 ========== node { calculator: "HeadPoseCalculator" input_stream: "LANDMARKS:face_landmarks" input_side_packet: "MODEL:pose_model_path" output_stream: "POSE:head_pose" options { [mediapipe.HeadPoseOptions.ext] { euler_angles: true } } executor: "cpu_executor" }
# ========== EAR 计算 ========== node { calculator: "ARCalculator" input_stream: "LANDMARKS:face_landmarks" output_stream: "EAR:left_ear" output_stream: "EAR:right_ear" output_stream: "EAR:avg_ear" executor: "cpu_executor" }
# ========== PERCLOS 计算 ========== node { calculator: "PERCLOSCalculator" input_stream: "EAR:avg_ear" input_stream: "TIMESTAMP:timestamp" input_side_packet: "CONFIG:fatigue_config" output_stream: "PERCLOS:perclos" output_stream: "PERCLOS_WINDOW:window_stats" executor: "cpu_executor" }
# ========== 视线区域分类 ========== node { calculator: "GazeZoneCalculator" input_stream: "GAZE:gaze_vector" input_stream: "POSE:head_pose" input_side_packet: "CONFIG:distraction_config" output_stream: "ZONE:gaze_zone" output_stream: "LOOK_AWAY:look_away_duration" executor: "cpu_executor" }
# ========== 疲劳评分融合 ========== node { calculator: "FatigueScoreCalculator" input_stream: "EAR:avg_ear" input_stream: "PERCLOS:perclos" input_stream: "POSE:head_pose" input_stream: "VEHICLE_SPEED:speed" input_side_packet: "CONFIG:fatigue_config" output_stream: "FATIGUE_SCORE:fatigue_score" output_stream: "FATIGUE_LEVEL:fatigue_level" executor: "cpu_executor" }
# ========== 分心检测融合 ========== node { calculator: "DistractionScoreCalculator" input_stream: "ZONE:gaze_zone" input_stream: "LOOK_AWAY:look_away_duration" input_stream: "POSE:head_pose" input_side_packet: "CONFIG:distraction_config" output_stream: "DISTRACTION_SCORE:distraction_score" output_stream: "DISTRACTION_LEVEL:distraction_level" executor: "cpu_executor" }
# ========== DMS 结果融合 ========== node { calculator: "DMSAggregatorCalculator" input_stream: "FATIGUE_SCORE:fatigue_score" input_stream: "FATIGUE_LEVEL:fatigue_level" input_stream: "DISTRACTION_SCORE:distraction_score" input_stream: "DISTRACTION_LEVEL:distraction_level" input_stream: "CAN_SIGNAL:can_data" output_stream: "DMS_RESULT:dms_output" output_stream: "METRICS:metrics" executor: "cpu_executor" }
# ========== 告警触发 ========== node { calculator: "AlertCalculator" input_stream: "DMS_RESULT:dms_output" output_stream: "ALERT:alert" executor: "cpu_executor" }
# ========== 执行器配置 ========== executor { name: "gpu_executor" type: "ThreadPool" options { [mediapipe.ThreadPoolExecutorOptions.ext] { num_threads: 2 thread_priority: HIGH } } }
executor { name: "cpu_executor" type: "ThreadPool" options { [mediapipe.ThreadPoolExecutorOptions.ext] { num_threads: 4 thread_priority: NORMAL } } }
# ========== 全局配置 ========== options { [mediapipe.GraphOptions.ext] { max_queue_size: 10 report_deadlock: true } }
|
十二、调试技巧
12.1 Graph Visualizer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
npm install -g @mediapipe/visualizer
mediapipe-visualizer --config dms_graph.pbtxt
|
12.2 日志输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| # ========== 添加日志 Calculator ========== node { calculator: "PacketPresenceCalculator" input_stream: "input" output_stream: "presence" options { [mediapipe.PacketPresenceCalculatorOptions.ext] { tag: "CHECK_INPUT" } } }
# ========== 使用 PassThrough Calculator 打印 ========== node { calculator: "PassThroughCalculator" input_stream: "debug_stream" output_stream: "debug_stream_out" options { [mediapipe.PassThroughCalculatorOptions.ext] { log_packets: true } } }
|
12.3 性能分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| # ========== 启用性能分析 ========== options { [mediapipe.GraphOptions.ext] { enable_profiling: true } }
# ========== Calculator 级别性能分析 ========== node { calculator: "MyCalculator" options { [mediapipe.MyCalculatorOptions.ext] { enable_timing: true log_frequency: 100 # 每 100 帧打印一次 } } }
|
十三、总结
| 元素 |
说明 |
示例 |
input_stream |
输入流 |
input_stream: "IMAGE:image" |
output_stream |
输出流 |
output_stream: "DETECTIONS:detections" |
input_side_packet |
输入 Side Packet |
input_side_packet: "MODEL:model_path" |
node |
Calculator 节点 |
node { calculator: "..." } |
options |
配置选项 |
options { [...] { ... } } |
executor |
执行器 |
executor { name: "gpu" } |
subgraph |
子图 |
subgraph { name: "..." } |
下篇预告
MediaPipe 系列 06:Bazel 构建系统——MediaPipe 编译入门
深入讲解 Bazel 构建系统、WORKSPACE 配置、BUILD 文件编写、编译 MediaPipe 项目。
参考资料
- Google AI Edge. MediaPipe Graph Configuration
- Protocol Buffers. Text Format Language Specification
- Lugaresi et al. (2019). MediaPipe: A Framework for Building Perception Pipelines. arXiv:1906.08172
系列进度: 5/55
更新时间: 2026-03-12