MediaPipe 系列 33:Holistic——全身感知融合完整指南

前言:为什么需要 Holistic?

33.1 Holistic 的重要性

全身感知融合在 IMS/DMS 中的应用:

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
┌─────────────────────────────────────────────────────────────────────────┐
│ Holistic 在 IMS/DMS 中的应用 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ IMS/DMS 场景需求: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • 驾驶员状态综合分析(面部 + 身体 + 手部) │ │
│ │ • 危险行为检测(打电话、抽烟、喝水) │ │
│ │ • 乘员姿态监控(儿童座椅、宠物检测) │ │
│ │ • 人机交互(手势识别 + 视线追踪) │ │
│ │ • 疲劳综合判断(面部表情 + 身体姿态) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ MediaPipe Holistic 特点: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • 同时输出人脸、身体、手部关键点 │ │
│ │ • ROI 共享,避免重复计算 │ │
│ │ • 实时性能(~15ms GPU) │ │
│ │ • 统一的坐标系 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

33.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
┌─────────────────────────────────────────────────────────────────────────┐
│ Holistic Pipeline │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 输入层 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Input Image │ │
│ │ (任意尺寸 RGB) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 第一阶段:人体检测 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ BlazePose Detector │ │
│ │ • 输出:人体边界框 │ │
│ │ • 提取 ROI:Face ROI、Hand ROIs │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ ▼ ▼ ▼ │
│ 第二阶段:关键点检测 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ │ │ │ │ │ │
│ │ Pose │ │ Face Mesh │ │ Hand │ │
│ │ Landmark │ │ (with Iris) │ │ Tracking │ │
│ │ │ │ │ │ │ │
│ │ 33 点 │ │ 468 点 │ │ 21×2 点 │ │
│ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └───────────────────┼───────────────────┘ │
│ ▼ │
│ 输出层 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Pose Landmarks (33) │ │
│ │ Face Landmarks (468) │ │
│ │ Left Hand Landmarks (21) │ │
│ │ Right Hand Landmarks (21) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

33.3 ROI 共享机制

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
┌─────────────────────────────────────────────────────────────────────────┐
│ ROI 共享机制 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 传统方式(独立检测): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 输入图像 │ │
│ │ │ │ │
│ │ ├─► Face Detection ─► Face Landmark │ │
│ │ │ (单独的人脸检测) │ │
│ │ │ │ │
│ │ ├─► Pose Detection ─► Pose Landmark │ │
│ │ │ (单独的人体检测) │ │
│ │ │ │ │
│ │ └─► Hand Detection ─► Hand Landmark │ │
│ │ (单独的手部检测) │ │
│ │ │ │
│ │ 问题:重复检测,计算量大 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Holistic 方式(ROI 共享): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 输入图像 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Pose Detection (一次检测) │ │
│ │ │ │ │
│ │ ├─► 提取 Face ROI ─► Face Landmark │ │
│ │ │ │ │
│ │ ├─► Pose Landmark (直接使用) │ │
│ │ │ │ │
│ │ └─► 提取 Hand ROIs ─► Hand Landmarks │ │
│ │ │ │
│ │ 优势:避免重复检测,效率更高 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

三十四、Graph 配置

34.1 完整 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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# ========== Holistic Graph 配置 ==========

# mediapipe/graphs/holistic_tracking/holistic_tracking_gpu.pbtxt

input_stream: "INPUT:Input"

output_stream: "POSE_LANDMARKS:pose_landmarks"
output_stream: "FACE_LANDMARKS:face_landmarks"
output_stream: "LEFT_HAND_LANDMARKS:left_hand_landmarks"
output_stream: "RIGHT_HAND_LANDMARKS:right_hand_landmarks"

# ========== 1. 图像格式转换 ==========
node {
calculator: "ImageTransformationCalculator"
input_stream: "INPUT:Input"
output_stream: "IMAGE:converted_image"
options {
[mediapipe.ImageTransformationCalculatorOptions.ext] {
output_format: SRGB
}
}
}

# ========== 2. 人体检测与关键点 ==========
node {
calculator: "PoseDetectionCalculator"
input_stream: "IMAGE:converted_image"
output_stream: "DETECTIONS:pose_detections"
}

node {
calculator: "PoseLandmarkByRoiCalculator"
input_stream: "IMAGE:converted_image"
input_stream: "DETECTIONS:pose_detections"
output_stream: "LANDMARKS:pose_landmarks"
output_stream: "ROI:pose_roi"
}

# ========== 3. 从 Pose 提取 Face ROI ==========
node {
calculator: "FaceFromPoseRoiCalculator"
input_stream: "LANDMARKS:pose_landmarks"
output_stream: "ROI:face_roi"
}

node {
calculator: "FaceLandmarkCalculator"
input_stream: "IMAGE:converted_image"
input_stream: "ROI:face_roi"
output_stream: "LANDMARKS:face_landmarks"
options {
[mediapipe.FaceLandmarkCalculatorOptions.ext] {
refine_landmarks: true # 启用 Iris
}
}
}

# ========== 4. 从 Pose 提取 Hand ROIs ==========
node {
calculator: "HandsFromPoseRoiCalculator"
input_stream: "LANDMARKS:pose_landmarks"
output_stream: "ROI:left_hand_roi"
output_stream: "ROI:right_hand_roi"
}

node {
calculator: "HandLandmarkCalculator"
input_stream: "IMAGE:converted_image"
input_stream: "ROI:left_hand_roi"
output_stream: "LANDMARKS:left_hand_landmarks"
}

node {
calculator: "HandLandmarkCalculator"
input_stream: "IMAGE:converted_image"
input_stream: "ROI:right_hand_roi"
output_stream: "LANDMARKS:right_hand_landmarks"
}

三十五、IMS 实战:驾驶员行为分析

35.1 驾驶员行为分析 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
# ims_driver_behavior_analysis_graph.pbtxt

input_stream: "RGB_IMAGE:rgb_image"
output_stream: "BEHAVIOR_RESULT:behavior_result"
output_stream: "ALERT:alert"

# ========== 1. Holistic 检测 ==========
node {
calculator: "HolisticTrackingGpu"
input_stream: "IMAGE:rgb_image"
output_stream: "POSE_LANDMARKS:pose_landmarks"
output_stream: "FACE_LANDMARKS:face_landmarks"
output_stream: "LEFT_HAND_LANDMARKS:left_hand"
output_stream: "RIGHT_HAND_LANDMARKS:right_hand"
}

# ========== 2. 疲劳检测 ==========
node {
calculator: "FatigueDetectionCalculator"
input_stream: "FACE_LANDMARKS:face_landmarks"
output_stream: "FATIGUE:fatigue_result"
}

# ========== 3. 分心检测 ==========
node {
calculator: "DistractionDetectorCalculator"
input_stream: "FACE_LANDMARKS:face_landmarks"
output_stream: "DISTRACTION:distraction_result"
}

# ========== 4. 危险行为检测 ==========
node {
calculator: "DangerousBehaviorDetectorCalculator"
input_stream: "POSE_LANDMARKS:pose_landmarks"
input_stream: "LEFT_HAND_LANDMARKS:left_hand"
input_stream: "RIGHT_HAND_LANDMARKS:right_hand"
input_stream: "FACE_LANDMARKS:face_landmarks"
output_stream: "DANGEROUS_BEHAVIOR:dangerous_behavior"
}

# ========== 5. 综合判断 ==========
node {
calculator: "DriverBehaviorDecisionCalculator"
input_stream: "FATIGUE:fatigue_result"
input_stream: "DISTRACTION:distraction_result"
input_stream: "DANGEROUS_BEHAVIOR:dangerous_behavior"
output_stream: "BEHAVIOR_RESULT:behavior_result"
output_stream: "ALERT:alert"
}

35.2 危险行为检测 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
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
// dangerous_behavior_detector_calculator.h
#ifndef MEDIAPIPE_CALCULATORS_IMS_DANGEROUS_BEHAVIOR_DETECTOR_CALCULATOR_H_
#define MEDIAPIPE_CALCULATORS_IMS_DANGEROUS_BEHAVIOR_DETECTOR_CALCULATOR_H_

#include "mediapipe/framework/calculator_framework.h"

namespace mediapipe {

// ========== 危险行为类型枚举 ==========
enum class DangerousBehaviorType {
NONE = 0,
PHONE_CALL = 1, // 打电话
SMOKING = 2, // 抽烟
DRINKING = 3, // 喝水
EATING = 4, // 吃东西
REACHING = 5, // 伸手拿东西
TEXTING = 6, // 发短信
UNKNOWN = 7,
};

// ========== 危险行为结果 ==========
message DangerousBehaviorResult {
DangerousBehaviorType type = 1;
float confidence = 2;
bool left_hand_involved = 3;
bool right_hand_involved = 4;
uint64 timestamp_ms = 5;
}

// ========== Dangerous Behavior Detector Calculator ==========
class DangerousBehaviorDetectorCalculator : public CalculatorBase {
public:
static absl::Status GetContract(CalculatorContract* cc) {
cc->Inputs().Tag("POSE_LANDMARKS").Set<std::vector<NormalizedLandmarkList>>();
cc->Inputs().Tag("LEFT_HAND_LANDMARKS").Set<std::vector<NormalizedLandmarkList>>();
cc->Inputs().Tag("RIGHT_HAND_LANDMARKS").Set<std::vector<NormalizedLandmarkList>>();
cc->Inputs().Tag("FACE_LANDMARKS").Set<std::vector<NormalizedLandmarkList>>();
cc->Outputs().Tag("DANGEROUS_BEHAVIOR").Set<DangerousBehaviorResult>();

return absl::OkStatus();
}

absl::Status Process(CalculatorContext* cc) override {
// ========== 获取关键点 ==========
bool has_pose = !cc->Inputs().Tag("POSE_LANDMARKS").IsEmpty();
bool has_left_hand = !cc->Inputs().Tag("LEFT_HAND_LANDMARKS").IsEmpty();
bool has_right_hand = !cc->Inputs().Tag("RIGHT_HAND_LANDMARKS").IsEmpty();
bool has_face = !cc->Inputs().Tag("FACE_LANDMARKS").IsEmpty();

DangerousBehaviorType behavior_type = DangerousBehaviorType::NONE;
float confidence = 0.0f;
bool left_hand_involved = false;
bool right_hand_involved = false;

if (has_pose && has_face) {
const auto& pose =
cc->Inputs().Tag("POSE_LANDMARKS").Get<std::vector<NormalizedLandmarkList>>()[0];
const auto& face =
cc->Inputs().Tag("FACE_LANDMARKS").Get<std::vector<NormalizedLandmarkList>>()[0];

// ========== 检测打电话 ==========
if (has_left_hand || has_right_hand) {
auto result = DetectPhoneCall(cc, pose, face);
if (result.first != DangerousBehaviorType::NONE) {
behavior_type = result.first;
confidence = result.second;
left_hand_involved = has_left_hand;
right_hand_involved = has_right_hand;
}
}

// ========== 检测喝水/吃东西 ==========
if (behavior_type == DangerousBehaviorType::NONE &&
(has_left_hand || has_right_hand)) {
auto result = DetectDrinking(cc, pose, face);
if (result.first != DangerousBehaviorType::NONE) {
behavior_type = result.first;
confidence = result.second;
left_hand_involved = has_left_hand;
right_hand_involved = has_right_hand;
}
}

// ========== 检测抽烟 ==========
if (behavior_type == DangerousBehaviorType::NONE &&
(has_left_hand || has_right_hand)) {
auto result = DetectSmoking(cc, pose, face);
if (result.first != DangerousBehaviorType::NONE) {
behavior_type = result.first;
confidence = result.second;
left_hand_involved = has_left_hand;
right_hand_involved = has_right_hand;
}
}
}

// ========== 输出 ==========
DangerousBehaviorResult result;
result.set_type(behavior_type);
result.set_confidence(confidence);
result.set_left_hand_involved(left_hand_involved);
result.set_right_hand_involved(right_hand_involved);
result.set_timestamp_ms(cc->InputTimestamp().Value() / 1000);

cc->Outputs().Tag("DANGEROUS_BEHAVIOR").AddPacket(
MakePacket<DangerousBehaviorResult>(result).At(cc->InputTimestamp()));

if (behavior_type != DangerousBehaviorType::NONE) {
LOG(INFO) << "Dangerous behavior detected: " << static_cast<int>(behavior_type);
}

return absl::OkStatus();
}

private:
std::pair<DangerousBehaviorType, float> DetectPhoneCall(
CalculatorContext* cc,
const NormalizedLandmarkList& pose,
const NormalizedLandmarkList& face) {

// 检查手是否在耳朵附近
float left_ear_x = face.landmark(7).x(); // 左耳
float left_ear_y = face.landmark(7).y();
float right_ear_x = face.landmark(8).x(); // 右耳
float right_ear_y = face.landmark(8).y();

if (!cc->Inputs().Tag("LEFT_HAND_LANDMARKS").IsEmpty()) {
const auto& left_hand =
cc->Inputs().Tag("LEFT_HAND_LANDMARKS").Get<std::vector<NormalizedLandmarkList>>()[0];
float wrist_x = left_hand.landmark(0).x();
float wrist_y = left_hand.landmark(0).y();

float dist_left = std::sqrt(std::pow(wrist_x - left_ear_x, 2) +
std::pow(wrist_y - left_ear_y, 2));

if (dist_left < 0.15f) {
return {DangerousBehaviorType::PHONE_CALL, 0.8f};
}
}

if (!cc->Inputs().Tag("RIGHT_HAND_LANDMARKS").IsEmpty()) {
const auto& right_hand =
cc->Inputs().Tag("RIGHT_HAND_LANDMARKS").Get<std::vector<NormalizedLandmarkList>>()[0];
float wrist_x = right_hand.landmark(0).x();
float wrist_y = right_hand.landmark(0).y();

float dist_right = std::sqrt(std::pow(wrist_x - right_ear_x, 2) +
std::pow(wrist_y - right_ear_y, 2));

if (dist_right < 0.15f) {
return {DangerousBehaviorType::PHONE_CALL, 0.8f};
}
}

return {DangerousBehaviorType::NONE, 0.0f};
}

std::pair<DangerousBehaviorType, float> DetectDrinking(
CalculatorContext* cc,
const NormalizedLandmarkList& pose,
const NormalizedLandmarkList& face) {

// 检查手是否在嘴附近(喝水/吃东西)
float mouth_x = (face.landmark(61).x() + face.landmark(291).x()) / 2.0f;
float mouth_y = (face.landmark(61).y() + face.landmark(291).y()) / 2.0f;

// 实现类似 DetectPhoneCall 的逻辑
// ...

return {DangerousBehaviorType::NONE, 0.0f};
}

std::pair<DangerousBehaviorType, float> DetectSmoking(
CalculatorContext* cc,
const NormalizedLandmarkList& pose,
const NormalizedLandmarkList& face) {

// 检查手是否在嘴附近且手指姿势符合抽烟
// ...

return {DangerousBehaviorType::NONE, 0.0f};
}
};

REGISTER_CALCULATOR(DangerousBehaviorDetectorCalculator);

} // namespace mediapipe

#endif

三十六、总结

要点 说明
融合 Face + Pose + Hands
ROI 共享 从 Pose 提取 Face/Hand ROIs
性能 ~15ms (GPU)
IMS 应用 驾驶员行为分析、危险行为检测

下篇预告

MediaPipe 系列 34-40:其他内置 Solution 概览

快速回顾 Object Detection、Image Segmentation、Iris Detection 等其他内置 Solution。


参考资料

  1. Google AI Edge. Holistic
  2. MediaPipe. Holistic Tracking

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


MediaPipe 系列 33:Holistic——全身感知融合完整指南
https://dapalm.com/2026/03/13/MediaPipe系列33-Holistic:全身感知融合/
作者
Mars
发布于
2026年3月13日
许可协议