やりたいこと
darknet
というリアルタイムで物体検出できるオープンソースがあって、
これがけっこうすごい!
これをpythonでサーバ化して、イメージファイルを送ったら、
とりあえず、オブジェクトの名前とスコアが返ってくるようにしたい
https://pjreddie.com/darknet/yolo/
上記にチュートリアルとかあるので、まずはそちらをごらんください
前提
- darknetが動作すること
-
python 2系統であること-> python3系統であること(18/08/11)
動作環境
-
Ubuntu 14.04-> Ubuntu 16.04.4(18/08/11) -
python 2.7.13-> python 3.5.2 -
darknetのリビジョン
1b001a7f58aacc7f8b751332d3a9d6d6d0200a2d
->9a4b19c4158b064a164e34a83ec8a16401580850
最新のソースではバグが出る可能性はあります
ソースおよび動作方法
ソース
https://github.com/komorin0521/darknet_server
動作方法
-
darknet
のチュートリアルを動作させれるようgit cloneしてmake
する
できれば、darknet単独で予測が動くようにしてください
(チュートリアルで利用する~~yolo.weights
~~yolov3.weights
を利用します) -
ソースって書いてあるソース一式を
darknet/python
のフォルダにコピーしてください
()libdarknet.so
と同階層にdarknet_server.py
など全てのファイル・フォルダをおいてください -
必要なモジュールを
pip
でインストールしてください
(sudo) pip install -r requirements.txt
著者はpyenv + pyenv-virtualenv
環境を利用してます。
sudo
権限が必要な場合は、つけてください -
PYTHONPATH=${darknetPATH}/python python3 darknet_server.py -cf ./cfg/yolov3.cfg -df ./cfg/coco.data -wf ./yolov3.weights -ud ./upload
を実行してください
うまくいけけばdarknet
を単独で動作させた場合と同様にネットワークがロードされます
確認方法
curl
で以下のように画像ファイルをアップロードしてください
curl -XPOST -F file=@./data/person.jpg http://localhost:8080/detect
jsonで下記のように結果が返ってこればOKです!
{
"result": [
{
"name": "dog",
"score": 0.8622361421585083
},
{
"name": "person",
"score": 0.8603283762931824
},
{
"name": "horse",
"score": 0.8156660199165344
}
],
"status": "200"
}
いろいろと補足
githubのdarknet
のところに、python/darknet.py
があったので、それを参考にしました。
この中身を見てみると、config
ファイルとweight
をload_net
でロードしていて、load_meta
にてメタ情報(といってもこの場合は、クラス名に一致するオブジェクト名(cfg/coco.data
のnames
)を引っ張ってきているようでした。
さらに、ネットワークをロードしておいた上で、detect
している
また、結果を見てみると、N個の物体検出結果の配列で、
個々の検出結果はどうやら名前、スコア、バウンディングボックスの位置になっているっぽい
上記を前提に~~yolo.py
~~のdarknet.py
の中に定義したYolo
クラスの初期化メソッドで
ネットワークをロードしておいて、
/detect
が呼ばれたらdetect
を呼ぶようにすればよいのかと思い、そのようにしています。
なお、サーバからのレスポンスについては、記事を書いた当初(2017年10月9日)では、名前とyoloのスコアを返していましたが、2017年10月11日にバウンディングボックスの値も返すようにしました。
また、記事を書いた当初はHOSTやポートをハードコーディングしていましたが
引数で受け取れるように変更してます。
Flask
も(無駄に?)class化したら、
ちょっとはまったけど、stackoverflowにて解決!
はまったポイントは、FlaskではURIの定義を@app.route('/hoge')
で定義するけれど、@self.app.route('/detect')
ってしたら、エラーになった。
解決策を探してみると、どうやら'add_url_rule`(初めて知った!)にてURIを定義すればよいとのこと。
めでたしめでたし。
2017/11/1 APIの仕様追加
yolo本家では、確信度はdetectionを行う際に指定していたので、
APIでも指定できるように変更しました。
なお、デフォルト値は、(本家と同様なはずの)0.25にしてます。
curl -XPOST -F thresh=0.1 -F file=@./data/person.jpg http://localhost:8080/detect
と、-F
オプションで指定可能で、値を小さくするとより多くの物体検出結果が帰ってきますが、その分ご認識も多くなるので、ご注意ください。
# 2018/08/11 更新
- python3対応しました。python2とpython3で、ctypesの定義が変わっていたことに気づきました。参考:darknet yolo v3をpython3(.5.2)で試す
- yolov3対応.darknet側が更新されたので、現時点で最新版で動作することを確認しました。
- 予測埋め込み画像の更新しました。完璧ではないですが、予測結果画像を少しyolo本家に似せるように修正しました。
参考
-
Flask
- http://flask.pocoo.org/docs/0.12/api/#url-route-registrations
- https://stackoverflow.com/questions/40460846/using-flask-inside-class
- https://stackoverflow.com/questions/15421193/using-defaults-with-app-add-url-rule-in-flask
- https://stackoverflow.com/questions/39985064/view-multipart-form-request-parameter-values-using-flask