2020年10月27日にMicrosoft社が買収した Lobeのベータ版がリリースされていたので、
マスクをしているかどうか判定するツールをNode-REDを使ってローコードで作ってみました。
1. Lobeをインストールする
Lobeをダウンロード
下記URLにアクセスしてLobeのアプリをダウンロードします。
諸々情報を入力して 「Download」ボタンをクリックします。
ダウンロードしたファイルをインストールしておきます。
2. 学習データを登録する
2-1. プロジェクト作成
①プロジェクト名を入力して、右上の ②「Import」をクリックし、画像、カメラ、データセットからインポートしたい形式を選びます。今回は③カメラを選択しました。
2-2. 学習データ登録
カメラ画像の左下に学習するラベル名を入力します。入力したら、画面中央にあるシャッターボタンをクリックします。
長押しすることで連続して撮影することも可能です。学習が終われば、右上の「Done」をクリックします。
同様にマスクを付けているタイプの写真を撮影して学習させます。
2-3. トレーニング
撮影したデータの学習はバックグラウンドでされます。100%になれば効果音で知らせてくれます。
2-4. 確認する
学習したデータが正しくできているか確認します。左側メニューの「Play」から確認できます。判定ラベルはリアルタイムで切り替わります。
判定がおかしい場合は左下のアイコンで判定して正しいものを学習させてください。
3. Node-REDで使う
学習したデータは様々な形でプログラムに組み込むことができます。サポートされているのは、CoreML
TensorFlow
TensorFlow Lite
Local API
の4つがありました。
今回はNode-REDで使うので、 Local API
を使用します。
3-1. エクスポートする
メニューから「Export」をクリックします。
Local API
をクリックします。
APIのURLが発行されるので、メモしておきましょう。
3-2. Node-REDの設定
Node-RED Desktop アプリをインストールしてください。
ダウンロードしてアプリを起動して、右上のハンバーガーメニューにある「パレットの管理」をクリックします。
①「ノードを追加」タブをクリックして、②検索窓に下記4つのノード名を入力して検索します。それぞれ③ノードを追加してください。
- node-red-contrib-browser-utils
- node-red-contrib-image-output
- node-red-contrib-lobe-local-api
- node-red-node-base64
3-3. ノードを配置する
左側にあるパレットウィンドウから、 入力カテゴリにある Camera
ノードをフローエディタにドラッグ・アンド・ドロップします。
続いて、出力カテゴリにある image
ノードをドラッグ・アンド・ドロップして、先程配置した Camera
ノードと線でつなぎます。
パーサカテゴリにある、 base64
ノードをドラッグ・アンド・ドロップして、 Camera
ノードとつなぎます。
分析カテゴリにある、 lobe-local
ノードをドラッグ・アンド・ドロップして、 base64
ノードとつなぎます。ノードをダブルクリックして、プロパティ画面を開きます。
URLにLobeで発行されたLocal APIのURLを貼り付けます。
共通カテゴリにある、 debug
ノードをドラッグ・アンド・ドロップして、 lobe-local
ノードとつなぎます。最後に右上にある 「デプロイ」ボタンをクリックします。
Camera
ノードの左側にあるボタンをクリックするとPC内蔵カメラが起動します。デバッグタブをクリックして、結果が表示されていることを確認してください。
マスクをつけると、「マスクあり」と返ってきます。
うまくいかない方は下記をお試しください。
[
{
"id": "c74ee55b.52b928",
"type": "tab",
"label": "ハンズオン",
"disabled": false,
"info": ""
},
{
"id": "dfcc243c.fb35d8",
"type": "camera",
"z": "c74ee55b.52b928",
"name": "",
"x": 120,
"y": 240,
"wires": [
[
"1fcd09cb.659fc6",
"e0780598.440308"
]
]
},
{
"id": "1fcd09cb.659fc6",
"type": "image",
"z": "c74ee55b.52b928",
"name": "",
"width": 160,
"data": "payload",
"dataType": "msg",
"thumbnail": false,
"active": true,
"pass": false,
"outputs": 0,
"x": 170,
"y": 340,
"wires": []
},
{
"id": "e0780598.440308",
"type": "base64",
"z": "c74ee55b.52b928",
"name": "",
"action": "",
"property": "payload",
"x": 300,
"y": 240,
"wires": [
[
"897476b5.cd05b8"
]
]
},
{
"id": "897476b5.cd05b8",
"type": "lobe-local",
"z": "c74ee55b.52b928",
"name": "",
"modelUrl": "",
"base64Image": "",
"output": "best",
"x": 480,
"y": 240,
"wires": [
[
"db2c90a0.b4824"
]
]
},
{
"id": "db2c90a0.b4824",
"type": "debug",
"z": "c74ee55b.52b928",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 670,
"y": 240,
"wires": []
}
]
4. LINEと連携しよう
4-1. 新規チャネルを作成する
LINE DeveloperページへアクセスしてLINEログインしてください。
まだプロバイダー設定してない方は「新規プロバイダー作成」をクリックします。
プロバイダー名はお好きなものを入力して、「作成」ボタンをクリックします。
Messaging API
をクリックします。
各種項目を埋めていきます。
項目 | 値 |
---|---|
①チャネル名 | Lobeハンズオン |
②チャネル説明 | Lobeハンズオン |
③大業種 | 個人 |
④小業種 | 個人(その他) |
⑤メールアドレス | あなたのメールアドレスを入力してください |
2つのチェックを入れて、[作成]ボタンをクリックします。
[同意する]ボタンをクリックします。
4-2. ボットと友だちなる
作成したボットと友だちになっておきます。「Messaging API設定」タブにあるQRコードをLINEアプリから読み取って友だちになっておいてください。
4-3. アクセストークンを発行する
下の方にスクロールして、チャネルアクセストークンカテゴリにある「発行」ボタンをクリックして、チャネルアクセストークンを発行して、メモしておきましょう。終われば、応答メッセージにある「編集」をクリックします。
4-4. Webhookの設定を行う
応答メッセージ オフ
、Webhookは オン
にしてから「Messaging API設定」ボタンをクリックしてください。
WebhookのURLはNode-RED Desktopアプリのメニューバー①「エンドポイント - ngrokに接続する」で発行された②URLをコピーしておきます。Webhook URLはngrokのURLを貼り付けて、末尾に③「/bot
」と入力して、④「保存」ボタンをクリックします。
そして、最後に記載されている ⑤Channel secret
はメモしておきましょう。後ほど使います。
※例) https://xxxxxxxxxxxx.jp.ngrok.io/bot
4-5. LINE用ノードを設定する
パレット管理から下記ノードを追加しておいてください。
ネットワークカテゴリにある http in
ノードをドラッグ・アンド・ドロップします。メソッドは POST
を選択して、URLに「/bot
」と入力して、「完了」ボタンをクリックします。
項目 | 値 |
---|---|
メソッド | POST |
URL | /bot ※「 / 」を付け忘れないよう気をつけてください |
機能カテゴリにある function
ノードをドラッグ・アンド・ドロップします。 http in
ノードとつないでおきましょう。下記コードを反映させてください。LINEから送られてくる返信用のトークンを取得しています。
msg.replyToken = msg.payload.events[0].replyToken;
return msg;
LINEカテゴリにある line-image
ノードをドラッグ・アンド・ドロップします。 function
ノードとつないでおきましょう。AccessTokenは 4-3
で発行したチャネルアクセストークンを貼り付けます。出力形式は base64
を選択します。
項目 | 値 |
---|---|
AccessToken | 4-3で発行したチャネルアクセストークンを貼り付ける |
出力形式 | base64 |
line-image
ノードと lobe-local
ノードを線でつないでおきましょう。
機能カテゴリにある function
ノードをドラッグ・アンド・ドロップします。 lobe-local
ノードとつないでおきましょう。コード部分に下記コードを反映してください。
const ret = msg.payload;
const replyToken = msg.replyToken;
msg.payload = {};
msg.payload.events = [
{
"type": "message",
"replyToken": replyToken,
"message": {
"type": "text",
"text": `これは「${ret}」かな`
}
}
];
return msg;
LINEカテゴリにある ReplyMessage
ノード(※PushMessegeではないので気をつけてください)をドラッグ・アンド・ドロップします。 function
ノードとつないでおきましょう。
Secret(※4-4でメモしています)とアクセストークン(※4-3でメモしています)をそれぞれ反映してください。
項目 | 値 |
---|---|
Secret | 4-4でメモしたChannel Scret |
AccessToken | 4-3で発行したチャネルアクセストークン |
4-6. 動作確認する
LINEアプリで友だちになった「Lobeハンズオン」を開いてください。
トーク画面からマスクをつけていない画像とつけている画像をそれぞれ投稿して、結果が返ってくることを確認してみましょう。
4-7. 最終的なノード
どうしてもうまくいかない方は下記をNode-REDに適用してください。LINEのアクセストークンや、LobeのURLはご自身のものを設定してください。
[
{
"id": "c74ee55b.52b928",
"type": "tab",
"label": "ハンズオン",
"disabled": false,
"info": ""
},
{
"id": "dfcc243c.fb35d8",
"type": "camera",
"z": "c74ee55b.52b928",
"name": "",
"x": 130,
"y": 320,
"wires": [
[
"1fcd09cb.659fc6",
"e0780598.440308"
]
]
},
{
"id": "1fcd09cb.659fc6",
"type": "image",
"z": "c74ee55b.52b928",
"name": "",
"width": 160,
"data": "payload",
"dataType": "msg",
"thumbnail": false,
"active": true,
"pass": false,
"outputs": 0,
"x": 180,
"y": 420,
"wires": []
},
{
"id": "e0780598.440308",
"type": "base64",
"z": "c74ee55b.52b928",
"name": "",
"action": "",
"property": "payload",
"x": 310,
"y": 320,
"wires": [
[
"897476b5.cd05b8"
]
]
},
{
"id": "897476b5.cd05b8",
"type": "lobe-local",
"z": "c74ee55b.52b928",
"name": "",
"modelUrl": "",
"base64Image": "",
"output": "best",
"x": 490,
"y": 320,
"wires": [
[
"db2c90a0.b4824",
"e5b7608a.4514f"
]
]
},
{
"id": "db2c90a0.b4824",
"type": "debug",
"z": "c74ee55b.52b928",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 680,
"y": 320,
"wires": []
},
{
"id": "768e340b.3fa3fc",
"type": "http in",
"z": "c74ee55b.52b928",
"name": "",
"url": "/bot",
"method": "post",
"upload": false,
"swaggerDoc": "",
"x": 130,
"y": 200,
"wires": [
[
"78838a5f.9f3784"
]
]
},
{
"id": "6404378b.892d18",
"type": "line-image",
"z": "c74ee55b.52b928",
"name": "",
"MessageId": "",
"AccessToken": "",
"output": "base64",
"x": 570,
"y": 200,
"wires": [
[
"897476b5.cd05b8"
]
]
},
{
"id": "e5b7608a.4514f",
"type": "function",
"z": "c74ee55b.52b928",
"name": "結果取得",
"func": "const ret = msg.payload;\nconst replyToken = msg.replyToken;\n\nmsg.payload = {};\nmsg.payload.events = [\n {\n \"type\": \"message\",\n \"replyToken\": replyToken,\n \"message\": {\n \"type\": \"text\",\n \"text\": `これは「${ret}」かな`\n }\n }\n];\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 570,
"y": 420,
"wires": [
[
"c48a597a.950508"
]
]
},
{
"id": "c48a597a.950508",
"type": "ReplyMessage",
"z": "c74ee55b.52b928",
"name": "",
"channelSecret": "",
"channelAccessToken": "",
"replyMessage": "",
"x": 770,
"y": 420,
"wires": []
},
{
"id": "78838a5f.9f3784",
"type": "function",
"z": "c74ee55b.52b928",
"name": "リプライトークン取得",
"func": "msg.replyToken = msg.payload.events[0].replyToken;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 340,
"y": 200,
"wires": [
[
"6404378b.892d18"
]
]
}
]