dockerでtensorflowとOpenCVを使う

# 前置き

以下でtensorflowとOpenCVを使うがセットアップが面倒。
[ディープラーニングで顔写真から巨乳かどうかを判別してみる (うまくいったか微妙)](http://qiita.com/summer4an/items/db0124eee8103c1d3b85)

特にpythonのライブラリとかは環境が混ざりやすいっぽい。
virtualenvを使うのが正攻法なのかもしれないがあまりなじまず。

docker上にセットアップする方法が私には考えやすくてしっくりきたのでメモ。
しかもtensorflowにはdockerイメージが用意されているので楽。

ホストはubuntu16.04。


# dockerインストールとtensorflowのdockerコンテナ立ち上げ

```bash:
$ sudo apt-get install docker.io

$ docker pull gcr.io/tensorflow/tensorflow:latest
$ docker run -it --name tensorflow1 -p 8888:8888 gcr.io/tensorflow/tensorflow /bin/bash
```

これでもうtensorflowが使える。
以下の「Run TensorFlow from the Command Line」の箇所を参照し動作確認しておく。
https://www.tensorflow.org/versions/r0.10/get_started/os_setup.html#test-the-tensorflow-installation

以下はdocker上の操作。

# OpenCVインストール

```bash:
$ apt-get install wget unzip
$ apt-get install build-essential cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev

$ mkdir /opencv
$ cd /opencv

$ wget https://github.com/Itseez/opencv/archive/3.1.0.zip
$ unzip 3.1.0.zip
$ cd opencv-3.1.0
$ mkdir build
$ cd build

$ cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_IPP=ON ..

$ make -j7
$ make install
```

以下等で動作確認。

```C++:face_kirinuki.cpp
//画像の切り抜き http://reiji1020.hatenablog.com/entry/2014/10/28/225829

//実行方法は以下。
//  hoge.exe ターゲットのファイル名 出力ファイル名 cascadeファイル名
//戻り値は、0が正常終了、100より下は引数不足等の実行時の異常、100以上はOpenCVは走ったが検出出来なかった等

#ifdef __GNUC__
  #include <stdio.h>
  #include <stdlib.h>
#else
  #include <windows.h>
#endif

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

#define FACE_KAKUDAI_WARIAI 1.2 //例えば検出された顔が100px四方だとしたら、100×FACE_KAKUDAI_WARIAIpxのサイズで切り抜く

int main(int argc, char const *argv[]) {
    if(argc!=4){
        printf("error. too few argument.\n");
        exit(1);
    }
    printf("target file is %s\n", argv[1]);

    Mat image = imread(argv[1]);
    if(image.data==NULL){
        printf("error. can not read source picture file.\n");
        exit(2);
    }
    printf("image size width=%d height=%d\n", image.size().width, image.size().height);

    string cascade_filename = argv[3];
    CascadeClassifier cascade;
    if(cascade.load(cascade_filename)!=true){
        printf("error. can not read cascade file.\n");
        exit(3);
    }

    vector<Rect> faces;
    cascade.detectMultiScale(image, faces, 1.1, 3, 0);
    printf("detect %zu faces.\n", faces.size());

    if(faces.size()!=1){
        printf("too many faces or no faces detected. exit.\n");
        exit(100);
    }

    for (unsigned int i = 0; i < faces.size(); i++) {
        printf("face[%d] x=%d y=%d width=%d height=%d\n", i, faces[i].x, faces[i].y, faces[i].width, faces[i].height);

        //切り抜くサイズを少し大きくした時に画像をはみ出るかどうか確認。
        int kakudai_size = (int)(faces[i].width * (FACE_KAKUDAI_WARIAI-1) / 2); //左右で拡大するので2で割っている。
        int kirinuki_x = faces[i].x-kakudai_size;
        int kirinuki_y = faces[i].y-kakudai_size;
        int kirinuki_width  = faces[i].width + kakudai_size*2;
        int kirinuki_height = faces[i].height+ kakudai_size*2;
        if( kirinuki_x<0 ||
            kirinuki_y<0 ||
            image.size().width  <= kirinuki_x+kirinuki_width ||
            image.size().height <= kirinuki_y+kirinuki_height ){
            printf("face[%d] located on corner. skip.\n", i);
            exit(101);
        }

        Mat cut_img(image, Rect(kirinuki_x, kirinuki_y, kirinuki_width, kirinuki_height));
        imwrite(argv[2], cut_img);
        printf("face[%d] outputed as \"%s\"\n", i, argv[2]);

        rectangle(image, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height), Scalar(0, 200, 0), 1, CV_AA);
        rectangle(image, Point(kirinuki_x, kirinuki_y), Point(kirinuki_x + kirinuki_width, kirinuki_y + kirinuki_height), Scalar(200, 0, 0), 1, CV_AA);
    }

    //imshow("detect face", image);
    //waitKey(0);

    return 0;
}
```

```bash:
$ g++ -g `pkg-config --cflags opencv` -o face_kirinuki face_kirinuki.cpp -L/usr/local/share/OpenCV/3rdparty/lib/ `pkg-config --libs opencv`

$ face_kirinuki target.jpg output.jpg haarcascade_frontalface_alt2.xml
```

以上でdocker上でtensorflowとOpenCVが使えるようになったはず。


# 参考リンク
- dockerインストール http://www.atmarkit.co.jp/ait/articles/1406/10/news031_2.html
- tensorflowインストール https://www.tensorflow.org/versions/r0.10/get_started/os_setup.html#docker-installation
- OpenCVインストール http://docs.opencv.org/3.0-last-rst/doc/tutorials/introduction/linux_install/linux_install.html