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をインストールします。

https://cmake.org/download/

ここでは 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

GoPro HERO7 で撮影した動画からGPS情報をgpxファイルとして抜き出す方法

概要

GoPro HERO7 BLACK で撮影した動画(mp4)内には、GPS情報が含まれている。
そのGPS情報gpx形式で抜き出してやると、いろいろ面白いことが出来そうである。

gpx化についての流れは下記が分かりやすい。

GoPro HERO5 Blackで撮影した動画からKMLファイルを抽出してみました

ただし、こちらのサイトで作成されたものはHERO5に対応したものでHERO7で作成した動画に処したところエラーが出て止まってしまうので、HERO7でも使用できるものの作成方法について以後補足する。

(最終的にKML形式に変換しているが、大体のGPS情報入力ソフトはgpxをサポートしているので、途中のgpxの抽出で目標は達成である。)

簡単にまとめると

  1. ffmpegを使用して元動画(mp4)から、GPMF(GoPro Metadata Format もしくは General Purpose Metadata Format)形式で記録されたセンサーデータ(バイナリ)を分離する
  2. gopro2gpxというソフトを使用して分離したデータをgpx形式に変換する

折角なので、どのOSでも使えるようにUnixライクな環境で実行できるように話を進める。

その前に、Windowsユーザーには既にgopro2gpxの実行形式(exe)が配布されているので下記をダウンロードの上、動画からgpxファイルを作成する手順からお読みください。(Linux Shell ベースで書かれているので細かいところは脳内補完で対応してください)

http://tailorandwayne.com/download/GPMD2CSV.zip

Windows Subsystem for Linux (WSL)における環境設定

ここではWindows10上で動作するLinux(Ubuntu)サブシステムを使って環境を構築する方法について触れる。Windowsユーザー以外は読み飛ばしてください。
Windows Subsystem for Linux (WSL) のインストールの方法についてはたくさん情報があるので調べてください。

Go言語のインストール

sudo apt update
sudo apt install golang

・・・以上。

ちなみに、WSL上からWindowsのCドライブやDドライブへの参照はそれぞれ
/mnt/c
/mnt/d
となる。

gopro2gpxの作成

ここでは、Go言語を用いてgopro2gpxの実行形式を作成する手順を紹介する。Macを含むUNIXライクな環境があってGo言語までインストールされているならここから読めばよい。

必要なパッケージをダウンロードするためにGoではgetコマンドを用いる。そのダウンロード先として環境変数GOPATHであらかじめ指定する必要がある。
ここではホームディレクトリ直下にgoというフォルダを作成して、これをダウンロード先とする。

makedir -p ~/go

 

実行ファイルを作成したいディレクトリに移動して、下記を入力すると必要なパッケージをダウンロードして実行ファイル(gopro2gpx)が作成される。

export GOPATH=$HOME/go

go get github.com/JuanIrache/gopro-utils/bin/gopro2gpx
go install github.com/JuanIrache/gopro-utils/bin/gopro2gpx

 

動画からgpxファイルを作成する手順

ffmpeg と gopro2gpx が揃っていればここから読めばよい。

とりあえず作成

話を簡単にするためにgopro2gpx処理する動画(ここではGH010047.MP4とする)はカレントディレクトリにあるとする。

ffmpeg -y -i GH010047.MP4 -codec copy -map 0:3 -f rawvideo GH010047.bin
./gopro2gpx -i GH010047.bin -o GH010047.gpx

いろいろと警告が出てきて、もやっとするが、gpxが作成されていれば成功である。

gopro2gpx のオプションについて補足

Usage of ./gopro2gpx:
  -a int
        Optional: GPS accuracy threshold, defaults to 1000 (default 1000)
  -f int
        Optional: GPS fix state. Defaults to 0 (no fix), can be 2 (2D) or 3 (3D) (default 3)
  -i string
        Required: telemetry file to read
  -o string
        Required: gpx file to write
スイッチ 入力値 内容 初期値
-a <数値> 許容するGPSの精度閾値 小さいほど精度が高い 1000
-f <数値> GPSの測位モード? 3
-i <入力ファイル名> メタデータの記録されているファイル
-0 <出力ファイル名> GPX形式のファイル

使いやすいようにシェルにする(任意)

エディタ等で extract_gpx.sh と名付けて下記を作成
2行目のGOPRO2GPX_FILEに作成したgopro2gpxをフルパスで記入

#!/bin/sh
GOPRO2GPX_FILE=~/bin/gopro2gpx

if [ $# -lt 1 ]; then
    echo "Usage: $0 <input_movie_file> [<output_dir>]"
    exit 1
fi

IN_FILE_EXT=$1
IN_FILE=${IN_FILE_EXT##*/}
IN_BASE=${IN_FILE%.*}
OUT_DIR=${IN_FILE_EXT%/*}

if [ $# -ge 2 ]; then
    OUT_DIR=$2
fi

ffmpeg -y -i "${IN_FILE_EXT}" -codec copy -map 0:3 -f rawvideo "${OUT_DIR}/${IN_BASE}.bin"
${GOPRO2GPX_FILE} -i "${OUT_DIR}/${IN_BASE}.bin" -o "${OUT_DIR}/${IN_BASE}.gpx"

使用方法:
./extract_gpx.sh <元動画ファイル> [<出力先フォルダ>]
使用例:
./extract_gpx.sh “/mnt/d/Videos/GoPro/20181015/GH010047.MP4” “/mnt/d/GPX”

作成したgpxの確認

得られたgpxは下記のサイトにて正しく抽出できたか確認できる。

GPS Visualizer

技術参考

  1. Parser for GPMF™ formatted telemetry data used within GoPro® cameras.
  2. Tools to parse metadata from GoPro Hero 5 cameras
  3. EXTRACT SENSOR DATA FROM GOPRO VIDEOS

 

Ubuntu 18.04 LTS で ネットワークインターフェース名を昔の表記(eth0など)に変更する方法

Ubuntu 18.04LTSを導入したところ、ネットワークインターフェース名が「ens192」という表示になった。
udevのバージョン197以降、デフォルトの動作では予測可能なネットワークインタフェース名がつけられるように仕様が変更された結果である。
そうは言ってもやはり慣れ親しんだ「eth0」等の表記に戻したいと考えている人は少なくないのではないだろうか。

dmesg を見てみると、ブート時に一度eth0を認識した後、わざわざens192に変更されているのが分かる。

bang@steinsgate:~$ dmesg | grep -i eth
[    1.923038] vmxnet3 0000:0b:00.0 eth0: NIC Link is Up 10000 Mbps
[    1.954736] vmxnet3 0000:13:00.0 eth1: NIC Link is Up 10000 Mbps
[    2.179409] vmxnet3 0000:0b:00.0 ens192: renamed from eth0
[    2.279580] vmxnet3 0000:13:00.0 ens224: renamed from eth1

要はこの動作を無効にすれば良い。これはカーネルの起動オプションで
net.ifnames=0 biosdevname=0
を追加することにより可能となる。

grub に設定を登録

下記は grubからの起動時にカーネル起動オプションを永続的に追加する方法である。/etc/default/grub を開き GRUB_CMDLINE_LINUX に追加のカーネル起動オプションを設定する。

sudo vi /etc/default/grub
GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"

grubに設定を反映させる。

sudo grub2-mkconfig -o /boot/grub2/grub.cfg

インターフェースの変更をNetplanにも反映

次に起動時にインターフェースにIP等が設定されるようにNetplanのデバイス名も変えておく。

sudo vi /etc/netplan/50-cloud-init.yaml
network:
    ethernets:
        eth0:
            addresses: []
            dhcp4: true
        eth1:
            addresses: []
            dhcp4: true
    version: 2
参考
  1. CentOS7におけるNIC命名ルール

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}"));