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

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

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

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

```json:写真アップロード
[{"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":[]}]
```

```json: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の例）を以下に示します。
<img width="637" alt="Screen Shot 2016-12-15 at 16.03.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/30db0067-c1ae-d55b-7c8a-fc9c7f0117bf.png">

<img width="685" alt="Screen Shot 2016-12-15 at 16.02.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/041a24d8-9eb5-e2e1-a2b4-c17ce1e51d2f.png">

```html:写真アップロード
<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](https://qiita-image-store.s3.amazonaws.com/0/32083/fc711364-d416-2987-df54-ce35b0ff6caa.png)

「ファイルを選択」をタップすると選択肢が表示されますが、この中からカメラを選択して写真を撮影します。撮影した写真のjpegデータはHTMLフォームの中でfileタイプのインプット要素として扱われます。
写真撮影後に「Analyze」という名前のボタンをタップすると、写真のjpegデータを含むフォームが/vrimage3というパス宛にPOSTメソッドで送付されます。
以下に「ファイルを選択」タップ後に選択肢が表示されている画面の例を、iPhoneのSafariとAndroidのChromeの二種類載せておきます。
![IMG_0591.PNG](https://qiita-image-store.s3.amazonaws.com/0/32083/0a296c8c-ccab-1fde-2f7e-deda99fd1412.png)

![M2.jpg](https://qiita-image-store.s3.amazonaws.com/0/32083/7bbdf48f-a518-a367-ddb9-be44e5625a28.jpeg)


## 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](https://www.w3.org/Graphics/JPEG/itu-t81.pdf)より抜粋）
<img width="748" alt="Screen Shot 2016-12-15 at 14.23.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/e7de6ba6-5991-826a-944b-7b10b417c6b7.png">

<img width="719" alt="Screen Shot 2016-12-15 at 14.22.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/f35353f7-f5b4-e3f3-d6b4-53df4f36a870.png">



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

jpegデータを抽出するプログラムは、functionノードを使ってJavaScriptで実装します。作成したNode-REDフローとfunctionノードの実装を以下に示します。
<img width="597" alt="Screen Shot 2016-12-15 at 15.41.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/f0c6c7fc-27b9-ac07-f441-33056905e69e.png">

<img width="499" alt="Screen Shot 2016-12-15 at 15.41.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/f2518649-6976-f7a7-5511-b4aa7d0ab902.png">

```js: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'）で始まるデータが抽出できていることを確認したので良しとしておきます。
<img width="869" alt="Screen Shot 2016-12-15 at 15.54.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/8165501e-bc72-714d-eaca-57b940340826.png">


以上です。

P.S. この記事は、「[スマホで撮った写真をWatson Visual Recognitionに分析させる](http://qiita.com/takeyan/items/b93f046be02af91b4cf5)」の一部です。本編の方も御覧ください。



参照：
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