1.はじめに
ちょっと前まで、顔・手・ポーズのリアルタイム検出をしようと思ったら高性能なGPUを積んだPCが必要でしたが、今回はこれらをCPUだけでも行える MediaPipe をご紹介します。
2.MediaPipeとは?
MediaPipieは、Googleが提供しているライブメディアやストリーミングメディア向けのMLソリューションで、以下の特徴があります。
- 高速なエンドツーエンド処理:一般的なハードウェアでも高速化されたML推論と処理を実現
- 様々なプラットフォームで使用可能:Android、iOS、C++、Pythonに対応
- 無料のオープンソース: Apache 2.0で、拡張やカスタマイズが可能
現在、4つのプラットフォームで、15種類のソリューションを提供されています。今回はその中から、Pythonプラットフォームの、顔検出、手検出、ポーズ検出、ホリスティック検出(顔・手・ポーズの統合検出)についてご紹介します。
3.コード(動画から検出)
最初のデモはオジサンより、女優やダンサーの方が見ていて楽しいと思うので、あえて動画からホリスティック検出(顔・手・ポーズの統合検出)をやってみます。
コードはGoogle Colabで動かす形にしてGithubに上げてありますので、それに沿って説明して行きます。自分で動かしてみたい方は、この「リンク」をクリックし表示されたノートブックの先頭にある「Colab on Web」ボタンをクリックすると動かせます。なお、GPUは使っていません。
まず、セットアップを行います。
1 2 3 4 5 6 7 |
# ライブラリーのインストール !pip install numpy==1.19.3 !pip install mediapipe # github からコードをコピー !git clone https://github.com/cedro3/mediapipe.git %cd mediapipe/ |
1 2 3 4 5 6 7 8 9 10 11 |
# 初期設定 import mediapipe as mp mp_holistic = mp.solutions.holistic # Initialize MediaPipe Holistic. holistic = mp_holistic.Holistic( static_image_mode=True, min_detection_confidence=0.5) # Prepare DrawingSpec for drawing the face landmarks later. mp_drawing = mp.solutions.drawing_utils drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1) |
次に、サンプル動画を読み込み静止画に分解し、imagesフォルダーに保管します。既にimages フォルダーがある場合(2回目以降)は、既にあるフォルダーを一旦削除してから処理を開始します。
1 2 |
# ビデオの指定 video_path = './video/satomi.mp4' |
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 |
# video2images import os import shutil import cv2 # 既にimagesフォルダーがあれば削除 if os.path.isdir('images'): shutil.rmtree('images') os.makedirs('images', exist_ok=True) def video_2_images(video_file= video_path, image_dir='./images/', image_file='img_%s.png'): # Initial setting i = 0 interval = 3 length = 300 # 最大300枚 cap = cv2.VideoCapture(video_file) while(cap.isOpened()): flag, frame = cap.read() if flag == False: break if i == length*interval: break if i % interval == 0: cv2.imwrite(image_dir+image_file % str(i).zfill(6), frame) i += 1 cap.release() def main(): video_2_images() if __name__ == '__main__': main() |
そして、MediaPipeを使って、images フォルダーの中にある静止画から顔・手・ポーズを検出し、結果を上書きして行きます。
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 |
import cv2 from google.colab.patches import cv2_imshow import numpy as np import glob # image file names to files in list format files=[] for name in sorted(glob.glob('./images/*.png')): files.append(name) # Read images with OpenCV. images = {name: cv2.imread(name) for name in files} for name, image in images.items(): # Convert the BGR image to RGB and process it with MediaPipe Pose. results = holistic.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) # Draw pose landmarks. annotated_image = image.copy() mp_drawing.draw_landmarks(annotated_image, results.left_hand_landmarks, mp_holistic.HAND_CONNECTIONS) mp_drawing.draw_landmarks(annotated_image, results.right_hand_landmarks, mp_holistic.HAND_CONNECTIONS) mp_drawing.draw_landmarks( image=annotated_image, landmark_list=results.face_landmarks, connections=mp_holistic.FACE_CONNECTIONS, landmark_drawing_spec=drawing_spec, connection_drawing_spec=drawing_spec) mp_drawing.draw_landmarks( image=annotated_image, landmark_list=results.pose_landmarks, connections=mp_holistic.POSE_CONNECTIONS, landmark_drawing_spec=drawing_spec, connection_drawing_spec=drawing_spec) cv2.imwrite(name, annotated_image) |
最後に、images フォルダーの中にある検出結果を読み込んでgif動画を作成します。
1 2 3 4 5 6 7 8 9 |
# images2gif from PIL import Image import glob files = sorted(glob.glob('./images/*.png')) images = list(map(lambda file: Image.open(file), files)) images[0].save('./out.gif', save_all=True, append_images=images[1:], duration=100, loop=0) |
1 2 3 |
# display gif from IPython.display import Image Image('./out.gif', format='png') |

顔と手は、かなり細かい検出がされていることが分かります。写っている範囲が狭いので、ポーズは肩の線のみ見えています。
ビデオ指定のところを、video_path = ‘./video/mana.mp4’ に変更して、それ以降のコードを実行すると、

上半身が写ると、ポーズも検出していることが分かります。シングルパーソン用のため、より大きく写っている人物を優先して検出するので、後ろの男性は検出されません。
ビデオ指定のところを、video_path = ‘./video/dance.mp4’ に変更して、それ以降のコードを実行すると、

全身を写した場合の検出例です。一時的に右手の検出が外れますが、まずまずではないでしょうか。
4.コード(PCのカメラから検出)
※ご注意
以下のコードは、google colabでは動作しません。ご自分のPCに、このリンクを参考にJupyter Notebookをインストールして実行して下さい。また、Git(コードをコピーするライブラリー)をインストールしてなければ、このリンクを参考に合わせてインストールして下さい。
いよいよ、メインのリアルタイム検出です。カメラ付きのPCで Jupyter Nootbook を開いて、適当なフォルダーに移動し、下記のコードを実行します。GPUは不要です。
1 2 3 4 5 6 |
# MediaPipeをインストール !pip install mediapipe # Gitgub からコードをコピー !git clone https://github.com/cedro3/mediapipe.git %cd mediapipe/ |
ホリスティック検出(顔・手・ポーズの統合検出)をする場合は、下記のコードを実行するだけです。左上にFPSが表示されています。私のPCは3年前に買った MacbookAir ですが、それでも10FPSくらい出ます。凄い!
1 |
!python sample_holistic.py |

顔の検出をする場合は、下記のコードを実行します。顔だけだと、20〜30FPSくらいでしょうか。
1 |
!python sample_face.py |

手の検出をする場合は、下記のコードを実行します。手だけだと、15FPSくらいでしょうか。
1 |
!python sample_hand.py |

ポーズの検出をする場合は、下記のコードを実行します。ポーズだけだと、15FPSくらいでしょうか。
1 |
!python sample_pose.py |

MediaPipeの狙いはエッジデバイスでの検出です。特に、スマホで、顔・手・ポーズなどの検出を使って色々な応用が出来そうですね。
では、また。
(Googleオリジナル) https://mediapipe.dev/index.html
(Githubオリジナル)https://github.com/Kazuhito00/mediapipe-python-sample
%cd mediapipe/ の後、!python sample_holistic.py
を実行したが、何も表示されません。他の3つも同じです。webカメラのライトが正常なら2つ点灯するのですが、最初から点灯している1つしか点灯していません。
bravoastroさん
コメントありがとうございます。
詳しい状況を知りたいので、下記3点教えて下さい。
1)下記を実行した時に、返って来るコメントを教えて下さい。
————
# MediaPipeをインストール
!pip install mediapipe
# Gitgub からコードをコピー
!git clone https://github.com/cedro3/mediapipe.git
%cd mediapipe/
————
2)下記を実行した時に、返って来るコメントを教えて下さい。
——-
!python sample_holistic.py
——-
3)ご自分で設定したフォルダーの中に、mediapipeというファルダーは出来ていますでしょうか。
!python sample_holistic.py を実行した時のメッセージを次に示します。
[ WARN:0] global /io/opencv/modules/videoio/src/cap_v4l.cpp (802) open VIDEOIO ERROR: V4L: can’t open camera by index 0
INFO: Created TensorFlow Lite XNNPACK delegate for CPU.
また、「自分で設定したフォルダー」と言う意味が分かりません。%cd mediapipe を実行しても何のエラーも出ないので出来ているのではないでしょうか?
bravoastro さん
ご連絡ありがとうございます。
!python sample_holistic.py を実行した時のメッセージを拝見しますと、google colabで動かそうとされている様です。このコードは、google colabでは動作しません。
ブログにあります様に、リンク(https://qiita.com/tttamaki/items/0d4fc01c10dd40a13552)をご覧頂き、PCにJupyter Notebook をインストールして実行して下さい。
また、Git(コードをコピーするライブラリー)をインストールしてなければ、このリンク(https://qiita.com/libra219/items/e49f9cc833ad9bb5d1ca)を参考に合わせてインストールして下さい。
*「自分で設定したフォルダー」というは、!git clone する時のディレクトリという意味です。分かり難くてすみません。
bravoastro さん
ブログの該当部分が勘違いし易いように思いましたので、修正します。
ありがとうございます。