Python
人工知能
画像認識
Darknet

リアルタイム物体検出システムdarknetをpythonでサーバ化

やりたいこと

darknet というリアルタイムで物体検出できるオープンソースがあって、
これがけっこうすごい!
これをpythonでサーバ化して、イメージファイルを送ったら、
とりあえず、オブジェクトの名前とスコアが返ってくるようにしたい

https://pjreddie.com/darknet/yolo/

上記にチュートリアルとかあるので、まずはそちらをごらんください

前提

  1. darknetが動作すること
  2. 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

    最新のソースではバグが出る可能性はあります

ソースおよび動作方法

ソース

  1. https://github.com/komorin0521/darknet_server

動作方法

  1. darknetのチュートリアルを動作させれるようgit cloneしてmakeする できれば、darknet単独で予測が動くようにしてください (チュートリアルで利用するyolo.weights yolov3.weightsを利用します)
  2. ソースって書いてあるソース一式をdarknet/pythonのフォルダにコピーしてください (libdarknet.soと同階層にdarknet_server.pyなど全てのファイル・フォルダをおいてください
  3. 必要なモジュールをpipでインストールしてください
    (sudo) pip install -r requirements.txt
    著者はpyenv + pyenv-virtualenv環境を利用してます。
    sudo権限が必要な場合は、つけてください

  4. 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ファイルとweightload_netでロードしていて、load_metaにてメタ情報(といってもこの場合は、クラス名に一致するオブジェクト名(cfg/coco.datanames)を引っ張ってきているようでした。

さらに、ネットワークをロードしておいた上で、detectしている

また、結果を見てみると、N個の物体検出結果の配列で、
個々の検出結果はどうやら名前、スコア、バウンディングボックスの位置になっているっぽい

上記を前提にyolo.pydarknet.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 更新
1. python3対応しました。python2とpython3で、ctypesの定義が変わっていたことに気づきました。参考:darknet yolo v3をpython3(.5.2)で試す
2. yolov3対応.darknet側が更新されたので、現時点で最新版で動作することを確認しました。
3. 予測埋め込み画像の更新しました。完璧ではないですが、予測結果画像を少しyolo本家に似せるように修正しました。
20180811_180947_person_pred.jpg

参考