2026年8月7日に、いまのMediaPipeに合わせて全面的に書き直しました
もともと3本に分かれていた記事を1本にまとめ、本文を現在の書き方(Tasks API)に置き換えました。以前ここに載せていた mp.solutions の書き方は、MediaPipe 0.10.31(2025年12月)で削除されています。
→ いつ何が消えたのか、モデルファイルの入手先の一覧は MediaPipeの mp.solutions が消えた〜Tasks APIへの移行と .taskモデルファイル完全ガイド〜 にまとめています。
この記事について
アバターを動かしてみたい——というところから、まず自分の顔や手の位置をパソコンに認識させるところを試しました。使ったのはGoogleのMediaPipeです。
この記事では、顔・手・姿勢のランドマーク検出を、いまの書き方で一通り動かすところまでをまとめます。実際に取り出した座標は、あとで UnityへUDPで送る ところまで繋げています😊
MediaPipeとは
Googleが公開しているオープンソースの機械学習フレームワークです。学習済みのモデルが用意されているので、機械学習の知識がなくても、数十行で顔や手の位置を取れるのが特徴です。
ランドマークというのは「目印の点」のことです。顔なら輪郭・目・鼻・口に沿って点が並び、それぞれの点の位置が数値で返ってきます。この点の集まりを使って、アバターの表情を動かしたり、手の形を判定したりします。
MediaPipeでできること
| できること | 中身 | 取れる点の数 |
|---|---|---|
| Face Landmark | 顔の輪郭・目・鼻・口の位置 | 478点 |
| Hand Landmark | 手の関節の位置。左右の判定つき | 片手 21点 |
| Pose Landmark | 全身の姿勢 | 33点 |
| Face Detection | 顔がどこにあるかだけを四角で返す | — |
| Object Detection | 画像の中の物体を検出する | — |
| Image Segmentation | 人物と背景を切り分ける(背景の差し替えなど) | — |
アバターを動かすなら、使うのは主に上の3つです。顔で表情、手で指の動き、姿勢で体全体、という分担になります。
環境を用意する
pip install mediapipe opencv-python
Pythonのパッケージを入れるだけです。カメラ映像を扱うので、あわせて OpenCV も入れておきます。
⚠ Pythonの版に注意
1.0.0時点の MediaPipe は Python 3.9〜3.12 に対応しています。またIntel Mac向けの配布は 0.10.31 で終了しました。うまく入らないときは、まず Python の版を確かめてみてください。
★モデルファイルを自分で渡す
ここが、以前と大きく変わったところです。使うモデルのファイルを、自分で用意して渡します。
昔は、必要になった時点でMediaPipeが勝手にダウンロードしてくれていました。いまはパッケージにモデルが同梱されておらず、face_landmarker.task のようなファイルを自分で置いて、その場所を指定します。
| 用途 | モデルファイル | 大きさ |
|---|---|---|
| 顔 | face_landmarker.task | 約3.8MB |
| 手 | hand_landmarker.task | 約7.5MB |
| 姿勢 | pose_landmarker_full.task | 約9MB |
入手先の一覧と、どのモデルを選べばいいかは 別記事 にまとめてあります。中身は複数の学習済みモデルをzipでまとめたものです。
顔のランドマークを取る
import mediapipe as mp
from mediapipe.tasks import python as mp_python
from mediapipe.tasks.python import vision
# 1. 画像を読み込む(専用の型に変換する)
img = mp.Image.create_from_file("portrait.jpg")
# 2. どのモデルを使うかを指定する(★ここが旧APIとの最大の違い)
options = vision.FaceLandmarkerOptions(
base_options=mp_python.BaseOptions(
model_asset_path="face_landmarker.task"),
output_face_blendshapes=True, # 表情の度合いも取る
num_faces=1)
# 3. 検出する
with vision.FaceLandmarker.create_from_options(options) as detector:
result = detector.detect(img)
# 4. 結果を取り出す(1人目の478点)
for lm in result.face_landmarks[0]:
print(lm.x, lm.y, lm.z)
流れは「画像を読む → どのモデルを使うか決める → 検出する → 結果を取り出す」の4段です。
返ってくる座標は0〜1に正規化されています。画像の左上が (0, 0)、右下が (1, 1) です。画素の位置にしたいときは、幅と高さを掛けます。z は奥行きで、こちらはおおよその相対値です。
表情の度合いを取る
顔については、点の位置だけでなく「どの表情をどれくらいしているか」も取れます。52種類あり、アバターの表情を動かすときはこちらのほうが扱いやすいことも多いです。
# 表情の度合い(52種類)を強い順に見る
bs = result.face_blendshapes[0]
for c in sorted(bs, key=lambda c: -c.score)[:5]:
print(f"{c.category_name:28s} {c.score:.3f}")
# 出力例
# eyeBlinkLeft 0.412
# mouthSmileLeft 0.311
# browInnerUp 0.208
eyeBlinkLeft(左目のまばたき)や mouthSmileLeft(左の口角)といった名前で、0〜1の強さが返ります。アバターのモデル側に同じ名前の表情が用意されていれば、そのまま渡せます。
手のランドマークを取る
img = mp.Image.create_from_file("hands.jpg")
options = vision.HandLandmarkerOptions(
base_options=mp_python.BaseOptions(
model_asset_path="hand_landmarker.task"),
num_hands=2)
with vision.HandLandmarker.create_from_options(options) as detector:
result = detector.detect(img)
for i, hand in enumerate(result.hand_landmarks):
label = result.handedness[i][0].category_name # Left / Right
print(label, len(hand), "点")
手は左右の判定つきで返ります。num_hands=2 にすれば両手を同時に取れます。21点の内訳は、手首1点+各指4点×5本です。
姿勢のランドマークを取る
img = mp.Image.create_from_file("pose.jpg")
options = vision.PoseLandmarkerOptions(
base_options=mp_python.BaseOptions(
model_asset_path="pose_landmarker_full.task"),
num_poses=1)
with vision.PoseLandmarker.create_from_options(options) as detector:
result = detector.detect(img)
for lm in result.pose_landmarks[0]:
print(lm.x, lm.y, lm.z)
姿勢は33点です。顔まわりも含まれますが、細かさは顔専用のモデルには及びません。全身の動きを取りたいときは姿勢、表情を取りたいときは顔、という使い分けになります。
Webカメラで動かす
ここまでは静止画でした。カメラ映像で動かすときは、動画向けの動かし方に切り替えます。
import cv2
import mediapipe as mp
from mediapipe.tasks import python as mp_python
from mediapipe.tasks.python import vision
options = vision.FaceLandmarkerOptions(
base_options=mp_python.BaseOptions(model_asset_path="face_landmarker.task"),
running_mode=vision.RunningMode.VIDEO, # ★動画向けの動かし方
num_faces=1)
cap = cv2.VideoCapture(0)
with vision.FaceLandmarker.create_from_options(options) as detector:
t = 0
while cap.isOpened():
ok, frame = cap.read()
if not ok:
break
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb)
result = detector.detect_for_video(img, t) # 時刻(ミリ秒)を渡す
t += 33
if result.face_landmarks:
h, w = frame.shape[:2]
for lm in result.face_landmarks[0]:
cv2.circle(frame, (int(lm.x * w), int(lm.y * h)), 1, (0, 255, 0), -1)
cv2.imshow("MediaPipe", frame)
if cv2.waitKey(1) == 27: # Escで終了
break
cap.release()
cv2.destroyAllWindows()
静止画のときと違うのは2か所です。
running_modeに VIDEO を指定するdetectではなくdetect_for_videoを使い、時刻(ミリ秒)を渡す
時刻を渡すのは、MediaPipeが前のフレームの結果を使って安定させるためです。ここを毎回0にすると、検出が細かく揺れます。
旧APIとの書き方の違い
ネット上には、まだ古い書き方のコードがたくさん残っています。見分けられるように並べておきます。
以前(0.10.31より前):
# ⚠これは 0.10.31(2025年12月)で削除された書き方です
import mediapipe as mp
with mp.solutions.pose.Pose(static_image_mode=True) as pose:
result = pose.process(image) # モデルは自動でダウンロードされていた
いま:前述のとおり、モデルのファイル名を自分で渡します。
| 以前 | いま | |
|---|---|---|
| 入口 | mp.solutions.xxx | mp.tasks.python.vision.Xxx |
| モデル | 自動でダウンロードされた | 自分で用意して渡す |
| 画像 | NumPy配列をそのまま | mp.Image に変換する |
| 実行 | process() | detect() / detect_for_video() |
| 結果 | multi_face_landmarks | face_landmarks[0] |
検出される座標そのものは、ほとんど変わりません
書き方は変わりましたが、返ってくる点の位置は旧APIとほぼ同じでした(顔478点の平均のズレは0.173%)。古い記事のコードでも、考え方の部分は今も使えます。
まとめ
- MediaPipeは学習済みモデルつきなので、数十行で顔・手・姿勢を取れる
- 取れる点は顔478点・片手21点・姿勢33点。座標は0〜1に正規化されている
- ★いまはモデルファイル(.task)を自分で渡す。同梱されなくなった
- 顔は点だけでなく表情の度合い52種類も取れる。アバター向けにはこちらが扱いやすい
- カメラで動かすときは VIDEO モード+時刻を渡す。渡さないと検出が揺れる
- 旧APIの
mp.solutionsは削除済み。ただし取れる座標はほとんど同じ
ここまでで「自分の顔や手の位置が数値で取れる」ところまで来ました。この座標を UnityへUDPで送る と、画面の中のアバターが自分の動きに合わせて動くようになります😊
それでは、今回はここまで。最後までありがとうございました😊