MediaPipe 系列 05:Graph 配置文件(pbtxt)详解完整指南

前言:为什么需要掌握 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>(); // input_stream_0
cc->Inputs().Index(1).Set<AudioData>(); // input_stream_1
cc->Outputs().Index(0).Set<Result>(); // output_stream_0
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 {
// 扩展 CalculatorOptions
extend CalculatorOptions {
optional MyCalculatorOptions ext = 1000000; // 唯一扩展 ID
}

// 基本类型
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 类型
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(); // 0.75 或默认值 0.5
int max_results = options.max_results(); // 默认值 10
bool use_gpu = options.use_gpu(); // 默认值 false

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
# ========== 使用 MediaPipe Visualizer ==========
# 访问:https://viz.mediapipe.dev/

# 粘贴 pbtxt 内容,即可:
# 1. 可视化 Graph 结构
# 2. 查看数据流向
# 3. 检查连接正确性
# 4. 导出为图片

# ========== 本地可视化 ==========
# 安装 MediaPipe Visualizer
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 项目。


参考资料

  1. Google AI Edge. MediaPipe Graph Configuration
  2. Protocol Buffers. Text Format Language Specification
  3. Lugaresi et al. (2019). MediaPipe: A Framework for Building Perception Pipelines. arXiv:1906.08172

系列进度: 5/55
更新时间: 2026-03-12


MediaPipe 系列 05:Graph 配置文件(pbtxt)详解完整指南
https://dapalm.com/2026/03/12/MediaPipe系列05-Graph配置文件pbtxt详解/
作者
Mars
发布于
2026年3月12日
许可协议