はじめに
生活の乱れを採点することで、生活改善につなげたいと思います。
この頃、部屋の片付けが面倒になってきたので、
部屋に設置したカメラの映像から、部屋の汚さをリアルタイム採点してみました。
作ったもの
- 左上の数字:汚部屋スコア
- 点数が高いほど汚い
- 左上の英字:3段階判定
- [CLEAN] きれい
- [MESSY] 散らかっている
- [OBEYA] 汚部屋
- 緑色の線
- 散らかっている場所
使ったもの
- Python 3.5.4
- OpenCV 4.1.1.26
- MacOS 10.14.6
- USBカメラ ELECOM UCAM-C310FBBK
処理フロー
1. 画像処理
1-1. カメラからフレームを読み込みます。 参考:動画を扱う — OpenCV-Python Tutorials 1 documentation |
|
1-2. グレースケールに変換します。cv2.cvtColor()
|
|
1-3. Canny法によるエッジ検出を行います。輪郭線を強調した画像が取得できます。cv2.Canny()
|
2. ベクトル・数値処理
2-1. 輪郭線の抽出
1-3
の画像から、cv2.findContours()
で輪郭線のベクトルデータを取得します。
2-2. ちょうどよい長さの輪郭線を選別
2-1
で取得したベクトルを for 文に突っ込むと、輪郭線1本分のデータが得られるので、cv2.arcLength()
を使って輪郭線の長さを取得します。
長すぎる、または短すぎる輪郭線は捨てちゃいます。
2-3. スコアの集計
2-2
で得られた輪郭線の長さを単純に足し算して、点数を計算します。
3. 完成
最後に、1-1
で読み込んだ元の画像、2-2
で選別された輪郭線、2-3
で計算したスコアなどを重ね合わせて出力すれば完成です。
ソースコード
import cv2
import numpy as np
import math
from collections import deque
from statistics import median
FONT = cv2.FONT_HERSHEY_SIMPLEX
RED = (0, 0, 255)
GREEN = (0, 255, 0)
BLUE = (255, 0, 0)
ORANGE = (0, 130, 255)
SCORE_AP = (0, 80)
SCORE_FONT_SIZE = 2
SCORE_FONT_WEIGHT = 8
score_queue = deque([])
def get_score_summary(total_arc_length):
score_queue.append(math.floor(total_arc_length / 100))
if len(score_queue) > 30:
score_queue.popleft()
score = median(score_queue)
if score > 200:
return score, '[OBEYA]', RED
elif score > 150:
return score, '[MESSY]', ORANGE
else:
return score, '[CLEAN]', BLUE
def is_obeya_contour(arc_length):
return 100 < arc_length < 900
def show_obeya_score(frame):
score_frame = frame.copy()
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
canny_frame = cv2.Canny(gray_frame, 120, 200)
contours, hierarchy = cv2.findContours(canny_frame, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE )
total_arc_length = 0
for contour in contours:
arc_length = cv2.arcLength(contour, True)
if is_obeya_contour(arc_length):
cv2.drawContours(score_frame, contour, -1, GREEN, 3)
total_arc_length += arc_length
score, decision, color = get_score_summary(total_arc_length)
cv2.putText(score_frame, ' '.join([str(score), decision]),
SCORE_AP, FONT, SCORE_FONT_SIZE, color, SCORE_FONT_WEIGHT, cv2.LINE_AA)
cv2.imshow('frame: contour', score_frame)
if __name__ == '__main__':
cap = cv2.VideoCapture(0)
while(True):
ret, frame = cap.read()
show_obeya_score(frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()