新着情報

3-1. tkinter Canvas Widgetを利用した人物追跡GUI動画処理システムの作成

前の記事(3. 動画ファイルの人物検出とその追跡プログラム)で、OpenCVライブラリを使用してお仕着せの表示ウィンドウ内に映像を表示し人物を追跡するpythonプログラム(trackingofbody.py)を紹介した。しかし、OpenCVライブラリーだけを使用すると映像を表示するウィンドウはOpenCV 任せで、プログラマーがコントロールが出来ないという不満が残る。

ここではtkinterライブラリーのGUI部品であるCanvas widgetを利用して、プログラマーが映像表示ウィンドウをコントロールできる人物追跡GUIシステムを開発したのでメモとして残す。

この人物追跡GUIシステム(VideoWriteCanvasbodytrack.py)は、まず、ビデオファイルから映像を取得するVideoCap( )関数と人物を追跡するBody_track( )関数、及びシステムを終了させるClose( )関数の3つの関数で構成される。

まず、VideoCap( )関数を予め起動させ、ビデオファイルから映像の読み込み設定、処理映像の書き込み設定、人物を認識するカスケードファイル、Shi-Tomas法による移動体のコーナー検出パラメータやLucas-Kanade法のパラメータの設定データを予め読み込んで置く。また、動く物体の検出点の最初の位置も初期映像から読み込んで記憶させて置く。VideoCap( )関数の起動により、パソコンの画面上にこちらが設定したrootウィンドウ(横1000画素 , 縦750画素)の枠内の中央に映像を表示する(横800画素 , 縦600画素)のサイズのCanvas widgetを配置し、その左下にVideoStartボタンとFinishボタンを配置したGUIシステムがFig.1に示す様に表示される。次に、[BodyTracking]ボタンをクリックして、Body_track( )関数を起動し、Canvas widget内にビデオ映像を表示すると共に映像中に登場する人物を赤枠で囲みその追跡を実行し、また、アルタイムで現在の追跡人数のカウントや処理映像の表示をする。読み込み映像は1フレームずつ処理され、表示されると共に新しいビデオファイルにその処理映像を書き込む。この処理は下記プログラムの138行のroot.after(3,body_track)により、3ミリ秒毎にBody_track( )関数の先頭への戻りを繰り返す。読み込む映像がなくなると、その時点で映像は停止する。最後に、[Close]ボタンをクリックして、終了確認メッセージを呼び出し、メッセージボックスにおいてOKボタンを選択してシステムの終了となる。

OpenCVライブラリーだけを使用する場合、最初の検出点位置を求める場所と次の検出点位置を求める場所は区別可能なので、繰り返し位置を次の検出点位置に戻すだけで、人物の追跡が簡単にできる。OpenCVだけを利用する場合は、Body_track( )関数1つだけで人物追跡が可能であり、tkinterライブラリーのwidget部品を利用する場合に比べ、処理は簡単である。

しかし、tkinterライブラリーのwidget部品を使用する場合、Body_track( )関数だけでは、人物の追跡は上手くいかない。新しくVideoCap( )関数を導入し、この関数において最初の検出点位置の読み込みを行い、別なBody_track( )関数において次の検索点位置を繰り返し求めることで人物の追跡は始めて可能となる。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Dec  2 17:16:28 2019

@author: PBK-****
"""

import tkinter as tk
from tkinter import *
import cv2
from PIL import Image,ImageTk
import numpy as np

global maxbody,fps,out
global c, w, h, img,endflag,frame,gray_prev,feature_prev,mask,lk_params,feature_params,color,fullbody_detector 

root=tk.Tk()
root.title("Body-Tracking-Video")

root.geometry('1000x750')
root.resizable(width=True, height=True)

fullbody_detector = cv2.CascadeClassifier('./haarcascades/haarcascade_fullbody.xml')  
# Shi-Tomasiのコーナー検出パラメータ
feature_params = dict( maxCorners = 10,
                       qualityLevel = 0.2,
                       minDistance = 5,
                       blockSize = 7 )

# Lucas-Kanade法のパラメータ
lk_params = dict( winSize  = (50,50),
                  maxLevel = 2,
                  criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

# ランダムに色を100個生成(値0~255の範囲で100行3列のランダムなndarrayを生成)
color = np.random.randint(0, 255, (100, 3))

canvas=tk.Canvas(root, width=800, height=600, bg='#44aaaa')
#canvas=tk.Canvas(root, width=768, height=720, bg="white")
canvas.pack()
maxbody = 0


def VideoCap():
    print('camera-ON')
    global maxbody,fps,out
    global cap, w, h, img,endflag,frame,gray_prev,feature_prev,mask,lk_params,feature_params,color,fullbody_detector 

    cap = cv2.VideoCapture('768x576.avi')
#   cap = cv2.VideoCapture(0)

#   int()にする必要があり
    w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = cap.get(cv2.CAP_PROP_FPS)

    print('fps:'+str(fps)+'w:'+str(w)+'px+h:'+str(h)+'px')

#   ビデオファイルへの映像書き込みの設定
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    out = cv2.VideoWriter('outputA.mp4',fourcc, fps, (w,h))
#    カラー画像の場合、省略はOK もしgray映像なら第5パラメータにFalseを入れる必要あり    
#    out = cv2.VideoWriter('outputA.mp4',fourcc, fps, (w,h),False)
#    fourcc = cv2.VideoWriter_fourcc(*'XVID')
#    out = cv2.VideoWriter('outputA.avi',fourcc, fps, (w,h))


# 最初のフレームの処理
    endflag, frame = cap.read()

    gray_prev = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    feature_prev = cv2.goodFeaturesToTrack(gray_prev, mask = None, **feature_params)
    mask = np.zeros_like(frame)


def Body_track():#update
    global maxbody,fps,out
    global cap, w, h, img,end_flag,frame,gray_prev,feature_prev,mask,lk_params,feature_params,color,fullbody_detector 


    endflag, frame = cap.read()
    if endflag:
        # グレースケールに変換
        gray_next = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 全身の人を検出   
        # minSize:物体が取り得る最小サイズ。これよりも小さい物体は無視される  
        # minNeighbors:物体候補となる矩形は,最低でもこの数だけの近傍矩形を含む  
        body = fullbody_detector.detectMultiScale(gray_next,scaleFactor=1.1, minNeighbors=3, minSize=(40, 40))

#       検出人数の最大値を求める
        if len(body) > maxbody:
            maxbody = len(body)

        # オプティカルフロー検出
        feature_next, status, err = cv2.calcOpticalFlowPyrLK(gray_prev, gray_next, feature_prev, None, **lk_params)

    # オプティカルフローを検出した特徴点を選別(0:検出せず、1:検出した)
        good_prev = feature_prev[status == 1]
        good_next = feature_next[status == 1]

    # オプティカルフローを描画
        for i, (next_point, prev_point) in enumerate(zip(good_next, good_prev)):
            prev_x, prev_y = prev_point.ravel()
            next_x, next_y = next_point.ravel()
            mask = cv2.line(mask, (next_x, next_y), (prev_x, prev_y), (0,255,0), 2)
            frame = cv2.circle(frame, (next_x, next_y), 5, (0,0,255), -1)
        img = cv2.add(frame, mask)
        imgrec = img
        # 人検出した数表示のため変数初期化  
        body_num = 0  
        # 人検出した部分を長方形で囲う  
        for (x, y, ww, hh) in body:  
            cv2.rectangle(img, (x, y),(x+ww, y+hh),(255,0,0),2)  
        # 人検出した数を加算  
            body_num += 1  
    # 人検出した数を表示  
        cv2.putText(img, "Body Cnt:{}".format(int(body_num)),(10,550), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)
        cv2.putText(img, "Max Body Cnt:{}".format(int(maxbody)),(150,550), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)

    # ビデオ映像を書き込む   
        out.write(img)

        if endflag:
            rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            img=ImageTk.PhotoImage(Image.fromarray(img))
            canvas.create_image(400,305,image=img)
#        canvas.pack()
        else:
            print('tracking-Failed')
          # 1フレームずつ書き込む            
        
        gray_prev = gray_next.copy()
        feature_prev = good_next.reshape(-1, 1, 2)
        endflag, frame = cap.read()

    root.after(3,body_track)
    
def Close():
    if messagebox.askokcancel('Confirm Close', 'Are you sure you want to close?'):
        root.quit() 
        exit() 
        cap.release

b1 = Button(root,text='VideoStart',command=Body_track,width=10).pack(side='top',padx=50, pady=20,anchor='nw')
b2 = Button(root,text='Finish',command=Close,width=10).pack(side='top',padx=50, pady=5,anchor='nw')



VideoCap()
#Body_track()
root.mainloop()
cap.release()

参考文献:

1. OpenCVで遊んでみた
https://qiita.com/MuAuan/items/5d968d41e993848a4332

1.の記事でcv2.VideoWriter( )においてカラー映像の書き込みにおいて第5パラメータは省略可能だが、

白黒映像書き込みでは第5パラメータは必須であることを知った。
fourcc = cv2.VideoWriter_fourcc(*’XVID’) #fourccを定義
out = cv2.VideoWriter(‘output.avi’,fourcc, 20.0, (640,480)) #動画書込準備
out = cv2.VideoWriter(GRAY_FILE_NAME, cv2.VideoWriter_fourcc(*’XVID’), pfs, (width, height), False)

color映像ではなく、gray映像の時は第5パラメータにFalseを入れる必要がある。
ビデオ映像の書き込みの時、カラー映像の時はビデオファイル作成ができるのに、

白黒映像を書き込んでも白黒のビデオファイルが作成できず謎のままだったが、この記事で納得できた。

コメント投稿フォーム

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