Watson Visual Recognitionで画像認識してみた！

# 概要
WatsonAPIが面白そうなので、試してみました！
中でも一番そそられたのは「Visual Recognition」です。
https://www.ibm.com/watson/jp-ja/developercloud/visual-recognition.html

> Visual RecognitionはWatsonの画像認識機能です。すぐにお使いいただけるようにWatsonが既に学習をしており、画像・映像フレームに写った複数のものや、情景を分析・認識することができます。また、機械学習によりWatsonに独自の学習をさせることもできます。すでに、自社製品の認識・分類や、製造ラインにおける欠陥検出といった多種多様なお客様の業務で、高い精度の画像認識を少ない画像枚数による短時間の機械学習で実現しています。さらに、日本語・英語を含む多数の言語で認識結果を返すことができます。

ということで、画像から「意味」を見出すという･･･ワクワクが止まらねぇ～～～

# IBM Cloudアカウント登録
## 1. IBM Cloudのライトアカウントを登録
こちらにアクセスし、
https://www.ibm.com/watson/jp-ja/developercloud/visual-recognition.html
無料で使用できるライトアカウントがありますので、「IBM Cloudのライトアカウントを登録」してください。
![pic1.png](https://qiita-image-store.s3.amazonaws.com/0/147118/ba615ce4-a9fc-ad4c-9611-d5156a855bc9.png)

## 2. 検索欄で「Visual Recognition」を検索します。
![pic2.png](https://qiita-image-store.s3.amazonaws.com/0/147118/faf362c9-3e0c-3b42-eaea-4506da07d400.png)

## 3. サービスを作成します。
![pic3.png](https://qiita-image-store.s3.amazonaws.com/0/147118/ea292744-674f-53da-b226-eb9261205793.png)

## 4. アカウント情報を入力し、登録
![pic4.png](https://qiita-image-store.s3.amazonaws.com/0/147118/9e63f30d-b070-e77a-9c59-d262cbd2f738.png)

# API接続情報を確認
左上ハンバーガーメニューから「ダッシュボード」を開くと、登録したサービス一覧が表示されます。
そこから先ほど登録したVisualRecognitionのサービスを開くと、資格情報が表示されます。
![pic6.png](https://qiita-image-store.s3.amazonaws.com/0/147118/2766bf17-c930-76be-aef4-b0d39c4c17a8.png)


資格情報は後ほど使います。

# プログラム作成
今回はNode.jsでVisualRecognitionAPIを使用しました。

## 1. Expressアプリの作成
```terminal:DOS
express myFirstVR
cd myFirstVR
npm install
```

## 2. watson-developer-cloudのインストール
```terminal:DOS
npm i --save watson-developer-cloud
```

## 3. APIへの接続・呼び出し
API接続を管理するクラスを作成します。
ルートフォルダに`api`フォルダを作成し、その中に`VisualRecognition.js`ファイルを作成します。

{API鍵}には前段で取得したAPI鍵を入力してください。

```js:api/VisualRecognition.js
const VisualRecognitionV3 = require('watson-developer-cloud/visual-recognition/v3');
const fs = require('fs');

const visualRecognition = new VisualRecognitionV3({
	version: '2018-03-19',
	'iam_apikey': '{API 鍵}',
	'url': 'https://gateway.watsonplatform.net/visual-recognition/api',
});

module.exports = class VisualRecognition {
	constructor(imagePath) {
		this.image = fs.createReadStream(imagePath);
		this.params = {
			images_file: this.image,
			accept_language: 'ja'
		};
	}

	analyze() {
		return new Promise((resolve, reject) => {
			visualRecognition.classify(this.params, (err, response) => {
				if (err) {
					reject(err);
				};
				resolve(response);
			});
		});
	}
}
```

## 4. Routerの作成
ブラウザからのHTTPリクエストに応答するRouterを作成します。

画像のアップロードに[Multer](https://www.npmjs.com/package/multer)を使用するので、npm installしておきます。

```terminal:DOS
npm i --save multer
```

```js:routes/visualrecofnition.js
var express = require('express');
var router = express.Router();

// 画像アップロードに必要
const multer = require('multer');

const storage = multer.diskStorage({
	destination: function (req, file, cb) {
		// 保存したいパス
		cb(null, './public/images')
	},
	filename: function (req, file, cb) {
		// アップロードしたときのファイル名で保存
		cb(null, file.originalname)
	}
})

const upload = multer({
	storage: storage
})

// VisualRecognitionクラスを読み込み
const VisualRecognition = require('../api/VisualRecognition');
const fs = require('fs');

// 入力フォームへのルーティング
router.get('/', function (req, res, next) {
	res.render('VisualRecognition/index', {
		title: "VisualRecognition API Test"
	});
});

// 入力フォームからのPOST待ち付け
router.post('/', upload.single('thumbnail'), function (req, res, next) {
	console.log(JSON.stringify(req.file, null, 2));
	var filePath = './' + req.file.path;

	var vr = new VisualRecognition(filePath);

   // 分析実行
	vr.analyze().then((resJson) => {
		var image = resJson.images[0].image;
		var datas = resJson.images[0].classifiers[0].classes;

		res.render('VisualRecognition/result', {
			title: "VisualRecognition API Test",
			name: image,
			path: "/images/" + image,
			datas: datas
		});
	}).catch((error) => {
		console.log(error);
		res.send(error);
	});
});

module.exports = router;
```

## 5. Routerの読み込み

先ほど作成したRouterをアプリで使用するため、`app.js`に下記2行を追加しています。
`var vrRouter = require('./routes/visualrecognition');`
`app.use('/vr', vrRouter);`

```js:app/js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var vrRouter = require('./routes/visualrecognition'); // 追加

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/vr', vrRouter); // 追加

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;
```

## 6. layoutファイルの編集
よーし画面まで作るぞ！ってことでBootstrapを使っていきます。

共通アイテムは`layout.jade`に書くことができますので、活用していきます。

```html:layout.jade
doctype html
html
	head
		title= title
		link(rel='stylesheet', href='/stylesheets/style.css')
		link(rel='stylesheet', href='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css', integrity='sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm', crossorigin='anonymous')
		meta(charset='utf-8')
		meta(name='viewport', content='width=device-width, initial-scale=1, shrink-to-fit=no')
		meta(name='description', content='')
		meta(name='author', content='')
		link(rel='icon', href='../../../../favicon.ico')
	body
		nav.navbar.navbar-expand-md.navbar-dark.bg-dark.fixed-top
			a.navbar-brand(href='#') #{title}
			| 
			button.navbar-toggler(type='button', data-toggle='collapse', data-target='#navbarsExampleDefault', aria-controls='navbarsExampleDefault', aria-expanded='false', aria-label='Toggle navigation')
				span.navbar-toggler-icon
			| 
			#navbarsExampleDefault.collapse.navbar-collapse
				ul.navbar-nav.mr-auto
					li.nav-item.active
						a.nav-link(href='/')
							| Home 
							span.sr-only (current)
		main.container(role='main', style="margin-top:20px;")
			block content
		script(src='https://code.jquery.com/jquery-3.2.1.slim.min.js', integrity='sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN', crossorigin='anonymous')
		script(src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js', integrity='sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q', crossorigin='anonymous')
		script(src='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js', integrity='sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl', crossorigin='anonymous')
```
ナビゲーションバーを配置し、BootstrapのCDNを指定しました。
各コンテンツは`block content`にincludeされます。

## 7. 画像アップロードフォームの作成
`views`フォルダに`VisualRecognition`というフォルダを作成し、`index.jade`ファイルを作成します。

```html:views/VisualRecognition/index.jade
extends ../layout

block content
	form(method="POST", enctype="multipart/form-data", action="./vr")
		.form-group
			input.form-control(type="file", name="thumbnail")
		.form-group
			button.form-control.btn.btn-primary(type="submit") 送信
```

アップロード画面ができたので、一度確認してみましょう。

```terminal:DOS
npm start
```
でアプリを起動し、http://localhost:3000/vr にアクセスすると見れると思います。

![pic7.png](https://qiita-image-store.s3.amazonaws.com/0/147118/b4e775c1-f30d-8a83-0153-8453851cc71b.png)

ただのファイルアップロードフォームと送信ボタンです。

## 8. 結果確認画面の作成

それでは、結果を確認する画面を作成していきます。
`views/VisualRecognition`フォルダの中に`result.jade`ファイルを作成します。

```html:views/VisualRecognition/index.jade
extends ../layout

block content
	h1= name
	
	img.img-fluid(src="#{path}")
	table.table
		thead
			th class
			th score
		tbody
			for data in datas
				tr
					td #{data.class}
					td #{data.score}
```

再度動作させてみます。
適当な画像をアップロードし、送信！！

![pic15.png](https://qiita-image-store.s3.amazonaws.com/0/147118/4afaa6ff-166d-4e8f-18b3-4944e458af64.png)

「ホテル建築」や「ホテル・カジノ」といった印象だそうです。夜とか、キラキラ感からそうなるのでしょうか･･･？
本当にホテルなのかは知りません（）が、そう言われればそう見えます。
また、「時計台」という判断は完璧ですね！！

別の画像も試してみます。
![pic16.png](https://qiita-image-store.s3.amazonaws.com/0/147118/deaa9bb5-80ba-fa16-cdb1-269a5db4ed4d.png)

かなりの精度ですね！！すご！！！おもしろ！！！！

# 感想
めっちゃおもしろいので一日中遊べますが、APIの利用回数制限が厳しいので、サービスにするにはライトアカウントじゃちょっときついですかね！！

# 参考
* [Visual Recognition - API reference | IBM Watson Developer Cloud](https://www.ibm.com/watson/developercloud/visual-recognition/api/v3/curl.html?curl)
* [入門チュートリアル](https://console.bluemix.net/docs/services/visual-recognition/getting-started.html#-)