LoginSignup
10
11

More than 5 years have passed since last update.

LINE MessagingAPIとIBM Visual Recognitionで画像認識Bot作ってみた!

Last updated at Posted at 2018-06-18

AngelHackOsaka2018(https://angelhackosaka2018.peatix.com )というハッカソンイベントに運営スタッフとして参加しました.深夜にかけて時間の余裕があったので”ひとりハッカソン”と称して,協賛企業さんのAPIを使ってサービスを作ってみたのでご紹介します.

構想

  • 画像処理のエンジニア → AIやりたい
  • 香川県出身 → うどん
  • モバイル → LINEをプラットホームに.

目標

  • LINEでBotを作り,画像を投稿すると,AI処理した結果を返す (ミニマムサクセス)
  • Webで適当に集めた画像でクラス分類問題を学習し,Webサービスとして提供(ミドルサクセス)
  • うどん画像から店名と値段を推定 (フルサクセス)

結果

  • ミニマムサクセスまで達成しました. Capture+_2018-06-17-12-38-15.png
  • 時間切れでそれ以降の構想は開発断念.LineBotのタイトルが”うどん鑑定ごっこ”になっているのは断念の名残ですw

要素技術

  • IBM Visual Recognition ・・・ 画像を送るとクラブ分類結果とその確信度を教えてくれる.30日無料
  • IBM Node-Red Starter ・・・ Webブラウザ上でブロックを配置するだけでWebアプリサービスを実装&ローンチできるサービス.30日無料
  • LINE Messaging API ・・・ LINE上にBotを作成する.ライトユーザは基本無料.

手順

参考にすべき情報

先駆者の情報がいろいろネットに上がっているので,まずはそちらを参考に環境構築.

おおまかな流れ

  • とりあえず↑の2つをやる
  • IBM Node-Red Starterにて,ブロック図を編集.
    • BlueMixのダッシュボードへ移動 https://console.bluemix.net/dashboard/apps/
    • Cloud Foundry アプリケーションの箇所にNode-Redが動いているレコードがあるのでクリック → ページ遷移.
    • Cloud Foundry アプリケーションの詳細,っていう画面が出るので「経路」ボタンwをクリックし,ドロップダウンメニューの一番上にあるURLを選択 → ページ遷移
    • Node-RED on IBM Cloud ,っていう画面になる.「Go to your Node-RED flow editor」をクリック.
    • Node-REDのエディター画面になる.画面右上のメニューから,読み込み→クリップボード
    • この記事末尾のコードをコピペ
    • Node-REDのエディター画面にて,それっぽいブロックのフローが表示されていればOK
  • LINEのほうは変更不要
  • スマホからBotに画像を投げつけると,クラス分類判定第一位の名前と確信度が返ってくるはず・・・!

はまった点

  • Node-RED
    • 画像を扱う例は多数あるものの,Web上に画像単体がUPされている例(http://test.com/test.jpg みたいな)とかブラウザからPOSTされた画像を扱う例が多数派で,LINEから画像をとってきてそれを処理する例が見当たらなかった.
    • LINE Messaging APIからPOSTされた画像の回収と,Visual RecognitionへのPOSTとを,シーケンシャルにやろうとすると,LINE Messaging APIへの応答文を作るのが難しい.2本のパラレルなルーチンを実行し,最後にmerge(=力技で結合)するという形で実現.
  • Visual Recognition
    • Web上に画像単体がUPされている例の画像を扱う,あるいは,サーバ内に画像がある場合を扱う(?)のが多数派
    • Node-Redのコード内で,画像データをgetしてmsg変数に格納し,っていう処理はあまりなかった
  • LINE Messaging API
    • 画像を扱うドキュメントが少なく情報がわからん...Node-Redとなるとなおさら.

感想など

  • モバイルプラットホーム⇔AIのapi,をあっさりと繋げることができて,さくっと面白いアプリを作れた印象
  • 今回はデフォルトのVisualRecognitionを使ったので,一般物体が対象になった.自分で収集した画像でAIをスクラッチ(?)作成する機能も提供されているらしい.ぜひやりたい.
  • Node-RedはWebサービスをさくっと作りたいときにとっても有用そうだ.
  • 国内だとやっぱLINE最強.どんなユーザでも持っているので敷居が低くて◎

Node-REDのエディター画面

a.png

Node-REDのコード

[{"id":"e2e8c7c9.3d94d8","type":"http in","z":"f47f9794.9e13","name":"post /image_recog","url":"/image_recog","method":"post","upload":true,"swaggerDoc":"","x":130,"y":93.99999237060547,"wires":[["794accf4.28039c"]]},{"id":"6819ac4f.32c0bc","type":"visual-recognition-v3","z":"f47f9794.9e13","name":"","apikey":"__PWRD__","vr-service-endpoint":"https://gateway.watsonplatform.net/visual-recognition/api","image-feature":"classifyImage","lang":"en","x":874.9999389648438,"y":92.99999237060547,"wires":[["eda37ad.22d5a08","3772cebc.660e02"]]},{"id":"eda37ad.22d5a08","type":"debug","z":"f47f9794.9e13","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"result","x":1037.9999389648438,"y":180,"wires":[]},{"id":"5ee73f81.59e6b8","type":"function","z":"f47f9794.9e13","name":"reserve headers","func":"msg.url = \"https://api.line.me/v2/bot/message/\" + msg.payload.events[0].message.id +'/content'\nmsg.headers  ={\"Content-Type\": \"application/json\",    \"Authorization\": \"Bearer YYm5YOlPsfTC2bzXFJwe/nhAkNuUeDaPhhxxp2+KThyOlkn4XbbftDzZ5URIf2VUIkP2umeUvEHq6UIxJDig3L/sqQSJCMmx5+NKeLfDpKlAwPaHISh6qdx4G4n+TxkuoduHvNT+2RBexJnhQ8VzpwdB04t89/1O/w1cDnyilFU=\"};\n\nreturn msg","outputs":1,"noerr":0,"x":487.86663818359375,"y":94.48332977294922,"wires":[["e648faa6.d4292"]]},{"id":"e648faa6.d4292","type":"http request","z":"f47f9794.9e13","name":"","method":"GET","ret":"bin","url":"","tls":"","x":665.8666381835938,"y":92.64995574951172,"wires":[["4d36507b.e9ce7","6819ac4f.32c0bc"]]},{"id":"4d36507b.e9ce7","type":"debug","z":"f47f9794.9e13","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":803.8666381835938,"y":181.433349609375,"wires":[]},{"id":"53d54378.3a798c","type":"function","z":"f47f9794.9e13","name":"generate header","func":"var event = msg.payload[\"events\"][0];\n//if(event[\"message\"][\"type\"] != \"text\"){\n//    return msg;\n//}\nvar message = event[\"message\"][\"text\"];\nvar replyToken = event[\"replyToken\"];\nvar replyMessage = {\"type\": \"text\", \"text\": message}\nmsg.payload = {\"messages\": [replyMessage], \"replyToken\": replyToken};\nmsg.headers  ={\"Content-Type\": \"application/json\",    \"Authorization\": \"Bearer YYm5YOlPsfTC2bzXFJwe/nhAkNuUeDaPhhxxp2+KThyOlkn4XbbftDzZ5URIf2VUIkP2umeUvEHq6UIxJDig3L/sqQSJCMmx5+NKeLfDpKlAwPaHISh6qdx4G4n+TxkuoduHvNT+2RBexJnhQ8VzpwdB04t89/1O/w1cDnyilFU=\"};\n\ncontext.global.payloadGenerated = msg.payload;\ncontext.global.headersGenerated = msg.headers;\ncontext.global.msg = msg;\n\nreturn msg","outputs":1,"noerr":0,"x":263.8666534423828,"y":281.3166809082031,"wires":[["da3949da.d249b8","33455b2a.45919c"]]},{"id":"da3949da.d249b8","type":"function","z":"f47f9794.9e13","name":"merge","func":"if(\n    (context.global.resultGenerated !== null) && (context.global.headersGenerated !== null) && (context.global.payloadGenerated !== null)\n){\n    //msg.url = null;\n    //msg.payload = context.global.payloadGenerated;\n    targetText = context.global.resultGenerated.images[0].classifiers[0].classes[0].class + \", score:\" + context.global.resultGenerated.images[0].classifiers[0].classes[0].score;\n    //msg.payload[\"messages\"] = [\"test\"];\n    //msg.headers = context.global.headersGenerated;\n    //var replyMessage = {\"type\": \"text\", \"text\": \"test test\"}\n    //msg.payload = {\"messages\": [replyMessage], \"replyToken\": replyToken};\n    //msg.payload = {\"messages\": [replyMessage], \"replyToken\": replyToken};\n    //msg.payload = context.global.payloadGenerated;\n    //msg.payload = {\"messages\": [replyMessage],  \"replyToken\": msg.payload[\"replyToken\"]};\n    //msg.payload = {\"messages\": [replyMessage],  \"replyToken\": msg.payload[\"replyToken\"]};\n    //msg.headers  ={\"Content-Type\": \"application/json\",    \"Authorization\": \"Bearer YYm5YOlPsfTC2bzXFJwe/nhAkNuUeDaPhhxxp2+KThyOlkn4XbbftDzZ5URIf2VUIkP2umeUvEHq6UIxJDig3L/sqQSJCMmx5+NKeLfDpKlAwPaHISh6qdx4G4n+TxkuoduHvNT+2RBexJnhQ8VzpwdB04t89/1O/w1cDnyilFU=\"};\n    msg = context.global.msg;\n    var replyMessage = {\"type\": \"text\", \"text\": targetText}\n    msg.payload = {\"messages\": [replyMessage],  \"replyToken\": msg.payload[\"replyToken\"]};\n\n    return msg;\n    \n}else{\n    //return msg;\n}\n\n\n\n\n","outputs":1,"noerr":0,"x":661.8666381835938,"y":445.3166809082031,"wires":[["ccb7c49.4b08a38","9ca59f7e.5be838"]]},{"id":"3772cebc.660e02","type":"function","z":"f47f9794.9e13","name":"reserve msg.result","func":"context.global.resultGenerated = msg.result;\nreturn msg;","outputs":1,"noerr":0,"x":1106.86669921875,"y":95.31664276123047,"wires":[["fee60f45.d04228","da3949da.d249b8"]]},{"id":"ccb7c49.4b08a38","type":"http request","z":"f47f9794.9e13","name":"LINE REPLY API実行","method":"POST","ret":"txt","url":"https://api.line.me/v2/bot/message/reply","tls":"","x":874.8666229248047,"y":445.4833679199219,"wires":[["ee18b3b6.a93838"]]},{"id":"ee18b3b6.a93838","type":"debug","z":"f47f9794.9e13","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1035.8666229248047,"y":515.1832885742188,"wires":[]},{"id":"9ca59f7e.5be838","type":"debug","z":"f47f9794.9e13","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":778.8666381835938,"y":514.1832885742188,"wires":[]},{"id":"fee60f45.d04228","type":"debug","z":"f47f9794.9e13","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":1144.86669921875,"y":249.183349609375,"wires":[]},{"id":"794accf4.28039c","type":"function","z":"f47f9794.9e13","name":"global","func":"context.global.resultGenerated = null; \ncontext.global.headersGenerated = null; \ncontext.global.payloadGenerated = null;\n\nreturn msg;","outputs":1,"noerr":0,"x":301.86663818359375,"y":93.73332977294922,"wires":[["5ee73f81.59e6b8","53d54378.3a798c"]]},{"id":"33455b2a.45919c","type":"debug","z":"f47f9794.9e13","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":397.86663818359375,"y":347.183349609375,"wires":[]}]
10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11