Multiple Object Trackingとは?
Multiple Object Tracking(MOT)とは、名前の通り映像に写っている複数の物体を追跡する手法の総称です。MOTではそれぞれの追跡物体にIDを割り振りますが、同じ対象物には可能な限り同じIDを与え続けることを目標とします。
cv2 Select Multiple ROIs
別記事にて作成したSelect Multiple ROIsを使用すると、簡易的なMOTを実現できます。
コードを実行するには事前にモジュールのインストールが必要です。
pip install numpy
pip install keyboard
pip install opencv-contrib-python==4.6.0.66
コードを再掲します。
import numpy as np
import cv2
from copy import deepcopy
from random import randint
import keyboard
import time
class MultiBoxDrawer:
# 参考 https://qiita.com/ryo_ryo/items/973007667c528ef23abb
def draw(self, img):
m = MouseEventHandler(img)
cv2.imshow('Select Rois',img)
cv2.setMouseCallback ("Select Rois",
lambda event, x, y, flags, param:
m.mouse_event(event, x, y, flags, param, img))
while True:
img = m.clone_img
cv2.imshow("Select Rois", img)
k = cv2.waitKey(10)
if keyboard.is_pressed("space") | keyboard.is_pressed("enter"):
time.sleep(0.3)
break
cv2.destroyWindow("Select Rois")
# 複数のバウンディングボックスの座標情報をreturn
return m.ROIRegion
class MouseEventHandler:
def __init__(self, img):
self.drawing = False # true if mouse is pressed
self.ix, self.iy = -1, -1
self.ROIRegion = []
self.init_img = deepcopy(img)
self.clone_img = deepcopy(img)
self.tmp_img = deepcopy(img)
def mouse_event(self, event, x, y, flags, param, img):
if event == cv2.EVENT_LBUTTONDOWN: # 左クリック(押し込み)した瞬間の処理
self.drawing = True
self.ix, self.iy = x,y
self.tmp_img = deepcopy(img)
elif event == cv2.EVENT_MOUSEMOVE: #ドラッグ中の処理
if self.drawing == True:
self.clone_img = deepcopy(self.tmp_img)
cv2.rectangle(self.clone_img, (self.ix, self.iy), (x, y), (0, 0, 255), 2)
elif event == cv2.EVENT_LBUTTONUP: # 左クリック(離し)した瞬間の処理
self.drawing = False
if self.ix != x and self.iy != y:
self.ROIRegion.append([min(self.ix, x), min(self.iy, y), abs(x-self.ix), abs(y-self.iy)])
elif event == cv2.EVENT_RBUTTONDOWN: # 右クリック(離し)した瞬間の処理
self.ROIRegion = []
self.clone_img = deepcopy(self.init_img)
Object Tracker
opencv-contrib-python には古典的なObject Trackerが用意されています。
今回は例としてKCFを使用します。
コードは以下の通り。
import numpy as np
import cv2
from copy import deepcopy
from random import randint
import keyboard
import time
import copy
def is_end(window_names=["Video"]):
return_value = False
# イメージウインドウの x ボタンを押すか、ESC キー入力で終了処理
window_property = [cv2.getWindowProperty(window_name, cv2.WINDOW_AUTOSIZE) for window_name in window_names]
if max(window_property) > 0 or (cv2.waitKey(1) & 0xff == 27):
return_value = True
return return_value
def main(filename):
# 動画ファイルsetting
cap = cv2.VideoCapture(filename)
bboxes = []
colors = []
multiTracker = None
track_object_flg = False
cv2.namedWindow("Video", cv2.WINDOW_NORMAL)
if (cap.isOpened()== False):
print("ビデオファイルを開くとエラーが発生しました")
while(cap.isOpened()):
if len(bboxes) == 0:
track_object_flg = False
multiTracker = None
ret, frame = cap.read()
if ret == True:
if track_object_flg:
# get updated location of objects in subsequent frames
# multiTrackerの出力は bbox = [x0, y0, width(x1-x0), hight(y1-y0)]
success, boxes = multiTracker.update(frame)
print(boxes)
# draw tracked objects
for i, newbox in enumerate(boxes):
p1 = (int(newbox[0]), int(newbox[1]))
p2 = (int(newbox[0] + newbox[2]), int(newbox[1] + newbox[3]))
cv2.rectangle(frame, p1, p2, colors[i], 2, 1)
cv2.imshow("Video", frame)
if keyboard.is_pressed("t"):
multiboxdrawer = MultiBoxDrawer()
bboxes = multiboxdrawer.draw(frame)
print(bboxes)
track_object_flg = True
multiTracker = cv2.legacy.MultiTracker_create()
for bbox in bboxes:
# bbox = [x0, y0, x1, y1]
bbox = [bbox[0], bbox[1], bbox[2], bbox[3]]
# multiTrackerへの追加は bbox = [x0, y0, width(x1-x0), hight(y1-y0)]
multiTracker.add(cv2.legacy.TrackerKCF_create(), frame, bbox)
colors.append((randint(0, 255), randint(0, 255), randint(0, 255)))
# # 画像表示のため遅延を入れておく
cv2.waitKey(30)
# qキーが押されたときbreak
if keyboard.is_pressed("q"):
break
if is_end():
break
else:
break
# 後始末
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
# https://learnopencv.com/multitracker-multiple-object-tracking-using-opencv-c-python/
filename = 'sample.mp4'
main(filename)
t キー入力で追跡領域設定モードに入ります。領域を指定したらスペースキーで抜けます。指定領域の追跡が始まります。q キー入力で終了します。
gifが荒くてすみません。元画質ではアップロードが不可能でしたので、ファイルサイズを1/20に圧縮しています。
githubにソースコードがあります。
参考