OpenCVで動体検知カウンター

前回7年ぶりに再トライしているPICですが、なかなか進みません。難しい。全然したいことの情報に行き着かない。。。

って感じで案の定、頓挫中なのですが、高機能プランターを作りたいっと言った友人はビールを作りたいと言い始めました。→ブログ http://brewojisan.com/

 

その際、発酵させる過程で炭酸ガスが出るのですが、発酵状態の把握の目安として、ガス発生の推移を計測したいと言いました。

具体的には、下の動画のように、水封管がガスでコポコポする頻度を測りたいとのこと。

 

久々のOpenCVで挑戦だ!

スマホ手持ちで撮影してるので、まずは手ブレを補正しました。固定カメラで撮ってればしなくていいんだけど。

ソースおよび参考サイト
http://code.tiblab.net/python/opencv/movie_stabilization

一発でいい感じにできて、ちょっとした感動を覚えました。OpenCV、やはり良いライブラリだ・・・。

 

次に本題、動体検知をしてみました。

ソースおよび参考サイト
http://code.tiblab.net/python/opencv/detect_motion

こちらも一発OK。素晴らしい。

 

この段階で水封部をクリッピングして(実際には最初にクリッピング)、白色の面積比(ピクセル比)をグラフ化するとこんな感じ。

既にもうカウントできそうですね。でもちょっとノイズ処理を加えます。

 

先ほどの動体検知結果の細かなノイズを除去するためにメディアンフィルタをかけるとこんな感じになりました。

いい感じにノイズが消えています。

このままでも良さそうですが、最新5データの平均(移動平均)を取ってみます。

ピーク値が少し減りますが、さらに細かいブレが平坦化されます。

これに閾値(今回は0.3)を設け、2値化します。

カウントは閾値未満から閾値を超えた際にするようにしました。

 

ちなみに移動平均をする前に0.3で、閾値分けしてみると、最初の山で細かいブレにより微妙に0.3を前後してカウントがずれてしまいました。

場合によっては、移動平均かけても、そうならないとも限らないので、前回カウントから一定の間隔が開かないと、カウントしない処理も加えました。これで、上記移動平均をかけてないグラフでもカウント数は3回となります。

 

あとはカウントした際に時間をログしていけば、頻度の傾向がわかりますね。

ソース

# -*- coding: utf-8 -*-
import cv2
import numpy as np

cap = cv2.VideoCapture(r'C:\Temp\input.avi')

size = (40,60)
fourcc = 'DIB '
fps = 24
video = cv2.VideoWriter(r'C:\Temp\output.avi', cv2.VideoWriter_fourcc(*fourcc), fps, size)

threshold = 0.3
countLimit = 10

count = 0
n = 0
buff = []
avg = None
hasMotion = False
f = open('output.txt','w')
while True:
    # フレーム取得
    ret, frame = cap.read()
    if not ret:
        break
    
    #クリッピング
    frame = frame[120:180,80:120]
    
    #動体検知
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    if avg is None:
        avg = gray.copy().astype('float')
        continue
    cv2.accumulateWeighted(gray, avg, 0.5)
    frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
    thresh = cv2.threshold(frameDelta, 3, 255, cv2.THRESH_BINARY)[1]
    blur = cv2.medianBlur(thresh,5)
    
    #白の面積比
    whitePixels = cv2.countNonZero(blur)
    whiteAreaRatio = (float(whitePixels)/(size[0]*size[1]))
    
    #移動平均
    if len(buff) >= 5:
        buff.pop(0)
    buff.append(whiteAreaRatio)
    total = 0.0
    for b in buff:
        total += b
    v = total/len(buff)
    f.write(str(v)+'\n')
    
    #カウント
    if not hasMotion:
        if v > threshold:
            hasMotion = True
            if countLimit < n:
                count += 1
                n = 0
                print('count %d' % count)
    else:
        if v < threshold:
            hasMotion = False
    n += 1
    
    # 表示&動画出力
    cv2.imshow('frame', blur)
    video.write(blur)
    
    #qで途中終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

f.close()
cap.release()
video.release()
cv2.destroyAllWindows()

Updated: 2019年7月12日 — 08:16

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です