チラシを撮って、オンラインサイトに飛びます！(追記修正9/26)

# 1.概要
　チラシとWEBサイトの連携企画（チラシ掲載の商品が店頭だけでなく、オンラインでも購入できたり予約できる）において、チラシの商品部分を写真に撮って、LINE Botに投稿することで、QRコードを使わずに、オンラインサイトの該当商品ページへのリンクを表示させ、すぐに購入してもらえるようにします。

# 2.動作の様子
https://twitter.com/kaz_furuta/status/1572252589701632000


# 3.制作の動機
　課題解決の為の非線形の発想法というフレームワークを学習したので、自分の身の回りの業務に応用した結果、取り組んでみたいと思った課題です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/ffa2dc97-33bd-7b28-a95f-20091cb4247e.png)


[詳細はこちらにも投稿](https://note.com/kaz_furuta/n/nf0c03bdbf7c1)

　上記表では、④と⑤です。基本的には、同じ技術で可能です。

　　　　④．チラシの商品画像を学習させ、チラシの商品画像を送ることで、WEBサイトの商品ページに飛ぶツール。
　　　　⑤．チラシの商品画像を学習させ、チラシの商品画像を送ることで、詳しい商品情報が得られるツール。

　④.については、最近は某R社さんでも、カタログにQRコードを載せて、WEBサイトに誘導するというパターンを時々やっておられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/5c61b2b3-3ba0-29cd-14fb-3a0fa891a721.png)

　ただし、これをやると、紙面上にQRコードが並び、少々見苦しいデザインになってしまいます。
　また、全ての商品に対応するQRコードを付与するのは、制作コストを押し上げることになります。
　上記の非線形の発想法の表で、課題として出てきたのには、理由があります。某R社制作のカタログを某T社用のエリア版に再構成する際、制作費の都合上、付いていた個々のQRコードを泣く泣くカットしています。（サイトが異なる為、そのままでは使えません。コードの差し替えは、カットよりもずっと割高です。）
　QRコードを利用せずに、同様の事が出来れば、コストを上げずにお客さまの利便性を上げられる可能性があります。コードがない方が、商品画像が映えます。
　応用として、より詳しい商品説明の載ったメーカーのホームページや、アレルゲン情報などを出力させると言うような事が可能になるかもしれません。

:::note info
　QRコードは株式会社デンソーウェーブの登録商標です。
:::


# 4.制作のツボ

## 1.今回使用したサービス・ツール
　　　[Teachable Machine](https://teachablemachine.withgoogle.com/):Googleの提供する機械学習サービス
　　　[Node-Red](https://nodered.jp/)：IBM社が開発したビジュアルプログラミング開発ツール
　　　[heroku](https://jp.heroku.com/home)：Node-Redを走らせるための無料でも使えるクラウドサービス（Paas）
　　　[SSS API](https://console.sssapi.app/home):GoogleSheetsの表をAPI化し、検索できる。書き込みは不可。

## 2.標準ではないNode-Redのノード
完成後も使用し続けるもの

      node-red-contrib-teachable-machine:Teachable Machineとデータをやり取りするためのノード
      node-red-contrib-line-messaging-api：LINE Botとして機能させるためのノード

製作中は、必要だが、完成後はなくてもよいもの

      node-red-contrib-image-output:製作中、入力した画像をモニタするために使用。
      node-red-contrib-browser-utils：製作中PCのWEBカメラの画像を入力に使うためのノード

試したが、最終的には使わなかったもの

      node-red-contrib-line-image-api：Lineからの画像を取り込むために試したノード
      node-red-contrib-publicgooglesheets：GoogleSheetsに読み書きするノード。VLOOKUP関数的な使用ができない。

:::note info
 今回使用したサービス・ツールは、現在のところすべて無料で使用可能です。
事前に登録が必要なものが多いですが、登録の仕方については、今回は触れません。
:::

## 3.概念図とNode-Red全体図
データの流れを示した概念図です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/f33a30aa-2f3f-53f4-0d01-24ff181d0bf3.png)
次にNode-Redに配置したノードの全体図を示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/966b9620-d033-9d74-88ae-2df1ed323f54.png)

作業中は、各ノードの後に、msgノードを付けて、出力されるメッセージを追っかけます。
プロパティの対象で、「msgオブジェクト全体」に変えておくと、細かい所まで確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/c9bb61ec-3b7f-9b85-e3ca-b306f4fbd6c3.png)

## 4.各ノードの設定
　上記全体図の吹き出しの番号順に内容を説明します。
### ①webhook
 設定の中身は、これだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/816d3e39-dcac-5e95-7c0c-ad938a911b80.png)

:::note warn 
　しかし、これにたどりつくまでかなりの時間を要しました。


ここでは、概念図を示したように、ここではLineから送られてくるメッセージの最初の受け取り口を設定するはずです。
> Webhookノードの設定は、URLのパス部分（ホスト名のうしろのスラッシュ移行）を指定するだけです。[引用元](https://zenn.dev/ukkz/articles/a389105e400ea2)

　さらっと、書いてあるのが、何を示しているのかピンときませんでした。
node-redを編集しているページの最後の数列を入力してみたりして、時間を浪費しましたが、
いろいろ試した結果、実はなんでもよいことが判りました。
重要なのは、スラッシュで始まる自分の決めたフォルダの名前で、その名前をLine側に設定してやることで、Lineからのデータが受信できるようになるわけです。
あえて、何でもよいことを示す意味で名前を設定しています。
:::


　次に今決めたフォルダの名前（Path）をLine側に設定します。
Line Developersサイトにて、Messaging API設定を開きます。
設定項目の中のWebhook URLというところに設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/46a05193-3f36-f96e-994f-971bd0a956ef.png)
黒四角のところは、herokuのアカウントIDが入ります。
編集中のNode-RedのURLをみるとこんな風になっています。

    https://node-red-■■■■.herokuapp.com/red/#flow/123456789012345

red以降を削除して、先ほどのPathの文字列を付け足したアドレスが、Lineからの受け口ということになります。

### ②http request
　ここも難しかったのですが、重要です。ここでは、Lineから送られてきたメッセージの内容より、
画像が保存されているURLを指定して、画像のデータを貰うための設定を行います。

Node-Red側の設定に先立って、Line Official Account Managerの設定で、アカウント設定の内容を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/d6c29c49-8c2e-a99d-ddb3-c036df283d7c.png)
Botの製作をやっていると、写真や動画の受け取りを「受け取らない」に設定しがちですが、
今回は画像の送受信ができないと困るからです。


http requestの設定内容です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/ea9c6ce1-d8eb-15dd-ecb0-420252b040d2.png)

・メソッド：今回は、Line側に画像データを要求するので、「GET」です。
・URL：中身は、次のようになります。

    https://api-data.line.me/v2/bot/message/{{{line.event.message.id}}}/content

　Lineのメッセージは、画像などの大きいデータを直接送らず、代わりにデータを保持しているIDを埋め込んでいるそうです。ここでは、画像のありかとしてのURLアドレスをもとめデータを取得し、次のTeachable Machineにデータを送ります。

:::note warn
　マニュアルには、画像のありかを示すURLとして、次のURLが示されています。
　[https://api-data.line.me/v2/bot/message/{messageId}/content](https://developers.line.biz/ja/reference/messaging-api/#get-content)
(リンクをクリックするとLine Developersのマニュアルに飛びます。)

　実は、このままでは、使えません。送られてきた画像を示す{messageId}は、どこにあるでしょうか？
　その手掛かりが、一番目のDebugノードがデバッグウィンドウに吐き出す内容に隠れています。
　実際のメッセージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/98ca5404-4b45-a859-d908-e471f4d1736b.png)
　ドリルダウンしてidを見つけました。ただしこの数字は、1回限りの番号なのです。パスをコピーという部分をクリックすると、このidが、Node-Redでは[line.event.message.id]という中にしまわれていることが判ります。
　先ほどの、画像のありかを示すURLの｛messageId｝という部分と置き換えた文字列が、URLの設定すべき内容となり上の黒地の文字列を設定します。
:::

・ペイロード：データ本体を意味し、通常は、メッセージの本文などが格納されていますが、今回は、上の画像にも写っているように、"(media)" 画像や動画の類であることの区分が入っているだけなので、「無視」します。

・認証：Lineから、Teachable Machineが画像のデータを貰うためには、認証が必要です。
LINE Botを作るときにお約束のように出てくるチャンネルアクセストークンの出番です。
トークンを使用する一般的な認証の名前が「Bearer認証」で、Line-DevelopersのMessaging API設定から、チャンネルアクセストークンをコピーして貼り付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/5967f26a-6790-5a3a-367c-8124fb022ec7.png)

・出力形式：Teachable Machineに送る画像の形式を指定します。バイナリバッファです。

### ③teachable machine
　いよいよ、ここでTeachable Machineに対して画像のデータ送ります。

:::note warn
　説明の都合上、ここに至ってやっと[Teachable Machine](https://teachablemachine.withgoogle.com/)の本体に触れますが、実際には、先に学習モデルを用意していました。
　当初、チラシのコマは小さくて複雑なので、うまく判別できない可能性を疑っていました。最初に作ったモデルは、大きめの3コマのデータでしたが、ほぼ100%識別できていたので、制作を開始しました。完成後に学習モデルを21アイテムに増やしました。
:::
　Teachable Machineの使い方は、むしろ簡単です。モデルの学習のさせ方は奥が深そうですが、使い方はすぐに理解できると思います。
　リンク先のTeachable Machineに飛び、「使ってみる」→「新しいプロジェクトから画像プロジェクト」を選びます。
　ここでは、Classに商品のカタログ番号を付け、各商品のチラシ画像を5～6枚ずつ登録しました。
注意した点は、出来るだけ商品を画像の真ん中に置くこと。ピントが合うできるだけ近くで撮った画像。少しだけ離れた画像、右左に少し傾けて撮った画像などを入れるようにしました。

　　実際に登録しトレーニングさせた画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/5b60ebd0-9636-c702-8046-5fa211efa2a7.png)
今回は、某T社さんのチラシのWEB連動企画の所から、21品目を選んでいます。
後は、「モデルをエクスポート」をクリックして、「モデルをアップロード」を選びます。
少し待って、「共有可能なリンク」が、表示されますので、そのURLをノードに設定します。（下画像　緑枠）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/99500b03-07bf-5c99-5ac7-3e0ba6b75907.png)
Mode：Online
Output:今回は、最有力候補を一つだけ返してくれればよいので、「Best predition」を選びます。

### ④template
　ここでは、Teachable Machineからの返事を次のGoogle sheetsのデータから情報を取り出すための準備をします。
③のTeachable Machineからのメッセージの内容をデバッグウィンドウで確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/8412c279-b0ac-78c2-83f6-afb38f308212.png)
　内容が、配列になっており、A013というカタログ番号と0.66038････という、確度が入っています。
　今回必要なのは、カタログ番号だけですので、パスをコピーしてデータが「payload[0].class」の中に入っていることを得ます。
templateの中身は下の赤線部分だけ、設定します。これにより、メッセージの本文がカタログ番号だけに変更されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/d1e0b562-2f88-671e-9740-73d936a2ee2b.png)

### ⑤http request
　ここでは、予めGoogle Sheetsに下のような、カタログ番号と、WEBサイトのURLの表を作成しておき、Teachable Machineから貰ったカタログ番号で引くという働きをします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/7f70b0f1-48b4-3e9c-5073-139abe05f106.png)

:::note warn
　最初、GoogleSheetsから、データを引くのに、最初Node-Redの外部ノードを検索して引っかかったのが、これでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/79adda71-14ab-b79a-26b5-879a982cec1c.png)
　しかし、用意されている機能が、セルの値の読み出し・書き出し・行の追加・削除の４つしかありませんでした。JavaScriptで処理する覚悟であればこれでもよいのでしょうが、ExcelのVLOOKUP関数のごとく、お手軽に検索結果を返してほしいと思いました。
　色々調べて、たどり着いたのが、[SSS API](https://console.sssapi.app/home)でした。
    日本語化されたサイトで、[1件だけのデータも取得できます。](https://sssapi.app/help/api_call#1%E4%BB%B6%E5%8F%96%E5%BE%97)（ただしSheet側に情報を書き込むことが出来ないので、用途によっては不向きです。）
:::

 [SSS APIでの1件取得の仕方](https://sssapi.app/help/api_call#1%E4%BB%B6%E5%8F%96%E5%BE%97)

    https://api.sssapi.app/<"SSS API_ID">/<"sheetの一番左の列の値">

　上のURLをhttp requestノードに設定すればよいのですが、最後の<"sheetの一番左の列の値">の入れ方が判らなくて、時間を浪費しました。結局教えていただいたのですが、Payloadを三重の{}でくるんでURLに付け足すというシンプルな手段で解決できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/b1aec199-048c-8703-752d-d667a2b40538.png)
緑枠の中身は、次のとおりです。

    https://api.sssapi.app/<"SSS API_ID">/{{{payload}}} 


### ⑥split
　SSS APIからの返事が、求めるSheetのB列のURLだけなら、楽だったのですが、次の画像のように検索に使ったカタログNoとURLの1行分のデータが戻ってきています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/de654a1d-7a64-fe1b-8339-45e162dbee5f.png)
　そこで、カタログNoとURLを分けるために使っています。
設定は特になく、初期設定のままです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/db8c7e72-2784-f2a0-e24a-15e3e59417b9.png)
　これにより、次の出力へ変化します。二つに分かれています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/91d17d3f-fafb-f62d-4f3f-2c6c9e1ed852.png)

### ⑦switch
　ここでは、二つに分けたメッセージを内容によって、３つに分けています。
1番目は、正しくURLを返してきた場合のデータ　：「https」という単語が含まれる場合
2番目は、WEBの取り扱いがない商品だった場合のデータ：「WEB」という単語が含まれる場合
3番目は、もはや不要になったカタログ番号のデータ：このデータは使用しないので終点です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/8f3f0f71-6c29-5af2-b8d1-b218e6321c8d.png)

### ⑧template
　ここでは、URLが含まれている場合のリプライメッセージを成形します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/735e10c3-d661-e2c5-56d5-d54c77d5f66e.png)

### ⑨template
　ここでは、WEBの取り扱いがない場合のリプライメッセージを成形するつもりでしたが、
流れてくるデータのままでよかったので、必要のないノードでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/f557e0d4-f487-2934-ec3e-2e6c08509cad.png)

### ⑩ReplyMessage
　いよいよ最後です。ここで、Line側に最終の返信メッセージを送信するための設定です。
ここでの設定は、Line Developersのサイトから、自分のアカウントの値を拾ってきて貼り付けるだけです。
Secret:　チャンネル基本設定にある、「チャンネルシークレット」の値です。
AccessToken：　Messaging API設定にある、「チャンネルアクセストークン」の値を貼り付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819670/cf9aae47-5098-be08-3292-205cce3cb97b.png)

　これにて完成です。お疲れさまでした。本当に疲れたよ((+_+))


## 5.感想と反省
　Node-Redは、初心者にはとっつきにくい仕組みですが、JavaScriptを学習すれば、非常にパワフルなツールだと思います。何でもないメッセージの処理や、引数の与え方など、自分には癖が強いように感じられ、参考記事を探しても、ここぞという部分が説明されていないことが多く難儀しました。その反動もあって、今回はできるだけ詳しく作り方を書いたつもりです。
　チラシやカタログの商品画像を撮って、商品を区別し何らかのサービスを提供するというパターンは、結構実務にも生かせるように思いましたので、部内のミィーティングで紹介してみたころ、真っ先に送った画像の判別を間違うことがないかを質問されました。写真の撮り方によって間違うことはあります。
　実際、今回のモデルは、商品によっては、高い確率で別の商品として判別されてしまいます。完成後に、急遽、登録商品数を3アイテムから、21アイテムまで増やしたため、一部確度の低いデータが混ざってしまいました。原因として考えられることは、そもそも登録した画像が、1アイテムあたり5～6枚と少ないこと、チラシを右手に持って、左のスマホで撮影するという雑な手振れ画像を登録していること、室内の照明で撮影していますが、光の当たり方でも大きく変わることを無視しています。
　また、Teachable Machineは、確度を返してきていますが、今回は確度を無視して、一番高いものを選ぶだけにとどまっています。本来ならば、60%以下の場合は、再度画像を撮影しなおしてくださいというメッセージを返すなどの対応が必要です。画像以外のメッセージが入力された場合の処理なども端折って作っています。
　さらに、チラシ１コマに色違いなど複数の商品がある場合、現在は、とりあえず若いカタログ番号の商品を返すように、リストを作っていますが、どちらの商品を希望するか確認する対話形式の処理も必要かと思います。
　改めて時間をとって、データの学習のさせ方と確度を研究したうえで、実用性があるのか判断したいと思います。
　画像を学習させるのは、実は、とても骨の折れる作業です。サンプルの画像を増やすとなると、一層困難になります。印刷会社の手間は増やしませんが、自分たちで登録するとなると、むしろ仕事が増えてしまいます。実用化の為には、登録作業の自動化も視野に入れねばなりません。