LoginSignup
12
6

More than 3 years have passed since last update.

Videopose3D ビルド自分メモ&スケルトンビューワー

Last updated at Posted at 2019-09-12

(2019.09.18更新)
@timtoronto634 さんのおかげで、DetectronとVideopose3Dのビルドが成功。感謝感謝。
この元記事を必ず参照のこと
https://qiita.com/timtoronto634/items/ee018ac89e6b9f779194
ここで記されている、condaで作る環境、どのパッケージのどのバージョンを入れるかがキモ。@timtoronto634さんもそこをかなりご苦労された模様。

僕の場合は、上のやり方で環境を整え、それより前に成功していたVideopose3D公式のビルドから必要そうなものを都度もってきてみた。
Pythonだから、エラーが出るたびにそれをすれば進んでいくんだが、Cythonで作ったオブジェクトをもってくるところで詰まった。Cythonだもんね。Pythonのコードではない。
なので、公式の方法でもう一度makeし(cythonの部分がそれで作られる)、そのフォルダー群を、tobiasczさんのコードでビルドできたフォルダーであるVideoPose3Dフォルダー以下にコピーした。
detectrpm_tools/detectronフォルダーがtobiasczさんの実装での本体なので、ここに多くをコピーすることになるが、公式のを一度ビルドすれば、どこに置くべきかすぐわかる。
なので,一回は,公式の方法でビルドしてみるのを強くオススメ

ここで一つ、ご注意。
上記のやり方で順にやってきた直後だと問題ないのだが、後日また処理をしようとすると、caffe2まわりでエラーが出ることが多い。
その場合、@timtoronto634さん作成のcondaの環境になっているかどうか、再確認必要。さまざまなライブラリーの様々なバージョンを使うことになるので、それがふとバージョン違いがおきていることがある。以下で確認、バージョン違いだったら

conda list
conda install あるいは uninstall [package名]

後日動かすと、なぜかエラーというのはcaffe2まわりではおおいらしく、先ほど動かすと、

ImportError: No module named past.builtins

というのが出た。これは以下で簡単に解決

$ pip install future

https://github.com/facebookarchive/caffe2/issues/712
から。なぜこれで解決するのか、ここの書き込みの最後の人も僕も??なのだが、まあ古いcaffe2なので気にしないようにしよう。

Detectronで、自分のビデオを処理することになるが、ここで
AttributeError: 'NoneType' object has no attribute 'astype'
に悩まされた。
https://github.com/alexgkendall/SegNet-Tutorial/issues/87
と、ここにあるリンク先を見ると、OpenCVではよくあるそうで、入力するイメージやビデオのファイルのあるフォルダーへのパスの記述が融通きかない

僕のケースでは、~/VideoPose3D/detectron_tools の下に skateframesというフォルダ(ムービーから書きだしたpngがある)と、demo/outputフォルダーがある。~/VideoPose3D/detectron_toolsにおいて、上のコマンドでdetectronを起動した場合、普通ならこれらのフォルダ指定に"./"が不要のはずが必要だった。

python infer_simple.py --cfg e2e_keypoint_rcnn_R-101-FPN_s1x.yaml --output-dir ./demo/output --image-ext png --wts model_final.pkl ./skateframes

うまく行くと、demo/outputのなかに、スケルトンがオーバーレイされたpdfファイル(フレームの数だけある)と、data_2d_detections.npzファイルが生成される

3D推定部分(Videopose3D本体)

~/VideoPose3D/ で
out_cutted.mp4は、detectronの入力用とした(ffmpegでバラすまえの)動画ファイル名、output_video.mp4は、その動画の横に、推定したスケルトンの動画をおいた出力動画ファイル名

python run_wild.py -k detections -arc 3,3,3,3,3 -c checkpoint --evaluate d-pt-243.bin --render --viz-subject S1 --viz-action Directions --viz-video out_cutted.mp4 --viz-camera 0 --viz-output output_video.mp4 --viz-size 5 --viz-downsample 1 --viz-skip 0

なお、僕の場合、detectronのpython2.7 conda環境と、videopose3Dのpython3.6 conda環境を何度も行ったり来たりしていると、caffe2が謎エラーを出すようになるので、ここはちょっと注意。rebootで治るけど.
行ったり来たりよりは,ターミナル2つ開いて,condaもdetectronとvideopose3Dのconda環境がどちらも走っている状態でやったほうが,僕の場合は安定しています

推定された座標データの出力

https://github.com/facebookresearch/VideoPose3D/issues/40
3次元np.arrayに格納されているで、binaryでないと出せない flatten()してテキストファイルに出す方法もあるが,構造を壊さず尊重する方針にしました.バイナリーだと,えぇ〜と
おもってしまうのですが,
np.arrayのバイナリーでの読み書きの解説はこちらです。簡単です
https://note.nkmk.me/python-numpy-load-save-savez-npy-npz/

本体であるrun_wild.pyの304行目に、以下の行をいれただけです

    # save predicted marker positions (np.array) to np binary file
    np.save('predictions',prediction)

(内輪連絡〜 ゼミの諸君は、うえのパッチをあてたmyrun_wild.pyをつかってね)

推定されたスケルトンのビューワー

Jupyter notebookで動くように分割して書いてしまいましたが,
ひとまとめにしても動くとおもいます.
自分が作ったデータ形式ではないので.確認しながら動かすのが吉..

    import numpy as np
    import pandas as pd
    import matplotlib
    matplotlib.use('TkAgg') # Need to use in order to run on mac",
    from matplotlib import pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    from matplotlib.colors import cnames
    from matplotlib import animation
    %matplotlib notebook

    arrayMarker3Dcoordinates = np.load('../data/predictions.npy')

一旦,2次元にする.列が,marker1x, marker1y, marker1z, marker2x, marker2y.....の形. マーカーは17種類
マーカーデータの並びは,なかなかにカオスで,どうしてこういう並びにしてるんだ??
僕の場合は,描画しながら特定していきました.間違いがあったらぜひ教えて
なお,3次元テンソルのデータを,一度,2Dの表の形式にしてしまうのは,プログラム的には無駄ですが,他の伝統的なバイオメカニクスのデータ分析と互換性をもたせるためと,xz,yz,xyの平面ごとの射影でみる,細かい分析のためにやっています

arrayMarker3Dcoordinates.shape
df = pd.DataFrame(arrayMarker3Dcoordinates.reshape(584,17*3),
                  columns=['pelvisX','pelvisY','pelvisZ', 'R-troX','R-troY','R-troZ', 'R-kneeX','R-kneeY','R-kneeZ', 'R-ankleX','R-ankleY','R-ankleZ',  'L-troX','L-troY','L-troZ', 'L-kneeX','L-kneeY','L-kneeZ','L-ankleX','L-ankleY','L-ankleZ','centerX', 'centerY','centerZ', 'neckX','neckY','neckZ','noseX','noseY','noseZ', 'headX','headY','headZ',  'L-shoX','L-shoY','L-shoZ', 'L-elbX','L-elbY','L-elbZ', 'L-wristX','L-wristY','L-wristZ','R-shoX','R-shoY','R-shoZ','R-elbX','R-elbY','R-elbZ', 'R-wristX', 'R-wristY','R-wristZ'])

3Dアニメーションを書くためのnumpy array x_tは,[マーカー数[フレーム数[x,y,z] ] ]の3次元テンソルになっているので,これに座標データを入れ直しています.

Stickpicture animationは
https://stackoverflow.com/questions/21367541/3d-animation-with-matplotlib-connect-points-to-create-moving-stick-figure を下敷きにして実装しました

 import numpy as np
    import pandas as pd
    import matplotlib
    #matplotlib.use('TkAgg') # Need to use in order to run on mac
    from matplotlib import pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    from matplotlib.colors import cnames
    from matplotlib import animation
    %matplotlib notebook

    t_start = 0 # start frame
    t_end = df.shape[0] 

    N_tag = df.shape[1]# nr of tags used (all)
    #print(N_tag)
    N_trajectories = N_tag

    t = np.linspace(0,t_end/30,df.shape[0]) # pseudo time-vector for first walking activity
    x_t = np.zeros(shape=(int(N_tag),df.shape[0],3)) # empty animation array (3D)

    for tag in range(17):
        # store data in numpy 3D array: (tag,time-stamp,xyz-coordinates)
        x_t[tag,:,:] = df.iloc[:,tag*3 :tag*3+3]

    #===STICK-LINES========================================================================================
    #xx = [x_t[1,:,0],x_t[2,:,0]]
    #yy = [x_t[1,:,1],x_t[2,:,1]]
    #zz = [x_t[1,:,2],x_t[2,:,2]] 
    #======================================================================================================


    # Set up figure & 3D axis for animation
    fig = plt.figure()
    ax = fig.add_axes([0, 0, 1, 1], projection='3d')
    ax.axis('on')

    # choose a different color for each trajectory
    colors = plt.cm.jet(np.linspace(0, 1, int(N_trajectories)))
    #colors
    # set up trajectory lines
    lines = sum([ax.plot([], [], [], '-', c=c) for c in colors], [])
    # set up points
    pts = sum([ax.plot([], [], [], 'o', c=c) for c in colors], [])
    # set up lines which create the stick figures

    stick_defines = [
        (0,1),
        (0,4),
        (4,5),
        (5,6),
        (0,7),
        (1,2),
        (2,3),
        (7,8),
        (8,9),
        (9,10),
        (8,14),
        (8,11),
        (11,12),
        (12,13),
        (14,15),
        (15,16)
    ]

    stick_lines = [ax.plot([], [], [], 'k-')[0] for _ in stick_defines] #上で定義されたstick_definesにある点の間に線をひく

    # Automatically set axes limits
    # prepare the axes limits 
    #ax.set_xlim(df_minmax.loc['x'].values)
    #ax.set_ylim(df_minmax.loc['y'].values) 
    #ax.set_zlim(df_minmax.loc['z'].values) 

    # Setting the axes properties
    # https://matplotlib.org/2.1.2/gallery/animation/simple_3danim.html
    mid_x =0
    x_range = 1
    ax.set_xlim3d(mid_x - x_range, mid_x + x_range)
    ax.set_xlabel('X')

    mid_y =0
    y_range = 1
    ax.set_ylim3d(mid_y - x_range, mid_y + x_range)
    ax.set_ylabel('Y')

    mid_z = 0.5
    z_range = 1
    ax.set_zlim3d(mid_z - z_range, 1.5)
    ax.set_zlabel('Z')

    ax.set_title('markers in 3D')

    # set point-of-view: specified by (altitude degrees, azimuth degrees)
    ax.view_init(20,45)

    # initialization function: plot the background of each frame
    def init():
        for line, pt in zip(lines, pts): # lines, ptsのリストから,同時にi番目のデータを取得する.linesからline, pts からpt
            # trajectory lines
            line.set_data([], [])
            line.set_3d_properties([])
            # points
            pt.set_data([], [])
            pt.set_3d_properties([])
        return lines + pts + stick_lines

    # animation function.  This will be called sequentially with the frame number
    def animate(i):
        # 1 * i means show every frame, 2*i means one frame show one frame no-show
        i = (1 * i) % x_t.shape[1] 

        for line, pt, xi in zip(lines, pts, x_t):
            x, y, z = xi[:i].T # note ordering of points to line up with true exogenous registration (x,z,y)
            print(x)
            pt.set_data(x[-1:], y[-1:])
            pt.set_3d_properties(z[-1:])

        for stick_line, (sp, ep) in zip(stick_lines, stick_defines):
            stick_line._verts3d = x_t[[sp,ep], i, :].T.tolist()

        #ax.view_init(30, 0.3 * i)
        ax.view_init(20,60+i*2)
        fig.canvas.draw()
        return lines + pts + stick_lines

    # instantiate the animator.
    anim = animation.FuncAnimation(fig, animate, init_func=init, frames=t_end, interval=1, repeat = False, blit=True)

    # GIF animationで保存の場合.画面描画と両立する.
    #anim.save('3Dplotanimation.gif', writer='pillow', fps=10)

    plt.show()

自分メモなので,足りないところたくさんあると思います.
ご質問ください.
下のGIFアニメが自動で出なかったら,白いところをクリックして別ウインドウで見てください↓
3Dplotanimation-compressor.gif

12
6
8

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
6