dlibのお試し

dodosoftアドベントカレンダーネタで、ちょっと変わった顔画像認識をやってみてます!dlibとkeras使ってやる予定!ほんとにできるかは分からんけど・・・。

www.adventar.org

今回はそれに向けた環境構築なので面白くないやつ! Dockerで環境を構築し、その環境でアップロードした画像から顔のパーツを検出する、簡単なサンプルアプリを動かしました!

Dockerfile作成

Dockerfile

初めてのDockerfile作成でしたが、作成する上で特に躓いたところはなかったので成果物だけ!気付いてないだけで躓いてるだけかもしれないけど! 以下のようなライブラリを含むイメージのDockerfileです。

ライブラリ 目的
OpenCV3 画像の読み込みや編集に利用。
dlib 顔のパーツの認識に利用。
Flask Web APIとして公開するために利用。
Keras Deep Learningに利用。
Pandas データの読み込み、書き出しに利用。
scikit-learn その他機械学習に利用。
jupyter 機械学習の検証に利用。

注意: Dockerfileは、いかに最小の構成に抑えてイメージファイルを小さくするか、というところが腕の見せどころの一つのようですが、その点作成したDockerfileは非効率です。 できるだけ小さいイメージにしようと、Minicondaをベースに使ってみたものの、いろいろインストールしてたら非常にでかくなりました。miniじゃない!!まあ今回とりあえず使ってみたかったので! ちっちゃくしたかったら、alpineベースで、自前で各種ライブラリビルドしたらいいかもね!

Dockerイメージのビルド&実行

Dockerfile等、イメージ作成に必要なファイルを取得します。

$ git clone https://github.com/uphy/image-recognition-example.git
$ cd image-recognition-example

上記リポジトリにはサイズが大きすぎて含めませんでしたが、追加で必要なファイルが一つあります。顔パーツの学習済みのデータです。shape_predictor_68_face_landmarks.dat.bz2をダウンロードして、解凍して、上記リポジトリワークツリーのwork直下においてください。 こんな感じになるように。

$ ls -l work
total 194736
-rw-r--r--  1 uphy  staff       267 12  1 21:55 client.py
-rw-r--r--  1 uphy  staff      2913 12  1 21:54 main.py
-rw-r--r--  1 uphy  staff  99693937 12  1 20:46 shape_predictor_68_face_landmarks.dat

続いて、以下のコマンドで、Dockerイメージのビルドし、コンテナを開始します。取得するファイルは2GBほどありますので、気長にお待ち下さい。ちゃんとビルドできるといいですね!

$ ./start.sh

start.shの中でやっていることは非常に単純です。docker buildでイメージをビルド、docker runで作成したイメージを用いたコンテナの作成&起動そんだけ!Dockerfile作成はtry and errorの繰り返しなので、こういったシェルスクリプト書いておくと便利でした。

workディレクトリはコンテナと共有しているため(docker run -vオプションで指定)、ホストマシンでソースを書き換えると、変更がそのままコンテナにも反映されるようになっています。

start.shが実行完了したら、以下のアドレスにアクセスしてみてください。

http://localhost:5000/

次のような、つまらないフォームが表示されます。 f:id:uphy:20161201231453p:plain 顔を含む好きな画像ファイルをアップロードしてみてください。するとこう。

[
  {
    "bounds": {
      "height": 346, 
      "width": 314, 
      "x": 421, 
      "y": 234
    }, 
    "parts": {
      "contour": [
        {
          "x": 421, 
          "y": 332
        }, 
        ...
      ],
      "eye_left": [
        ...
      ]

こんな感じで、画像のどこに顔があって、顔のパーツはどのように構成されているかが点の集合で分かります。

こんな高度なことが簡潔なプログラムで書けるdlibすごい!

ちなみにブラウザからの他に、通常のHTTP POSTでも認識できるようにAPIにしてます。どうせ使わないけど興味本位で。client.pyが、リクエストを送るコマンドです。

躓き

プログラムはこちら。 dlibもOpenCVもFlaskも、世の中にたくさん情報が溢れてるので、躓いた所だけ。

Shape Predictorの68の点

shape_predictor_68_face_landmarks.datは、顔の各パーツの点の集合を返してくれます。 どの点からどの点までが目で、どの点からどの点まで鼻で・・というのがわからないので調べました。結果はこんな感じ。

PARTS = {
    'contour': range(0, 17),
    'eyebrow_right': range(17, 22),
    'eyebrow_left': range(22, 27),
    'nose': range(27, 36),
    'eye_right': range(36, 42),
    'eye_left': range(42, 48),
    'mouth': range(48, 68)
}

Flaskのリクエストボディから画像読み込み

detect_faces()関数で、顔検出のAPIを公開しています。 画像のバイナリデータをPOSTすると、顔のパーツなどをjsonで返すというものです。 そのリクエストボディ読み込みにちょっと癖がありました。

Flaskではリクエストボディは、request.get_data()でbytes型として取得できます。これをインメモリでOpenCVで読み込むには、以下のように、bytearrayに変換し、numpy配列に変換しないとOpenCVの画像デコード関数(cv2.imdecode())はデコードしてくれませんでした。Pythonの*-likeオブジェクト、よーわからん。

    data = np.asarray(bytearray(request.get_data()), dtype=np.uint8)
    image = cv2.imdecode(data, cv2.IMREAD_COLOR)

以上、環境構築+ちょっとお試しでした。

次回は、今回構築した環境で実用的な認識をやってみる予定! おもしろい画像認識は成功するのでしょうか!

明日はchikulla!おもろいぞー!