大量の写真の中から猫写真を抽出

# はじめに
猫の居る会社で有名なqnoteのアドベントカレンダーが今年も始まります。
今年は書き手も増え、iOS/Android/Unity/PHP/JavaScript/サーバレスアーキテクチャ/IoTなど幅広くトレンドを追いかけた記事を豊富にお送りできるかと思います。どうかよろしくお願いします。

さて、一日目ですが今年はポエムではなくわりとまじめ(？)に書きます。

弊社[公式Facebook](https://fb.me/qnote.inc) では猫達の成長記録も兼ねて毎日「猫社員」の写真をアップしています。おかげでPCの写真フォルダには猫写真がたくさん。普段の旅行の写真などを分類しようとしても数が多すぎて大変。そこで今流行りのディープラーニングでどうにかしようと考えてみました。


# やりたいこと
フォルダの中の無数の写真データの中から猫画像だけを分類したいというのが今回の目的。今回は物理的にファイルを複製・移動するのではなく、macOS のファイル拡張属性でファイルに直接「猫さん」タグを付与します。尚、既にファイルにタグがついている場合は一旦クリアされるので試される方はご注意を。尚、プラットフォームが当たり前のように macOS ですが後述の TensorFlow が Windows 非対応なのでご了承下さい。（って思ったら11月29日に出てますね！:stuck_out_tongue: [リリースノート](https://github.com/tensorflow/tensorflow/releases/tag/0.12.0-rc0)）

# TensorFlow
ディープラーニングのツールはいくつかありますが、今回は比較的有名な TensorFlow (てんそるふろー) を使用したいと思います。と言っても学習ファイルから作成するのは大変なので公式のソースにある python スクリプトで、トレーニング済みの学習データ inception-v3 を使用するズボラっぷり。まぁ半分お遊びなので許してください。


# インストール
macOS 最新の Siera にプリインストールの python では tensorflow パッケージがインストール出来ないため、まずは python の仮想環境構築パッケージである virtualenv をインストールします。

```bash
$ sudo pip install --upgrade virtualenv
```

うまく入ったら仮想環境のルートディレクトリを作成。ここでは ~/Desktop/tensorflow に作成。

```bash
$ virtualenv --system-site-packages ~/Desktop/tensorflow
```

生成されたディレクトリに移動して、仮想環境を起動。

```bash
$ cd ~/Desktop/tensorflow
$ source bin/activate
```

pip コマンドで tensorflow パッケージをインストール

```bash
$ pip install tensorflow 
```

最後にTensorFlow のソースを落としてきます。

```bash
$ git clone https://github.com/tensorflow/tensorflow
```

以上で準備完了。


尚、仮想環境を終了する場合は deactivate コマンドを実行します。

```bash
(tensorflow)% deactivate
```

# とりあえず実行

git から落としたソースディレクトリに入り、tensorflow/models/image/imagenet ディレクトリに移動してclassify_image.py スクリプトを引数無しで実行します。

```bash
$ cd tensorflow/tensorflow/models/image/imagenet
$ python ./classify_image.py
```

すると下記のような出力が。

```
W tensorflow/core/framework/op_def_util.cc:332] Op BatchNormWithGlobalNormalization is deprecated. It will cease to work in GraphDef version 9. Use tf.nn.batch_normalization().
giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca (score = 0.88493)
indri, indris, Indri indri, Indri brevicaudatus (score = 0.00878)
lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens (score = 0.00317)
custard apple (score = 0.00149)
earthstar (score = 0.00127)
```

2行目以降に giant pand とか、panda bear とか出ていますが、classify_image.py を引数無しで実行するとデフォルトである下記パンダ写真を使用します。うん、たしかにパンダ。

![cropped_panda.jpg](https://qiita-image-store.s3.amazonaws.com/0/6102/25e7cef6-c9fb-1c6c-49dc-0575384dc4af.jpeg)


# 試しに「にゃー」な写真を

では引数を指定して任意の写真を認識してみましょう。classify_image.py の引数に  --image_file=画像パス を指定してみます。では試しにこんな写真を。

![IMG_0469.jpg](https://qiita-image-store.s3.amazonaws.com/0/6102/d7b19e0b-b971-4c02-39ea-2e5bd3dc1bb3.jpeg)

誰がどう見ても猫ですね。しかもにゃーって言ってますね。
では認識。

```bash
python classify_image.py --image_file=./IMG_0469.jpg
```

はい、結果

```
Egyptian cat (score = 0.44227)
tiger cat (score = 0.28209)
tabby, tabby cat (score = 0.19577)
lynx, catamount (score = 0.00442)
Siamese cat, Siamese (score = 0.00380)
```

いずれも猫っぽい結果ですね。日本語に訳すとそれぞれ、

Egyptian cat = エジプシャンキャット（ピラミッドのやつ？？）
tiger cat = ジャガー
tabby = タビーキャット
lynx = オオヤマネコ
catamount = ピューマ
Siamese = シャム猫

となります。エジプシャンキャットのスコア値がものすごく引っかかりますが、概ね猫という事で。（雑）
scoreは完全一致を1.0とみなした場合の近似値で、この場合44%の確率でエジプシャンキャットってことになりますね。壁画？

ついでにもういっちょ。今度は複数の猫が入った写真「弊社特製猫団子」を認識。

![IMG_0479.jpg](https://qiita-image-store.s3.amazonaws.com/0/6102/468a5046-d633-1cc4-188a-4d16df987c76.jpeg)

では再度実行。

```bash
$ python classify_image.py --image_file=./IMG_0479.jpg
```

はい結果。

```
W tensorflow/core/framework/op_def_util.cc:332] Op BatchNormWithGlobalNormalization is deprecated. It will cease to work in GraphDef version 9. Use tf.nn.batch_normalization().
tabby, tabby cat (score = 0.62249)
tiger cat (score = 0.21690)
Egyptian cat (score = 0.03015)
Bouvier des Flandres, Bouviers des Flandres (score = 0.00387)
bull mastiff (score = 0.00255)
```

ちゃんと認識していますね。写真の中の特徴ある1匹を認識しているのか、全てをひっくるめて猫と認識しているのかまでは調べきれていません。たぶん前者だと思います。


# タグ付け

では次にタグ付けです。macOS では Maverics あたりからファイルやフォルダにタグを付けることが可能になりました。一度付けたタグのファイルは Finder のサイドバーからフィルタリングして表示できます。
タグ付けはコマンドラインからも可能で、下記のコマンドで付与できます。

```bash
$ xattr -w com.apple.metadata:_kMDItemUserTags '("猫さん")' IMG_0042.jpg 
```

こうすると Finder に「猫さん」というタグが表示され、クリックすると「猫さん」でタグ付けされたファイルが表示されるわけです。

![スクリーンショット 2016-12-01 0.11.48.png](https://qiita-image-store.s3.amazonaws.com/0/6102/199d6569-0fec-dd54-8c8b-5f201f791f53.png)

今回はコマンドを1回1回手動で実行するのではなく classify_image.py で猫と認識したファイルだけタグ付けですので、手順としては、

1. 指定フォルダのファイルパスを取得
2. classify_image.py で認識結果を出力
3. 結果の中に「cat」という文字があれば猫写真！（雑！）
4. xattr で「猫さん」タグを付与

という手順になります。全部シェルで出来ちゃいますね。ということで完成したスクリプトがこちら。

```bash
#!/bin/sh

IMAGE_PATH="~/Desktop/pics/*.jpg" # 画像ファイルのパス

FILES=$(find $IMAGE_PATH)

for FILE in $FILES;
do
    RESULT=$(python classify_image.py --image_file=$FILE 2> /dev/null)
    if [[ `echo $RESULT | grep "cat"` ]]; then
       xattr -w com.apple.metadata:_kMDItemUserTags '("猫さん")' $FILE
    fi
done
```

## 完成、そして精度確認
6000枚くらいの写真をスキャンしましたが、一晩かかりました💦(classify_image.pyスクリプトが学習ファイルのダウンロードと展開を毎回行うので実際の認識はもっと短い）
6000枚中約2600枚の写真を猫と認識してくれました。たくさんありすぎて精度を出すのが難しいですが、8〜9割程度という感じです。

![スクリーンショット 2016-12-01 11.12.05.png](https://qiita-image-store.s3.amazonaws.com/0/6102/d10eb1a7-99d3-e168-2ca3-d9a79d64fe85.png)


## おわりに
実は今回ズボラをしたためこのスクリプトには大きな欠点がありました。それはスコア値を一切考慮していないこと。猫だと認識した写真の8〜9割が猫だったというだけですし、もちろん取りこぼしもあります。TensorFlow の猫認識が8〜9割というわけではないので念のため。
スコアについてはシェルじゃなくてちゃんと Phthon に手を入れれば簡単に出来ちゃいますね。あとは学習データの訓練すればもっと認識率は上がります。このあたりはまだまだ勉強中なので引き続き精進。

今回は TensorFlow を使用しましたが、Caffe というディープラーニングライブラリを使用して、猫の品種識別をされた方がいらっしゃいます。こちらも猫好きエンジニアにとっては非常に興味深い記事ですので是非お読み下さい。

http://qiita.com/wellflat/items/0b6b859bb275fd4526ed