FlashAir＋Face API＋FlashAir IoT Hubで人物カウンターを作ってみた

　昨年10月に東芝無線LAN内蔵SDカード「FlashAir」をIoTデバイスとして活用するためのプラットフォームとして「[FlashAir IoT Hub](https://iot-hub.flashair-developers.com)」が[発表](https://toshiba.semicon-storage.com/jp/company/news/news-topics/2016/10/memory-20161018-1.html)されました。現在、プレビュー公開が行われています。今回は、これにFlashAirからデータを送信して、グラフ表示することを試してみます。
　送信するデータは、デジタルカメラで撮影した写真画像ファイルをLuaスクリプトで[Face API](https://www.microsoft.com/cognitive-services/en-us/face-api)を呼び出して、写った人物の男女別人数、年齢層別人数をカウントしたデータを送信することにします。

　ただ、今回、Cognitive Servicesの[Face API](https://www.microsoft.com/cognitive-services/en-us/face-api)をFlashAirのLuaスクリプトから呼び出そうとしたのですが、どうしてもHTTP 415エラーとなり呼び出せませんでした。そのため、[Computer Vision API](https://www.microsoft.com/cognitive-services/en-us/computer-vision-api)のvisualFeaturesに「Faces」を指定して、Face APIと同等の結果を得ることにしました。
　REST APIの仕様は、こちらのドキュメント[Computer Vision API - v1.0](https://dev.projectoxford.ai/docs/services/56f91f2d778daf23d8ec6739/operations/56f91f2e778daf14a499e1fa)を参照してください。

#全体概要
![FlashAirFaceAPI.png](https://qiita-image-store.s3.amazonaws.com/0/89137/ca701fd9-fe91-4c79-aca8-94684747495a.png)

#FlashAir IoT Hubの準備
　こちらの[FlashAir IoT Hubチュートリアル](https://flashair-developers.com/ja/documents/tutorials/iot-hub/)を参考に、準備してください。今回は、[ご利用の流れ](https://flashair-developers.com/ja/documents/tutorials/iot-hub/1/)のFlashAir登録まで済んでいれば大丈夫です。

###FlashAirのファイル配置
　以下のファイルを配置しておきます。

・iothub.lua
　取得した「スクリプト」ファイルのひとつ、FlashAir IoT Hub SDKファイルです。
・credentials.json
　FlashAir IoT Hubのアクセストークン取得で取得したJSONファイルです。

###今回利用するコマンド
　FlashAir IoT Hub SDK（iothub.lua）のなかのデータ送信機能addMeasurementを利用します。
```
iothub.addMeasurement(arg1, arg2, arg3, arg4, arg5)
```
使い方は簡単で、addMeasurementの引数に最大５個まで送信するデータを指定するだけです。


#サブスクリプションキーの取得
　次に、Microsoft Cognitive Services - Computer Vision APIを利用するためのサブスクリプションキーを入手します。現時点では、月間5,000リクエストまで、または１分間に20リクエストまでは無料で利用できます。
　[Microsoft Cognitive Services サブスクリプション](https://www.microsoft.com/cognitive-services/en-us/subscriptions)に、自分のMicrosoftアカウント（もし、お持ちでなければ無料で作成できます）でログインします。すると、以下のようなページで各種APIのサブスクリプションキーを入手することができます。今回は、「Computer Vision API」のチェックをつけて、Subscribeボタンを押します。

![subscriptions取得画面1.png](https://qiita-image-store.s3.amazonaws.com/0/89137/df9cc09a-8b8f-1b62-d35c-93ef929cf7f1.png)

　つぎの画面で、以下のようにサブスクリプションキーが生成されます。Key 1のCopyという文字列をクリックすると、キー文字列がクリップボードにコピーされますので、どこかに保存しておいてください。後ほど使います。

![subscriptions取得画面2.png](https://qiita-image-store.s3.amazonaws.com/0/89137/50bffaae-0609-d191-56a2-1bcda30fff30.png)

#CONFIGファイルの設定
　FlashAirのSD_WLANフォルダ内のCONFIGファイルに、以下のLuaスクリプト起動オプションを付加します。
```
LUA_RUN_SCRIPT=/faceapi_sample.lua
```
　※SD_WLANフォルダとCONFIGファイルは不可視属性ファイルなので注意してください。

#Luaスクリプトファイル
　FlashAirのルートフォルダに以下のコードを記述したテキストファイル（faceapi_sample.lua）をコピーします。
　このスクリプトでは、最新の画像ファイルをDCIMフォルダ配下に探しに行きファイル名を取得、このファイルをComputer Vision APIに投げて、Facesの結果を得ます。得たJSONファイルのなかから必要なデータを取り出して、FlashAir IoT Hubに送信します。その後、１分待機して、同じ処理を繰り返します。
　処理を繰り返す中で、画像ファイルが更新されていない場合は、Face APIの呼び出しはスキップしますが、FlashAir IoT Hubには前回を同じデータを送信するようにしています。

```faceapi_sample.lua
cjson = require "cjson"
iothub = require("iothub")
latest_fpath = ""
faces_num_00 = 0
faces_num_20 = 0
faces_num_60 = 0
faces_num_male = 0
faces_num_female = 0
	  
function funcfacecount()
	last_fname = ""
	last_fpath = ""
	last_modif = 0
	
	last_moddir = 0
	last_dirname = "/DCIM"
	
	fpath = "/DCIM"
	for dirname in lfs.dir(fpath) do
	  dirpath = fpath .. "/" .. dirname
	  mod_dir = lfs.attributes( dirpath, "mode" )
	  if mod_dir == "directory" then
		dir_modifficate = lfs.attributes( dirpath, "modification" )
		if dir_modifficate > last_moddir then
		  last_moddir = dir_modifficate
		  last_dirname = dirpath
		end
	  end
	end
	
	for filename in lfs.dir(last_dirname) do
	  if(string.sub(filename, 1, 1) ~= ".") then
		filepath = last_dirname .. "/" .. filename
		mod = lfs.attributes( filepath, "modification" )
		if mod > last_modif then
		  last_modif = mod
		  last_fname = filename
		  last_fpath = filepath
		end
	  end
	end
	
	if(latest_fpath == last_fpath) then
	  return
	end
	latest_fpath = last_fpath
	
	boundary = "1234567890"
	contenttype = "multipart/form-data; boundary=" .. boundary
	mes = "--" ..  boundary .. "\r\n"
	  .."Content-Disposition: form-data; name=\"file\"; filename=\""..last_fname.."\"\r\n"
	  .."Content-Type: image/jpg\r\n\r\n"
	  .."<!--WLANSDFILE-->\r\n"
	  .."--" .. boundary .. "--\r\n"
	
	blen = lfs.attributes(last_fpath,"size") + string.len(mes) - 17
	b, c, h = fa.request{url = "https://api.projectoxford.ai/vision/v1.0/analyze?visualFeatures=Faces",
	  method = "POST",
	  headers = {["Content-Length"] = tostring(blen),
	  ["Content-Type"] = contenttype,
	  ["Ocp-Apim-Subscription-Key"] = "ここにサブスクリプションキーを記述する",
	  file = last_fpath,
	  body = mes
	}
	
	if(c == 200) then
	  res = cjson.decode(b)
	  
	  for idx=1,#res.faces do
		local agenum = tonumber(res.faces[idx].age)
		  if(agenum < 20) then
		  faces_num_00 = faces_num_00 + 1
		elseif(agenum >= 20 and agenum < 60) then
		  faces_num_20 = faces_num_20 + 1
		elseif(agenum >= 60) then
		  faces_num_60 = faces_num_60 + 1
		end
		
		if( res.faces[idx].gender == "Female" ) then
		  faces_num_female = faces_num_female + 1
		else
		  faces_num_male = faces_num_male + 1
		end
	  end
	end
end

while(1) do
  funcfacecount()
  iothub.addMeasurement({faces_num_00, faces_num_20, faces_num_60, faces_num_male, faces_num_female})
  sleep(60000)
  collectgarbage("collect")
end
```

#動作確認
　今回は、著作権フリー写真・イラスト素材集の[サンプル画像](http://gahag.net/tag/三世代家族/)で動作確認をしてみました。まずはFlashAirに適当な画像ファイルをコピーしてみます。１分ごとに画像ファイルをComputer Vision APIで分析して、その画像に写っている人物の認識結果がうまくグラフに表示されました。
　FlashAir IoT Hubでは５つのデータまでしか送信できないため、今回は、(1)20歳以下の人数、(2)20〜60歳の人数、(3)60歳以上の人数、(4)男性の人数、(5)女性の人数の５つのデータを送信しています。

![faceapi_sample1.png](https://qiita-image-store.s3.amazonaws.com/0/89137/dae6edfc-d766-0240-69d0-a8b0cac459a5.png)

#まとめ
　FlashAirからCognitive ServicesのComputer Vision APIを呼び出して、写真に写っている人物の男女別人数と年齢層別人数をカウントすることができるようになりました。
　デジタルカメラでインターバル撮影を行えば、定点観測による人物カウンターが実現できますね。

#参考資料
　[FlashAir IoT Hubチュートリアル](https://flashair-developers.com/ja/documents/tutorials/iot-hub/)
　[Microsoft Cognitive Services - Computer Vision API](https://www.microsoft.com/cognitive-services/en-us/computer-vision-api)
　[Microsoft Cognitive Services - Computer Vision API - Document](https://dev.projectoxford.ai/docs/services/56f91f2d778daf23d8ec6739/operations/56f91f2e778daf14a499e1fa)