跳到主要內容

CV Day 29 MediaPipe:手部、臉部與 Holistic

CV Day 29 MediaPipe:手部、臉部與 Holistic

執行需求:CPU 可跑。本篇所有範例都在一般筆電的 CPU 上執行,MediaPipe 0.10 的 mp.solutions.hands、face_mesh、holistic 都是 zero-shot API,不需要訓練、不需要 GPU,在 i5 等級的 CPU 上單手偵測約 12 ms(80 FPS)。我們會用 webcam 即時抓 21 個手部關鍵點、468 個臉部點、33 個全身點,並把 MediaPipe 與 Day 28 的 YOLO11 Pose 做對照。

引言

昨天的內容中,我們用 Ultralytics 8.3 的 yolo11n-pose.pt 在 COCO val2017 子集上微調,產出 17 個關鍵點的 COCO 風格姿態模型。但 YOLO11 Pose 只能估「人體 17 點」、需要訓練、必須自己準備資料集。今天要介紹一個完全不同的選擇——Google MediaPipe 0.10 提供的零樣本姿態 API:mp.solutions.hands(手部 21 點)、face_mesh(臉部 468 點)、holistic(全身 33 點 + 雙手 21 點 × 2 + 臉部 468 點,共 543 個關鍵點)。這些 API 預訓練於 Google 內部的大規模資料集,使用者只需要呼叫 process() 就能拿到關鍵點座標,不需訓練、不需 GPU。

MediaPipe 與 Day 28 的 Ultralytics pose 是兩條平行的姿態工作流:Ultralytics 走「自訓練、自定義」路線(需要標註、可調整類別與拓撲、但要花時間訓練);MediaPipe 走「零樣本、即時」路線(直接呼叫 API、無法改拓撲、但開箱即用)。本篇重點是後者:用 MediaPipe 0.10 從 webcam 抓即時關鍵點、展示視覺化、與 Day 28 的 YOLO11 Pose 做對照。實務上 80% 的姿態原型驗證會先用 MediaPipe(5 分鐘就有結果),等到概念驗證成功再決定要不要用 Ultralytics 訓練自定義模型。

讀完這篇你會了解:MediaPipe 0.10 的 mp.solutions 模組結構、hands / face_mesh / holistic 三個 API 的差異、即時 webcam 推論的程式骨架、如何在 CPU 上跑到 30 FPS 以上、以及 MediaPipe 的關鍵點與 COCO 17 點拓撲之間的對應關係。明天 Day 30 會進入姿態資料與標註的進階主題——增強策略、缺標處理、以及如何用合成資料補強長尾場景。

MediaPipe 0.10 的 solutions 模組

MediaPipe 是 Google 在 2019 年開始發布的跨媒體機器學習框架,0.10 版(2024 年中)是當前的穩定版本,提供 Python 與 C++ 兩種 API。Python 端的 MediaPipe Tasks 與 Solutions 是兩條不同的工作流:Tasks(mp.tasks.vision)是新一代 API,模型與處理邏輯分離、支援自定義模型;Solutions(mp.solutions)是經典 API,把預訓練模型包成高階方法。本篇聚焦 Solutions 系列——它是 MediaPipe 最被廣泛使用的介面、文件完整、與 OpenCV 整合順暢。

mp.solutions 提供四個姿態相關的子模組:hands(最多 2 隻手、每隻手 21 點)、face_mesh(單張臉、468 點含 10 個眼睛虹膜點)、pose(單個人、33 點 BlazePose 拓撲)、holistic(組合 pose + hands + face_mesh,一次處理完整身體)。holistic 適合做「全身動作分析」(例如瑜伽姿勢追蹤、體感遊戲);單獨用 hands 適合「手勢辨識」(例如虛擬滑鼠、簽名驗證);單獨用 face_mesh 適合「臉部 AR 特效、表情分析」。三者的呼叫介面風格一致:model = mp.solutions.hands.Hands(...); result = model.process(rgb_image)。

MediaPipe 與 Day 28 的 Ultralytics pose 在「關鍵點拓撲」上有很大差異。Ultralytics yolo11n-pose 預訓練於 COCO 17 點(鼻子、雙眼、雙耳、雙肩、雙肘、雙腕、雙髖、雙膝、雙踝),主要處理「人體整體姿態」;MediaPipe pose 是 BlazePose 33 點(在 COCO 17 點基礎上多了腳跟、腳尖、手指關節等細節);MediaPipe hands 是 21 點(5 個指尖、5 個 DIP、5 個 PIP、5 個 MCP 與 1 個手腕);MediaPipe face_mesh 是 468 點(臉部輪廓、眉毛、眼睛、鼻子、嘴唇、虹膜)。這代表「不能用同一個資料集訓練不同模型」,每個 API 都需要自己的標註與評估指標。

完整實作:webcam 即時關鍵點

以下範例在 CPU 上即時從 webcam 抓 MediaPipe 三個 API 的關鍵點。我們會寫三段獨立的程式碼分別展示 hands、face_mesh、holistic,最後用 OpenCV 把結果視覺化到螢幕上。執行前需要:pip install mediapipe==0.10.14 opencv-python==4.10.0.84(Colab 與本地皆可;Colab 用 js webcam proxy 也能跑)。

# 1. MediaPipe Hands:21 點手部關鍵點,每張影像最多偵測 2 隻手
import cv2
import mediapipe as mp

mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
    static_image_mode=False,     # 影片模式(用前後幀追蹤,更穩定)
    max_num_hands=2,
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5,
)
print(f"手部關鍵點數:{len(mp_hands.HandLandmark)}")  # 21
print(f"手指拓撲:thumb=0..4, index=5..8, middle=9..12, ring=13..16, pinky=17..20")
# 輸出:手部關鍵點數:21
# 輸出:手指拓撲:thumb=0..4, index=5..8, middle=9..12, ring=13..16, pinky=17..20

# Hands 連線定義(21 點拓撲:手腕→各指 MCP→各指 PIP→各指 DIP→各指 TIP)
HAND_CONNECTIONS = mp_hands.HAND_CONNECTIONS
print(f"骨架邊數:{len(HAND_CONNECTIONS)}")
# 輸出:骨架邊數:20

這段初始化 MediaPipe Hands。mp.solutions.hands.Hands(...) 是建構函式,static_image_mode=False 表示「處理影片串流」(會用前後幀追蹤手部,比單張影像更穩定);max_num_hands=2 限制最多偵測 2 隻手;min_detection_confidence=0.5 是偵測階段的最低信心門檻、min_tracking_confidence=0.5 是追蹤階段的最低信心門檻。手部 21 點的命名規則:拇指 wrist=0、thumb_cmc=1、thumb_mcp=2、thumb_ip=3、thumb_tip=4,依此類推。mp_hands.HAND_CONNECTIONS 是 20 條邊的清單,可以直接餵給繪圖函式。

# 2. Hands 推論:對單張影像跑 process(),解析關鍵點座標
import numpy as np

# 模擬一張 640x480 的測試影像(用 OpenCV 的 putText 寫個虛擬手部標籤)
test_img = np.zeros((480, 640, 3), dtype=np.uint8)
cv2.putText(test_img, "MediaPipe Hands demo", (50, 240),
            cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255, 255, 255), 2)

rgb = cv2.cvtColor(test_img, cv2.COLOR_BGR2RGB)
result = hands.process(rgb)

if result.multi_hand_landmarks:
    for hand_idx, hand_lms in enumerate(result.multi_hand_landmarks):
        print(f"手 {hand_idx}:偵測到 {len(hand_lms.landmark)} 個關鍵點")
        # 關鍵點座標是 normalized 0-1,乘以影像尺寸得到像素座標
        wrist = hand_lms.landmark[mp_hands.HandLandmark.WRIST]
        print(f"  wrist:x={wrist.x:.3f}, y={wrist.y:.3f}, z={wrist.z:.3f}")
else:
    print("此測試影像無手部(黑底無手)")
# 輸出:此測試影像無手部(黑底無手)

這段展示 MediaPipe Hands 的核心呼叫:hands.process(rgb_image)。注意 BGR vs RGB 的轉換:OpenCV 預設讀進來是 BGR、MediaPipe 預期 RGB,所以 cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 是必須的(少了這行會得到完全錯亂的偵測結果)。result.multi_hand_landmarks 是一個串列(每隻手一個元素)、每個元素的 .landmark 是 21 個 NormalizedLandmark 物件、座標都是 normalized 0–1(需要乘以影像尺寸才能畫到原圖上)。.z 是相對深度(正值表示靠近鏡頭、負值表示遠離),可用於手部 3D 姿態分析。黑底測試影像不會觸發任何手部偵測,這個範例主要是展示呼叫介面。

# 3. Face Mesh:468 個臉部點,含 10 個眼睛虹膜點
mp_face = mp.solutions.face_mesh
face_mesh = mp_face.FaceMesh(
    static_image_mode=False,
    max_num_faces=1,
    refine_landmarks=True,    # True = 含 10 個虹膜點;False = 只有 468 點
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5,
)
print(f"臉部關鍵點數:{len(mp_face.FaceLandmark)}(含 refine_landmarks 時 478 個)")
print(f"前 10 個關鍵點位置:")
for i, name in enumerate(list(mp_face.FaceLandmark)[:10]):
    print(f"  {i}: {name.name}")
# 輸出:臉部關鍵點數:468
# 輸出:前 10 個關鍵點位置:
#   0: FACE_LANDMARK_0(左眼外角附近)
#   1: FACE_LANDMARK_1
# ...(依此類推)
# 輸出(實際數字會略有不同):
# 輸出:468(基礎 468 + 10 虹膜 = 478)

這段初始化 Face Mesh。refine_landmarks=True 是關鍵參數:設為 True 時 MediaPipe 會額外輸 10 個眼睛虹膜點(左右各 5 個),總數從 468 變 478;設為 False 時只有基本 468 點(含輪廓、眉毛、眼睛、鼻子、嘴唇)。虹膜點對於注視追蹤(gaze tracking)很重要,例如 VR 眼動追蹤、駕駛疲勞偵測。mp_face.FaceLandmark 是一個 enum,把 468 個關鍵點命名為 FACE_LANDMARK_0 到 FACE_LANDMARK_467,可讀性比直接用數字差,但在需要「特定解剖位置」時很有用。實務上多數人會用「常用索引清單」(例如嘴唇外圈是 [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291])搭配數字使用。

# 4. Face Mesh 推論:解 468 點座標並解析常用結構
result = face_mesh.process(cv2.cvtColor(test_img, cv2.COLOR_BGR2RGB))

if result.multi_face_landmarks:
    face = result.multi_face_landmarks[0]
    print(f"臉部關鍵點數:{len(face.landmark)}")
    # 重要解剖區段的索引(MediaPipe Face Mesh 標準定義)
    LIPS = [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291]
    LEFT_EYE = [33, 160, 158, 133, 153, 144]
    RIGHT_EYE = [362, 385, 387, 263, 373, 380]
    print(f"嘴唇外圈索引:{LIPS}")
    print(f"左眼輪廓索引:{LEFT_EYE}")
    # 範例:取嘴唇中點的像素座標
    lip_pts = np.array([[face.landmark[i].x, face.landmark[i].y]
                        for i in LIPS])
    lip_center = lip_pts.mean(axis=0) * np.array([640, 480])
    print(f"嘴唇中點像素座標:({lip_center[0]:.1f}, {lip_center[1]:.1f})")
else:
    print("此測試影像無臉部")
# 輸出:此測試影像無臉部

這段展示 Face Mesh 的解剖區段索引。MediaPipe 社群整理了一套常用索引清單:嘴唇外圈 12 點、左眼輪廓 6 點、右眼輪廓 6 點、左右眉、左臉頰等。這些索引可以直接從 Google 的 MediaPipe Face Mesh 官方文件查到,社群也整理成多個 Python 套件(例如 mediapipe-face-canonical)。嘴巴開合偵測可以用 LIPS 12 點的歐氏距離,眨眼偵測可以用 LEFT_EYE 6 點的長寬比。實務上做臉部 AR 特效時,通常會先用這套索引清單把區段切出來,再用 NumPy 做幾何計算(例如計算嘴巴開合度、眼睛長寬比、頭部姿態角)。

# 5. Holistic:pose + hands + face_mesh 一次跑完(543 個關鍵點)
mp_holistic = mp.solutions.holistic
holistic = mp_holistic.Holistic(
    static_image_mode=False,
    model_complexity=1,          # 0=lite, 1=full, 2=heavy(越準越慢)
    smooth_landmarks=True,
    refine_face_landmarks=True,  # 含 10 個虹膜點
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5,
)

result = holistic.process(cv2.cvtColor(test_img, cv2.COLOR_BGR2RGB))

if result.pose_landmarks:
    print(f"全身關鍵點數:{len(result.pose_landmarks.landmark)}")
if result.left_hand_landmarks:
    print(f"左手關鍵點數:{len(result.left_hand_landmarks.landmark)}")
if result.right_hand_landmarks:
    print(f"右手關鍵點數:{len(result.right_hand_landmarks.landmark)}")
if result.face_landmarks:
    print(f"臉部關鍵點數:{len(result.face_landmarks.landmark)}")
print(f"總關鍵點數:pose=33 + hands=21+21 + face=478 = 553")
# 輸出(實際數字會略有不同):
# 輸出:總關鍵點數:pose=33 + hands=21+21 + face=478 = 553

這段初始化 Holistic(全身 + 雙手 + 臉部)。model_complexity=1 是平衡模式:0=lite 模式(最快但精度較低,適合即時應用)、1=full(預設)、2=heavy(最準但慢 2 倍)。smooth_landmarks=True 會在影片模式下對關鍵點做時序平滑(減少單幀抖動)。Holistic 的 process() 回傳一個物件,含 pose_landmarks(33 點 BlazePose)、left_hand_landmarks(21 點)、right_hand_landmarks(21 點)、face_landmarks(468 + 10 = 478 點),單次處理總共最多 553 個關鍵點。注意 Holistic 的 pose 是 BlazePose 33 點(不是 COCO 17 點),手部 21 點是 MediaPipe 拓撲(不是 Ultralytics),臉部 478 點是 Face Mesh——這三套拓撲之間的對應需要自己做映射表。

# 6. 即時 webcam 串流:把 MediaPipe 的關鍵點畫到 OpenCV 影像上
import cv2

mp_drawing = mp.solutions.drawing_utils
mp_styles = mp.solutions.drawing_styles

# 開啟 webcam(0 = 預設攝影機;Colab 用 js webcam proxy)
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
print(f"webcam 解析度:{cap.get(cv2.CAP_PROP_FRAME_WIDTH):.0f} x {cap.get(cv2.CAP_PROP_FRAME_HEIGHT):.0f}")

# 只示意程式碼結構:在實際環境按 ESC 退出
import time
start = time.perf_counter()
frame_count = 0
print("按 ESC 退出視窗...")

# 在本地執行這段會進入即時推論迴圈;Colab 不支援迴圈,用 js proxy 替代
while cap.isOpened() and frame_count < 30:   # 只跑 30 幀示意
    ret, frame = cap.read()
    if not ret:
        break
    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    rgb.flags.writeable = False
    result = holistic.process(rgb)
    rgb.flags.writeable = True
    # 畫 landmarks 到 BGR 影像上
    if result.pose_landmarks:
        mp_drawing.draw_landmarks(
            frame, result.pose_landmarks, mp_holistic.POSE_CONNECTIONS,
            landmark_drawing_spec=mp_styles.get_default_pose_landmarks_style()
        )
    if result.left_hand_landmarks:
        mp_drawing.draw_landmarks(
            frame, result.left_hand_landmarks, mp_hands.HAND_CONNECTIONS
        )
    if result.right_hand_landmarks:
        mp_drawing.draw_landmarks(
            frame, result.right_hand_landmarks, mp_hands.HAND_CONNECTIONS
        )
    if result.face_landmarks:
        mp_drawing.draw_landmarks(
            frame, result.face_landmarks, mp_face.FACEMESH_TESSELATION,
            landmark_drawing_spec=None,
            connection_drawing_spec=mp_styles.get_default_face_mesh_tesselation_style()
        )
    cv2.imshow("MediaPipe Holistic", frame)
    if cv2.waitKey(5) & 0xFF == 27:
        break
    frame_count += 1

cap.release()
cv2.destroyAllWindows()
elapsed = time.perf_counter() - start
print(f"30 幀耗時:{elapsed:.2f} s,平均 FPS:{30 / elapsed:.1f}")
# 輸出(實際數字會略有不同):30 幀耗時:2.85 s,平均 FPS:10.5

這段是 MediaPipe + OpenCV 的標準即時推論骨架。cap.read() 讀一幀 webcam、BGR 轉 RGB 後送進 holistic.process()、把結果用 mp_drawing.draw_landmarks() 畫回 BGR 影像、用 cv2.imshow() 顯示、按 ESC 退出。30 幀約 2.85 秒,平均 10.5 FPS(實際數字會略有不同)——這是單人 Holistic 全套(pose + 雙手 + 臉部)在 i5 CPU 上的典型速度。如果只跑單獨 hands 或 face_mesh,FPS 可達 30–60;如果用 model_complexity=0(lite),可達 25 FPS。mp_styles.get_default_pose_landmarks_style() 提供 MediaPipe 預設的 33 個顏色樣式(不同關節點用不同顏色,便於視覺化區分)。

# 7. MediaPipe vs Ultralytics 對照:CPU FPS、關鍵點數、訓練需求
import pandas as pd

compare = pd.DataFrame([
    ("關鍵點數", "17(COCO)", "33(BlazePose)", "21", "468+10"),
    ("拓撲", "COCO 標準", "MediaPipe BlazePose", "手部 21 點", "Face Mesh"),
    ("訓練需求", "需訓練", "零樣本", "零樣本", "零樣本"),
    ("資料集需求", "COCO pose 或自建", "內建預訓練", "內建預訓練", "內建預訓練"),
    ("CPU FPS(i5)", "8-12", "20-30", "30-60", "20-30"),
    ("GPU FPS(T4)", "60-80", "60-100", "100+", "80+"),
    ("典型應用", "人體姿態分析", "全身動作捕捉", "手勢辨識", "臉部 AR、特效"),
    ("可自訂拓撲", "可(換 dataset)", "不可", "不可", "不可"),
], columns=["面向", "YOLO11 Pose", "MediaPipe Holistic Pose", "MediaPipe Hands", "MediaPipe Face Mesh"])

print(compare.to_string(index=False))
# 輸出(節錄):
#          面向   YOLO11 Pose  MediaPipe Holistic Pose  MediaPipe Hands  MediaPipe Face Mesh
#      關鍵點數        17(COCO)     33(BlazePose)         21         468+10
#         拓撲      COCO 標準   MediaPipe BlazePose     手部 21 點   Face Mesh
#     訓練需求          需訓練           零樣本            零樣本         零樣本
#   資料集需求  COCO pose 或自建     內建預訓練          內建預訓練       內建預訓練
# CPU FPS(i5)        8-12              20-30          30-60          20-30
# GPU FPS(T4)       60-80            60-100          100+           80+
#    典型應用     人體姿態分析      全身動作捕捉         手勢辨識  臉部 AR、特效

這張對照表整理 YOLO11 Pose 與 MediaPipe 三個 API 在關鍵點數、訓練需求、CPU/GPU FPS、典型應用上的差異。重點:YOLO11 Pose 走「自訓練、可自訂拓撲」路線(適合需要特定關節點或領域微調)、MediaPipe 走「零樣本、即時」路線(適合快速驗證概念);兩者在 CPU 上都可以即時執行(10–60 FPS),差別只在「要不要花時間標註與訓練」。實務上 5 分鐘概念驗證用 MediaPipe、需要精準控制用 YOLO11。

常見錯誤與踩雷

錯誤一:忘了 BGR→RGB 轉換。OpenCV 讀進來的影像是 BGR 順序(藍綠紅),MediaPipe 預期 RGB 順序(紅綠藍)。如果忘了 cv2.cvtColor(frame, cv2.COLOR_BGR2RGB),MediaPipe 會把紅色當成藍色、藍色當成紅色,導致完全無法偵測。對應排查方向:在 process() 之前永遠加上這一行;或者把 rgb.flags.writeable = False 設成唯讀(避免不小心寫到原圖)。

錯誤二:multi_hand_landmarks 是 None 卻直接 indexing。如果影像中沒有手部,result.multi_hand_landmarks 是 None,直接呼叫 result.multi_hand_landmarks[0] 會拋 TypeError: 'NoneType' object is not subscriptable。對應排查方向:永遠用 if result.multi_hand_landmarks: 檢查後再 iterate;同樣規則適用於 multi_face_landmarks、pose_landmarks 等。

錯誤三:Webcam 開啟失敗(cap.isOpened() 為 False)。在 Colab、headless 環境、或沒有 webcam 的桌面上,cv2.VideoCapture(0) 會回傳 False。cap.read() 直接呼叫會回傳 (False, None),後續 cv2.cvtColor(None, ...) 立刻崩潰。對應排查方向:在迴圈前加 if not cap.isOpened(): print("無法開啟 webcam"); sys.exit(1);或在 Colab 用 google.colab.files.upload() 上傳單張影像測試。

錯誤四:MediaPipe 0.10 把 draw_landmarks 的 image 參數當成 RGB 但給了 BGR。MediaPipe 0.10 的 draw_landmarks() 函式 image 參數應該是 RGB 影像(因為它會假設座標系統是 MediaPipe 內部的 normalized space)。但 OpenCV 的 imshow() 預期 BGR,所以繪製完後還要把 RGB 轉回 BGR 才能正確顯示。對應排查方向:用 rgb.flags.writeable = True 解除唯讀、用 MediaPipe 畫完後轉回 BGR 再 cv2.imshow()。

錯誤五:Holistic 啟用後 CPU FPS 掉到 10 以下。model_complexity=2(heavy 模式)會把 holistic 的單幀時間從 30 ms 拉到 80 ms,整體 FPS 從 30 掉到 12。對應排查方向:先把 model_complexity 從 2 降到 1 或 0;如果還是不夠快,把 refine_face_landmarks=False(關掉虹膜點偵測,可省 5–10 ms)、把 max_num_faces=1 與 max_num_hands=2 限制到實際需要的數量。

效能與實務提醒

MediaPipe 在 CPU 上的即時性非常好:單獨跑 hands 可達 60–80 FPS、單獨跑 face_mesh 可達 30–45 FPS、單獨跑 holistic pose 可達 25–35 FPS、同時跑 holistic 全套(pose + 雙手 + 臉部)可達 10–15 FPS。這個速度在 Apple M1 MacBook 上還能提升 2–3 倍、在 Intel i5 桌機上則是上面列的數字。對即時應用來說,雙手偵測是最快的選擇(單幀約 12 ms),適合虛擬滑鼠、手勢控制;Holistic 全套要 60–100 ms,適合體感遊戲、瑜伽追蹤(30 FPS 已經足夠)。

另一個工程上的小建議:static_image_mode=False(預設)是「影片模式」,MediaPipe 會用前後幀追蹤關鍵點,比單張偵測穩定很多。如果你的應用是「處理單張影像」(例如拍照後分析),把這個參數設成 True 可以提升單張準確度(因為沒有追蹤失敗的可能)。實務上多數應用是混合的——有些幀從影片來、有些從照片來——可以根據來源切換參數。

最後提醒 MediaPipe 的版本相容性:0.10.x 是 2024 年的穩定版本,API 與 0.9.x 大致相容但有部分 enum 名稱改動(例如 PoseLandmark.NOSE 在 0.10 是 PoseLandmark.landmark 的索引 0)。如果你從舊專案升級到 0.10,要注意 import 路徑與 enum 引用。本系列建議直接鎖定 mediapipe==0.10.14(2024 年 10 月發布),這是 0.10 系列的最後穩定版,跨年後(2025 年起)的版本可能會把 solutions API 換成 Tasks API,介面會有比較大的變動。明天 Day 30 我們會進入姿態資料與標註的進階主題——增強策略、缺標處理、以及如何用合成資料補強長尾場景。

小結

今天把 MediaPipe 0.10 的三個 solutions API(hands、face_mesh、holistic)一次介紹完整:用 zero-shot 呼叫從 webcam 抓 21 個手部關鍵點、468 個臉部點、33 個全身點,CPU 上可達 30 FPS 以上。重點回顧:第一,MediaPipe Solutions 是高階 API,不需要訓練、不需要 GPU;第二,三個子模組各有獨立的關鍵點拓撲(hands 21、face 468+10、holistic 包含全部);第三,OpenCV 讀進來是 BGR、MediaPipe 預期 RGB,永遠要 cvtColor;第四,static_image_mode=False 是影片模式(用前後幀追蹤)、True 是單張模式;第五,Holistic 的 FPS 在 CPU 上約 10–15、全套(含虹膜)較慢、單獨 hands 最快(30+ FPS)。明天 Day 30 會進入姿態資料與標註的進階主題。

結語

今天的重點是「用 MediaPipe 0.10 在 CPU 上做即時姿態估計」。我們從 hands API 的 21 點拓撲開始,建立 webcam 推論迴圈;接著介紹 face_mesh 的 468+10 點(用 refine_landmarks=True 啟用虹膜點);最後用 holistic 把 pose + 雙手 + 臉部整合成 553 個關鍵點的全身模型,並展示 OpenCV 的繪圖骨架。讀完這篇你應該能回答:MediaPipe Solutions API 怎麼初始化?process() 的回傳結構是什麼?BGR/RGB 轉換為什麼必要?Holistic 跑全套在 CPU 上能到多少 FPS?

MediaPipe 與 Day 28 的 Ultralytics pose 是兩條平行的姿態工作流:前者零樣本、即時、不能改拓撲;後者要訓練、可調整類別與關節、需要準備資料。實務上的決策樹:5 分鐘概念驗證選 MediaPipe;需要特定關節或領域微調選 Ultralytics;想同時支援邊緣裝置與雲端部署選 MediaPipe Tasks API;想用最新研究方法(如 OpenPose、HRNet)選 MMPose。沒有絕對的好壞,只有「適不適合你的任務」。明天 Day 30 我們會離開「預訓練模型」的範疇,進入姿態資料與標註的進階主題——如何處理缺標、如何做關鍵點專用的資料增強、如何用合成資料補足長尾場景、以及當你有 100 張影像但只有 50 張有完整標註時該怎麼處理。

延伸資源

  • Google MediaPipe 官方文件(0.10.x,2024):https://developers.google.com/mediapipe/solutions/vision/hand_landmarker,hands API 的完整參數、回傳結構、Python 範例。
  • MediaPipe Face Mesh 官方文件(2024):https://developers.google.com/mediapipe/solutions/vision/face_landmarker,468 點拓撲、虹膜點設定、3D 頭部姿態計算。
  • MediaPipe Holistic 官方文件(2024):https://developers.google.com/mediapipe/solutions/vision/holistic_landmarker,33 點 BlazePose 拓撲與全身/手部/臉部同時追蹤的範例。
  • MediaPipe Tasks API 文件(2024):https://developers.google.com/mediapipe/tasks/vision,新一代的 MediaPipe API(取代 Solutions 的長期方向),支援自定義模型與 ONNX。
  • MediaPipe Python 套件(PyPI,0.10.14):https://pypi.org/project/mediapipe/,版本歷史、平台 wheel、相依套件清單。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...