はじめに
この記事はオートロックマンション用不在時荷物受け取りシステムの開発記事です。
前回までに、インターホン音の検知、共同玄関の解錠、インターホンモニタの撮影、スマホへのプッシュ通知、Google Calendarとの連携などができるようになった。これで不在中に荷物が届いても、あらかじめ配達時間帯を指定しておけば自動で共同玄関が開き、家の玄関前まで配達員がたどり着ける。そこまで来たらOKIPPAなどの簡易宅配ボックスに荷物を入れてもらうことができる。その意味で最低限の機能は実装できたが、もう少し工夫していこうと思う。
今回は、ウェブカメラで撮影してスマホにプッシュする画像を改良する。
具体的には、前回はウェブカメラで撮影した画像をそのままプッシュしていたが、今回は加えて実質的に重要なモニタ部分のみをPythonで抜き出し、射影変換して補正した上でプッシュする。
もしカメラの位置が固定されているのなら座標固定で抜き出せばいいのだが、今回の環境ではウェブカメラはきちんと固定されておらず、衝撃などで微妙に位置がずれる恐れがあったので、毎回自動的にモニタを抽出することとした。
なお、RaspberryPi 3B+、Python3.7.3を使っている。
撮影画像からモニタ部分の抽出する
Pythonで画像処理をするのに今回OpenCVを使ったわけだが、やはり似たことをした人はいるもので、今回は以下の記事を参考にしつつ実装した。(というかほとんどそのまま)
備忘録を兼ねてこの記事でも方法を述べていこうと思う。
今回相手にする画像は我が家のインターホンの画像だ。共同玄関で呼び出されるとモニタが表示される。スマホにプッシュするとしたら全体は必要なく、中央のモニタ部分だけユーザに送れば良い。
前回の記事に、ウェブカメラを使って撮影する方法を書いたが、それを使ってframe
という変数にBGRの画像データが入っているものとする。
まずはデータを二値化する。
import cv2 import matplotlib.pyplot as plt gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) ret, th = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY) plt.imshow(th, cmap="gray") plt.show()
明るい環境ではモニタ以外も二値化で抽出されてしまうが、このあとのfindContourとapproxPolyDPが最強なので気にせず進む。
次に二値化画像から輪郭を抽出する。抽出した輪郭の中から面積でインターホンモニタを選別し、四角形に近似する。計測してみたところ、インターホンモニタは面積が25000前後だったので、それ前後の輪郭があえばそれをモニタと見なした。
#輪郭検出 contours, hierarchy = cv2.findContours(th, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 面積がインターホンモニタに近いもののみ選別 areas = [] for cnt in contours: area = cv2.contourArea(cnt) if 22500 < area and area < 27500 : print(area) epsilon = 0.1*cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) areas.append(approx) img_contour = cv2.drawContours(frame, areas, -1,(255,0,0),3) plt.imshow(img_contour) plt.show()
無事にモニタ部分を四角形で抽出することができた。
ここでは何をやっているかというと、findContours
で二値化画像から輪郭を抽出し、approxPolyDP
で検出した輪郭をより少ない点数で近似している。epsilon
は近似のパラメータである。正直これらの関数が最強なので、周りの照明環境が変動してもほとんど問題ない。
最後に、抽出したモニタ領域を射影変換して補正する。つまり、斜めから撮影した画像をあたかも正面から見たかのように変換する。
import numpy as np # 射影変換 dst_size = [640,450] # 射影変換後の画像サイズ dst = [] pts1 = np.float32(areas[0]) # 抽出したモニタ領域の四隅の座標 pts2 = np.float32([[0,0],[0,dst_size[1]],[dst_size[0],dst_size[1]],[dst_size[0],0]]) # 射影変換後の四隅の座標 # ホモグラフィ行列を求め、射影変換する M = cv2.getPerspectiveTransform(pts1,pts2) dst = cv2.warpPerspective(frame, M, (dst_size[0],dst_size[1])) plt.imshow(dst) plt.show()
無事モニタ部分を射影変換することができた。
実際に配達員が来てインターホンを押した時にはこんな感じにスマホに通知される。このときは不在時だったが、無事に荷物を受け取ることができた。やったね。