dlibのお試し
dodosoftアドベントカレンダーネタで、ちょっと変わった顔画像認識をやってみてます!dlibとkeras使ってやる予定!ほんとにできるかは分からんけど・・・。
今回はそれに向けた環境構築なので面白くないやつ! Dockerで環境を構築し、その環境でアップロードした画像から顔のパーツを検出する、簡単なサンプルアプリを動かしました!
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/
次のような、つまらないフォームが表示されます。 顔を含む好きな画像ファイルをアップロードしてみてください。するとこう。
[ { "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!おもろいぞー!