ラズパイでホームセキュリティ

## 目的
ラズパイでホームセキュリティを実装しようと思います。
具体的にどんなことかというと、
**家の玄関の鍵が開いているかどうかをラズパイでチェックをしよう**
というものです。

最近の鍵は、一定時間開いていると自動的に閉まるタイプもありますが
基本的に、オートロックがない建物の場合、
玄関の鍵の閉め忘れはセキュリティ的にクリティカルとなります。

その閉め忘れを無くすため、ラズパイを使い玄関の鍵の状態をチェックをしよう！ということです。

※pythonコードを載せていますが、初心者なので、記載ルールミスなどは目を瞑ってください。
<br>
## 準備物
- ラズパイ3modelB + ケース
- microSDカード
- ラズパイ公式カメラ
- 3m級の電源ケーブル
- スマホ

ラズパイにはraspbianOSを予めインストール済み・セットアップも完了済み
pythonやpythonを使っての画像認識ライブラリOpneCV、
カメラ撮影機能、
Line連携ツールなどはインストール済みで使える状態となります。
<br>
## 実現方法
1. ラズパイを玄関の鍵の前に設置します。
2. ラズパイカメラで鍵の写真を撮ります。
3. ラズパイに入れたpythonのOpenCVライブラリで画像比較を使い、撮影した鍵の画像を解析します。
4. 撮影した画像が、「鍵が開いている状態」と同じと判断されれば、アラートとしてLine通知をスマホに出します。

ラズパイの設置ですが、とりあえずで簡易的にガムテで留めています。
私の家の玄関はドアの前に壁（下駄箱）があるタイプなので、そこにラズパイを設置しました。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482782/b22984bb-3447-b515-17fd-260b00e6a3f1.png)


鍵は以下のような形となっており、開いている状態となります。
![taImageのコピー-2-min.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482782/e8652422-d903-b350-8331-aede17a081cf.jpeg)


ちなみに、こちらが閉じている状態です。
![taImageのコピー2-min.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482782/19143739-0da0-8119-7c60-ab5a2cde09b1.jpeg)

開いている状態を比較基準とします。赤色や突起の位置を判断できるかがポイントとなります。
<br>
## 実装
`準備物`で書いたとおり、すでにプログラムを実行できる環境です。
プログラムはpythonで以下のようにしました。

```
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import cv2
import picamera
import requests
import time

# 比較元画像を読み込む
okImage = cv2.imread("/home/pi/work/okImage.JPG",1)

def diff():
    with picamera.PiCamera() as camera:
        # 現在の鍵をカメラで撮影　クロップ
        camera.resolution = (3280, 2464)
        camera.capture('/home/pi/work/taImage.JPG')
        taImage = cv2.imread("/home/pi/work/taImage.JPG",1)
        taImage = taImage[830:1110, 1070:1380]

        # 画像を比較する
        try:
            orb = cv2.ORB_create()
            kpOk, desOk = orb.detectAndCompute(okImage, None)
            kpTa, desTa = orb.detectAndCompute(taImage, None)
            bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
            matches = bf.match(desOk, desTa)
            matches = sorted(matches, key = lambda x:x.distance)
            dist = [m.distance for m in matches]
            
            # 比較結果を出力して値出力
            result = sum(dist) / len(dist)
            return result
        except Exception:
            return 99

result = diff()
# 54はしきい値
if result < 54:
    time.sleep(10)
    # 念の為2度比較する
    resultSecond = diff()
    if resultSecond < 54:
        # 自分のスマホにLINEする
        line_notify_token = '設定したLINEトークン'
        line_notify_api = 'https://notify-api.line.me/api/notify'
        message = '鍵開いてるよ'

        payload = {'message': message}
        headers = {'Authorization': 'Bearer ' + line_notify_token}
        line_notify = requests.post(line_notify_api, data=payload, headers=headers)
```
上記プログラムをcronに登録して、一定間隔で実施します。


プログラム内に軽くコメントしていますし、プログラム自体簡単なので、説明は省略しますが、
画像比較の部分だけ説明させていただきます。

先に比較元画像（鍵が開いている状態）を読み込み、それと撮影した現状の鍵画像と比較します。
比較はOpenCVライブラリを使用します。
OpenCVは画像の類似度として数値を算出してくれるようです。
前準備で何度か実施していて、`開いている`と`閉じている`の画像判断のしきい値は洗い出しています。
今回は**54**としております。
数値が小さいほど画像が似ているということのようです。

つまり
しきい値より低い結果となる
  =>  比較元画像と現状の画像が似ている
  =>  鍵が開いている！！
という事になります。
<br>
## 実際動かしているところ
4倍速動画となりますが以下が動かしているところです。(最後、編集ソフトで加工しています。)
![ezgif-4-b983f2af42dc.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482782/fbb605fa-51da-cbcc-6475-3a7fad5a0e22.gif)

鍵が開いている状態なのですが。
しばらくすると、スマホのLineにコメントがきています。
この環境であれば、うまくいきました。
<br>
## 大成功です！と書きたかったのですが。。
動画のようなテスト環境ではたまたまうまくいっただけで、
少し環境が変われば利用できない、未熟なプロダクトでした！

以下の2つの理由により、うまく運用できておりません。

#### うまく運用できない理由 : 1つ目
実施場所が玄関ということで、外の光が撮影に影響されます。
そうするとライブラリの判定にも影響するようです。

設定した しきい値だと鍵の開閉の状態が時間経過で、誤判定してしまいます。

こういう場合のために、写真をグレースケール化して比較するべきと思ったのですが
今度はカメラの性能が良くないのか、
鍵が開いていても閉まっていても同じ画像として判定してしまいます。

カメラ性能アップや、私のOpenCVなどの画像認識の知識向上が必要だと思いました。
<br>
#### うまく運用できない理由 : 2つ目
電源ケーブルの長さも問題点です。
玄関のそばにコンセントは無く、近くのコンセントから引いていますが
廊下をまたいでしまっています。
![スクリーンショット 2019-09-25 10.31.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482782/0cbc0cee-0676-4e1a-2570-bad2df5af985.png)

同居人から通行の邪魔との苦情がきました。

ワイヤレス給電？遠隔給電？とでもいうのでしょうか、あれが一般化しているのであれば利用できると思うのですが
これを解決できる方法が私の知識では思いつきません。

<br>
失敗してしまいましたが、せっかく製作したので、Qiitaに載せることにしました。
電源ケーブル問題の解決目処が立ったら、続きを試行錯誤したいと思います。