LoginSignup
10
4

More than 3 years have passed since last update.

はやい!やすい!うまい!Custom Vision と LINE bot でお寿司の判定をしてみた

Posted at

はじめに

MicrosoftのCustomVisionというサービスで、
はやい!(試行錯誤含めて3時間くらい)
やすい!(無料アカウント)
うまい!(お寿司はおいしい)
で、めちゃくちゃ気軽に機会学習ができるとのことなので、LINEbotを作ってみました。

事前準備

CustomVisionの登録

MicrosoftアカウントのOutlookメールアドレスを取得する方法
Microsoft Learn | Microsoft Docs にアクセスしてサインインします

CustomVisionの準備

学習用の画像を準備します
google-images-downloadを使うとそれ用のフォルダを勝手に作ってダウンロードしてくれるのでめっちゃ便利でした
スクリーンショット 2019-08-28 21.33.10.png

参考 -> LINE動物図鑑の作り方

画像が準備できたらこちらのトレーニングを参考に画像判定用のモデルを作成します
こちら (ただし、うまくAPIを発行できない場合があるので事前にこちらを参考に、事前にAzure Portal側でCustom Vision用リソースグループやCustom Visionリソースを作っておくと上手くいきました(2019/08/29 現在))

今回は、「お寿司屋さんで写真を取ってLINEbotに送る」ことを想定してたので、そういう感じの画像に選別します
だんだん、「これは本当にマグロなのか?マグロとはなんなのか?」とゲシュタルト崩壊してきて興味深いです

タグを登録し、先ほどダウンロードした画像たちをアップロードします
スクリーンショット 2019-08-28 23.03.18.png

登録し終えたら、学習させます。
テストしてみると、イクラはかなりの精度で判定できましたが、
スクリーンショット 2019-08-28 23.04.35.png
ハマチは少し微妙でした。
スクリーンショット 2019-08-28 23.05.03.png

準備できたらpublishします
スクリーンショット 2019-08-28 23.05.48.png

Prediction APIをクリックすると以下のようなポップアップが表示されます
今回使うのは赤枠の部分のURLPrediction-keyの二つなので控えておきます
スクリーンショット 2019-08-28 23.06.45.png

他参考
https://azure-recipe.kc-cloud.jp/2017/12/custom_vision_2017adcal/
https://azure.microsoft.com/ja-jp/services/cognitive-services/custom-vision-service/

実装

LINEbotの準備

1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefestを参考にLINEのbotの作成を行います
APIは個人で使う想定(金額的な意味で)なのでBotはngrokを使って公開します


'use strict';

//必要なものは都度 npm i してください
const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3002;
const cv = require('customvision-api');
const fs = require('fs');
const bodyParser = require('body-parser');
const Request = require('request');

const line_config = {
    channelSecret: 'botのチャンネルシークレット',
    channelAccessToken: 'botのアクセストークン'
};

const app = express();
const client = new line.Client(line_config);

app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/webhook', line.middleware(line_config), (req, res) => {
    console.log(req.body.events);

    // botに送られる画像にアクセスするための設定
    const options = {
        url: `https://api.line.me/v2/bot/message/${req.body.events[0].message.id}/content`,
        method: 'get',
        headers: {
            'Authorization': 'Bearer ' + line_config.channelAccessToken,
        },
        encoding: null
    };
    Request(options, function(error, response, body) {
        if (!error && response.statusCode == 200) {
            //画像を保存します
            fs.writeFileSync(`/tmp/` + req.body.events[0].message.id + `.jpg`, new Buffer(body), 'binary');
            console.log('file saved');
            const filePath = `/tmp/` + req.body.events[0].message.id + `.jpg`;
            const cv_config = {
                "predictionEndpoint": "先ほどpublishしたPrediction APIのURL",
                "predictionKey": "先ほどpublishしたPrediction APIのpredictionKey"
            };

            cv.sendImage(
                filePath,
                cv_config,
                (data) => {
                    console.log(data); 
                    // => 出力結果
                    //   { id: 'xxxxxxxxxxxxxxxxxxxxxxxxx',
                    //   project: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
                    //   iteration: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
                    //   created: '2019-08-28T15:01:24.855Z',
                    //   predictions:
                    //    [ { probability: 0.833851159,
                    //        tagId: 'a5f078e9-490d-4040-afbf-6918d7744011',
                    //        tagName: 'hamachi' },
                    //      { probability: 0.09781464,
                    //        tagId: '35660c82-9df6-4511-9bd6-0e26db54ad4c',
                    //        tagName: 'maguro' },
                    //      { probability: 0.013582103,
                    //        tagId: '5d170ee1-9d02-40f6-ab8c-addeaec6cefc',
                    //        tagName: 'engawa' },
                    //      { probability: 0.011500218,
                    //        tagId: '37f42876-ce01-4cc3-8951-d7169e41b936',
                    //        tagName: 'tako' },
                    //      { probability: 0.0102164745,
                    //        tagId: 'c46ae2a3-d532-4b5f-8287-2ebd5537583d',
                    //        tagName: 'salmon' },
                    //      { probability: 0.00753958151,
                    //        tagId: 'b1a939d5-2a61-4518-90bf-dfd8542e646d',
                    //        tagName: 'hotate' },
                    //      { probability: 0.00548384944,
                    //        tagId: '0d944222-e4e5-4b95-9084-8c17c52c6ea2',
                    //        tagName: 'tobikko' },
                    //      { probability: 0.005075191,
                    //        tagId: '8f7b69ab-877e-464c-91d8-f5faf2d32633',
                    //        tagName: 'ikura' },
                    //      { probability: 0.004676578,
                    //        tagId: 'c1a43c10-6bcd-4893-b5b0-3016816e3c91',
                    //        tagName: 'ebi' },
                    //      { probability: 0.00426511373,
                    //        tagId: 'fbe51510-a6f3-4fba-a141-afa2ed6fcec0',
                    //        tagName: 'ika' },
                    //      { probability: 0.00379423774,
                    //        tagId: '730eb665-a967-444f-9a0f-7cd16527e249',
                    //        tagName: 'tamago' },
                    //      { probability: 0.00220071618,
                    //        tagId: '9e1789d3-dd45-48b9-b116-5aa8f711afec',
                    //        tagName: 'hokkigai' } ] }
                    let tagName = data.predictions[0].tagName;
                    let probability = data.predictions[0].probability;

                    client.replyMessage(req.body.events[0].replyToken, {
                        type: 'text',
                        text: tagName + 'の確率、' + probability 
                      }); 
                }
            );
        }
    })
    Promise
        .all(req.body.events.map(handleEvent))
        .then((result) => res.json(result));
})

app.listen(PORT);
console.log(`Server running at ${PORT}`);

めっちゃくちゃ簡単ですが、以下のように判定を返してくれました!!
iOS の画像.png

参考
LINE BOTからNode.jsで画像を受け取って保存する

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4