OpenPose を VisualStudio Community 2017 でビルドする方法

概要


OpenPose とは、映像や画像からその中に映っている人物の姿勢を推定し、データ化することができるライブラリです。映像はファイル以外にもWebカメラ(1台でも!)からもリアルタイムに解析することが可能です。

ここでは、Windowsを対象として、現在最も入手が容易な VisualStudio Community 2017 でビルドできるようにする方法を紹介いたします。(VisualStudio Community 2017は導入済みとします)

インストールに関してプライマリに参考すべきURLを記しておきます。
https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/doc/installation.md

CUDAのインストール

nVidia製のビデオカードを持っているならCUDAと呼ばれる仕組みを利用することでGPUによる並列計算を利用することができます。というか、これがなければおそらく話にならないでしょう。

OpenPoseでは、CUDA8.0 CuDNN5.1 という組み合わせを前提としているので、それ以外の組み合わせだと、インストールで苦労する可能性があります。より新しいものを導入することで更なる速度向上が見込まれますが、まずはビルドできてからチャレンジするべきでしょう。

CUDA 8.0 の入手

https://developer.nvidia.com/cuda-80-ga2-download-archive

上記よりOSにあわせたものを入手し、インストールしてください。

CuDNN 5.1 の入手

CuDNNを入手するためには、nVidiaのdeveloper登録(無料)が必要です。

https://developer.nvidia.com/rdp/cudnn-archive

登録したら上記より、「Download cuDNN v5.1 (Jan 20, 2017), for CUDA 8.0」 を入手しインストールしてください。

cuda8.0をインストールしたフォルダにcudaフォルダ以下をそのまま展開します。 デフォルトでは下記になると思います。

C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v8.0

CUDA8.0をVisual Studio 2017 で使うための準備

CUDA8.0 が出た頃は、Visual Studio の最新は 2015だったので、関連モジュールは2017のライブラリに対応していないことがあります。20172015のツールセットが使えるようにコンポーネントを導入します。

Visual Studio を立ち上げ、「ツール>ツールと機能を取得」を開きます。
「個別のコンポーネントパック」タブから、「デスクトップ用VC++ 2015.3 v14.00(v140) ツールセット」を導入してください。

また、「コンパネ>プログラムと機能>Windowsの機能の有効化または無効化」より「.NET Framework 3.5」を導入してください。

CMakeによるOpenPoseのビルド環境構築

OpenPoseソースの入手と配置

OpenPoseのGitHubを開き、ZIPファイルを入手します(勿論、gitから直接でも構いません)。

download openpose
openposeを配置

ここでは D:\development\openpose  に展開したとして、話を進めます。

CMake のインストール

OpenPoseのビルド環境を自動生成するためのツールとしてにCMakeをインストールします。

ここでは cmake-3.13.0-rc3-win64-x64.msi をインストールしましたが、より新しいものでも問題はないと思います。ただし、古いものがインストールされているならアンインストールしてからの方が良いでしょう。

CMakeによるビルド環境の構築

CMakeを開き、ソースコードの場所ビルド環境を構築する場所を指定します。

ここでは、それぞれ

D:/development/openpose
D:/development/openpose/build_windows

として、「Configure」を押します。

ビルド環境を構築する場所が存在しない場合は、許諾ウィンドウが出るので「Yes」を押してください。

開発環境を指定します。

「Visual Studio 15 2017 Win64」と指定し、Optional toolset to use のところに「v140」と追記してください。これがないと、Configureはうまくいかない場合があります。

「Finish」を押すと、必要な環境変数の生成や必要なファイルのダウンロードが始まります。初めてだと数分はかかると思います。
(ダウンロードでエラーが出てしまったときは
「./3rdparty/windows」 にあるZIPファイルを消去してやり直してください。)

エラー表示がなく「Configuring done」が出て、変数内容に特に怪しいところがなければ次に進みます。
チェックのついていない項目を入れたい場合はチェックして再度「Configure」を押してください。
その際はエラーがわかりやすいように一つ一つやることをお勧めします。

次に「Generate」を押して、「Generating done」が出たらソリューションファイル(.sln)等が生成されているので、「Open Project」を押すと、 Visual Studio 2017 でソリューションが開かれます。

OpenPoseのビルド

Visual Studio 2017 上ではソリューションに沢山のプロジェクトがぶら下がってますが、OpenPoseDemoがスタートアッププロジェクトになっているのでこれをビルドDebug構成でビルドすると一見うまく言っているように見えますが、gflag系モジュールのstring入力(例えば -write_json=”d:\output”)がうまく働かず、オプションで指定した値が反映されません。したがって、Release構成でビルドしましょう。

うまくビルドできたら、IDE上から開始することができます。

生成した実行ファイル(.exe)から起動したいとき

生成した実行ファイル(.exe)から起動するときは、外部ファイル(DLL等)が必要なのでパスを通すか同じディレクトリに持ってくる必要があります。

実際に必要なのは下記になります。

  1. 生成した実行ファイル(.exe)
  2. openpose.dll
  3. 各種DLLファイル
  4. 解析に使うモデル一式(openpose\models)

3. は、「openpose\build_windows\bin」 に集められているので(ただし、CMakeBUILD_BIN_FOLDERON になっていること)、1. 2. 4. をここに集めるのが一番簡単でしょう。4. は modelsフォルダごとコピーしてください。

参考

  1. カーネギメロン大学 Perceptual Computing Lab openpose github
  2. Using CUDA with Visual Studio 2017

SharedPreferences の putStringSet()メソッドの不具合

Stringのリストがうまく永続化できない!

SharedPreferences を用いて Stringのリストを保存しようと思い下記のようにした。

// プリファレンスから、保存している値を得る
val pref = getSharedPreferences("prefs", Context.MODE_PRIVATE)
val stringSet = pref.getStringSet("key1", mutableSetOf())

// 適当な値をリストに追加
stringSet.add(stringSet.size.toString())

// プリファレンスに保存する
pref.edit().putStringSet("key1", stringSet).apply()

ところが次のような不思議な現象に遭遇した。

  • アプリの動作中は問題ない
  • アプリをいったん終了し、再度立ち上げると、一番最初に記録した値のみが再現され、その後に追加した値はなかったことになっていた

どうやら、(環境により異なるが)
/data/data/(applicationId)/shared_prefs
辺りに生成される「prefs.xml」(SharedPreferencesの実態)が最初の書き込み以降更新されていないようなのだ。
putString() とか putInt() で同じようにしたときには意図したとおりに動作するのに、なぜか putStringSet() ではそのようになる。

これはバグなのだろうか?調べてみたところJavaでも同じことが起こっているようだ。

解決策として、いくつか考えてみた。

putString()等でダミーを一緒に書き込む

// プリファレンスに保存する
pref.edit().putString("dummy", "dummy")
pref.edit().putStringSet("key1", stringSet).apply()

これは putString() ならうまくxmlファイルの更新がかかるのでそれを利用した。冗長なデータを書き込むのであまり気分はよくない。

一旦、全てをクリアしてしまう

// プリファレンスに保存する
pref.edit().clear()
pref.edit().putStringSet("key1", stringSet).apply()

気分一新、全ての内容をクリアしてから再度上書きする。この場合はこれでもなんとかなってしまうのだが、このデータベースに他の情報が一緒に記録されていれば(というかその可能性は高い)目も当てられないことになる。

該当する情報だけ一旦消してから書き直す

// プリファレンスに保存する
pref.edit().remove("key1").apply()
pref.edit().putStringSet("key1", stringSet).apply()

該当情報だけ消して書き直す。一番角が立たない方法だろう。
remove() で消去したあと、apply() するのがポイント!これを忘れるとやっぱり最初の現象と同じになってしまう。

LINQ

C# LINQ メモ

# 文字列のアスキーコードを取得の例
string str1 = "This is a pen";
string a1 = string.Join("-", System.Text.Encoding.ASCII.GetBytes(str1).Select(x => $"{x:X2}"));
string a2 = string.Join("-", str1.Select(x => $"{Convert.ToInt32(x):X2}"));