編集履歴一覧に戻る
jonajiroのアイコン画像

jonajiro が 2021年05月10日21時56分55秒 に編集

コメント無し

本文の変更

# 作成意図 外出先で家の鍵閉めたっけ?と不安になることはないでしょうか? この不安を解消するため、出先から鍵の様子を確認できるシステムを作りました。 # 主要部品 | 品名 | 用途 | 価格 | |:---:|:---:|:---:| | [obniz Board 1Y](https://obniz.io/ja/products) | メインコンピュータ | 6,930円(税込) | | [Arducam Mini 2MP Plus](https://www.arducam.com/product/arducam-2mp-spi-camera-b0067-arduino/) | カメラ | 25.99$ | | [リレーモジュール](https://www.amazon.co.jp/gp/product/B08CKGNHP9/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1) | 照明スイッチング用 | 1000円くらい | | 照明 | 撮影時の光源 | 家に転がってたので値段不明 | | [人体赤外線感応モジュール](https://www.amazon.co.jp/gp/product/B081GYJ9CR/ref=ppx_yo_dt_b_asin_title_o05_s00?ie=UTF8&psc=1) | 人検知用 | 499円くらい | # システム構成 ざっくり説明すると人感検知をトリガーにカメラ撮影してSlackに画像と鍵開閉判断結果を送信している。 ![仕組み](https://camo.elchika.com/1866b24ca4561b92aacf02acba59d43af4e9c900/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f34363161616130382d656337642d343230392d386663332d313635616163353262633661/) システム構成を下記に示す。 ![システム構成](https://camo.elchika.com/e5983ef313954de2cf638d2834249c2a77161716/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f33616330656236332d336466362d346232392d613061322d313566313236343230353261/) 外観を下記に示す。 ![キャプションを入力できます](https://camo.elchika.com/ceda6b6bd55127bb24504b4fc485d08e92449afa/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f32643562663866302d306634652d343561352d393034632d313938343137313261313637/) # ソフトウェア 以下に論理アーキテクチャを示す。※Pythonで開発 ![論理アーキテクチャ](https://camo.elchika.com/7c134bc62ffdb367ba6cb82937d5d5b6ee421f29/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f61306266363161622d636332652d343162332d613662362d373138633162313464653235/) 1. Slack送信 Slack公式ページに画像のアップロードの手法が詳細に記載してある。 [本URL](https://api.slack.com/methods/files.upload/code)参照のこと。 1. 鍵開閉判断 OpenCVとTensorFlowを使用し実現。 2.1. Step1 二値化  画像を二値化する。いきなり二値化すると具合が悪いのでグレースケールしてから二値化するといい感じになる。 ![元画像](https://camo.elchika.com/26a12e6c94a3a41195983ac25967dc037f18573a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f36646339373239632d383230302d343932362d383961332d653632386361613734643939/) ![グレースケール](https://camo.elchika.com/e7f73722b872494762b7f3aace8aedc6fa0bf637/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f31663930373738362d623431362d343865322d613338342d656465656431613861663034/) ![二値化](https://camo.elchika.com/541d176b74141286aa623f57b4918ed4e14a61cf/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f66663462393832392d303231332d343437312d393266362d303165303433323135373461/) 2.2. Step2 テンプレートマッチング  二値化した画像でテンプレートマッチングを実施し、ドアの鍵部分のみをトリミングする。 ![元画像(テンプレートマッチング結果)](https://camo.elchika.com/35cc41d7d2e29c12e1c7e453152a05d9373c2bce/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f36363030643461642d396230642d346661312d613465642d306261346335656138623032/) ![](https://camo.elchika.com/cd4441e0ac1a0110ad2919bfa4a7499d42d895e8/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f38616264653464352d353932392d346336382d613435372d623261633062313739633766/) 2.3. Step3 機械学習  ドアの鍵部分の画像をいっぱい集めてラベルを付けて学習用画像データを作成する。ラベルは「Close」、「Open」、「Can not detect」の三項目。目視で開いてるか閉じてるか確認してラベルつけるからクッソめんどくさい。ラベルつけた後、学習用画像データをグレースケール化し、分類器を学習させる。 ![キャプションを入力できます](https://camo.elchika.com/ddae2763a4e33cc3ce5e47474842cd9eebc8a686/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f65376564323730312d333438332d346134642d396334372d383332303266363730393733/) 2.4. Step4 分類器  学習させた分類器に撮影した画像を入力すると、鍵が開いてるか閉じてるかを分類してくれる。下記画像は学習で使用していない画像を用いて分類させた結果を示している。それぞれの画像横の青いバーは、左から順に「Close」、「Open」、「Can not detect」の適合度を表しており、各テスト結果100%の適合度を示している。ほんとに合ってるかは不明。 ![学習済分類器テスト結果](https://camo.elchika.com/41dc88fc6f95edff0de5dfbd9ee8284479fe130a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f63313736656430382d643162352d343939342d616263302d3934323065333537313538652f66626336623439372d626438352d343233632d393361322d306264333239613066303933/) 1. カメラ撮影制御 obniz-python-sdkを使用。 [本URL](https://github.com/obniz/obniz-python-sdk/blob/master/obniz/parts/Camera/ArduCAMMini/README-ja.md)参照のこと。

+

この画像撮影において詰まったポイントが二つあるので紹介しておく。  3. 1. wifi接続が切れる事象 time.sleep関数を使用すると、obnizがフリーズの後wifi接続が切れる事象が発生した。おそらくtime.sleepによりバックグラウンドでやってたなんかの処理がtimeoutしたという落ちだろうと思う。あくまでも推測。そんな感じの動きだった。  3. 2. JPEGが正常に表示されない事象 Arducamから取得したJPEGファイルを保存し、ビュアーで開くと「このファイルはサポートされていない形式のようです。」と表示される。バイナリエディタで中身を確認すると

+

1. 照明制御 カメラ撮影前にオンにして撮影終了後にオフするだけ。 以下にソースコードを示す。 ```python:家の鍵開閉状態を外出先から確認するシステム import asyncio from obniz import Obniz import datetime import base64 import io import time import threading import codecs from slack import WebClient import aiohttp import json import requests import sys import os import tensorflow as tf from tensorflow import keras import numpy as np import glob import re import cv2 import shutil import matplotlib.pyplot as plt #dooropen TOKEN = "xoxb-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" CHANNEL = "XXXXXXXXXXXXX" file_url = "" save_path = 'download.jpeg' TARGETPATH = "media/TARGET/" TEMPPATH = "media/TEMP/" OPENPATH = "open.jpeg" CLOSEPATH = "close.jpeg" TARGEFILETPATH = "*.jpeg" last_vol = 0 last_time = 0 f_detect = 0 f_getpic = 0 f_delaypic = 0 obniz = Obniz('XXXX-XXXX') async def onconnect(obniz): global f_detect global f_getpic cam = obniz.wired("ArduCAMMini", { "cs": 7, "mosi": 6, "miso": 5, "sclk": 4, "gnd": 3, "vcc": 2, "sda": 1, "scl": 0 ,"spi_frequency":100000,"module_version":1 }) obniz.io9.pull("5v") while True: vol = await obniz.ad8.get_wait() hdetect(vol) if f_getpic == 1: obniz.io9.output(True) await cam.startup_wait() # data = await cam.take_wait('640x480') data = await cam.take_wait('800x600') head = 255 f_cnt = 0 cnt1 = 0 f = open("list.jpeg", 'bw') f.write(head.to_bytes(1,'big')) for x in data: f.write(x.to_bytes(1,'big')) if f_cnt == 1: if data[cnt1] == 217: if data[cnt1-1] == 255: break f_cnt = 1 cnt1 = cnt1 + 1 f.close() print("done") worker() obniz.io9.output(False) obniz.onconnect = onconnect def hdetect(vol): global last_vol global last_time global f_detect global f_getpic global f_delaypic if vol - last_vol > 3.0: print("detect!!!") last_time = time.time() f_detect = 1 if time.time() - last_time < 60.0 and f_detect == 1: f_getpic = 1 f_delaypic = 1 f_detect = 0 if time.time() - last_time > 60.0 and f_delaypic == 1: f_getpic = 1 f_delaypic = 0 last_vol = vol def get_grayscale(image_path): img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) return gray def do_grayscale(image_path): img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) save_image(image_path,"src", "gray", gray) def do_binarization(image_path): img = cv2.imread(image_path,0) img_thresh = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,41,2) # ret, img_thresh = cv2.threshold(img, 110, 255, cv2.THRESH_BINARY) save_image(image_path, "gray", "binary", img_thresh) def do_template_matching(target_path,tmp_path): template_img = cv2.imread(tmp_path + "binary/" + OPENPATH) img_list = glob.glob(target_path + "binary/" + TARGEFILETPATH) for path in img_list: img = cv2.imread(path) result = cv2.matchTemplate(img, template_img, cv2.TM_CCOEFF) minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(result) ya, yb, xa, xb = maxLoc[0] + 0, maxLoc[0] + 45, maxLoc[1] + 0, maxLoc[1] + 45 lx, ly, rx, ry = maxLoc[0] + 45, maxLoc[1] + 45, maxLoc[0] + 0, maxLoc[1] + 0 source = cv2.imread(path.replace('binary', 'src')) img_clip = source[xa : xb, ya : yb] tmp_path = path.replace('.jpeg', '_') tmp_path2 = tmp_path.replace('binary', 'result') cv2.imwrite(tmp_path2 + "clip.jpeg" ,img_clip) def save_image(img_path,bese_dir,dir, img): dir_name = img_path.replace(bese_dir,dir) cv2.imwrite(dir_name, img) def worker(): global file_url global f_getpic file_url = "list.jpeg" files = glob.glob('*.jpeg') cnt = 0 for f in files: shutil.copyfile(f,TARGETPATH + "src/" +str(cnt)+'_0.jpeg' ) cnt = cnt + 1 do_grayscale(TEMPPATH + "src/" + OPENPATH) do_grayscale(TEMPPATH + "src/" + CLOSEPATH) for path in glob.glob(TARGETPATH + "src/" + TARGEFILETPATH): do_grayscale(path) do_binarization(TEMPPATH + "gray/" + OPENPATH) do_binarization(TEMPPATH + "gray/" + CLOSEPATH) for path in glob.glob(TARGETPATH + "gray/" + TARGEFILETPATH): do_binarization(path) do_template_matching(TARGETPATH,TEMPPATH) test_images = np.array(get_grayscale("media/TARGET/result/0_0_clip.jpeg")) class_names = ['Close', 'Open', 'Cannot Detect'] model = tf.keras.models.load_model('saved_model/my_model') model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) test_images = (np.expand_dims(test_images,0)) predictions = model.predict(test_images) index_name = np.argmax(predictions) if index_name > 2: index_name = 2 if index_name < 0: index_name = 2 print(class_names[index_name]) param = { "token": TOKEN, "channels": CHANNEL, # "filename":"filename", "initial_comment": class_names[index_name] # "title": "title" } files = {'file': open(file_url, 'rb')} url = "https://slack.com/api/files.upload" requests.post(url, data=param, files=files) f_getpic = 0 if __name__=="__main__": print("start") asyncio.get_event_loop().run_forever() ``` # 動作確認 @[twitter](https://twitter.com/j03964608/status/1389080544412016643) @[twitter](https://twitter.com/j03964608/status/1389080976681095171) - まとめ 作った後に分類器なんか使わんでも画像送信だけでいいのでは?と考えたが、読み上げ機能と組み合わせることで目が不自由な人や画像を確認できない状況にある人に対して効果を発揮できると考えられる。 この出先から鍵の様子を確認できるシステムにより、外出先で家の鍵閉めたっけ?と不安になることはないでしょう。やったね!!!