MotoJapan's Tech-Memo

技術めも

【Raspberry Pi 3】sambaサーバーが接続できないときのハマリポイントまとめチェック

久しぶりにsambaを入れたらうまく動かないとか、ubuntu16.04ではsambaサーバーが動いていたのに、raspberry pi 3ではうまくいかないときのためのチェック項目

基本手順

インストール

$ sudo apt-get install samba

設定変更

$ sudo vim /etc/samba/smb.conf 
 
[global]
interfaces = 127.0.0.0/8 eth0
[homes]
browseable = yes
public = yes
read only = no
create mask = 0775    #Sambaにより作成されるファイルに許容される最大アクセス権 (権限は各自のレベルで設定) 
directory mask = 0775 #Sambaにより作成されるディレクトリに許容される最大アクセス権 (権限は各自のレベルで設定)

sambaを再起動

sambaを起動する

$ sudo service samba restart
//もしくは
$ sudo /etc/init.d/samba restart

【チェック1】sambaが起動しているか確認する

下記コマンドでステータス[OK]をチェックする

$ sudo /etc/init.d/samba restart
[ ok ] Restarting nmbd (via systemctl): nmbd.service.
[ ok ] Restarting smbd (via systemctl): smbd.service.
[ ok ] Restarting samba-ad-dc (via systemctl): samba-ad-dc.service.

【チェック2】ファイアーウォールを切断してみる

network問題を切り分ける

$ sudo apt-get install ufw
$ sudo ufw allow 137/udp
$ sudo ufw allow 138/udp
$ sudo ufw allow 139/tcp
$ sudo ufw allow 445/tcp
$ sudo ufw reload

【チェック3】interfacesを明示的に指定してみる

自分の環境は、192.168.0.xなのでこれを設定してみる

$ sudo vim /etc/samba/smb.conf 
 
[global]
interfaces = 192.168.1. 127.0.0.0/8 eth0

【チェック4】パスワードを設定したか確認する

接続したいユーザのパスワードをチェックする
なければ登録が必要

//確認方法
pdbedit -L
//ユーザ名、uid、フルネームの順で表示される

//無ければ登録 (ユーザー : pi)
sudo smbpasswd -a pi

【エラー】samba再起動で「Failed to restart samba.service: Unit samba.service is masked.」と出る場合

下記実行時に、「Failed to restart samba.service: Unit samba.service is masked.」が出る

$ sudo service samba restart

この場合次で置き換え可能

//こっち
$ sudo service nmbd restart
$ sudo service smbd restart
$ sudo service samba-ad-dc restart

//もしくは既出だが
$ sudo /etc/init.d/samba restart
[ ok ] Restarting nmbd (via systemctl): nmbd.service.
[ ok ] Restarting smbd (via systemctl): smbd.service.
[ ok ] Restarting samba-ad-dc (via systemctl): samba-ad-dc.service.

【iOS11 / CoreML】自作した回帰予測モデル(.mlmodel)の結果を得る実装方法 (Regression, VNCoreMLRequest)

iOS11がリリースされて3日経過したが、本家が提供するモデルはMobileNet、SqueezeNet、Places205-GoogLeNet、ResNet50、Inception v3と分類モデルが多く、サンプルコードも分類しか見つけられない。
なので、今回は回帰予測モデル(具体的には、1次元配列 : 3次元ベクトル回帰)について説明。(世間で回帰関連の記事もないので)

説明すること:回帰の場合の実装方法(比較として分類の場合も記載)
説明しないこと:CoreMLの基本的な使い方、実行方法(巷に溢れているので割愛)

参考資料

本家で提供される学習済みモデル一覧
Machine Learning - Apple Developer

CoreMLの分類サンプルコード
Classifying Images with Vision and Core ML | Apple Developer Documentation

回帰モデルの説明

自作したモデルは、1次元配列(3次元ベクトル)を求めるようなモデル。
(まぁ多次元配列でもスカラーでも、話は同じだったので割愛)
Kerasで学習させた回帰モデルをCoreMLToolsを用いて変換する。

変換イメージ

  • Keras model (.h5) → [CoreMLTools] → CoreML model (.mlmodel)

生成されたモデル (例えば、MyRegression.mlmodel) はXcodeでプロジェクトにドラック&ドロップすることで見える。

モデルの確認方法

  • プロジェクト上にドラック&ドロップした自作モデルをクリック -> [Model Evaluation Parameters] -> [outputs]

モデル情報は次の通り。

Name Type Description
output1 MultiArray<Double, 3> (未記入)

MultiArrayとして、3要素のDouble型1次元配列を回帰するモデルとなっていると確認できる。

[分類/回帰]モデルをロード

なんてことはない、分類も回帰も同じ手順

//学習済みmodelをロード (分類)
let model = try! VNCoreMLModel(for: MyClassification().model) # 提供される学習済みであればMobileNet()とか

//学習済みmodelをロード (回帰)
let model = try! VNCoreMLModel(for: MyRegression().model)

[分類/回帰]モデルと結果を受け取るコールバックを設定

ここも特に差はない、分類も回帰も同じ手順

//分類
let coremlRequest = VNCoreMLRequest(model: model, completionHandler: { (request, error) in
self?.processClassification(for: request, error: error)
})

//回帰
let coremlRequest = VNCoreMLRequest(model: model, completionHandler: { (request, error) in
self?.processRegression(for: request, error: error)
})

[分類/回帰]コールバック関数 / 結果の読み取り【最重要】

ポイントは下記で、分類と回帰でクラスを使い分ける

分類の場合 回帰の場合
VNClassificationObservation VNCoreMLFeatureValueObservation
//分類(VNClassificationObservation)
func processClassification(for request: VNRequest, error: Error?) {
    guard let observations = request.results as? [VNClassificationObservation] else { fatalError() }
    guard let best = observations.first else { fatalError() }
  //best.identifier : best時のクラス情報
    //best.confidence : best時の信頼度
}

//回帰(VNCoreMLFeatureValueObservation)
func processClassification(for request: VNRequest, error: Error?) {
    guard let observations = request.results as? [VNCoreMLFeatureValueObservation] else { fatalError() }
    guard let values = observations[0].featureValue.multiArrayValue!
    //valuesはここでArrayになっているので
    //あとはランダムアクセスすればスカラー値が取得できる
    //values[0], values[1], values[2]
}

ちなみに、回帰でbservations[0]の0は、学習時のバッチサイズの問題だと思っている。(確か)

感想

  • 単純な回帰だけでなく生成モデルとかやる場合は使いそう。
  • 17/09/23時点では、 Webで調べても全く記事が無く、世間はあまり回帰に興味が無いのか?
  • もっといい方法があれば教えてください。

Linuxのジョブ/プロセス(jobs/process)関連のコマンドまとめ (起動、中断、終了、検索、ジョブ強制終了、ログアウト継続実行)

ubuntu/raspberry piでの開発で重宝しているコマンドを覚書
今回は、hoge.pyというスクリプトを例にとる

プロセスを起動する方法

$ python hoge.py   #フォアグラウンド
$ python hoge.py & #バックグランド
[1] 20664          # [ジョブID]プロセスID

特定のプロセスを検索する方法

hoge.pyを検索する場合

$ ps aux | grep hoge.py
pi       20664  100  0.5   8992  4992 pts/0    R    15:44   4:01 python hoge.py
pi       20744  100  0.5   8992  4916 pts/0    R    15:46   2:30 python hoge.py
pi       20745  3.1  0.5   8992  4928 pts/0    T    15:46   0:04 python hoge.py
pi       20903  0.0  0.1   4272  1844 pts/0    S+   15:48   0:00 grep --color=auto hoge.py

3回実行しているとこんな感じ

フォアグラウンドのプロセスを終了する方法

python hoge.pyを実行中に下記コマンド

ctrl+c 強制終了 ジョブにから消える
ctrl+z 中断 ジョブに残る(Stopped)

バックグランドのプロセスを終了する方法

方法1. python hoge.py &を実行すると、実行直後のログに" [ジョブID]プロセスID "が出力されるのでそれをメモしてkill
方法2. プロセスを検索してkill

プロセスを検索する

$ ps aux | grep hoge.py

ここで見つかるプロセスIDをメモ

プロセスkill

$ sudo kill プロセスID

ジョブを確認する方法

現在のジョブを確認するコマンドは下記

$ jobs
[1]   Running                 python hoge.py &
[2]-  Running                 python hoge.py &
[3]+  Stopped                 python hoge.py

Ctrl+Zで終わるとstatusが[Stopped]になっている
これを再起動したり、強制終了させる方法は下記

中断したジョブをフォアグラウンド/バックグラウンドで再開する方法

フォアグラウンドで再開する場合

$ fg %ジョブ番号

バックグラウンドで再開する場合

$ bg %ジョブ番号

ジョブの切り替えは下記
Stoppedのジョブをバックグランドで再開し、フォアグラウンドに切り替える場合

$ bg %ジョブ番号 # バックグランドで再開
$ fg %ジョブ番号 # バックグランドからフォアグラウンドに切り替え

中断したジョブを強制終了する方法

中断した場合はstatusがstoppedになるので、ジョブを終了する

$ sudo kill %ジョブ番号

※実際に即ジョブが強制終了されるわけではないので、少し待つ

ログアウト後もコマンドを実行する方法

sshで繋いでいると切りたくなることがある
(私の場合、特に機械学習とか)
下記コマンドで可能

sudo nohup python hoge.py &

ログアウト後も実行しているコマンドを終了する方法

バックグランドのプロセスを終了する方法と同じ

【お家IT#1】 googleスプレッドシートで管理した賞味期限リストをGASでメールで定期通知してみた

GAS(Google Apps Script)という、Google製サービス上で動くJavascript互換のスクリプトを初めて触ってみた。

家の課題を解決するために。

課題:家の調味料や保存食の賞味期限がいつの間に切れていく

こんなことありますよね。(え、ないですか?)
私と妻で掃除してみたところ65品目ありましたが、この期限を全て把握することは記憶力が鶏レベルの私には無謀です。

調味料だけでもよくわからないほどあった。

写真は調味料棚の一部
f:id:motojapan:20170918224818p:plain
実際棚はこの9倍くらいの容積があるので覚えられるわけがない。

これに対して、賞味期限を定期通知することで、家の中の賞味期限食品を管理し、ゴミを減らし、地球温暖を防ぎ、さらに世界の家庭で蔓延しているらしい「夕食何にする?」という問いを「今晩あれを使って何か作ろう」という積極的な思考へ変換することで、世の中全ての家庭が円満になることを妄想して作ってみた。

概略図
f:id:motojapan:20170919232912p:plain

やったこ

0.夫婦共有のスプレットシートを作る

夫婦共有のGoogle Driveフォルダにスプレットシートを作る。

1.スプレットシートに賞味期限を書く

大掃除がてらキッチン周りの掃除と一緒にやる。
15分程度で書き込み終わり。

こんな感じ。
f:id:motojapan:20170919222810p:plain

最低限、B列とC列があればOK。

説明
A列 今日の日付(A1セル)
B列 品目名
C列 賞味期限
D列 残り日数

今回はスクリプトで完結するので使わないが
A1セルは、[=TODAY]で今日の日付を取得。
D2セルは、[=C2-$A$1]で残り日数を取得。

2.スプレットシートの情報をemailで送るスクリプトを書く

スプレットシートの[ツール]->[スクリプトエディタ]を選択。

ここで空プロジェクトができるので下記を記載。
ソース自体は30行くらいでできた。

ソースコード全体は下にあるが重要なところだけ先んじて覚書。

getRangeについて
  var MAX_LINES = 200
  //シート情報を取得
  var sheet = SpreadsheetApp.getActiveSheet();  
  //B,C列の情報を、配列で取得
  var lines = sheet.getRange(2, 2, MAX_LINES, 2).getValues();  

getRangeでシート情報を配列として取得できるが、多重定義されてるのでいろいろなデータの取り方がある。
本家API referenceを参考にする。

今回の場合下図の対応。
getRange(row, column, numRows, numColumns)

f:id:motojapan:20170919235816p:plain


ここ以外特にハマったとこはなく終わった。

ソースコード全体

下記の通り。

function myFunction() {
  var MAX_LINES = 200
  var today = new Date();

  var sheet = SpreadsheetApp.getActiveSheet();  
  var lines = sheet.getRange(2, 2, MAX_LINES, 2).getValues();  
  var text = ""
  var flag = true
  
  for (var idx in lines) {
      var date      = lines[idx][1]
      var item_name = lines[idx][0]
      if(date == null || date == "") {
        continue;
      }    
      if(item_name == null || item_name == "") {
        continue;
      }
      
      if(flag && date > today) {
        today_info = Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy年M月d日')
        text +=  "------- today : " + today_info + " ------- \n"
        flag = false
      }

      var dt = date.getTime() - today.getTime();
      var diff_day = dt / (1000 * 60 * 60 * 24) + 1;
      
      day_info = Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy年M月d日')
      text +=  day_info + "(" + Math.floor(diff_day) + ") \t" + item_name + "\n"
  }
  
  GmailApp.sendEmail("xxxxxx@gmail.com", "notify", text);  // 私のアドレス
  GmailApp.sendEmail("yyyyyy@gmail.com", "notify", text);  // 妻のアドレス
}

3.定期配信のためのスケジュール設定する

[スクリプトエディタ] → 時計マークの[現在のプロジェクトのトリガー]を選択
月曜の夜と金曜の夜に計画的な買い出しをするため、月曜と木曜に定期配信設定。
f:id:motojapan:20170919224700p:plain

結果

通知されたメールはこんな感じ(やったぜ)。
f:id:motojapan:20170919225936p:plain

雑感

  • GASはかなり簡単にかけた、必要な部分だけで済むので実装が楽
  • デバックやログも簡単に出力可能
  • 翌日だったが、apps-scripts-notifications@google.com からエラーレポートが届くので監視が楽 (下図参考)

f:id:motojapan:20170919225532p:plain


そのうち画像認識と文字認識を組み合わせて、賞味期限リストへの登録を半自動化したいと思う。
それ以外であっても業務効率化で使えそう。

では、はっぴー食べ物らいふを!(今晩はマグロの漬け丼になりました)

【行動認識 #9】t-SNEをtensorboard(Embedding Visualization)で可視化

今回はtensorboardのEmbedding Visualization対応コードを実装し、センサーデータ入力にt-SNEを可視化してみた。

センサー系の文脈ではこれの続き
【行動認識 #6】tensorflowでCAE(Convolutional Auto-Encoder)を実装してみた - MotoJapan's Tech-Memo

行動認識系全般ではこれの続き
【行動認識 #8】OpenPoseのBodyParts推定をTensorFlowでCPU実行する方法 (Linux : Ubuntu 16.04 LTS) - MotoJapan's Tech-Memo

目次

t-SNEとは?

  • t-distributed Stochastic Neighbor Embedding
  • Laurens van der Maaten & Geoffrey Hinton 提唱 [2008]
  • 高次元データに対する次元圧縮/可視化手法の1つ(PCA, ICA, etc)
  • KL情報量を目的関数とした、高次元から低次元へのデータのローカル距離の保つ非線形マッピングを行う
    • 各点間のユークリッド距離の近さを確率分布で表現
    • 分布の近さを測る方法であるKL情報量で、次元圧縮前後の分布の近さを測り、これを最小化する
    • PCAの場合、分散最大化を目的関数とした、高次元から低次元へのデータの線形構造を保つマッピングを行う(線形アルゴリズム
  • ハイパーパラメータ
    • 「perplexity」 (候補数, 複雑度)
      • 小さいほど局所的、大きいほど大域的な特性を考慮できる
      • 5 - 50 に収まるのが良いが、データ数より小さい方がよい
    • 「iteration」 (学習回数)
      • 同じデータセットであっても同じiterationで安定せず、安定するタイミングは不明
      • 構造の再現性を保証しない
    • 「epsilon」 (学習率)
  • 他の手法に比べて
    • 視覚的な図示が綺麗なことが多い (case-by-case)
    • 計算コストとメモリ消費が大きい
    • (PCAで前処理をしてからt-SNEを試行する高速化手法もある)
    • 次元圧縮も可能だが、2次元から2次元への変換さえ可能

詳細は他に譲りますが参考文献をみればよくわかる。

結果

学習データ 7344 [samples]に対してt-SNEを試行。(512次元を3次元に次元圧縮)
わかりやすく可視化するため、1 [sample]毎にmeta情報(index, labelなど)をEmbeddingした。
(慣れればtensorboardはすごく扱いやすい。)

Iterationによるクラスタリング遷移

・600 [iteration]程で大体収束
f:id:motojapan:20170831054135g:plain
見ていて面白い。

3D操作例

センサーから読み取れる行動は、中心から幾つもエッジが生える様な形状を有していた。
f:id:motojapan:20170831054155g:plain
すごく便利。
動画では途中でmetaデータのembeddingを実行してみたが、グラフィックが切り替わり、データ解析が捗る。

形状から読み取れることは

  • 中心部には異なる行動でも、似たような動きのサンプルが集まる
  • エッジに伸びるほど、同じ行動に特徴的なサンプルが集まる

中心部拡大

別々のクラスが密集しているところでわかることは、

  • WALKING_xxx 3つは隣接している
    • 「共通かつ繰り返し動作で根本的に似ていること」がt-SNEから表現
  • STANDING - WALKING_UPSTAIRSは隣接している
    • 体幹を上げるという点で似ていること」がt-SNEから表現
  • STANDING - SITTINGは隣接していない





メモ

tensorflowでの実装メモ

embeddingするmetaデータのフォーマット.tsvについて
1行目は、ヘッダー、2行目以降は、embedding情報。
なので.tsvの総行数は、[ヘッダー(1行)+データ数]となる
(詳細は本家)

今回は次の要領で書き込んだ。

[metadata.tsv]
'index \t activity \t subject \n'
'1 \t WALKING \t 15 \n'
'2 \t WALKING \t 15 \n'
'3 \t SITTING \t 15 \n'
...

python上での実装について
実装は次を参考(すごくわかりやすかったです)
TensorFlow 0.12 のEmbedding Visualizationを動かす - すぎゃーんメモ

学習処理後に下記を追記

embedding_var = tf.Variable(tf.stack([tf.squeeze(y) for y in <labels>], axis=0), trainable=False, name='fc2')

summary_writer = tf.summary.FileWriter(<dst_dir>)
config = projector.ProjectorConfig()
embedding = config.embeddings.add()
embedding.tensor_name = embedding_var.name #'fc2/Variable'
embedding.metadata_path = <meta_path>
projector.visualize_embeddings(summary_writer, config)

sess.run(tf.variables_initializer([embedding_var]))
saver = tf.train.Saver([embedding_var])
saver.save(sess, <store_model_path>)

Embedding Visualizationでできること

  • 2D/3Dへの次元圧縮
  • 次元圧縮の選択肢は、PCA, t-SNE, CUSTOM
  • t-SNEは perplexity, learning rateをGUIで操作
  • 任意点の近傍探索
  • meta情報(複数可能)をembeddingして、Coloring / Sort / Grepが簡単 (超絶便利)

【行動認識 #8】OpenPoseのBodyParts推定をTensorFlowでCPU実行する方法 (Linux : Ubuntu 16.04 LTS)

OpenPoseのBodyParts推定をTensorFlowでCPU実行する方法について。(想定環境はUbuntu 16.04 LTS)

どうしてもTensorFlowで動かしたい人はやってみてもいいかも。(そんな人いる?)
個人的にはTensorFlowのアーキテクチャの方が好きですが。

OpenPoseの大枠を知りたいなら前回を参考。
motojapan.hateblo.jp

アプローチ

OpenPoseのBodyParts推定モデルはCaffeで提供されるので、これをTensorflowモデルに変換すればできるはず。

想定環境

手順

1. OpenPoseのCaffeモデルをダウンロード

本家からportable OpenPose demo 1.0.1.をダウンロード
この中に pose_deploy_linevec.prototxt(モデル情報)、pose_iter_440000.caffemodel(重み情報)が格納されている。

2. Caffeモデル/重みをTensorFlow用に変換

ここ(caffe-tensorflow)のconvert.pyで変換できるらしい。
このスクリプトで、.prototxt→.py、.caffemodel→.npyに変換される。
例えば、model.py/model.npyに変換したとする。

変換方法、変換済みファイルを扱う基本実装の参考
テンソルフローでfinetuning | OpenBook

3. Tensorflowモデルを読み込んでヒートマップを推論

下記で読み込み。
重要なのは、placeholderのテンソル順序で次の通り。
[batch_size, image_height, iamge_width, color_channel]

import tensorflow as tf
# networkはmodel.pyを編集して各自定義(例えばBodyPartsNet)
from model import BodyPartsNet 
# 入力(画像)用テンソルを準備
# [batch_size, image_height, iamge_width, color_channel]
x = tf.placeholder(tf.float32, [None, H, W, 3])
# 出力(ヒートマップ)用テンソルを準備
pred = net.layers['Mconv7_stage6_L2']
#モデルの読み込み
net = BodyPartsNet({'iamge': x})
sess = tf.Session()
sess.run(tf.initialize_all_variables()) 
#重みの読み込み
net.load('model.npy', sess)

後は、forwardのsession.runをすれば動くはず。

BodyParts出力順は本家の通り以下の順。

    POSE_COCO_BODY_PARTS {
        {0,  "Nose"},
        {1,  "Neck"},
        {2,  "RShoulder"},
        {3,  "RElbow"},
        {4,  "RWrist"},
        {5,  "LShoulder"},
        {6,  "LElbow"},
        {7,  "LWrist"},
        {8,  "RHip"},
        {9,  "RKnee"},
        {10, "RAnkle"},
        {11, "LHip"},
        {12, "LKnee"},
        {13, "LAnkle"},
        {14, "REye"},
        {15, "LEye"},
        {16, "REar"},
        {17, "LEar"},
        {18, "Bkg"},
    }

caffe-tensorflowエラー対策

caffe-tensorflowは環境によって時折エラーがでるのでいくつか対策が必要。

エラーケース1:TypeError: Descriptors should not be created directly, but only retrieved from their parent.

convert.pyでの変換時のエラー

TypeError: Descriptors should not be created directly, but only retrieved from their parent.

対策:
protobufのバージョン変更。

pip install protobuf==3.0.0b2

エラーケース2:TypeError: concat() got an unexpected keyword argument 'concat_dim'

networkにconcatが含まれる場合の実行時エラー

TypeError: concat() got an unexpected keyword argument 'concat_dim'

対策:
concatの呼び出し記述が古いのが原因。
tensorflow 1.0.0に対応した記述にNetwork.pyを変更(下記のような感じ)

    @layer
    def concat(self, inputs, axis, name):
-        return tf.concat(concat_dim=axis, values=inputs, name=name)
+        return tf.concat(axis=axis, values=inputs, name=name)

おまけ

1.Caffeの環境を構築するのが億劫な人にはいいかも
Caffe環境構築も大変だし。

2.windowsでは簡単には動かないと思う
理由は、tensorflowがwindows x python 2.7系に対応していない。
そして、convert.pyはpython 2.7系。

最近はiOS stand-aloneでも動かしたりしていて、320x320の画像は5秒くらいで認識できた。
需要があれば方法だけでも纏めようかなー。

以上。

【行動認識 #7】OpenPoseのBodyParts推定をCPU実行してみた雑感

今回は、今年のCVPR2017でも報告されている何かと話題のOpenPoseの内、人体部位-BodyParts-推定をCPUで動かしてみた。

下記のセンサー入力周りとは文脈が変わるが、画像から行動を特定するタスクもあるという点では関連性があるテーマなので今回取り扱う。
【行動認識 #6】tensorflowでCAE(Convolutional Auto-Encoder)を実装してみた - MotoJapan's Tech-Memo

もし、Ubuntu 16.04 x tensorflow x CPUで動かしたい人がいれば次回#8(17/08/29)の記事が参考になるかも。

そもそも、OpenPoseの全体像は、BodyParts推定(18点)からボトムアップ手法で複数人2次元ポーズ推定を実現している模様。
そのうち、BodyParts推定は深層学習で行われており、BodyParts推定だけでも動かして、どんなものなのか把握してみたい。

OpenPoseのCVPR2017 paperを知りたい人は下記。(面白かったです)
[1611.08050] Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields

ライセンス上の注意ですが、「非商用研究目的での使用ということ」、「派生物や改変は可能であるが、派生物や改変は非営利の内部的研究目的でのみ使用すること」に気を付けてください。
商用で利用したい場合、別途契約が必要です。
OpenPose - Realtime Multiperson 2D Keypoint Detection from Video | Flintbox

参考記事

学習済みモデル

最近、本家からportable OpenPose demo 1.0.1.がリリースされている。
例えば、この中にある pose_deploy_linevec.prototxt(モデル情報)、pose_iter_440000.caffemodel(重み情報)が格納されているので、これを駆使するとForward処理ができたりする。
細かい環境、手順はまた今度、纏めようと思う。

一応実行環境としては、

で動かしてみた。

動かしてみて

結構いろいろできそうで面白い。

【画像上に人物が1人の場合】

(左画像)入力画像
(中央画像)ある程度確率の高い結果の上位3座標を取り出してみた
(右画像)適当に関節をつないでみるとポーズ推定っぽい感じになる

f:id:motojapan:20170820213829p:plain

詳細の出力は以下。
関節データの出力は、各関節の座標ではなくピクセル毎のヒートマップとなる。
ポイントは、BodyParts18点+backgroundの19枚のヒートマップが出力されるという点。

雑感ですが、

  • 期待する人物付近で期待通り反応する
  • 期待箇所付近だけでなく、"RHip"、"RKnee"、RAnkle"、"LHip"、LKnee" (id: 8~12) は木の枝の複雑なクロス部分にも微弱に反応している
  • OpenPoseは論文を読むとわかるが、ボトムアップ手法なので深層学習で積極的に人を認識しているようではなさそうという印象







































画像は左上から次の順でBodyParts出力(本家記載)

    POSE_COCO_BODY_PARTS {
        {0,  "Nose"},
        {1,  "Neck"},
        {2,  "RShoulder"},
        {3,  "RElbow"},
        {4,  "RWrist"},
        {5,  "LShoulder"},
        {6,  "LElbow"},
        {7,  "LWrist"},
        {8,  "RHip"},
        {9,  "RKnee"},
        {10, "RAnkle"},
        {11, "LHip"},
        {12, "LKnee"},
        {13, "LAnkle"},
        {14, "REye"},
        {15, "LEye"},
        {16, "REar"},
        {17, "LEar"},
        {18, "Bkg"},
    }

【画像上に人物が複数人の場合】

(左画像)入力画像
(右画像)ある程度確率の高い結果をプロット
f:id:motojapan:20170820222253p:plain

出力ヒートマップは例えば、LShoulder、hip、backgroundはこんな感じ。

雑感ですが、

  • LShoulderは、3人が写る画像上で、特徴的に3箇所強い反応
  • hipは、斜め後ろ向きの女性において、少し弱い反応(他の2人に比べて)
  • 男性2人の輪郭がぼやけていても認識が割とできている(すごい)
  • 同一部位に対して複数検出するヒートマップを、ボトムアップに上手く複数人ポーズ推定ができるのがOpenPoseの強みということかな(OpenPoseすごい)







スループット

画像サイズにも依るが、320 x 240 で 2秒、640 x 480 で 10秒程度だった。
あと同じ画像サイズでも人数が増えると、スループットが変化した。(たまたまか?)
iOS stand-aloneでは機種にも依るが、320 x 320であれば5秒程度。

CPUでもいろいろ出来そうだなぁという印象。

おわりに

近々、手元のノートPC windows 8/10 でも動かしてみたな。
本家では、windowsでも実行できるバイナリも提供され始めたので、おもしろそう。

根気強く頑張れば、Tensorflowでも動くしね。

以上。