LoginSignup
17
21

More than 5 years have passed since last update.

スマホで撮った写真をNode-REDにアップロードする

Last updated at Posted at 2016-12-18

スマホで撮った写真をBluemixのNode-REDにアップロードし、Node-REDフローで写真データを利用できるようにします。
写真撮影にはモバイルアプリもJavaScriptも使わず、HTML記述とブラウザの機能でスマホのカメラを起動し、撮影した写真はjpegファイルとしてHTMLフォームを使って送信します。

手順は以下のとおりです。
1. 写真アップロード用のNode-REDフローを作成する
2. アップロードされたデータから写真データを抽出するNode-REDフローを作成する

それぞれのNode-REDフローのエクスポート・データも添付しておきます。

写真アップロード
[{"id":"bf065758.c35d38","type":"template","z":"456ff1.fa06001","name":"写真アップロード","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<html>\n<head><title>Watson Visual Recognition on Node-RED</title></head>\n<body>\n<h1>Watson Visual Recognition on Node-RED</h1>\n<h2>Select an image file</h2>\n<form  action=\"/vrimage3\" method=\"post\" enctype=\"multipart/form-data\">\n    <input type=\"file\" name=\"imagedata\" accept=\"image/*\"  />\n    <input type=\"submit\" value=\"Analyze\"/>\n</form>\n</body>\n</html>","x":348,"y":108,"wires":[["c02f4753.6d8888"]]},{"id":"da2dbf9d.6d0bc","type":"http in","z":"456ff1.fa06001","name":"[GET] /vrimage3","url":"/vrimage3","method":"get","swaggerDoc":"","x":147.5,"y":74,"wires":[["bf065758.c35d38"]]},{"id":"c02f4753.6d8888","type":"http response","z":"456ff1.fa06001","name":"http response","x":557.5,"y":108,"wires":[]}]
jpeg抽出
[{"id":"f31e3df1.61dd5","type":"http in","z":"456ff1.fa06001","name":"[POST] /vrimage3","url":"/vrimage3","method":"post","swaggerDoc":"","x":150,"y":232,"wires":[["9faf39cb.85ed58"]]},{"id":"9faf39cb.85ed58","type":"function","z":"456ff1.fa06001","name":"jpeg抽出","func":"var buf = msg.req.body;\nvar SOI = new Buffer(\"FFD8\",\"hex\");\nvar EOI = new Buffer(\"FFD9\",\"hex\");\nvar iSOI = 0;\nvar file = \"\";\n\nfor (var i=0 ; i<=buf.length ; i++) {\n    if(SOI.equals(buf.slice(i,i+2))) {\n        iSOI = i;\n        }\n    if(EOI.equals(buf.slice(i,i+2))) {\n        file = buf.slice(iSOI,i+2);\n        break;\n        }\n    }\n\nmsg.file = file;\nreturn msg;","outputs":1,"noerr":0,"x":335,"y":264,"wires":[["6b5ec310.9fec0c"]]},{"id":"6b5ec310.9fec0c","type":"debug","z":"456ff1.fa06001","name":"","active":true,"console":"false","complete":"file","x":530.5,"y":264,"wires":[]}]

1. 写真アップロード用のNode-REDフローを作成する

作成したNode-REDフローとテンプレートノードの中味(HTML)、及びこのHTMLのブラウザ表示画面(iPhoneのSafariの例)を以下に示します。
Screen Shot 2016-12-15 at 16.03.06.png

Screen Shot 2016-12-15 at 16.02.39.png

写真アップロード
<html>
<head><title>Watson Visual Recognition on Node-RED</title></head>
<body>
<h1>Watson Visual Recognition on Node-RED</h1>
<h2>Select an image file</h2>
<form  action="/vrimage3" method="post" enctype="multipart/form-data">
    <input type="file" name="imagedata" accept="image/*"  />
    <input type="submit" value="Analyze"/>
</form>
</body>
</html>

IMG_0590.PNG

「ファイルを選択」をタップすると選択肢が表示されますが、この中からカメラを選択して写真を撮影します。撮影した写真のjpegデータはHTMLフォームの中でfileタイプのインプット要素として扱われます。
写真撮影後に「Analyze」という名前のボタンをタップすると、写真のjpegデータを含むフォームが/vrimage3というパス宛にPOSTメソッドで送付されます。
以下に「ファイルを選択」タップ後に選択肢が表示されている画面の例を、iPhoneのSafariとAndroidのChromeの二種類載せておきます。
IMG_0591.PNG

M2.jpg

2. アップロードされたデータから写真データを抽出するNode-REDフローを作成する

フォームのデータはマルチパートという形式でアップロードされるのですが、そこには写真のjpegデータ以外のデータも混ざっています。欲しいのはjpegデータだけなので、マルチパートのデータからjpegデータを抽出します。

Node-REDにはjpeg抽出のためのノードは残念ながら用意されていません。マルチパートを扱うためのJavaScriptのライブラリを利用する手もありますが、ライブラリがNode-REDに標準で組み込まれていないと面倒なので、マルチパートからjpegデータを取り出すプログラムを書くことにしました。もしかしたら標準で使える簡単な方法があるのかもしれませんが、プログラムが意外と簡単だったので真剣には調べていません。

jpegデータは仕様によりSOI(Start of Image, X'FFD8')で始まり、EOI(End of Image, X'FFD9')で終わると決められているので、マルチパートから該当部分を抽出すればjpegデータが得られるはずです。(下図はITU CCITT Recommendation T.81より抜粋)
Screen Shot 2016-12-15 at 14.23.26.png

Screen Shot 2016-12-15 at 14.22.58.png

もしかしたらマルチパートの中にたまたまSOI,EOIに一致してしまう部分があるかも知れませんが、その時は運が悪かったと諦めることにして、プログラミングは必要最小限に留めておきます。

jpegデータを抽出するプログラムは、functionノードを使ってJavaScriptで実装します。作成したNode-REDフローとfunctionノードの実装を以下に示します。
Screen Shot 2016-12-15 at 15.41.30.png

Screen Shot 2016-12-15 at 15.41.48.png

jpeg抽出
var buf = msg.req.body;
var SOI = new Buffer("FFD8","hex");
var EOI = new Buffer("FFD9","hex");
var iSOI = 0;
var file = "";

for (var i=0 ; i<=buf.length ; i++) {
    if(SOI.equals(buf.slice(i,i+2))) {
        iSOI = i;
        }
    if(EOI.equals(buf.slice(i,i+2))) {
        file = buf.slice(iSOI,i+2);
        break;
        }
    }

msg.file = file;
return msg;

debug出力(msg.fileの出力)は以下のようになります。jpegデータが正しく抽出できているかどうかは別途検証するとして、ここではSOI(X'FFD8')で始まるデータが抽出できていることを確認したので良しとしておきます。
Screen Shot 2016-12-15 at 15.54.33.png

以上です。

P.S. この記事は、「スマホで撮った写真をWatson Visual Recognitionに分析させる」の一部です。本編の方も御覧ください。

参照:
http://hp.vector.co.jp/authors/VA032610/JPEGFormat/StructureOfJPEG.htm
https://www.w3.org/Graphics/JPEG/
https://www.w3.org/Graphics/JPEG/itu-t81.pdf

17
21
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
17
21