#はじめに
AI連携フレームワーク『シンプルエーアイ』を使って遊ぶ、第4回目です。
・(1)準備編
・(2):AIっぽい事をさせてみたい(その1)
・(2):AIっぽい事をさせてみたい(その2)
・(3):「魔法の鏡」を作るぞ
公式サイトはこちら:https://smartaifw.com/simpleai-trial/
#実用性も大事ですが…
前回の『(3):「魔法の鏡」を作るぞ』では、起動したら現在日時が表示されている鏡が表示され、「人物判断して」と声を掛けると、結果が鏡に表示されるようにしました。
続いては、前回の前書きに書いた「前にどんなお化粧をしたかが判れば便利」という要素の実現を考えていましたが…
折角のお遊びですので、今回は更に”魔法の鏡”っぽい、使う楽しみを上乗せする方向で進めたいと思います。
#”魔法の鏡らしさ”とは何ぞや
とは言うものの、”らしさ”って何だろう?
色々考えてみましたが、やはりここは『シンプルエーアイ』の使いやすさを生かした、お遊び要素を加える事にします。
『シンプルエーアイ』は、AIの返答の仕方等も柔軟に変更することができます。
今は「了解」と返事をしてくれるのですが、これを変えてしまいます。
そもそも「人物判断」という言葉も、実際の機能が分かり辛いので、一緒に変更します。
それから、お化粧する時に音楽が流れていたら、作業が捗るのではないでしょうか。
ですので、「音楽をかけて」等と呼びかけると、音楽を流すようにしたいと思います。
ついでと言っては何ですが、天気予報やニュースも見られるようにしてしまいましょう。
#やりたい事
今回の記事では、以下のような機能を追加します。
- AIの応答の言葉を変える
- 人物判断の言葉の変更
- 「音楽をかけて」と呼びかけると、鏡の上部に画面を開いて音楽を流す(Youtube起動)
- 「天気教えて」や「ニュースを見たい」と呼びかけると、鏡の上部に天気予報やニュースを表示する(Youtube起動)
#AIの応答の言葉を変える
それでは、本記事で想定しているサンプルプログラムの動作を説明します。
まず、鏡に呼びかけた時に「かしこまりました」と返答をしてくれるようにします。
『シンプルエーアイ』のスマートエージェント機能には様々なパラメータがあります。
サンプルプログラムでは「SimpleAI.param.SmartAgent」と記述している部分が、そのパラメータ部分です。
このうちの「Prompt」が応答の言葉なので、この値を設定しましょう。スマートエージェント開始処理の後に行って下さい。
またAIのお喋りの語尾を変えることも出来ます。「PromptEnd」というパラメータです。
ここで冒頭に「、」を入れているのは、お喋りする際に一泊置いてもらう為です。
ここでは、語尾に「ご主人様」と言うようにしました。
追加部分のソースは以下の通りです。
//AIの応答の言葉を変更する
SimpleAI.param.SmartAgent.Prompt = "かしこまりました、";
//AIの語尾の言葉を変更する
SimpleAI.param.SmartAgent.PromptEnd = "、ご主人様";
#人物判断の言葉の変更
続けて、人物判断を呼び出す言葉・音声キーを変更します。
お化粧情報も含めて表示するので、「メイクチェック」などの言葉にしましょう。
まず、以前に設定した、人物判断処理追加部分のソースがこちら。
//人物判断(スマートエージェントコマンド追加サンプル)
var cmd;
cmd=SimpleAI.getSmartAgentCommand();
cmd.MainCommand.push(
{
//人物判断の音声キーを追加する
label: "PersonalJudgment",
labelname: "人物判断", //ラベル名称、現在のモード読み上げ等に使用
word:[/人物/,/判断/],
message:"人物判断を行います",
おさらいになりますが、音声キーそのものは「word:」の部分を設定すればOKです。
ただし「message:」の部分にも”人物判断”という言葉が入っているので、今回は併せて変更します。
変更後のソースはこちらです。
//人物判断の音声キーを追加する
label: "PersonalJudgment",
labelname: "人物判断", //ラベル名称、現在のモード読み上げ等に使用
word:[/メイク|make|化粧|けしょう|仕上げ/,/check|確認|判断|判定|見て/],
message:"しばらくお待ちください",
###余談:AIが聞き取り辛い言葉がある
当初は「幾つに見える?」という言葉にしようとしたのですが、AIにとっては大変聞き取り辛い言葉らしく、うまく判定してくれませんでした。
「メイク」という言葉も同様で、なぜか違う言葉に判断されてしまいがちです。
では、AIが言葉をどのように聞き取ったかを、どうやって確認すればいのでしょう。
ブラウザの機能の一つである開発ツールを使えば、AIがどのように判断したかを見る事ができます。
Chromeの場合は、画面右上のメニューより「その他のツール」→「デベロッパーツール」を開いて下さい。
「Console」欄が開いていない場合は、クリックして開きます。
すると、以下のように処理ログが表示されるようになります。
下図の場合で言うと、上の赤枠部分は「メイクチェック」と呼びかけた場合、下の赤枠は「メイク判断」と呼びかけた場合です。
「メイクチェック」という言葉が、うまく聞き取られていない事が判りますね。
ですので今回は、「メイク」だけではなく「化粧」など、幾つかの言葉を設定しました。
このように、AIにとって聞き取り易い/聞き取り辛い言葉がありますので、キーワードを設定する際は、何度か試す必要があるでしょう。
#「音楽をかけて」と呼びかけると、鏡の上部に画面を開いて音楽を流す(Youtube起動)
それでは次に、音楽を流す処理を追加したいと思います。
サンプルプログラムの動作を説明します。
「音楽をかけて」「音楽ききたい」といった呼びかけをすると、Youtubeの小さな画面を鏡の右上に上乗せして開き、指定したキーワードに該当する音楽を流してくれるようにします。
音楽呼び出し後の画面は、こんな風になります。画面右上の赤枠部分がYoutubeの画面です。
###Youtubeを扱えるようにする
この動作を行うためには、まずはYoutube動画を扱えるように変更する必要があります。
まずHTMLのヘッダ部分に、Youtubeを扱う為の宣言を追加して下さい。
追加部分のソースは以下の通りです。
<script type="text/javascript" src="https://www.youtube.com/iframe_api"></script>
また、Youtubeを表示する部分をHTMLに追加します。
サンプルプログラムでは、情報表示部分とvideoタグの間に挿入しました。
修正後のソースは以下の通りです。
<p class="msg-over-top" id="dispMsg"></p>
<p class="youtube-over-top" id="dispYTPlayer"></p>
<div class="video-wrap">
<video id="denaripamControlSensor_video" width="800px" height="600px" controls autoplay>
</video>
</div>
勿論、CSS側にもYoutube重ね合わせ表示の追加が必要です。サンプルプログラムのソースは以下の通り。
/* YoutubePlayer表示部 */
.youtube-over-top {
opacity: 0.5; /* 対象の透過度(0:完全透明 1:不透明) */
position: absolute;
left: 580px;
top: 2%;
z-index: 1;
padding-left: 5px; padding-right: 5px;
}
続いて、JavaScriptでYoutube起動処理を組み込みます。
サンプルプログラムでは YouTubePlayerAPI を利用する事にしました。
最初に YouTubePlayerAPI のオブジェクトを作成し、「YouTubeIframeAPI=true;」で有効にします。
それから「YouTubeplayer_start」という関数を作成し、実際のYoutube起動処理を記述します。
オブジェクトがあればいったん停止処理を行い、そして改めてnewで作成しています。
Youtubeの表示サイズ等は、ここで指定します。
追加部分のソースは以下の通り。
//youtube
var YouTubeplayer;
var YouTubeIframeAPI=false;
function onYouTubeIframeAPIReady() {
YouTubeIframeAPI=true;
}
//youtube表示ウィンドウ
var html_temp_modal =
'<!-- YoutubePlayer -->'
+'<div class="embed-responsive embed-responsive-16by9">'
+' <div class="embed-responsive-item" id="player"></div>'
+'</div>';
//youtube起動
var YouTubeplayer_start=function(keyword,volume) {
if(YouTubeIframeAPI) {
YouTubeplayer_stop();
YouTubeplayer = new YT.Player('player', {
height: '150',
width: '200',
controls: 2, //コントロールの表示
iv_load_policy: 3, //アノテーション非表示
showinfo: 0, //インフォメーション非表示
events: {
onReady: function(event){
if(keyword) YouTubeplayer.loadPlaylist({listType:"search",list:keyword,});
if(volume) YouTubeplayer.setVolume(100);
},
},
});
}
};
起動処理と対になる停止処理も、ここで入れてしまいましょう。
「YouTubeplayer_stop」という関数を作成し、Youtube停止処理を記述します。ここではdestroy、つまり破棄をしています。
追加部分のソースは以下の通り。
//youtube停止
var YouTubeplayer_stop=function() {
if(YouTubeplayer)YouTubeplayer.destroy();
};
###音楽をかける音声キーの追加
これでYoutubeを扱えるようになりましたが、勿論これだけでは足りませんね。音声キーの追加が必要です。
以前に追加した「人物判断」の後ろに処理を加えましょう。
音声キーの追加そのものは以前の記事で行っていますので、今回のサンプルプログラムにおける新しい要素だけを説明します。
まず、サンプルプログラムの対象部分のソースを見て下さい。
//音楽をかける音声キーを追加する
{
label: "PlayMusics",
labelname: "音楽をかける", //ラベル名称、現在のモード読み上げ等に使用
word:[/音楽|mugic|曲/,/かける|かけて|流す|流して|きく|きかせて|ききたい/],
message:"こちらの曲をどうぞ",
messageafter:function(sender,event,param) {
document.getElementById("dispYTPlayer").innerHTML = html_temp_modal;
YouTubeplayer_start("やる気の出る クラシック音楽 ピアノBGM");
},
},
先程追加したJavaScript関数「YouTubeplayer_start」を呼び出す際に、キーワードを指定していますね。
これは実際にYoutubeで検索処理を行う為のキーワードで、この言葉をYouTubeIframeAPIに引き渡して検索処理を行うようになっています。
下記ソースの「if(keyword) YouTubeplayer.loadPlaylist({listType:"search",list:keyword,});」の部分です。
なお、次行の「if(volume) YouTubeplayer.setVolume(100);」の部分で、音量を設定しています。
if(keyword) YouTubeplayer.loadPlaylist({listType:"search",list:keyword,});
if(volume) YouTubeplayer.setVolume(100);
以上の処理で、鏡に向かって「音楽かけて」等と告げると、鏡の下にYoutubeの音楽動画が表示されるようになりました。
###音楽を止める音声キーの追加
始まりがあれば終わりがあるので、音楽を止める音声キーも必要ですね。さくっと追加してしまいましょう。
先程作成した「YouTubeplayer_stop」を呼び出すようにして下さい。
対象部分のソースは以下の通りです。
//音楽を止める音声キーを追加する
{
label: "StopMusics",
labelname: "音楽を止める", //ラベル名称、現在のモード読み上げ等に使用
word:[/音楽|mugic|曲/,/止める|止めて|やめる|やめて|終わり|終了/],
messageafter:function(sender,event,param) {
YouTubeplayer_stop();
},
},
#「天気教えて」や「ニュースを見たい」と呼びかけると、鏡の上部に天気予報やニュースを表示する(Youtube起動)
Youtubeの起動/停止という、大きな処理が追加できました。
ここをクリアしたら、天気予報やニュースの表示も軽いものです。
やり方は音楽呼び出しとそれ程変わらず、それぞれの起動処理と停止処理の音声キーを追加します。
それでは、サンプルプログラムの追加部分のソースを見て下さい。
//朝のニュースを呼び出す音声キーを追加する
{
label: "ShowNews",
labelname: "ニュース表示", //ラベル名称、現在のモード読み上げ等に使用
word:[/ニュース/,/見る|見せて|見たい|流す|流して/],
message:"どうぞ",
messageafter:function(sender,event,param) {
document.getElementById("dispYTPlayer").innerHTML = html_temp_modal;
var date = new Date();
YouTubeplayer_start("nhk 朝のニュース");
//前ゼロなし日付だと予想と異なるものが表示される。前ゼロ付きスラッシュ日付だと、うまく再生できなくなる。
//YouTubeplayer_start("nhk ニュース " + formatYMD(date));
},
},
//朝のニュースを止める音声キーを追加する
{
label: "StopNews",
labelname: "ニュース停止", //ラベル名称、現在のモード読み上げ等に使用
word:[/ニュース/,/止める|止めて|やめる|やめて|終わり|終了/],
messageafter:function(sender,event,param) {
YouTubeplayer_stop();
},
},
//天気予報を呼び出す音声キーを追加する
{
label: "ShowWeatherForecast",
labelname: "天気予報表示", //ラベル名称、現在のモード読み上げ等に使用
word:[/天気/,/教えて|見る|見せて|見たい/],
message:"どうぞ",
messageafter:function(sender,event,param) {
document.getElementById("dispYTPlayer").innerHTML = html_temp_modal;
var date = new Date();
YouTubeplayer_start("全国の天気予報 朝");
},
},
//天気予報を止める音声キーを追加する
{
label: "StopWeatherForecast",
labelname: "天気予報停止", //ラベル名称、現在のモード読み上げ等に使用
word:[/天気/,/止める|止めて|やめる|やめて|終わり|終了/],
messageafter:function(sender,event,param) {
YouTubeplayer_stop();
},
},
###余談:YoutubePlayerAPIにスラッシュ編集の日付をキーワードとして引き渡すと、うまく引き当ててくれない
ニュースにしても天気予報にしても当日の日付が必要だろうと、スラッシュ編集した(つまり'2018/05/16’のような書式ですね)日付をキーワードに引き渡したところ、なぜかうまく動画が表示されません。
YoutubePlayerの枠は表示されるのですが、どうやらキーワード検索が出来ていないようです。
もし、解決方法など詳しい方がいらっしゃいましたら、コメントいただければ幸いです。
#サンプルコード
最後に、今回作成したサンプルプログラムの全文を記載しておきます。
サンプルコード
<!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 type="text/javascript" src="https://www.youtube.com/iframe_api"></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();
//AIの応答の言葉を変更する
SimpleAI.param.SmartAgent.Prompt = "かしこまりました、";
//AIの語尾の言葉を変更する
SimpleAI.param.SmartAgent.PromptEnd = "、ご主人様";
//人物判断(スマートエージェントコマンド追加サンプル)
var cmd;
cmd=SimpleAI.getSmartAgentCommand();
cmd.MainCommand.push(
{
//人物判断の音声キーを追加する
label: "PersonalJudgment",
labelname: "人物判断", //ラベル名称、現在のモード読み上げ等に使用
word:[/メイク|make|化粧|けしょう|仕上げ/,/check|確認|判断|判定|見て/],
message:"しばらくお待ちください",
// 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);
//判断結果を画面表示用に編集する
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);
},
},
);
},
},
//音楽をかける音声キーを追加する
{
label: "PlayMusics",
labelname: "音楽をかける", //ラベル名称、現在のモード読み上げ等に使用
word:[/音楽|mugic|曲/,/かける|かけて|流す|流して|きく|きかせて|ききたい/],
message:"こちらの曲をどうぞ",
messageafter:function(sender,event,param) {
document.getElementById("dispYTPlayer").innerHTML = html_temp_modal;
YouTubeplayer_start("やる気の出る クラシック音楽 ピアノBGM");
},
},
//音楽を止める音声キーを追加する
{
label: "StopMusics",
labelname: "音楽を止める", //ラベル名称、現在のモード読み上げ等に使用
word:[/音楽|mugic|曲/,/止める|止めて|やめる|やめて|終わり|終了/],
messageafter:function(sender,event,param) {
YouTubeplayer_stop();
},
},
//朝のニュースを呼び出す音声キーを追加する
{
label: "ShowNews",
labelname: "ニュース表示", //ラベル名称、現在のモード読み上げ等に使用
word:[/ニュース/,/見る|見せて|見たい|流す|流して/],
message:"どうぞ",
messageafter:function(sender,event,param) {
document.getElementById("dispYTPlayer").innerHTML = html_temp_modal;
var date = new Date();
YouTubeplayer_start("nhk 朝のニュース");
//前ゼロなし日付だと予想と異なるものが表示される。前ゼロ付きスラッシュ日付だと、うまく再生できなくなる。
//YouTubeplayer_start("nhk ニュース " + formatYMD(date));
},
},
//朝のニュースを止める音声キーを追加する
{
label: "StopNews",
labelname: "ニュース停止", //ラベル名称、現在のモード読み上げ等に使用
word:[/ニュース/,/止める|止めて|やめる|やめて|終わり|終了/],
messageafter:function(sender,event,param) {
YouTubeplayer_stop();
},
},
//天気予報を呼び出す音声キーを追加する
{
label: "ShowWeatherForecast",
labelname: "天気予報表示", //ラベル名称、現在のモード読み上げ等に使用
word:[/天気/,/教えて|見る|見せて|見たい/],
message:"どうぞ",
messageafter:function(sender,event,param) {
document.getElementById("dispYTPlayer").innerHTML = html_temp_modal;
var date = new Date();
YouTubeplayer_start("全国の天気予報 朝");
},
},
//天気予報を止める音声キーを追加する
{
label: "StopWeatherForecast",
labelname: "天気予報停止", //ラベル名称、現在のモード読み上げ等に使用
word:[/天気/,/止める|止めて|やめる|やめて|終わり|終了/],
messageafter:function(sender,event,param) {
YouTubeplayer_stop();
},
},
);
}
//--------------------------------------------
};
//--------------------------------------------
//現在日時表示
function showClock(){
var now = new Date();
document.getElementById("dispClock").innerHTML = now.toLocaleString();
}
//--------------------------------------------
//youtube
var YouTubeplayer;
var YouTubeIframeAPI=false;
function onYouTubeIframeAPIReady() {
YouTubeIframeAPI=true;
}
//youtube表示ウィンドウ
var html_temp_modal =
'<!-- YoutubePlayer -->'
+'<div class="embed-responsive embed-responsive-16by9">'
+' <div class="embed-responsive-item" id="player"></div>'
+'</div>';
//youtube起動
var YouTubeplayer_start=function(keyword,volume) {
if(YouTubeIframeAPI) {
YouTubeplayer_stop();
YouTubeplayer = new YT.Player('player', {
height: '150',
width: '200',
controls: 2, //コントロールの表示
iv_load_policy: 3, //アノテーション非表示
showinfo: 0, //インフォメーション非表示
events: {
onReady: function(event){
if(keyword) YouTubeplayer.loadPlaylist({listType:"search",list:keyword,});
if(volume) YouTubeplayer.setVolume(100);
},
},
});
}
};
//youtube停止
var YouTubeplayer_stop=function() {
if(YouTubeplayer)YouTubeplayer.destroy();
};
//--------------------------------------------
//前ゼロ付き日付変換
function formatYMD(date){
if (date){
var yyyy = date.getFullYear();
var mm = ("0"+ (date.getMonth()+1) ).slice(-2); // 前ゼロ付き2桁
var dd = ("0"+date.getDate()).slice(-2); // 前ゼロ付き2桁
return yyyy + '年' + mm + '月' + dd + "日";
}
};
//------------------------------------------------------------------------------------------------------------------------
</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;
}
/* YoutubePlayer表示部 */
.youtube-over-top {
opacity: 0.5; /* 対象の透過度(0:完全透明 1:不透明) */
position: absolute;
left: 580px;
top: 2%;
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>
<p class="youtube-over-top" id="dispYTPlayer"></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>
#最後に
今回はここまでです。お疲れ様でした。
数回に分けて掲載してきた「AI連携FW『シンプルエーアイ』を使ってみた」、如何でしたか?
まだ前書きの冒頭で書いた機能が残っているのですが、『シンプルエーアイ』に関する大まかな機能や使い方はご紹介できましたので、これで一区切りとさせていただきます。
また日を改めて、新しい記事を投稿することもあるやもしれません。その際はお付き合いいただければ幸いです。
記事をご覧下さった皆さま、また記事に関してご指摘下さった方々、有難うございました。