Watson Visual Recognition でみそ煮込み分類器をつくろう

## はじめに
- 画像認識がはじめての方でも分類器がつくれるレベルを目指します
- HTML/CSSの基礎知識が必要です
- プログラミングはjavascriptをやったことがあれば充分です。経験なければがんばろう！
- 作成するアプリは[Vue.js](https://jp.vuejs.org/index.html)を利用していますが、この記事で詳細については触れません

***
これは何の写真かわかりますか？
<img width="362"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/c7b62e9b-0ba5-f7f6-bc22-7f903dfbce51.png">
人は経験から写真に写っているモノを認識して無意識に分類できます。
***
# 「認識する」とは「分類する」
***
### 画像認識とは？

[【事例付き】様々なサービスに活用！画像認識技術とは](https://techacademy.jp/magazine/17068)

この記事がとてもわかりやすいので、簡単に記事の内容を紹介していきます。

***
### 画像認識の基本 
- 画像認識とは、画像や動画から特徴をつかみ、対象物を識別する**パターン認識技術**の1つ
- コンピューターは画像に何が写っているかを「理解」することができない
- 対象物が何であるかを「確率」として表現することができる

***
### ディープラーニング技術

>2012年に開催された「ILSVRC2012」という画像認識の大会です。この大会では、ImageNetという大量のラベル付き画像（画像と写っている物の名前のセット）を基に画像認識率を競い会います。前年度までの正解率は、高くても70%台前半でした。ところが、一気に**約85%の正解率**を叩き出します。翌年からは、ディープラーニングを使ったモデル同士が競い合うようになり、正解率もどんどん向上して行きました。現在では正解率95%以上、人間を超えるレベルにまで到達しています。


***
### 画像認識の仕組み
<img width="586"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/bd3d297a-af1a-dc03-8c91-c7cfd69b01d7.png">

***
### 画像認識の前処理
ピクセル単位の色しかわからないコンピュータに学習させるために通常は画像データに前処理をします。

- 画像のノイズや歪みなどを取り除く
- オブジェクトの輪郭を強調したり、明るさや色合いを調整することで、オブジェクトを抽出しやすくする
- 画像からオブジェクトの領域を切り出す（領域抽出）

***
# Watsonでできること

***
### Visual Recognition （画像認識）
[Watson Visual Recognition](https://www.ibm.com/watson/jp-ja/developercloud/visual-recognition.html)は画像認識のためのツール群です。すでに学習済みのモデルも用意されており、2019年7月時点で以下の機能があります。

- General （画像からタグ抽出）
- Faces （顔認識）
- Food　（料理認識）
- Explicit　（不適切表現の判断）
- **Classify Images　（カスタム分類器）**
- Detect Objects　（物体検出）*private beta*
- Text　（文字認識）*private beta*


***
# みそ煮込み分類器をつくろう

***
### 何故「みそ煮込み分類器」か？
名古屋人にとってソウルフードともいえる「みそ煮込み」。そこには２つの名店の存在があります。

- [山本屋本店](http://yamamotoyahonten.co.jp/profile/)
- [山本屋総本家](http://yamamotoya.co.jp/belief/)

名前も似てるし、観光にきた方に説明するにも大変です。みそ煮込みを食べようとした時もどっちがどっちなのか分からなくなってしまいます。そこでWatsonにどっちのみそ煮込みなのか判断してもらいましょう！

***
### 画像データを収集する前に
AIと著作権について理解しておきましょう。AIの著作権については２つの切り口があります。

- AIが作成したモノの著作権
- **AIを作成するために利用するデータの著作権**

利用するデータの著作権については2019年1月に改正された「著作権法３０条の４」が当てはまります。基本的に学習のための画像データをネットから集めて作ることは違法ではないことになります。
> 第三十条の四
著作物は、次に掲げる場合その他の当該著作物に表現された思想又は感情を自ら享受し又は他人に享受させることを目的としない場合には、その必要と認められる限度において、いずれの方法によるかを問わず、利用することができる。ただし、**当該著作物の種類及び用途並びに当該利用の態様に照らし著作権者の利益を不当に害することとなる場合**は、この限りでない。
（中略）
二　情報解析（**多数の著作物その他の大量の情報から、当該情報を構成する言語、音、影像その他の要素に係る情報を抽出し、比較、分類その他の解析を行うこと**をいう。第四十七条の五第一項第二号において同じ。）の用に供する場合
（以下略）

***
### 画像データを収集しよう
Watsonは一つのモデルに画像データが**10枚以上**あれば分類器をつくれます。２つのお店を混同しないように画像を集めてください。オリジナルの画像そのものよりもPCのキャプチャツールで切り抜きをした画像の方がデータ量が少なく済みます。

<キャプチャツール>
- Windows10 の場合は`Win + Shift + S`
- Mac の場合は`Cmd + Shift + 4`

***
### Watsonに学習させよう
集めた画像をWatsonに学習させるには以下のステップで行います。

1.IBM Cloudのアカウントを作成する
2.Watson Studio から Visual Recognition Service を作成する
3.集めた画像をアップロードする
4.モデルを作成し、アップロードした画像を学習させる 

操作手順の詳細は[こちらの記事](https://qiita.com/bird_tomita/items/3c409cf22ef3a0d6782a)を参考にしてください。（「Node-REDと接続」の手前まで）

追加するclass名は「honten」と「sohonke」にしてください。
<img width="593" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/4816fb6f-9616-ce00-e5f9-2fb41b86f9cc.png">


***
### スマホで使える「みそ煮込み分類器」をつくります
スマホで写真をとって、すぐに判定できるようWebアプリをつくります。完成のイメージはこちらです。
<img width="162"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/cae2ff06-6cb6-14c3-b391-edbac7a1e6a2.png">

***
### Node-REDでWebアプリをつくる
Node-REDでWebアプリをつくる手順は以下の通りです。コピペばかりだとつまらないのでWebアプリのつくり方を説明していきたいと思います。

1.IBM CloudでNode-REDの環境をつくる
2.Node-REDとWatson Visual Recognitionを接続する
3.Webアプリの画面をつくる
4.画像をWatson Visual Recognitionで判定する

1と2の操作手順の詳細は再度[こちらの記事](https://qiita.com/bird_tomita/items/3c409cf22ef3a0d6782a)を参考にしてください。（「Node-REDと接続」からNode-REDフローエディターが起動するところまで）
3から説明していきます。

***
### Node-REDでWebアプリをつくる
フローの完成のイメージはこんな感じです。
<img width="706" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/394e0e7e-b80b-3ee1-844d-e98929cdffce.png">

時間の無い方はこちらをコピペしてインポートしてください。

```
[{"id":"e8a18d31.d9ed7","type":"http in","z":"c61718a0.d3c958","name":"","url":"index","method":"get","upload":false,"swaggerDoc":"","x":140,"y":129,"wires":[["5a60e348.f0cfcc"]]},{"id":"1f1c3e2e.a9ff42","type":"http response","z":"c61718a0.d3c958","name":"","statusCode":"","headers":{},"x":530,"y":129,"wires":[]},{"id":"5a60e348.f0cfcc","type":"template","z":"c61718a0.d3c958","name":"画面","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!DOCTYPE html>\n<html>\n<head>\n    <meta charset='utf-8'>\n    <meta http-equiv='X-UA-Compatible' content='IE=edge'>\n    <title>みそ煮込み分類器</title>\n    <meta name='viewport' content='width=device-width, initial-scale=1'>\n    <link rel=\"stylesheet\" href=\"https://unpkg.com/element-ui/lib/theme-chalk/index.css\">\n</head>\n<body>\n<div id=\"app\">\n   <!-- 画面 START -->\n    <el-row>\n        <el-col :span=\"24\" style=\"text-align:center\">\n            <h2><% result %></h2>\n            <!-- 撮影画像 -->\n            <p><video id=\"myVideo\" width=\"100%\" height=\"300\" autoplay playsinline=\"true\"></video></p>\n            <p><canvas id=\"myCanvas\" style=\"display:none\"></canvas></p>\n            <p>\n                <el-button-group>\n                    <el-button @click=\"sendImage\" :loading=\"buttonLoading\" icon=\"el-icon-camera\">Take a picture</el-button>\n                    <el-button @click=\"changeCamera\" icon=\"el-icon-refresh\"></el-button>\n                </el-button-group>\n            </p>\n            <p>\n                <el-upload\n                action=\"/upload\"\n                :on-change=\"handleChange\"\n                :on-success=\"handleSuccess\"\n                :file-list=\"fileList\"\n                list-type=\"picture\">\n                    <el-button icon=\"el-icon-picture-outline\">Click to upload</el-button>\n                </el-upload>\n            </p>\n        </el-col>\n    </el-row>\n    <!-- 画面 END -->\n</div>\n<!-- ここからはJavascript -->\n<script src=\"https://cdn.jsdelivr.net/npm/vue\"></script>\n<script src=\"https://unpkg.com/element-ui/lib/index.js\"></script>\n<script src=\"https://unpkg.com/axios/dist/axios.min.js\"></script>\n<script>\n    var app = new Vue({\n      el: '#app',\n      data: function() {\n        return {\n            buttonLoading: false,\n            result:'',\n            fileList:[],\n            cameraMode:'user'\n        }\n      },\n      delimiters: [\"<%\",\"%>\"],\n      methods: {\n        handleChange:function(file, fileList){\n            this.fileList = fileList.slice(-1);\n        },\n        changeCamera:function() {\n            this.cameraMode = (this.cameraMode === 'user') ? 'environment' : 'user';\n            this.initCamera();\n        },\n        initCamera:function() {\n            //ストリーム作成とカメラ画像のストリーミング開始\n            const constraints = {facingMode:this.cameraMode};\n            const promise = navigator.mediaDevices.getUserMedia({video: constraints,audio: false});\n            promise.then(function(stream){\n                video.srcObject = stream;\n                //video.play();\n            }).catch(function(err){\n                this.result = err;\n                console.log(err);\n            });\n        },\n        sendImage: function() {\n            this.buttonLoading = true;\n            this.result=\"...判定中...\";\n            video.pause();                                                // ビデオ停止\n            var canvas_image = ctx.drawImage(video, 0, 0, canvas.width, canvas.height);          // カメラ→imgに変換\n\n            var dataURL = canvas.toDataURL(\"image/jpeg\");    // DataURLに変換\n            var img_base64 = dataURL.replace(/^.*,/, '');    // プレフィックスを削除してBase64部分だけ取り出し\n            var param = {image:img_base64};\n            axios.post('/picture', param\n                ).then(response => {\n                    this.handleSuccess(response.data);\n                }).catch(error => {//エラー処理\n                    console.log(\"post failed\"+error);\n                });\n        },\n        handleSuccess: function(data,file,fileList) {\n            console.log(JSON.stringify(data));\n            let buf = '';\n            //クラス分類結果を表示\n            if (data.class) {\n                for (let i=0; i<data.class.length;i++) {\n                    buf += \" \" + data.class[i].class + \"(\"+data.class[i].score+\")\";   \n                }\n            }\n            buf = buf.replace(\"honten\",\"山本屋本店\").replace(\"sohonke\",\"山本屋総本家\")\n            this.result = buf;\n            this.buttonLoading = false;\n            video.play();\n        } \n      }\n    })\n    //------------------------//\n    //初期化\n    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;\n    window.URL = window.URL || window.webkitURL; \n    //カメラ入力\n    var video = document.getElementById('myVideo');\n    //中間出力\n    var canvas = document.getElementById('myCanvas');\n    var ctx = canvas.getContext('2d');\n    app.initCamera();\n\n</script>\n</body>\n</html>\n","output":"str","x":330,"y":129,"wires":[["1f1c3e2e.a9ff42"]]},{"id":"1684e49.f4c0c1b","type":"comment","z":"c61718a0.d3c958","name":"画面","info":"","x":130,"y":69,"wires":[]},{"id":"ce12d408.33c238","type":"http in","z":"c61718a0.d3c958","name":"","url":"picture","method":"post","upload":false,"swaggerDoc":"","x":150,"y":280,"wires":[["192997c8.cd9eb8"]]},{"id":"453ff247.1cd8fc","type":"visual-recognition-v3","z":"c61718a0.d3c958","name":"","vr-service-endpoint":"https://gateway-a.watsonplatform.net/visual-recognition/api","image-feature":"classifyImage","lang":"ja","x":590,"y":280,"wires":[["4ea6dec6.62d93","74d38315.15e7bc"]]},{"id":"657e8870.5aa688","type":"http response","z":"c61718a0.d3c958","name":"","statusCode":"","headers":{},"x":910,"y":280,"wires":[]},{"id":"4ea6dec6.62d93","type":"function","z":"c61718a0.d3c958","name":"後処理","func":"msg.payload = {};\n\nif (msg.result.images[0].classifiers) {\n    msg.payload.class = msg.result.images[0].classifiers[0].classes;\n}\n\nif (msg.result.images[0].faces) {\n    msg.payload.faces = msg.result.images[0].faces;\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":280,"wires":[["657e8870.5aa688"]]},{"id":"6d68e42c.45980c","type":"comment","z":"c61718a0.d3c958","name":"画像認識","info":"","x":140,"y":200,"wires":[]},{"id":"74d38315.15e7bc","type":"debug","z":"c61718a0.d3c958","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"result","targetType":"msg","x":790,"y":340,"wires":[]},{"id":"192997c8.cd9eb8","type":"function","z":"c61718a0.d3c958","name":"デコード","func":"msg.payload = new Buffer(msg.payload.image, 'base64');\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":280,"wires":[["49251580.d7bf6c"]]},{"id":"be94e94.b87ab18","type":"http in","z":"c61718a0.d3c958","name":"","url":"upload","method":"post","upload":true,"swaggerDoc":"","x":150,"y":340,"wires":[["d9623a99.0d5828"]]},{"id":"49251580.d7bf6c","type":"function","z":"c61718a0.d3c958","name":"前処理","func":"msg.params = {};\n//msg.params.classifier_ids = \"misomodel_336019170\";\nmsg.params.classifier_ids = \"default\";\nmsg.params.threshold = \"0\";\nreturn msg;","outputs":1,"noerr":0,"x":430,"y":280,"wires":[["453ff247.1cd8fc"]]},{"id":"d9623a99.0d5828","type":"function","z":"c61718a0.d3c958","name":"ファイル","func":"msg.payload = msg.req.files[0].buffer;\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":340,"wires":[["49251580.d7bf6c"]]}]
```
***
### Node-REDでWebアプリをつくる
#### Webアプリの画面をつくる（1/2）
フローエディターの左側にある**入力**から「http」と**出力**から「http response」をドラッグ&ドロップします。
<img width="722"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/09c3f286-29bc-9512-7492-4dffeaff1d8a.png">

続いて**機能**から「template」をドラッグ&ドロップします。
<img width="722"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/b04dd056-852f-e877-87a0-7475483b5e5e.png">

入力の「http」をダブルクリックしてプロパティを開きます。URLに「index」と入力して完了ボタンを押してください。
<img width="624"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/07b385f1-bac0-643c-7e16-31970295a122.png">

「template」のプロパティを開き、最初に書いてある文字を消し「みそ煮込み分類器」と入力して完了ボタンを押してください。
<img width="637"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/c591e8e9-5cbd-3553-31cb-892365179e8e.png">

フローを線でつなげて右上の「デプロイ」ボタンを押します。
<img width="721"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/acf1f521-12d3-8689-e79d-4c0d59f2bed9.png">

ブラウザのURLに
''https://XXXXX.mybluemix.net/index''
と入力して「みそ煮込み分類器」と表示されればオーケーです。（XXXXは各自のWebアプリ用に変更してください）

***
### Node-REDでWebアプリをつくる
#### Webアプリの画面をつくる（2/2）
Webアプリの画面はこちらのソースを使います。[Vue](https://jp.vuejs.org/index.html)と[Element UI](https://element.eleme.io/)を利用して画面を作っています。こちらをコピペして「template」に貼り付けしてください。

```HTML:index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <meta http-equiv='X-UA-Compatible' content='IE=edge'>
    <title>みそ煮込み分類器</title>
    <meta name='viewport' content='width=device-width, initial-scale=1'>
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<div id="app">
   <!-- 画面 START -->
    <el-row>
        <el-col :span="24" style="text-align:center">
            <h2><% result %></h2>
            <!-- 撮影画像 -->
            <p><video id="myVideo" width="100%" height="300" autoplay playsinline="true"></video></p>
            <p><canvas id="myCanvas" style="display:none"></canvas></p>
            <p>
                <el-button-group>
                    <el-button @click="sendImage" :loading="buttonLoading" icon="el-icon-camera">Take a picture</el-button>
                    <el-button @click="changeCamera" icon="el-icon-refresh"></el-button>
                </el-button-group>
            </p>
            <p>
                <el-upload
                action="/upload"
                :on-change="handleChange"
                :on-success="handleSuccess"
                :file-list="fileList"
                list-type="picture">
                    <el-button icon="el-icon-picture-outline">Click to upload</el-button>
                </el-upload>
            </p>
        </el-col>
    </el-row>
    <!-- 画面 END -->
</div>
<!-- ここからはJavascript -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    var app = new Vue({
      el: '#app',
      data: function() {
        return {
            buttonLoading: false,
            result:'',
            fileList:[],
            cameraMode:'user'
        }
      },
      delimiters: ["<%","%>"],
      methods: {
        handleChange:function(file, fileList){
            this.fileList = fileList.slice(-1);
        },
        changeCamera:function() {
            this.cameraMode = (this.cameraMode === 'user') ? 'environment' : 'user';
            this.initCamera();
        },
        initCamera:function() {
            //ストリーム作成とカメラ画像のストリーミング開始
            const constraints = {facingMode:this.cameraMode};
            const promise = navigator.mediaDevices.getUserMedia({video: constraints,audio: false});
            promise.then(function(stream){
                video.srcObject = stream;
                //video.play();
            }).catch(function(err){
                this.result = err;
                console.log(err);
            });
        },
        sendImage: function() {
            this.buttonLoading = true;
            this.result="...判定中...";
            video.pause();                                                // ビデオ停止
            var canvas_image = ctx.drawImage(video, 0, 0, canvas.width, canvas.height);          // カメラ→imgに変換

            var dataURL = canvas.toDataURL("image/jpeg");    // DataURLに変換
            var img_base64 = dataURL.replace(/^.*,/, '');    // プレフィックスを削除してBase64部分だけ取り出し
            var param = {image:img_base64};
            axios.post('/picture', param
                ).then(response => {
                    this.handleSuccess(response.data);
                }).catch(error => {//エラー処理
                    console.log("post failed"+error);
                });
        },
        handleSuccess: function(data,file,fileList) {
            console.log(JSON.stringify(data));
            let buf = '';
            //クラス分類結果を表示
            if (data.class) {
                for (let i=0; i<data.class.length;i++) {
                    buf += " " + data.class[i].class + "("+data.class[i].score+")";   
                }
            }
            buf = buf.replace("honten","山本屋本店").replace("sohonke","山本屋総本家")
            this.result = buf;
            this.buttonLoading = false;
            video.play();
        } 
      }
    })
    //------------------------//
    //初期化
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
    window.URL = window.URL || window.webkitURL; 
    //カメラ入力
    var video = document.getElementById('myVideo');
    //中間出力
    var canvas = document.getElementById('myCanvas');
    var ctx = canvas.getContext('2d');
    app.initCamera();

</script>
</body>
</html>
```

***
### Node-REDでWebアプリをつくる
#### 画像をWatson Visual Recognitionで判定する（1/2）
部品を並べよう。「http」を2つ、「function」を4つ、「http response」を1つ。最後に**IBM Watson**から「visual recognition」を1つ。
<img width="727"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/2d9b196b-7ee2-32c9-ebe9-8cf9d425203c.png">


カメラで写真を撮影したときの処理をつくります。画面の以下の処理を受ける部分になります。

```javascript:index.html
             var param = {image:img_base64};
             axios.post('/picture', param
                ).then(response => {
                    this.handleSuccess(response.data);
                }).catch(error => {//エラー処理
                    console.log("post failed"+error);
                });
```
httpのプロパティを変更します。
<img width="307"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/d3c3edc9-2ca7-5d14-17dd-dd0c9d6c140b.png">

Base64をデコードします。「function」の中身を以下に書き換えます。

```javascript
msg.payload = new Buffer(msg.payload.image, 'base64');
return msg;
```
Base64エンコーディングとは？ 
画像ファイルや音声ファイル等のバイナリーデータを文字列で表現できます。文字列で扱うことができるのはWebで便利な時があります。
> base64とは、64進数を意味する言葉で、すべてのデータをアルファベット(```a~z```, ```A~z```)と数字(```0~9```)、一部の記号(```+```,```/```)の**64文字**で表すエンコード方式です
ただ、データ長を揃えるためにパディングとして末尾に記号の```=```を使用するので、厳密にはbase64は、65文字の英数字から表現されます
(URLや正規表現のなかでbase64を用いると一部の記号(```+```,```/```)は特別な意味を持つことがあるので```-```や```_```などが用いられることがあります)

Watson Visual Recognitionのパラメータを設定します。「function」の中身を以下に書き換えます。

```javascript
msg.params = {};
msg.params.classifier_ids = "（作成したモデルのClassifier ID）";
msg.params.threshold = "0";
return msg;
```
<img width="215" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/98378590-4d51-d86d-8a2e-3336472af2a6.png">

Visual Recognitionの判定結果をセットします。「function」の中身を以下に書き換えます。

```javascript
msg.payload = {};
if (msg.result.images[0].classifiers) {
    msg.payload.class = msg.result.images[0].classifiers[0].classes;
}
return msg;
```
フローをつないでデプロイします。これでカメラで写真を撮る部分は完成です。

***
### Node-REDでWebアプリをつくる
#### 画像をWatson Visual Recognitionで判定する（2/2）

最後に画像ファイルのアップロードに対応します。ほぼVue+Element UIでつくってあるのでやることはほとんどありません。

httpのプロパティを変更します。ファイルのアップロードにチェックを入れてください。
<img width="308" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151569/a96acef4-4d17-bf12-c21f-15dc108397fa.png">

アップロードしたファイルをpayloadに設定します。「function」の中身を以下に書き換えます。

```javascript
msg.payload = msg.req.files[0].buffer;
return msg;
```

これで完成です。早速、動かしてみましょう！

***
### カスタマイズしてみよう
Watson Visual Recognitionの学習済みモデルを使えば「Food判定機」や「画像判定機」が簡単に作れます。
Node-RedのVisual Recognitionに渡すパラメータを変更してみてください。

```javascript
msg.params.classifier_ids = "food"; //Food判定機
```
```javascript
msg.params.classifier_ids = "default"; //画像判定機
```

***
### 凄く役立つ参考リンク
#### 画像認識
- [画像認識とは？](https://techacademy.jp/magazine/17068)
- [Deep Learningによる画像認識革命　ー歴史・最新理論から実践応用までー](https://www.slideshare.net/mobile/nlab_utokyo/deep-learning-49182466)

#### Watson Visual Recognition + Node-RED
- [IBM Watson Visual Recognition Custom Class + Node-RED で簡単画像認識 2019年1月版](https://qiita.com/bird_tomita/items/3c409cf22ef3a0d6782a)
- [Watson Visual Recognition カスタム画像分類モデル作成とNode-REDによる画像判定プログラム開発](https://www.slideshare.net/kolinz/watson-visual-recognition-nodered)
- [Node-red 10本ノック（visual recognition apiを絡めて）](https://www.slideshare.net/okhiroyuki/nodered-10visual-recognition-api)

#### その他
- [名古屋の味噌煮込みうどん戦争　[うどん]](https://paulblog.blog.so-net.ne.jp/2007-02-15)
- [何が違う！？味噌煮込みうどん「山本屋本店」と「山本屋総本家」](https://icotto.jp/presses/943)
- [進化する機械学習パラダイス ～改正著作権法が日本のAI開発をさらに加速する～](https://storialaw.jp/blog/4936)
- [base64ってなんぞ？？理解のために実装してみた](https://qiita.com/PlanetMeron/items/2905e2d0aa7fe46a36d4)