研究室内コーヒーメーカーの課金処理を【画像認識＋クラウドソーシング】で全自動化した話

学生だった４年前ぐらいにやったことなんですけど、聞いてください。

**※国内に数少ないマイクロタスク型クラウドソーシング関連の記事をせっせと拡充しています。**
**ご興味ある方はフォローや過去記事も是非！**

- [【MTurk入門】機械学習とかでよく耳にするAmazon Mechanical Turkとは&活用例](https://qiita.com/ssmsaito/items/9ea350cc19e02c991aad)
- [Amazon Mechanical Turk API (Python, boto3)を使ってお手軽タスク外注](https://qiita.com/ssmsaito/items/c0c514d76abcd532b59e)

# あらすじ

### コーヒーマシン使用履歴をうまく記録したい

大学の研究室内で共用のコーヒーメーカー（豆も挽いてくれるやつ）があって、結構な人数が高頻度で使ってたんですが、
**コーヒー代（25円/杯）の集金がダルい**んですよね。意外と良い方法がなくて、

- 定額サブスク制　→　使用頻度のばらつきで不平が生じる
- コーヒー豆購入を割り勘　→　使用頻度のｒｙ
- つど集金箱で回収　→　小銭の用意が面倒すぎる
- 使用履歴をウェブシステムに登録　→　し忘れの可能性。
- 使用履歴をすぐ横のメモに記帳　→　し忘れの可能性。何よりダサい。情報系の研究室とは？

などなど、案を出しては没になる日々。

要は、

- **使った分だけ従量課金**
- **使用履歴をちゃんと残す**

さえ簡単にできれば良かったんです。
あとはまあ、数ヶ月おきぐらいにまとめて請求するぐらいで手間はだいぶ減りますからね。

### 自動化の機運！

まず思いついたのはMLで**顔画像認識**とかで自動化できるやん？ということなんですが、

- 人がチェック入れないと不安　→　精度次第だし、いちおう金が絡むので。。
- 新規ユーザー登録めんどい　→　カテゴリ追加（＝顔画像の学習データ集め）が現実的じゃない

という問題が発生します。

そんなとき、
**<font color="red">じゃあ人手をオンデマンドで外注すればいいじゃない！</font>**
という発想が浮かんでしまいました。

まあ、案外悪くないのではと思っていて、
**顔画像認識の二重チェックを人間が行ってくれる**だけではなく、
もしユーザーの顔が登録ユーザーの誰にも合致しなかった場合、**新規ユーザーとして識別＆登録（学習データのラベリング＆モデル再学習も含め）**することも出来るわけです。

MLでも出来ないことはないですが、「新規ユーザークラス」とかユーザー毎のモデルを別途用意したりするのは面倒くさいですよね。

面白そう！？はい作りましょう！


# 作ったもの

![Screenshot_2021-02-16 15.31.18_Nfr48R.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/d14a9381-f222-05ac-306c-b41cc02d4a62.png)

実際の見てくれ↓
![Screenshot_2021-02-16 16.39.38_SOnC6b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/625fcf79-861f-5179-9122-c50e131ec838.png)

モニターの表示↓
![ezgif-4-f2b8cb98c749.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/5fcfb00c-c0b0-176f-e5d2-1a275322a186.gif)

# システム構成

※以下、自分が動かしたときと微妙に違いがあるかも知れません。（なんせ数年前なので、すみません...

まず全体のアーキテクチャです。
コーヒーメーカーの内部は一切いじくりません。外側からなんとかする方針です。

![Screenshot_2021-02-17 15.53.06_uTaOXr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/1967d0a9-b5bd-89d7-e63d-0a162aee17e6.png)

細かくてすみません...（クリックで拡大）。
それぞれ、
**<font color="#aaa">灰色（①〜②）</font>**の部分は**エッジデバイス側の共通処理**、
**<font color="#bc6123">オレンジ（③〜⑥）</font>**の部分は**ユーザー識別処理（識別フェーズ）**、
**<font color="#f5bf17">黄色（⑦〜⑨）</font>**の部分は**学習データラベリング＆モデル再学習処理（学習フェーズ）** です。

大まかにまとめれば、
<font color="#aaa">豆を挽く時の振動をコーヒーマシン動作とみなし、振動を検知したら撮り溜めていた画像シーケンス（計17枚）をバックエンドに送る</font>ということをしたのち、

- **<font color="#bc6123">識別フェーズ</font>**
    - **代表の撮影画像１枚**に対しOpenFaceで顔検出&識別処理を行い、出力に自信が無ければAmazon Mechanical Turkのワーカーに聞いた結果を元に最終識別結果を決定
- **<font color="#f5bf17">学習フェーズ</font>**
    - **代表画像以外の全ての撮影画像**において、代表画像の人物と同じ人物が写っているかどうかをAmazon Mechanical Turkのワーカーに聞き、該当する画像全てに同一ユーザークラスをラベリングしてOpenFaceを再学習

ということをしています。なおフェーズとか言ってますが、識別・学習は**同時進行が可能**です。


# 作業

話したいのは主にクラウドソーシングの部分なので、他はざっくり説明します。

## フロントエンドの準備

### ラズパイ周り

**加速度センサ情報受信、カメラ撮影、顔画像識別リクエスト送信**などを引き受ける、エッジ側のコアの部分としてラズパイを用いています。
OSはRaspbianを使ったと思います。

- [Raspberry Pi 2 Model B](https://jp.rs-online.com/web/p/raspberry-pi/8326274/)（当時）
    - 2 Model Bだと[Wifi モジュール](https://www.planex.co.jp/products/gw-usnano2a/)が別途必要でしたね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/1c4e92aa-e0c7-3dff-59d0-43566889e578.png)

===

カメラとMONOSTICK（と当時はWifiモジュール）をUSBでぶっ刺しています。

- [BUFFALO BSW200MBK](https://www.buffalo.jp/product/detail/bsw200mbk.html)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/3fbdbfe1-0627-bce4-6ebb-bba0af0aaf7e.png)

- センサ受信用親機：[MONO-WIRELESS MONOSTICK](https://mono-wireless.com/jp/products/MoNoStick/index.html)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/f0036600-c669-900c-9d2e-83b37a331d60.png)

===

進捗表示用HDMIディスプレイはユーザー識別処理状況をフィードバックする用途ですが、最初から接続しておけば開発にも便利です。

- [全視野178°IPSモニター](https://www.amazon.co.jp/%E3%80%90%E5%85%A8%E8%A6%96%E9%87%8E178%C2%B0IPS%E3%83%A2%E3%83%8B%E3%82%BF%E3%83%BC%E3%80%91Kuman-1080P%E5%B0%8F%E5%9E%8B%E6%B6%B2%E6%99%B6%E3%83%87%E3%82%A3%E3%82%B9%E3%83%97%E3%83%AC%E3%82%A4-%E5%86%85%E8%94%B5%E3%82%B9%E3%83%94%E3%83%BC%E3%82%AB%E3%83%BC%E4%BB%98%E3%81%8D-Raspberry-%E7%94%A8%E3%83%87%E3%82%A3%E3%82%B9%E3%83%97%E3%83%AC%E3%82%A44/dp/B07W6GJFM8) （・・・覚えてないけどこのあたりだったような）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/ccf4516f-c520-9f6d-569d-71446b8f2173.png)

進捗表示画面は単純にラズパイの中でHTML/CSS/JSでローカルのウェブページとして実装し、Chromiumで全画面表示しました。
順番前後してしまいますが、進捗ステータスは全部**Firebase Realtime Database**から、コールバック関数へプッシュされてくるデータをもとに動的にレンダリングしてました。

最近だと公式ドキュメントは[このあたり](https://firebase.google.com/docs/database/web/read-and-write?hl=ja)でしょうか。
`someRef.on('value', (snapshot) => { });`...的なアレです。

===

あ、コーヒーメーカーは以下を教授がポケットマネーで買ってくれたのでした。太っ腹〜。

- [デロンギ　ESAM1000SJ](https://www.amazon.co.jp/DeLonghi-%E3%83%87%E3%83%AD%E3%83%B3%E3%82%AE-ESAM1000SJ-%E5%85%A8%E8%87%AA%E5%8B%95%E3%82%B3%E3%83%BC%E3%83%92%E3%83%BC%E3%83%9E%E3%82%B7%E3%83%B3/dp/B00243GLWC)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/23964da0-849d-2b37-88f2-f4e9ba76b93f.png)

===

さてラズパイでの実装ですが、Pythonで書いたプログラム内で、スレッドなりプロセスなりで

1. カメラで2fpsで写真を撮り、過去４秒分までオンメモリで取っておく
2. 加速度センサーの出力を監視（し、閾値と比較）する

の２つの処理のループを回しておきます。
1.は良いとして、2.が若干厄介で、以下のような手順を踏んだのを覚えてます。

- [TWE-Lite-2525Aで開閉センサ(Twitter連携) を作ってみた](https://qiita.com/maechi/items/55afe464ef3a0ee8e578)

TWE-Lite-2525A（後述する加速度センサ子機）では**シリアル通信**で加速度データが送られてくるので、それをPython側で拾ってあげる必要があるんですね。

#### カメラの設置

カメラはコーヒーマシンの上に置き、テープで固定しました。あまり手前に置くとユーザーの顔が近づきすぎるので、テストしながらいい塩梅を探ります。
![Screenshot_2021-02-16 18.21.10_3iuyqk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/084d88f5-e58d-0b32-7f54-bcf60474c91d.png)

#### 加速度センサ子機の設置

- 送信用子機：[MONO-WIRELESS TWI-LITE-2525-A](https://mono-wireless.com/jp/products/TWE-Lite-2525A/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/5a0415d9-de5f-2539-cfd2-325b71834a06.png)

組み立てた後のセンサは、ボタン電池をはめ込むのと反対側が平らになっているので、
そことコーヒーメーカーの側面（豆を入れる場所に近いところ）とを両面テープで接着します。

後から剥がしやすいように上半分だけテープを貼りましたが、振動は割といい具合に取れていたので大丈夫でした。

![Screenshot_2021-02-16 18.15.45_K7aiZU.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/eac13321-3768-e710-7f51-24fd07a488f9.png)

## 各種バックエンドサーバ―の準備

※顔識別処理サーバー・タスク配信サーバー用のコンピューターノードは、当時は研究室内のマシンとかを使ったかもですが、今の御時世[AWS](https://aws.amazon.com/jp/)なり[GCP](https://console.cloud.google.com/?hl=ja)なり何でもokかと思います。

### メインデータのやり取り部分

システムのバックエンドの根幹として、[Firebase Realtime Database](https://firebase.google.com/?hl=ja)を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/11de80ae-a33c-691f-521c-e29a59130728.png)

これを使う理由は、もちろんデータを簡単にクラウドに保存できることですが、やはり**自分でバックエンドサーバーを立てる必要がなくなる**ことも大きな利点です。

KVS内での**データの追加・更新・削除の際のトリガー**機能があるので、各サーバープログラム側でリスナーを設定しておくことで、

- **顔識別処理開始**のリクエスト
    - エッジ側にキャッシュしていた画像データをFirebaseに保存することで実現
- **クラウドソーシングタスク発注**のリクエスト
    - 顔識別処理結果の確信度をFirebaseに保存することで実現
- **進捗表示用ディスプレイ**の描画ステータス更新
    - 各処理のステータスのフラグをFirebase上で保存・上書きすることで実現

という機能がそれぞれカバーできます。

### 顔識別処理サーバー

こちらは[OpenFace](https://github.com/TadasBaltrusaitis/OpenFace)を使います。Carnegie Mellon Universityで開発されたフリーの顔検出・認識エンジンです。
識別クラス数にもよると思いますが、**ごくわずかな学習データ（10クラスぐらいのとき、各クラス20枚ずつとか）でかなり正確に識別**できていて驚いた記憶があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/d74c98af-232e-fd78-c2c3-a29b7c82ca83.png)

## クラウドソーシング関連の準備

**ここが一番アツい部分**なので少々ちゃんと取り上げたいと思います。

### プラットフォーム

クラウドソーシングプラットフォームの中でも最大規模なサービスの一つである、[Amazon Mechanical Turk](https://www.mturk.com)を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/f55fafd4-e583-d0c0-77bb-e9055b9ca205.png)

他の多くのプラットフォームとは違い、**APIが使える**という利点があります。**外注したいタスクをプログラムから動的に発注したり中止したりできる**ため、今回のケースにも最適！ということでこちらを採用しました。

詳しくは是非以下の記事で！（宣伝）

- [【MTurk入門】機械学習とかでよく耳にするAmazon Mechanical Turkとは&活用例](https://qiita.com/ssmsaito/items/9ea350cc19e02c991aad)
- [Amazon Mechanical Turk API (Python, boto3)を使ってお手軽タスク外注](https://qiita.com/ssmsaito/items/c0c514d76abcd532b59e)

実際のタスク（HITと呼ばれている）を発注するときには、いくつか方法があるんですが、
一番自由度が高いのは**HTTPS経由でアクセスできるウェブサイト**のURLをパラメータとして渡してあげる方法です。
こうすると、実際のMTurkワーカー側の作業画面では、そのウェブサイトがIFRAMEで読み込まれる形になります。
若干設計で気をつけるべきポイントがありますが、それは[こちらの記事](https://qiita.com/ssmsaito/items/c0c514d76abcd532b59e)で説明してます。

### タスク配信サーバーの準備

というわけでそのタスク（HIT）のウェブページを作ってあげます。
タスク配信サーバーと言ってますが実体はウェブサーバーです。

**①静的配信ページ（タスクの外枠・エントリーポイント用；下図中心部の「ウェブページ」）**と、
**②Web APIのアクセスポイント（タスク内容の動的読み込み＆回答記録処理用；下図下部の「タスク配信用ウェブサーバー」）**を用意していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/6d8bf6f4-f323-e9fc-4c6d-9cf3c7eedf14.png)

↑は[過去記事](https://qiita.com/ssmsaito/items/9ea350cc19e02c991aad)からの引用で、今回はDBの部分がFirebaseになります。

今回は、識別フェーズと学習フェーズそれぞれ用にわずかに異なるタスクUIを作りました。

**識別フェーズ**↓
左は過去画像から切り出された既存ユーザーの顔写真（複数の場合もあり）、
右は今回のユーザーの撮影画像（代表１枚）です。
過去にコーヒーメーカーを使ったユーザーのうち、誰に該当するかを総当りで（AとB、AとC、AとD...）確かめます。

![ttrim_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/1e7f4a58-7ac1-8fd6-13b4-2a26f8c47712.gif)

**学習フェーズ**↓
左は今回のユーザーの代表画像（加速度変化を検知した瞬間のフレーム）、
右は今回のユーザーの代表以外の画像（前後のいずれか１フレーム）です。
代表画像の前後フレームで同じ人物が写っているものがあれば、同一クラスラベルを付与して学習データに使えるので、それらを集めます。

![ttrim_2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/fd805bdb-ba5c-db05-a3b7-c484c7f816c4.gif)

#### 静的ページ（タスク外枠）の設計

ご覧の通りページのテンプレートは同じにしたので、ここでは１パターンだけ作ればOKです。

![Screenshot_2021-02-17 17.19.48_T5IXGx.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/523b53f6-19e9-2630-dfe6-66a5bf9dbb68.png)

こういうやつです。変数はimgs_leftとimg_rightのみです。
左は識別タスクの時に複数になりうるのでimg**s**_leftとしてます。

ここの情報を、後述のWeb APIへAjaxリクエストを飛ばして取ってきたりすればいいです。当時は[Django](https://docs.djangoproject.com/ja/3.1/)とかでサーバーサイドレンダリングしてましたが、昨今では[Vue](https://jp.vuejs.org/index.html)とか使ってフロント側で処理したほうが楽でしょう。

<details><summary>【申し訳程度のサンプルコードはこちら】</summary>
<div>

axiosのAPIは今回実装するものです。
以下は今回の投稿のために取り急ぎ実装したものなのでめちゃ雑ですが、一応大枠は動くはずです。

```vuejs:index.html
<html>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<style>
    #app { padding-top:20px;  width:100%;  text-align:center; }
    #instruction { background-color:#bb97bb;  width:500px;  margin:0 auto; }
    #imgbox-wrapper {  display:flex; width: 850px; margin: 20px auto; justify-content: center; }
    .imgbox { margin:20px; padding:5px; width:400px; height:250px; background-color:#eee; border:1px solid black; }
    .imgbox>img { max-height:250px; }
    .btn { width:200px; font-size:20px; height:40px; color:white; }
    #btn-yes { background-color:green; }
    #btn-no { background-color:red; }
</style>
<body>
    <div id="app">
        <h1>Compare the faces!</h1>
        <div id="instruction">
            Click "Yes" if a person in the right image corresponds to lorem ipsum ...
        </div>
        <div id="imgbox-wrapper">
            <div class="imgbox">
                <img v-for="url in img_urls_left" :key="url" :src="url" />
            </div>
            <div class="imgbox">
                <img :src="img_url_right" />
            </div>
        </div>
        <button @click="submitTask('yes')" class="btn" id="btn-yes">Yes</button>
        <button @click="submitTask('no')" class="btn" id="btn-no">No</button>
    </div>
</body>
<script>
new Vue ({
    el: "#app",
    data: {
        worker_id: "someworkerid",   // よしなに動的に変える
        img_urls_left: [""],
        img_url_right: ""
    },
    methods: {
        requestTask() {
            axios.get(`https://hogehoge.io/some/api/to/assign/task/to/${worker_id}`).then((res) => {
                this.img_urls_left = res.data.img_urls_left;
                this.img_url_right = res.data.img_url_right;
            });
        },
        submitTask(ans) {
            axios.post('https://hogehoge.io/some/api/to/submit/task/to',
                { worker_id, ans, img_urls_left, img_url_right }
            ).then((res) => {
                if(res.endTask==true) submitHIT();  // 未実装
                else requestTask();
            });
        }
    },
    created() {
        this.requestTask();
    }
});
</script>
</html>
```

</div>
</details>



#### Web APIアクセスポイントの設計

何かしらのウェブサーバーフレームワーク（Pythonなら[Django](https://docs.djangoproject.com/ja/3.1/)や[Flask](https://flask.palletsprojects.com/en/1.1.x/)もいいですが、最近はasync対応のある[FastAPI](https://fastapi.tiangolo.com/ja/)や[Sanic](https://sanic.readthedocs.io/en/latest/)がいいですね）で、以下を作ります。

1. ワーカーが要求した時、比較してもらいたい**画像対のURLあるいはblob**を返すGETリクエスト用エンドポイント
2. ある画像対における顔照合回答結果がワーカーから送られた時、**回答をFirebaseに送信・保存**するPOSTリクエスト用エンドポイント
    - 実はここで、**今まで集まった回答をもとに多数決**をして、最終的な答えを決定する的な処理もしてます。

（※すいません、ここらへんに関してはサンプルコード用意する元気が無かったので後日やる気が出ればで...:innocent:）




#### MTurk APIからタスクを発注する

AWSのアカウント等を取得し、API用のセキュリティトークンを発行しておく必要があるんですが、この記事のスコープ外なので触れません。このあたりの記事が参考になると思います↓

[AWS アクセスキーを作成する - @miriwo](https://qiita.com/miriwo/items/a513c91eac7186db59fe)

で、詳しくは[こちらの記事](https://qiita.com/ssmsaito/items/c0c514d76abcd532b59e)に書いてあるのですが、Amazon Mechanical Turk APIの**createHIT**メソッドを実行することでタスクを発注します。
タイミングとしては、**OpenFaceの顔認識結果がFirebaseに記録された時、確信度が閾値以下のとき**タスク発注、という感じで、ロジックをサーバープログラムに記述しておきます。

<details><summary>【サンプルコードはこちら】</summary>
<div>

```python
import boto3

def get_client():
    return boto3.client("mturk",
                        aws_access_key_id = "XXXXXXXXXXXXXXX",         # 自分のアカウントのAccessKeyIdを入れる
                        aws_secret_access_key = "xxxxxxxxxxxxxxxxx",   # 自分のアカウントのSecretAccessKeyを入れる
                        region_name = "us-east-1",
                        endpoint_url = "https://mturk-requester-sandbox.us-east-1.amazonaws.com"   # Sandbox(テスト環境)のエンドポイント
    )

if __name__=="__main__":
    client = get_client()
    res = client.create_hit(
        Title="Compare the faces",
        Description="Please compare the two suggested human faces and answer whether they are of the same person.",
        Keywords="comparison,human,face,easy,image",    # コンマ区切りで検索キーワードを指定
        Reward="0.05",
        MaxAssignments=3,                  # 受け付ける回答数（＝ワーカー数）上限
        LifetimeInSeconds=3600,            # HITの有効期限を3600秒（1時間）後に設定
        AssignmentDurationInSeconds=300,   # HITの制限時間を300秒（5分）に設定
        Question=f'''
            <ExternalQuestion
                xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd">
                <ExternalURL>https://hogehoge.io/main/index/page/for/task</ExternalURL>
                <FrameHeight>800</FrameHeight>
            </ExternalQuestion>'''
    )
    print("HIT ID:", res["HIT"]["HITId"])
    print("Status Code:", res["ResponseMetadata"]["HTTPStatusCode"])
```

</div>
</details>

# 完成！

やたらと端折りましたが動かしてみましょう！GOGO！

![ezgif-6-5d5fb0c1e28d.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/7c66d584-cb15-37b4-d9a0-80ffbc6642cc.gif)

スイッチをポチッとすると豆をひき始めます。
動画だと伝わらないんですけど、ｺﾞｱｱｱｱｰｰｰ!って音と振動がけっこうあるんですよね。自宅だと近所迷惑かも

![ezgif-6-c425fbf882ed.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/9cffee4d-eb31-3ce2-18f1-581e3cfc8142.gif)

おお...？ｻﾞﾜｻﾞﾜ
知らぬ間に撮られていた写真が、あっという間にOpenFace氏に解析されてしまいました。
右の方に出てるのは確信度ランキングTop2のユーザー顔写真です。（１位の数値0.993とか高いけど、この時は閾値設定してなかったんだな...）

![ezgif-6-ce981cbfcd83.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052741/a2380d1a-ce18-a21e-fcdf-00ff7f3221e3.gif)

ものの数十秒でクラウドワーカーが二重チェックしてくれました（**実はスピードアップのために、ここで触れなかった工夫もしてます。詳しくは未来の別記事にて**）。
残高がマイナスになってるのは普通に計算ミスです。すみません。



# 考察

ドリャッと作ったので、改善点はたくさんあります。

- カメラ
    - 照明とか角度の細かい設定は必要。
    - 常時ONなので慣れるまで気味悪がられる。
- 加速度センサー
    - 閾値の細かい設定は必要。
    - 電池交換は半年〜１年に一回必要らしい。
- クラウドソーシング
    - システム運用費がかかる。
    - プライバシー配慮的には目元を隠すぐらいはしたほうがいいのかも。検証精度は落ちるだろうけど
    - レスポンスの早さにはブレがあるけど、ワーカーを引き留めておくための金額とトレードオフ。

中でも**プライバシー**が気になる人は多いのではと思うのですが、たしかに人間の顔をネットに公開するのは慎重にならないといけないところです。
一応合意の取れているラボメンとかならまだしも、不特定多数の人が映るような環境のカメラ画像とかを公開するのは色々肖像権の問題とかありますので。。この辺りは他の記事で機会があったら話したいと思います。

...他の改善点も多々ありますが、この辺りでお許しくださいませ。

===

...まあ正直なところ、**面白そう先行で作ったのであんまり運用は長続きしませんでした**っていうのがオチなんですけど... :sweat_smile:
色々と細々とした問題に向き合う時間は必要ですが、逆に時間さえあれば本格的運用もそんなに難しくないシステム構成ではあるのかなと思ってます。

# まとめ

以上、**Human-In-The-Loop IoT**作ってみた記録でした。
また機会があれば他にもクラウドソーシング駆動アプリ、作ってみたいものです。