MediaPipeという面白いツールを見つけたので、コンテンツ作りに使えそうか試してみます。
ふだん使うPythonに必要なモジュールをインストール
MediaPipe, OpenCV
pip3 install mediapipe
pip3 install opencv-python
Bpy
Bpyの結果を見るにはBlender内でスクリプトを実行する必要があるが、スクリプトを書いたり実行したりの作業はふだん使っているIDEで行いたい。というわけで、ふだんのPythonにもBpyを入れておく。
pip3 install bpy
bpyを外部環境で読み込む時、「ディレクトリがない」というメッセージが気になるならスクリプトの始めで作っておく
if not os.path.exists("/run/user/1000/gvfs"):
os.mkdir("/run/user/1000/gvfs")
Blender付属のPythonに必要なモジュールをインストール
Blender Pythonのある場所にディレクトリを変更
cd /Path/To/Blender/3.4/python/bin/
get-pip.py
githubからget-pip.pyをダウンロードしてBlender付属のPythonで実行
./python3.10 get-pip.py
OpenCV
./pip install opencv-python
MediaPipe
インストールする場所を明示的に指定する
./pip install --target=/Path/To/Blender/3.4/python/lib/python3.10/site-packages mediapipe
SSL
Blender内でmediapipeをインポートする時、SSL関係のエラー(“SSL: CERTIFICATE_VERIFY_FAILED”)が出るようであれば、スクリプトの始めで以下の行を実行
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
MediaPipeの顔ランドマークからBlenderのメッシュを作る
# mp_to_blend.py
# ========================================================================
# 準備
# ========================================================================
# bpyを外部環境でインポートする時のメッセージを防ぐ
import os, ssl
if not os.path.exists("/run/user/1000/gvfs"):
os.mkdir("/run/user/1000/gvfs")
# Blender内部でmediapipeをインポートする時[SSL: CERTIFICATE_VERIFY_FAILED]を回避する
ssl._create_default_https_context = ssl._create_unverified_context
# ...................................................................
# 必要なモジュールを読み込む
import cv2
import mediapipe as mp
import bpy
# パス、ファイル名
PrjDir = "/Path/to/Project/"
imgName = "Input.webp" # png, jpegも可
imgIn = PrjDir + imgName
# ...................................................................
# MediaPipeのオブジェクト
mp_face_mesh = mp.solutions.face_mesh
mp_hands = mp.solutions.hands
mp_pose = mp.solutions.pose
# 顔の各部を形成する頂点セットが予め用意されている
Face_Dict = { "CONTOURS":mp_face_mesh.FACEMESH_CONTOURS,
"LEFT_EYE":mp_face_mesh.FACEMESH_LEFT_EYE,
"RIGHT_EYE":mp_face_mesh.FACEMESH_RIGHT_EYE,
"LIPS":mp_face_mesh.FACEMESH_LIPS,
"LEFT_EYEBROW":mp_face_mesh.FACEMESH_LEFT_EYEBROW,
"RIGHT_EYEBROW":mp_face_mesh.FACEMESH_RIGHT_EYEBROW,
"TESSELATION":mp_face_mesh.FACEMESH_TESSELATION,
"FACE_OVAL":mp_face_mesh.FACEMESH_FACE_OVAL,
"IRISES":mp_face_mesh.FACEMESH_IRISES,
"LEFT_IRIS":mp_face_mesh.FACEMESH_LEFT_IRIS,
"RIGHT_IRIS":mp_face_mesh.FACEMESH_RIGHT_IRIS,
"NUM_LANDMARKS":mp_face_mesh.FACEMESH_NUM_LANDMARKS,
"NUM_LANDMARKS_WITH_IRISES":mp_face_mesh.FACEMESH_NUM_LANDMARKS_WITH_IRISES
}
hand_conn = mp_hands.HAND_CONNECTIONS
pose_conn = mp_pose.POSE_CONNECTIONS # Blender用コネクションリストを別途作るので要らないかも
# ...................................................................
# 画像オブジェクト
img = cv2.imread(imgIn)
h = img.shape[0]
w = img.shape[1]
# ========================================================================
# サブ
# ========================================================================
def vertsCalc(h, w, lm):
'''
画像の高さ・幅とMediaPipeのランドマークデータから
各頂点の座標を求める
'''
vList = []
for i in lm:
V = str(i).split("\n")
Vx = float(V[0].split(": ")[1]) * w * 0.001
Vy = float(V[1].split(": ")[1]) * h * 0.001
Vz = float(V[2].split(": ")[1]) * w * 0.001
#vList.append((Vx, Vy, Vz))
vList.append((Vx, Vz, Vy*-1)) # Blenderではzが上、-yが前
return vList
def edgeData(mpConn):
'''
MediaPipeで用意されているコネクションの頂点ペア
'''
pairList = []
for i in mpConn:
pairList.append(i)
return pairList
# ========================================================================
# メイン
# ========================================================================
# 顔 ...................................................................
with mp_face_mesh.FaceMesh(
static_image_mode = True,
max_num_faces = 1,
refine_landmarks = True,
min_detection_confidence = 0.5 ) as Face:
results = Face.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
lm_detect = bool(results.multi_face_landmarks)
if lm_detect:
Face_verts = vertsCalc(h, w, results.multi_face_landmarks[0].landmark)
Face_edges = edgeData(Face_Dict["TESSELATION"])
FaceMesh = bpy.data.meshes.new("face")
FaceMesh.from_pydata(Face_verts, Face_edges, [])
FaceMesh.validate()
FaceMesh.update()
ob = bpy.data.objects.new("Face", FaceMesh)
scene = bpy.context.scene
scene.collection.objects.link(ob)
実行結果
上記のスクリプトをBlenderのテキストエディタに読み込み実行すると"Face"という名前のメッシュオブジェクトができる
各頂点のインデックス番号が決まっているので、面を作成するのもスクリプトでなんとかなりそう。
次回は手の検知を試します。😉