AI連携FW『シンプルエーアイ』を使ってみた(3)：「魔法の鏡」を作るぞ

#はじめに

**2018/05/16**
**現在日時表示のタイマー処理部分に誤りがありましたので、記事の一部とサンプルコードを修正しました。申し訳ございません。**

AI連携フレームワーク『シンプルエーアイ』を使って遊ぶ、第3回目です。
・[(1)準備編](https://qiita.com/denaripam/items/0c128c50788d9553f88e)
・[(2)：AIっぽい事をさせてみたい(その1)](https://qiita.com/denaripam/items/1aede5526136729ea0c5)
・[(2)：AIっぽい事をさせてみたい(その2)](https://qiita.com/denaripam/items/c53c01ea8856ab18f0f0)

**公式サイトはこちら：[https://smartaifw.com/simpleai-trial/](https://smartaifw.com/simpleai-trial/)**


#これって何だか…

前回の記事では、自分を写してAIにお喋りさせてみました。
それだけでは寂しいので、人物判断による「私幾つに見える？」というのを、AIに答えて貰えるようにしました。

実際に動かしてみて、ふと連想したものがあります。
とっても有名な童話の…そう、「鏡よ鏡、鏡さん」という、あれです。
白雪姫の継母が所持している魔法の鏡。
この記事では、前回記事の内容を改良し、アレを再現してみたいと思います。


#魔法の鏡にするためには

…とはいっても、流石に「世界で一番美しいのは誰？」という問いかけをしたい人はいないでしょう。
鏡を使う場面といえば、身だしなみを整える時ですね。
そして鏡をよく使うのは、圧倒的に女性が多いのではないでしょうか。
ですので今回は、女性が使いたい「魔法の鏡」を考えてみました。

周囲に聞いてみたところ、「お化粧後の自分が何歳に見えるか知りたい」という意見が。
「昨日より若く見えるという事は、うまくお化粧できた」とか、「今日は雨だから顔色を少し明るめにしよう」とかを考えたいんだとか。
更に「前にどんなお化粧をしたかが判れば便利」との事。
「この服の時にこんなお化粧をした」というのを、思い出したい時があるそうです。

成る程、これらを再現できれば、なかなか「魔法の鏡」らしくて面白いのではないでしょうか。
それに年齢なら、既に「人物判断」で判るようになっていますからね。


#現代版はこうなる：やりたい事

という訳で、今回の記事ではまず、以下のような機能を追加する事にします。

* ボタン等を取り去り、より鏡らしい見た目にする。
* 鏡の端に日時を表示し続ける。
* 声を掛けると人物判断を行い、「性別」「年齢」「お化粧の情報」等を表示する。


#ボタン等を取り去り、より鏡らしい見た目にする。

それでは、本記事で想定しているサンプルプログラムの動作を説明します。
前回の『[(2)：AIっぽい事をさせてみたい(その2)](https://qiita.com/denaripam/items/c53c01ea8856ab18f0f0)』では、画面を開くとカメラが起動し画像が表示されると共に、「人物判断」ボタン等も表示されるようになっています。
正直なところ、見た目があまり鏡っぽくありません。
なので、まずこの部分を整えて「魔法の鏡」らしくします。

始めに「人物判断」ボタンやサウンドビジュアライザーといった、いかにもな部品をHTMLから削除します。
人物判断結果を表示している「denaripamSimpleAI_responseTextArea」の部分は処理に必要なので、非表示にしておきます。

併せて、飾り枠の画像をオーバーレイで表示します。
変更後に起動すると、このように表示されます。
これだけでも、ぐっと鏡らしくなりますね。

![3-1.png](https://qiita-image-store.s3.amazonaws.com/0/255412/9da56e9d-6c12-485f-4e57-00ec6d10fffd.png)

 

###画像の重ね表示

画像を重ねて表示するのは、CSSで実装しています。
CSSの「position」と「z-index」を用いる事で、複数の画像や文字列を重ねて表示する事ができます。
CSSのソースは、以下の通りです。

```css
/* 鏡フレーム */
.img-over-top {
	position: absolute;
	z-index: 1;
}
/* 鏡本体 */
.video-wrap {
	position: relative;
}


```

HTML側の変更後のソースは、以下の通りです。
imgタグで飾り枠を、その下に鏡本体のvideoを表示しています。

```html
<img class="img-over-top" src="fl_001.png" width="800px" height="600px">
<div class="video-wrap">
  <video id="denaripamControlSensor_video" width="800px" height="600px" controls autoplay>
  </video>
</div>
```

飾り枠の画像については、「素材　飾り枠」等で検索してみて下さい。
あまり大きくない枠でしたら[Frame Design(フレームデザイン](http://frames-design.com/)さんが多種多様で素敵な素材を提供して下さっており、お勧めです。
サンプルプログラムでは大きい鏡にしたかったので、残念ながら丁度良い素材が見つからず、GIMPで簡単に自作しましたが…。

なお、使用される飾り枠については、中央の鏡部分にあたる箇所が透明になっている画像を使って下さいね。
（文字を乗せる前提の）中央が透明でない背景色の素材を使うと、肝心の顔が映らなくなってしまいます。


#鏡の端に日時を表示し続ける。

次に、日時を画面に表示し続けるようにします。
朝の貴重な時間にお化粧するのですから、見えやすい位置に時計があれば楽ちんですからね。

サンプルプログラムでは、時計の表示部分に実際に表示する日時を取得するJavaScriptを追加しました。
1秒ごとに日時を取得するようにする関数「showClock()」を新規作成します。

**//-----2018/05/16修正・追記ここから-----//**
```js
//現在日時表示
function showClock(){
	var now = new Date();
	document.getElementById("dispClock").innerHTML = now.toLocaleString();
}
```

画面書き換え用のタイマー処理は「window.onload」で行います。
```js
//現在日時のタイマー起動
setInterval('showClock()',1000);
```

ここでは秒まで表示していますが、忙しないと思われた方は、「setInterval」とnow.toLocaleString();の記述を変更し、分単位に切り替えて下さい。
**//-----2018/05/16修正・追記ここまで-----//**


時計の表示部分は、先に出てきた画像の重ね合わせと同じ要領で実現できます。

```css
/* 時計表示部 */
.clock-over-top {
	background: #cbb994;
	color: #eaf4fc;
	font-family: serif;          
	font-size: 100%;
	position: absolute;
	left: 2%;
	top: 2%;
	z-index: 1;
	padding-left: 5px; padding-right: 5px;
}
```

HTML側で、先ほど作成したJavaScriptの関数「showClock」を呼び出して下さい。
これで、秒単位で日時が表示されるようになります。

```html
<img class="img-over-top" src="fl_001.png" width="800px" height="600px">
<p class="clock-over-top" id="dispClock"><script>showClock();</script></p>
<div class="video-wrap">
  <video id="denaripamControlSensor_video" width="800px" height="600px" controls autoplay>
  </video>
</div>
```


#声を掛けると人物判断を行い、「性別」「年齢」「お化粧の情報」等を表示する。

前回の『[(2)：AIっぽい事をさせてみたい(その2)](https://qiita.com/denaripam/items/c53c01ea8856ab18f0f0)』では、「人物判断してください」と声を掛けると、分析結果の情報を答えてくれるようにしました。
この機能を修正し、「人物判断してください」と声を掛けると、日時の下に結果を表示するようにします。

まず、サンプルプログラムの動作結果はこんな風になります。
「人物判断して下さい」と声を掛けた後の画面です。

![3-2.png](https://qiita-image-store.s3.amazonaws.com/0/255412/63618eb3-801e-e8e9-55c1-cd6540f95900.png)

 


情報の表示部分は、このように表示されます。
実際は眼鏡の種類（普通の眼鏡か、サングラスか…等）も取得できるのですが、ここでは眼鏡を掛けているかどうかだけを判断しています。

![3-3.png](https://qiita-image-store.s3.amazonaws.com/0/255412/f067386d-10ca-1c9f-d9fe-6e8dc59b2d63.png)

 
###FaceAPI で取得したい情報を指定する方法

FaceAPIで取得したい情報を指定するためには、パラメータを正しく設定する必要があります。
眼鏡を掛けているかどうか等の情報は「FaceAttributes」に含まれています。詳しい情報をお知りになりたい方は、APIの仕様を確認して下さい。

サンプルプログラムでは、APIキーの指定部分で「MicrosoftAzureFaceAPIparams:」に対して設定しています。
修正後のコードは以下の通り。

```js
//-----uriBase、APIキーを設定してください START-----
			MicrosoftAzureFaceAPI:true,
			MicrosoftAzureFaceAPIuriBase:"",		//FaceAPIのエンドポイントを入力
			MicrosoftAzureFaceAPIsubscriptionKey:"",		//FaceAPIのキー②を入力
			MicrosoftAzureFaceAPIparams:{
				"returnFaceAttributes": "gender,age,glasses,facialHair,makeup",											//取得したい情報をここで指定する
			},

			MicrosoftBingSpeechAPIREST:true,
			MicrosoftBingSpeechAPIRESTuriBase:"https://speech.platform.bing.com/speech/recognition/<RECOGNITION_MODE>/cognitiveservices/v1?language=<LANGUAGE_TAG>&format=<OUTPUT_FORMAT>",		//※※※※変更不要※※※※
			MicrosoftBingSpeechAPIsubscriptionKey:"",		//BingSpeechAPIのキー②を入力

			MicrosoftAzureComputerVisionAPI:true,
			MicrosoftAzureComputerVisionAPIuriBase:"",		//ComputerVisionAPIのエンドポイントを入力

			MicrosoftAzureComputerVisionAPIsubscriptionKey:"",	//ComputerVisionAPIのキー①を入力

//-----uriBase、APIキーを設定してください END-----

```

###APIの処理結果は、JSON形式

取得した情報は、そのままでは表示しづらい状態ですので、編集を行います。
少々おさらいになりますが、サンプルプログラムでは処理結果を「output」に格納し、データがあれば実際の分析結果内容を「responsedata」に格納。
「responsedata」に格納されている取得結果は「var responsedata = JSON.parse(output.value);」とある通り、JSON形式で戻ってきます。

```js
SimpleAI.Personinformation(		//人物判断処理
	{
		done:function(data) {
			msg = "";
			var output=document.getElementById("denaripamSimpleAI_responseTextArea");
			if(output) {
				var responsedata = JSON.parse(output.value);
```

今回欲しいのは、「responsedata[0]」のうちの「faceAttributes」の部分です。
「gender」「age」等の情報を取得して、見やすい文字列として編集します。

また、鏡にうまく映っておらず人物判断ができなかった場合は、以下のようなエラーを表示するようにしました。
![3-4.png](https://qiita-image-store.s3.amazonaws.com/0/255412/c8eb65a1-204b-e572-14cb-97166912898a.png)

　

編集処理を行ったソースがこちらです。

```js
	SimpleAI.Personinformation(		//人物判断処理
		{
			done:function(data) {
				msg = "";
				var output=document.getElementById("denaripamSimpleAI_responseTextArea");
				if(output) {
					var responsedata = JSON.parse(output.value);
					//responsedata.Processingtext=Processingtext;
					////判断結果をAIが音声で返す
					//if(responsedata[0]) SimpleAI.SpeechSynthesis(responsedata[0].Processingtext);
					//判断結果を画面表示用に編集する
					if(responsedata[0]) {
						if(responsedata[0].faceAttributes.gender){
							if(responsedata[0].faceAttributes.gender=='female'){
								msg = msg + "性別：女性" + '<br>';
							} else {
								msg = msg + "性別：男性" + '<br>';
							}
						}
						if(responsedata[0].faceAttributes.age){
						msg = msg + "年齢：" + responsedata[0].faceAttributes.age + '<br>';
					}
					if(responsedata[0].faceAttributes.glasses){
						if(responsedata[0].faceAttributes.glasses=='NoGlasses'){
							msg = msg + "眼鏡：なし" + '<br>';
						} else {
							msg = msg + "眼鏡：あり" + '<br>';
						}
					}
					if(responsedata[0].faceAttributes.makeup.eyeMakeup){
						msg = msg + "アイメイク：あり" + '<br>';
					} else {
						msg = msg + "アイメイク：なし" + '<br>';
					}
					if(responsedata[0].faceAttributes.makeup.lipMakeup){
						msg = msg + "リップメイク：あり" + '<br>';
					} else {
						msg = msg + "リップメイク：なし" + '<br>';
					}
					document.getElementById("dispMsg").innerHTML = msg;
				} else {
					msg = "認識できませんでした。明るい所で鏡に映るようにして下さい。";
				}
			} else {
				msg = "認識できませんでした。明るい所で鏡に映るようにして下さい。";
			}
			document.getElementById("dispMsg").innerHTML = msg;
		},
		fail:function(errstring) {
			msg = "APIサービスが無効です。";
			document.getElementById("dispMsg").innerHTML = msg;
			//alert("NG!!" + errstring);
		},
	},
);
```

###取得した情報を編集し、画面に表示する

編集した情報の表示については、時計の表示と同様です。
CSSとHTMLを変更した結果がこちら。

```css
/* 情報表示部 */
.msg-over-top {
	background: #cbb994;
	color: #eaf4fc;
	font-family: serif;          
	font-size: 80%;
	position: absolute;
	left: 2%;
	top: 7%;
	z-index: 1;
	padding-left: 5px; padding-right: 5px;
}
```

```html
<img class="img-over-top" src="fl_001.png" width="800px" height="600px">
<p class="clock-over-top" id="dispClock"><script>showClock();</script></p>
<p class="msg-over-top" id="dispMsg"></p>
<div class="video-wrap">
  <video id="denaripamControlSensor_video" width="800px" height="600px" controls autoplay>
  </video>
</div>
```


#サンプルコード

最後に、今回作成したサンプルプログラムの全文を記載しておきます。

<details><summary>サンプルコード</summary><div>

```js
<!DOCTYPE html>
<html>
<head>
    <title>魔法の鏡</title>
    <script src="https://code.jquery.com/jquery-3.0.0.js"></script>
    <script src="simpleai-0.1.0.min.enc.js"></script>
<script>
//------------------------------------------------------------------------------------------------------------------------
//起動時処理
window.onload = function () {
	var SimpleAI = new denaripamSimpleAI(
		this,
		{},
		{
			initCamera:true,
			initMicrophone:true,
			VoiceRecognition:{listen:true},

//-----uriBase、APIキーを設定してください START-----
			MicrosoftAzureFaceAPI:true,
			MicrosoftAzureFaceAPIuriBase:"",											//FaceAPIのエンドポイントを入力
			MicrosoftAzureFaceAPIsubscriptionKey:"",									//FaceAPIのキー②を入力
			MicrosoftAzureFaceAPIparams:{
				"returnFaceAttributes": "gender,age,glasses,facialHair,makeup",			//取得したい情報をここで指定する
			},

			MicrosoftBingSpeechAPIREST:true,
			MicrosoftBingSpeechAPIRESTuriBase:"https://speech.platform.bing.com/speech/recognition/<RECOGNITION_MODE>/cognitiveservices/v1?language=<LANGUAGE_TAG>&format=<OUTPUT_FORMAT>",					//※※※※変更不要※※※※
			MicrosoftBingSpeechAPIsubscriptionKey:"",									//BingSpeechAPIのキー②を入力

			MicrosoftAzureComputerVisionAPI:true,
			MicrosoftAzureComputerVisionAPIuriBase:"",									//ComputerVisionAPIのエンドポイントを入力

			MicrosoftAzureComputerVisionAPIsubscriptionKey:"",							//ComputerVisionAPIのキー①を入力

//-----uriBase、APIキーを設定してください END-----
		},
	);
//-----------------------------------------------
	//情報表示用
	var msg="";
	//現在日時のタイマー起動
	setInterval('showClock()',1000);

	if(SimpleAI) {
		//スマートエージェント開始
		SimpleAI.SmartAgent();

		//人物判断（スマートエージェントコマンド追加サンプル）
		var cmd;
		cmd=SimpleAI.getSmartAgentCommand();
		cmd.MainCommand.push(
			{
				//人物判断の音声キーを追加する
				label: "PersonalJudgment",
				labelname: "人物判断",				//ラベル名称、現在のモード読み上げ等に使用
				word:[/人物/,/判断/],
				message:"人物判断を行います",
				messageafter:function(sender,event,param) {
					SimpleAI.Personinformation(		//人物判断処理
						{
							done:function(data) {
								msg = "";
								var output=document.getElementById("denaripamSimpleAI_responseTextArea");
								if(output) {
									var responsedata = JSON.parse(output.value);
									//responsedata.Processingtext=Processingtext;
									////判断結果をAIが音声で返す
									//if(responsedata[0]) SimpleAI.SpeechSynthesis(responsedata[0].Processingtext);
									//判断結果を画面表示用に編集する
									if(responsedata[0]) {
										if(responsedata[0].faceAttributes.gender){
											if(responsedata[0].faceAttributes.gender=='female'){
												msg = msg + "性別：女性" + '<br>';
											} else {
												msg = msg + "性別：男性" + '<br>';
											}
										}
										if(responsedata[0].faceAttributes.age){
											msg = msg + "年齢：" + responsedata[0].faceAttributes.age + '<br>';
										}
										if(responsedata[0].faceAttributes.glasses){
											if(responsedata[0].faceAttributes.glasses=='NoGlasses'){
												msg = msg + "眼鏡：なし" + '<br>';
											} else {
												msg = msg + "眼鏡：あり" + '<br>';
											}
										}
										if(responsedata[0].faceAttributes.makeup.eyeMakeup){
											msg = msg + "アイメイク：あり" + '<br>';
										} else {
											msg = msg + "アイメイク：なし" + '<br>';
										}
										if(responsedata[0].faceAttributes.makeup.lipMakeup){
											msg = msg + "リップメイク：あり" + '<br>';
										} else {
											msg = msg + "リップメイク：なし" + '<br>';
										}
										document.getElementById("dispMsg").innerHTML = msg;
									} else {
										msg = "認識できませんでした。明るい所で鏡に映るようにして下さい。";
									}
								} else {
									msg = "認識できませんでした。明るい所で鏡に映るようにして下さい。";
								}
								document.getElementById("dispMsg").innerHTML = msg;
							},
							fail:function(errstring) {
								msg = "APIサービスが無効です。";
								document.getElementById("dispMsg").innerHTML = msg;
								//alert("NG!!" + errstring);
							},
						},
					);
				},
			},
		);
	}
//--------------------------------------------
};
//--------------------------------------------
//現在日時表示
function showClock(){
	var now = new Date();
	document.getElementById("dispClock").innerHTML = now.toLocaleString();
}
//------------------------------------------------------------------------------------------------------------------------
</script>
<style>/** CSS：Area */
/* 鏡フレーム */
.img-over-top {
	position: absolute;
	z-index: 1;
}
/* 鏡本体 */
.video-wrap {
  position: relative;

}
/* 時計表示部 */
.clock-over-top {
	background: #cbb994;
	color: #eaf4fc;
	font-family: serif;          
	font-size: 100%;
	position: absolute;
	left: 2%;
	top: 2%;
	z-index: 1;
	padding-left: 5px; padding-right: 5px;
}
/* 情報表示部 */
.msg-over-top {
	background: #cbb994;
	color: #eaf4fc;
	font-family: serif;          
	font-size: 80%;
	position: absolute;
	left: 2%;
	top: 7%;
	z-index: 1;
	padding-left: 5px; padding-right: 5px;
}
</style>
</head>
<body>
<img class="img-over-top" src="fl_001.png" width="800px" height="600px">
<p class="clock-over-top" id="dispClock"><script>showClock();</script></p>
<p class="msg-over-top" id="dispMsg"></p>
<div class="video-wrap">
  <video id="denaripamControlSensor_video" width="800px" height="600px" controls autoplay>
  </video>
</div>
<div hidden id="denaripamSimpleAI_responseRect"></div><br>
<textarea hidden id="denaripamSimpleAI_responseTextArea" class="UIInput" style="width:580px; height:400px;"></textarea><br>
</body>
</html>
```

</div></details>

#最後に

今回はここまでです。お疲れ様でした。
次回の記事では、更に機能を追加していきます。