LINE動物図鑑の作り方

#概要
スマホで撮影した動物の写真をLINE Bot宛てに送ると、
動物の種類を教えてくれる動物図鑑。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/c5d2d201-9d35-e177-d64a-e5784340ce9c.png)

###更新履歴
2019/09/07 デグーとチンチラとモルモットを追加。（結構げっ歯類の判定が厳しくなってきた！）
2019/09/01 ペンギンの種類を追加。
2019/08/30 一部要望のあった動物を追加。(ペンギン、トリ ※主にカラス を追加)
その他の動物もリクエストお待ちしております。

2019/08/20 LINEBotのQRコード追加

##作ったきっかけ
2019年8月某日からWindows10のログイン画面に急遽出現したカワウソ。
このカワウソの種類を簡単に調べるにはどうすれば良いか？
道端で動いている怪しい動物はなんていう動物なのか？
という非日常的な問題を簡単に解決できるようにしたい。


##完成デモ
https://www.youtube.com/watch?v=PeuqqgVQMRU&feature=youtu.be
※クリックするとYoutubeを開きます。
[![デモ](http://img.youtube.com/vi/PeuqqgVQMRU/0.jpg)](http://www.youtube.com/watch?v=PeuqqgVQMRU)

##実施環境
OS:Windows10 Pro 64ビット
画像認識:Azure CustomVisonService
動作:Node.js
デプロイ環境:Now


##事前準備(必要ツール)
1. Python3.7.4(32ビット)のインストール
ダウンロード先：
https://www.python.org/ftp/python/3.7.4/python-3.7.4.exe

####Add Python 3.7 to PATHのチェックは忘れずに！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/c77c7525-92f0-75e9-5d1b-d755a7d5d693.png)

2.Python起動確認
コマンドプロンプトから Python --versionを入力
Python3.7.4などインストールしたバージョンが表示されればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/031d0846-1e91-9b04-17d6-48d6c5d2a5b7.png)

3.pipの確認(Pythonと同時にインストールされるらしい)
コマンドプロンプトから pip -V を入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/a6c925e8-bb27-65d9-71c2-f0ea55b738e4.png)




4.google-images-downloadをインストールします。
Pythonで作成されているため、導入はコマンドライン上から'pip'のコマンドを使ってインストールします。

コマンドプロンプトを立ち上げ、下記のコマンドを入力。
```pip install google_images_download```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/d09f98e4-81d6-b440-f9f9-6ac2806d68b5.png)
Successfully ~が表示されれば成功。

##学習用の画像データの準備
google-images-download を使うことで、指定したキーワードの画像を一括入手できます。

1.コマンドプロンプトに下記のコマンドを入力し、実行します。
```googleimagesdownload -k 'コツメカワウソ' -la Japanese -l 100 -s medium```

バッチファイル化することでまとめて実行できます。
``` img-download.bat
cd C:\Users\[アカウント名]\Desktop\download
googleimagesdownload -k 'コツメカワウソ' -la Japanese -l 100 -s medium -co white
googleimagesdownload -k 'コツメカワウソ' -la Japanese -l 100 -s medium -co black
googleimagesdownload -k 'コツメカワウソ' -la Japanese -l 100 -s medium -co red
googleimagesdownload -k 'コツメカワウソ' -la Japanese -l 100 -s medium -co green
googleimagesdownload -k 'コツメカワウソ' -la Japanese -l 100 -s medium -co gray
googleimagesdownload -k 'コツメカワウソ' -la Japanese -l 100 -s medium -co yellow
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/eb3b9000-1724-319b-a976-05dc110eb3ba.png)

ダウンロードできる画像数は100を上限に指定できます。
100毎より多くダウンロードしたい場合は、chromedriver.exeが別途必要になります。

※オプションに -co red を付けると赤いカワウソがダウンロードできます。
各色毎にダウンロードして素材データとして使いました。


##学習用データの登録
1. Azure Cognitive Vision Servicesにアクセスします。
https://www.customvision.ai/projects

2. NEW PROJECTをクリックしてプロジェクトを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/b9fd2d2d-031b-0235-c6f9-9a2d30d9ebf6.png)

3. 各種項目を埋めていきます
Name：プロジェクト名
Description：プロジェクトの説明
Resource：AzurePortalで作成したリソースを選択します。無い場合は、Create Newから作成します。
ProjectTypes：Classificationを選択します。
Classification Types：Multilabel(Multiple tags per image)を選択します。
Domains：Generalを選択します。

Create projectをクリックします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/8a28daa6-998b-f542-c5b1-6bb7678687ba.png)


4.Training Images → Add imagesをクリックしてダウンロードしたファイルをまとめてアップロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/124839a7-600f-1644-6304-7a2ca61578b3.png)

アップロード時に関連するタグを入力してUpload n filesをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/b68931ff-224b-9d78-99a1-630f9d2e69ce.png)

各動物ごとタグを付けてアップロードしていきます。

5.アップロードとタグ付け完了後、Train　をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/179021bb-896a-d590-2aee-6b958ef65f3b.png)

Fast Training を選択し、Trainをクリックします。
※Advanced Trainingを選択するとかなり時間がかかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/c199462b-2dec-c581-add6-8a22dcf14269.png)

6.トレーニング完了後、QuickTestボタンより、分類したい画像をアップロードしてテストします。
99%コツメカワウソと判断されたのでOKですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/ee2eaa94-4623-b88c-3039-a8a30422212a.png)


## LINE Bot との連携

1.Azure Custom Vision ServicesのPerformanceからPublishをクリックし、Prediction APIを発行する。

※下記のようなresourceなんちゃらのエラーで発行できない場合は、
ProtoOut Studio 講師長田中さん が書いたここの記事を参考にしてください。
https://protoout.studio/posts/custom-vision-project-publish-error
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/2b7aa232-8366-0304-debd-868f15d0f25d.png)


2.Prediction APIが表示された場合は、APIキーを控えておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/541fcc14-1055-bb8d-f2fa-d1875b6cf355.png)

3.LINE BoTを作成します。
LINE Botの簡単な作り方はこちらの@n0bisukeさんの記事を参考にしてください。
https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d

4.下記のソースを参考にしてNowでデプロイすれば完成です。
## Node.jsのソース
``` server.js
'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;
const fs = require('fs');
const bodyParser = require('body-parser');
const Request = require('request');
const cv = require('customvision-api');

const config = {
    channelSecret: '作成したBOTのチャンネルシークレット',
    channelAccessToken: '作成したBOTのチャンネルアクセストークン'
};


const app = express();
app.use(bodyParser.json());
let middle = line.middleware(config);
const client = new line.Client(config);

app.post('/webhook', (req, res) => {
            console.log(req.body.events);

            if(req.body.events[0].message.type !== 'image') return;

// ユーザーがLINE Bot宛てに送った写真のURLを取得する
    const options = {
        url: `https://api.line.me/v2/bot/message/${req.body.events[0].message.id}/content`,
        method: 'get',
        headers: {
            'Authorization': 'Bearer [アクセストークン]'  ,
        },
        encoding: null
    };

    Request(options, function(error, response, body) {

        if (!error && response.statusCode == 200) {
            //保存

            console.log(options.url + '/image.jpg');
            let strURL = options.url + '/image.jpg';

            //Nowでデプロイする場合は、/tmp/のパスが重要
            fs.writeFileSync(`/tmp/` + req.body.events[0].message.id + `.png`, new Buffer(body), 'binary');

            const filePath = `/tmp/` + req.body.events[0].message.id + `.png`;

//Azure Custom Vision APIの設定
            const config = {
            "predictionEndpoint": '[Iteration]',
            "predictionKey": '[predictionKey]'
            };

            cv.sendImage(
                filePath,
                config,
                (data) => {
                    console.log(data); 
                    let strName = data.predictions[0].tagName;
                    let strTarget = "";

                    let strName2 = "";
                    let strTarget2 = "";
                    
                    if (data.predictions[0].probability > 0.8) {
                        strTarget="だねー"

                    }else if(data.predictions[0].probability > 0.4){
                        strTarget="っぽいねー"

                    }else if(data.predictions[0].probability > 0.2){
                        strTarget="みたいだねー"

                    }else if(data.predictions[0].probability > 0.1){
                        strTarget="かな？ちょっとよくわからないねー"

                    }else{
                        console.log(data.predictions[0].tagName);
                        strName="なんのどうぶつか"
                        strTarget="わからないねー"

                    }

                    if (data.predictions[1].probability > 0.2) {
                        strName2='\n' + data.predictions[1].tagName
                        strTarget2="っぽいところもあるよねー"
                    }


                    client.replyMessage(req.body.events[0].replyToken, {
                        type: 'text',
                        text: 'これは' + strName + strTarget + strName2 + strTarget2 //実際に返信の言葉を入れる箇所
                      }); 

                      try {
                        fs.unlinkSync(filePath);
                        return true;
                      } catch(err) {
                        return false;
                      }
                      
                      
                    return; 
    
                },
                (error) => { console.log(error) }
            );
            
        } else {
            console.log('imageget-err');
        }
    });
});

(process.env.NOW_REGION) ? module.exports = app : app.listen(PORT);
console.log(`Server running at ${PORT}`);

```

##最後に

~~今回作成したLINEBotのQRコードはAzureCustomVisonを使用しているため、
金額がとんでもないことになるのが嫌なので公開しません。
興味ある方はツイッターでDMください。~~
2019/08/29 なんか大丈夫そうなのでQRコード追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399986/160991a3-8518-5800-9f83-b5520d4ab551.png)