Translate

骨格推定アルゴリズムPHALPで使うことができるファイル形式.pklファイルの扱い方。骨格座標を出力するところまで

 以前に紹介した3Dの骨格推定アルゴリズムPHALP(Predicting Human Appearance, Location and Pose for Tracking )がありました(記事URL:https://himahimaknowledge.blogspot.com/2023/08/python-environment-phalp.html)。

この前の記事では、自分のローカル環境でオフラインかつWIndows環境で無理やり実行する方法を書きましたが、要求されるGPUがRTX4080レベルとなかなかに現実的なものではありませんでした。

いろいろと調べてみると、公式のHPのGoogleColabを使うと骨格推定結果の.pklファイルを出力するところまではできるようです。

GoogleColab特有のディレクト管理などちょっと癖がある処理にはなるとは思いますが、自分で複雑な環境構築を作ったり、高価なGPUを使わなくても3Dかつメートル座標化されている座標を取得できるのは魅力的です。

今回の記事では、骨格推定アルゴリズムPHALPで出力される.pklファイルから骨格座標データを取り出して可視化するところまでのpythonコードを紹介したいと思います。

そもそも.pklファイルとは何か



まずはじめに、そもそも.pklファイルとはいったいどんな特徴があるのでしょうか?.pklファイルとはいわゆるバイナリファイルみたいなもので、性質としては、.matファイルや.npyファイルのようなものに近く、構造化されたファイルでこれを読み込むと中の辞書型で保存されたデータを読み取ることができます。

ただ、.matファイルのようにMATLAB上でファイルの構造の中を簡単に確認するソフトウェアがあるわけではないので、pythonなり何かのスクリプトで.pklファイルを読み取る処理を各必要があります。

今回は、PHALPで出力される.pklファイルの構造をもとにコードを書いていますが、.pklファイルを読み取る部分は共通して利用することが可能です。

PHALPで出力される.pklファイルから座標データを読み取って定義するためのコード。

実際のpythonコードはこんな感じです(あくまでも一例ですが。。。)。基本的には、.pklファイルを読み取るパート、.pklファイルの中身から関節位置が入っている部分を探すして定義するパート。それぞれの関節位置を定義するパートになります。

)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import pandas as pd
import glob
import sys
from matplotlib.widgets import Slider, Button
import matplotlib.widgets as wg
from matplotlib import patches
plt.rcParams['font.family'] = 'MS Gothic'
import scipy
from scipy import signal
import time
import datetime
from numpy import linalg as LA
import math
import os, tkinter, tkinter.filedialog, tkinter.messagebox

filename = glob.glob('*.pkl')

import _pickle as pickle
import joblib

results =joblib.load(filename[0])
k_list  =list(results.keys())

joint1 = results[k_list[0]]['3d_joints'][0]

fig = plt.figure(figsize =(5,5))
ax = fig.add_subplot(111,projection = '3d')
ax.set_xlim(-0.75,0.75)
ax.set_ylim(-0.75,0.75)
ax.set_zlim(-0.75,0.75)

r_c_shoul = (joint1[2,:]+joint1[33,:])/2
r_c_elb = (joint1[3,:]+joint1[32,:])/2
r_c_wri = (joint1[4,:]+joint1[31,:])/2

l_c_shoul = (joint1[5,:]+joint1[34,:])/2
l_c_elb = (joint1[6,:]+joint1[35,:])/2
l_c_wri = (joint1[7,:]+joint1[36,:])/2

c_head = (joint1[17,:]+joint1[18,:])/2
c_neck = (joint1[1,:]+joint1[37,:]+joint1[40,:])/3

spine = joint1[41,:]

pel = joint1[8,:]
pel2 = joint1[39,:]
b_pel = joint1[44,:]
r_ASIS = joint1[27,:]
l_ASIS = joint1[28,:]

r_c_hip = joint1[9,:]
r_c_knee =(joint1[10,:]+joint1[26,:])/2
r_c_ank = joint1[11,:]
r_c_foot = (joint1[22,:]+joint1[23,:])/2
r_c_heel = joint1[24,:]

l_c_hip = joint1[12,:]
l_c_knee =(joint1[13,:]+joint1[29,:])/2
l_c_ank = joint1[14,:]
l_c_foot = (joint1[19,:]+joint1[20,:])/2
l_c_heel = joint1[21,:]

size = 30
alp = 0.8

ax.scatter(c_head[0],c_head[2],-c_head[1],color = 'gray',s = 120,alpha = alp)
ax.scatter(c_neck[0],c_neck[2],-c_neck[1],color = 'gray',s = size,alpha = alp)
ax.scatter(spine[0],spine[2],-spine[1],color = 'gray',s = size,alpha = alp)
ax.scatter(pel[0],pel[2],-pel[1],color = 'gray',s = size,alpha = alp)
ax.scatter(r_ASIS[0],r_ASIS[2],-r_ASIS[1],color = 'gray',s = size,alpha = alp)
ax.scatter(l_ASIS[0],l_ASIS[2],-l_ASIS[1],color = 'gray',s = size,alpha = alp)
ax.scatter(b_pel[0],b_pel[2],-b_pel[1],color = 'gray',s = size,alpha = alp)
# ax.scatter(pel2[0],pel2[2],-pel2[1],color = 'gray',s = size,alpha = alp)


ax.scatter(r_c_shoul[0],r_c_shoul[2],-r_c_shoul[1],color = 'navy',s = size,alpha = alp)
ax.scatter(r_c_elb[0],r_c_elb[2],-r_c_elb[1],color = 'navy',s = size,alpha = alp)
ax.scatter(r_c_wri[0],r_c_wri[2],-r_c_wri[1],color = 'navy',s = size,alpha = alp)

ax.scatter(l_c_shoul[0],l_c_shoul[2],-l_c_shoul[1],color = 'crimson',s = size,alpha = alp)
ax.scatter(l_c_elb[0],l_c_elb[2],-l_c_elb[1],color = 'crimson',s = size,alpha = alp)
ax.scatter(l_c_wri[0],l_c_wri[2],-l_c_wri[1],color = 'crimson',s = size,alpha = alp)

ax.scatter(r_c_hip[0],r_c_hip[2],-r_c_hip[1],color = 'navy',s = size,alpha = alp)
ax.scatter(r_c_knee[0],r_c_knee[2],-r_c_knee[1],color = 'navy',s = size,alpha = alp)
ax.scatter(r_c_ank[0],r_c_ank[2],-r_c_ank[1],color = 'navy',s = size,alpha = alp)
ax.scatter(r_c_foot[0],r_c_foot[2],-r_c_foot[1],color = 'navy',s = size,alpha = alp)
ax.scatter(r_c_heel[0],r_c_heel[2],-r_c_heel[1],color = 'navy',s = size,alpha = alp)

ax.scatter(l_c_hip[0],l_c_hip[2],-l_c_hip[1],color = 'crimson',s = size,alpha = alp)
ax.scatter(l_c_knee[0],l_c_knee[2],-l_c_knee[1],color = 'crimson',s = size,alpha = alp)
ax.scatter(l_c_ank[0],l_c_ank[2],-l_c_ank[1],color = 'crimson',s = size,alpha = alp)
ax.scatter(l_c_foot[0],l_c_foot[2],-l_c_foot[1],color = 'crimson',s = size,alpha = alp)
ax.scatter(l_c_heel[0],l_c_heel[2],-l_c_heel[1],color = 'crimson',s = size,alpha = alp)

ax.plot([c_neck[0],r_c_shoul[0]],[c_neck[2],r_c_shoul[2]],[-c_neck[1],-r_c_shoul[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([c_neck[0],l_c_shoul[0]],[c_neck[2],l_c_shoul[2]],[-c_neck[1],-l_c_shoul[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([c_neck[0],spine[0]],[c_neck[2],spine[2]],[-c_neck[1],-spine[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([spine[0],pel[0]],[spine[2],pel[2]],[-spine[1],-pel[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([r_ASIS[0],pel[0]],[r_ASIS[2],pel[2]],[-r_ASIS[1],-pel[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([l_ASIS[0],pel[0]],[l_ASIS[2],pel[2]],[-l_ASIS[1],-pel[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([l_ASIS[0],r_ASIS[0]],[l_ASIS[2],r_ASIS[2]],[-l_ASIS[1],-r_ASIS[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([l_ASIS[0],b_pel[0]],[l_ASIS[2],b_pel[2]],[-l_ASIS[1],-b_pel[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([r_ASIS[0],b_pel[0]],[r_ASIS[2],b_pel[2]],[-r_ASIS[1],-b_pel[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([l_c_hip[0],pel[0]],[l_c_hip[2],pel[2]],[-l_c_hip[1],-pel[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([r_c_hip[0],pel[0]],[r_c_hip[2],pel[2]],[-r_c_hip[1],-pel[1]],color ='gray' ,linewidth = 2,alpha = alp)

ax.plot([r_c_hip[0],r_ASIS[0]],[r_c_hip[2],r_ASIS[2]],[-r_c_hip[1],-r_ASIS[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([r_c_hip[0],b_pel[0]],[r_c_hip[2],b_pel[2]],[-r_c_hip[1],-b_pel[1]],color ='gray' ,linewidth = 2,alpha = alp)

ax.plot([l_c_hip[0],l_ASIS[0]],[l_c_hip[2],l_ASIS[2]],[-l_c_hip[1],-l_ASIS[1]],color ='gray' ,linewidth = 2,alpha = alp)
ax.plot([l_c_hip[0],b_pel[0]],[l_c_hip[2],b_pel[2]],[-l_c_hip[1],-b_pel[1]],color ='gray' ,linewidth = 2,alpha = alp)

ax.plot([r_c_hip[0],r_c_knee[0]],[r_c_hip[2],r_c_knee[2]],[-r_c_hip[1],-r_c_knee[1]],color ='navy' ,linewidth = 2,alpha = alp)
ax.plot([r_c_knee[0],r_c_ank[0]],[r_c_knee[2],r_c_ank[2]],[-r_c_knee[1],-r_c_ank[1]],color ='navy' ,linewidth = 2,alpha = alp)
ax.plot([r_c_ank[0],r_c_foot[0]],[r_c_ank[2],r_c_foot[2]],[-r_c_ank[1],-r_c_foot[1]],color ='navy' ,linewidth = 2,alpha = alp)
ax.plot([r_c_ank[0],r_c_heel[0]],[r_c_ank[2],r_c_heel[2]],[-r_c_ank[1],-r_c_heel[1]],color ='navy' ,linewidth = 2,alpha = alp)
ax.plot([r_c_foot[0],r_c_heel[0]],[r_c_foot[2],r_c_heel[2]],[-r_c_foot[1],-r_c_heel[1]],color ='navy' ,linewidth = 2,alpha = alp)

ax.plot([l_c_hip[0],l_c_knee[0]],[l_c_hip[2],l_c_knee[2]],[-l_c_hip[1],-l_c_knee[1]],color ='crimson' ,linewidth = 2,alpha = alp)
ax.plot([l_c_knee[0],l_c_ank[0]],[l_c_knee[2],l_c_ank[2]],[-l_c_knee[1],-l_c_ank[1]],color ='crimson' ,linewidth = 2,alpha = alp)
ax.plot([l_c_ank[0],l_c_foot[0]],[l_c_ank[2],l_c_foot[2]],[-l_c_ank[1],-l_c_foot[1]],color ='crimson' ,linewidth = 2,alpha = alp)
ax.plot([l_c_ank[0],l_c_heel[0]],[l_c_ank[2],l_c_heel[2]],[-l_c_ank[1],-l_c_heel[1]],color ='crimson' ,linewidth = 2,alpha = alp)
ax.plot([l_c_foot[0],l_c_heel[0]],[l_c_foot[2],l_c_heel[2]],[-l_c_foot[1],-l_c_heel[1]],color ='crimson' ,linewidth = 2,alpha = alp)


ax.plot([r_c_shoul[0],r_c_elb[0]],[r_c_shoul[2],r_c_elb[2]],[-r_c_shoul[1],-r_c_elb[1]],color ='navy' ,linewidth = 2,alpha = alp)
ax.plot([r_c_elb[0],r_c_wri[0]],[r_c_elb[2],r_c_wri[2]],[-r_c_elb[1],-r_c_wri[1]],color ='navy' ,linewidth = 2,alpha = alp)

ax.plot([l_c_shoul[0],l_c_elb[0]],[l_c_shoul[2],l_c_elb[2]],[-l_c_shoul[1],-l_c_elb[1]],color ='crimson' ,linewidth = 2,alpha = alp)
ax.plot([l_c_elb[0],l_c_wri[0]],[l_c_elb[2],l_c_wri[2]],[-l_c_elb[1],-l_c_wri[1]],color ='crimson' ,linewidth = 2,alpha = alp)

コードの中身はかなり冗長で申し訳ないです。ほとんどは関節座標を出力するパートになっているので、大事なのはその前半の部分です。

import _pickle as pickle
import joblib

results =joblib.load(filename[0])
k_list  =list(results.keys())

joint1 = results[k_list[0]]['3d_joints'][0]

ここがまさに.pklファイルを読み取る部分のコードになっています。joint1で定義されている部分に、特定の人物(ここでは0番目の人として定義されている骨格データ)の各関節位置が入っています。これが複数人映っている場合には、joint1の数字を変えることで違う人の骨格位置を読み取ることができます。

このモデルの良いところは、骨盤の座標が取れている点にあり、理学療法的な利点でいうと、座っているときの骨盤後傾をとることができる可能性があります。

まあそれ以前に、3Dの座標系で取れている時点でかなりポテンシャルがあると思っています。

.pklの出力にはGoogleColabを使うのが無難。

PHALPを回して.pklファイルを出力する部分は、公式のGitHubのサンプルコードを動かすのが一番無難です。

個人的にはGoogleColab(というかマークダウンのIpythonが)がとても嫌いなのでこちらは別のサイトでやっている人がいるので参考にしてみてください(http://cedro3.com/ai/phalp/)。おそらく.pklファイルはこのコードでいうところのoutputのところに出力されると思います(私のローカル環境と同じの場合ですが。。。)。

座標化してしまえば解析など使い道は無限大なので、習得してやってみる価値はあると思います。