一宣决、原理
OpenCV內(nèi)置目標(biāo)檢測(cè)
OpenCV內(nèi)置的目標(biāo)檢測(cè)主要是對(duì)于特定物體(人臉)或者運(yùn)動(dòng)物體進(jìn)行檢測(cè)掩幢,本例使用OpenCV中的MOG2算法對(duì)運(yùn)動(dòng)的目標(biāo)進(jìn)行檢測(cè)绑嘹,檢測(cè)后使用腐蝕唧席、膨脹進(jìn)行處理钾军,得到較合理的初步目標(biāo)檢測(cè)區(qū)域娄涩。
OpenCV內(nèi)置目標(biāo)跟蹤
使用OpenCV內(nèi)置的MIL跟蹤器或者DaSiamRPN跟蹤器窗怒,使用其對(duì)于已經(jīng)檢測(cè)到的目標(biāo)進(jìn)行跟蹤,以達(dá)到增強(qiáng)軟件檢測(cè)結(jié)果穩(wěn)定性的目的蓄拣。
軟件設(shè)計(jì)目標(biāo)
通過(guò)結(jié)合OpenCV內(nèi)置的兩種算法扬虚,達(dá)到較為穩(wěn)定的目標(biāo)檢測(cè)目的。
二球恤、軟件設(shè)計(jì)
軟件主要設(shè)計(jì)如圖所示辜昵,表征了軟件的循環(huán)結(jié)構(gòu)
需要強(qiáng)調(diào)的是,本例完成匆忙僅采用了簡(jiǎn)單的平均值濾波器咽斧,但是本例非常適合采用卡爾曼濾波器對(duì)于檢測(cè)和跟蹤的結(jié)果進(jìn)行預(yù)測(cè)堪置。
三、代碼
將上述軟件設(shè)計(jì)封裝在類中
import copy
import cv2
import numpy as np
class ImageProcessorCloseLoop:
def __init__(self, subtractor_type="MOG2", tracker_type="DaSiamRPN"):
self.sub = self.create_subtractor(subtractor_type)
self.tracker_type = tracker_type
self.trackers = list()
def create_subtractor(self, subtractor_type):
if subtractor_type == "MOG2":
return cv2.createBackgroundSubtractorMOG2()
elif subtractor_type == "KNN":
return cv2.createBackgroundSubtractorKNN()
def create_tracker(self, tracker_type):
if tracker_type == "MIL":
return cv2.TrackerMIL_create()
elif tracker_type == "GOTURN":
return cv2.TrackerGOTURN_create()
elif tracker_type == "DaSiamRPN":
return cv2.TrackerDaSiamRPN_create()
def process_one_frame(self, frame):
'''
:param frame: original video frame image (np.ndarray)
:return: bboxes (tuple of bbox)
'''
detect_result = self.simple_detect(frame)
track_result = list()
for tracker in self.trackers:
ok, bbox = tracker.update(frame)
if ok:
track_result.append(bbox)
else:
self.trackers.remove(tracker)
matched_detect, matched_track, only_detect, only_track = self.compare_result(detect_result, track_result)
res_matched = list()
for i in range(len(matched_track)):
bb1 = matched_detect[I]
bb2 = matched_track[I]
bb_res = self.bb_filter2(bb1, bb2)
res_matched.append(bb_res)
res_track = only_track
res_detect = only_detect
for res in res_detect:
tracker = self.create_tracker(tracker_type=self.tracker_type)
# tracker.init(frame, res)
res = res_matched + res_track + res_detect
return res
def simple_detect(self, img: np.ndarray):
mask = self.sub.apply(img)
thresh, bw = cv2.threshold(mask, 120, 255, cv2.THRESH_OTSU)
kernel = np.ones((3, 3), dtype=int)
dilated = cv2.erode(bw, kernel, iterations=1)
inflated = cv2.dilate(dilated, kernel, iterations=1)
# find contour:
cnts, hier = cv2.findContours(inflated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
bboxes = list()
for i in range(len(cnts), 0, -1):
c = cnts[i - 1]
area = cv2.contourArea(c)
if area < 10:
continue
bbox = cv2.boundingRect(c)
x, y, w, h = bbox
diameter = w if w > h else h
if diameter <= 15:
continue
bboxes.append(bbox)
return bboxes
def compare_two(self, bb1, bb2):
'''
compare two bbox
:param bb1: bbox tuple (x, y, w, h)
:param bb2: bbox tuple
:return: True or False
'''
x1, y1, w1, h1 = bb1
x2, y2, w2, h2 = bb2
IOU1 = (min(x1 + w1, x2 + w2) - max(x1, x2)) / (max(x1 + w1, x2 + w2) - min(x1, x2))
IOU2 = (min(y1 + h1, y2 + h2) - max(y1, y2)) / (max(y1 + h1, y2 + h2) - min(x1, x2))
IOU = IOU2 * IOU1
if IOU > 0.9:
return True
else:
return False
def compare_result(self, detect_res, track_res):
'''
compare the detect results by comparing IOU of the bboxes
:param last_res: list of bboxes
:param this_res: list of bboxes
:return: tuple (matched, new, only_last)
'''
matched_detect = list()
matched_track = list()
track = list()
detect = list()
for track_box in track_res:
for detect_box in detect_res:
is_matched = self.compare_two(track_box, detect_box)
if is_matched:
matched_track.append(track_box)
matched_detect.append(detect_box)
detect_res.remove(detect_box)
else:
track.append(track_box)
for detect_box in detect_res:
detect.append(detect_box)
return matched_detect, matched_track, detect, track
def bb_filter2(self, bbox1, bbox2, method="mean"):
'''
:param bbox1:bbox (x, y, w, h)
:param bbox2:bbox
:return: bbox
'''
if method == "mean":
return (bbox1[0] + bbox2[0])/2, (bbox1[1] + bbox2[1])/2, (bbox1[2] + bbox2[2])/2, (bbox1[3] + bbox2[3])/2
實(shí)例化類和視頻讀取循環(huán)寫(xiě)在main.py
中
main.py
import cv2
import processor
if __name__=="__main__":
cap = cv2.VideoCapture("/Users/wanglingyu/sources/video1.mp4")
proc = processor.ImageProcessorCloseLoop(tracker_type="MIL")
ret, frame = cap.read()
if not ret:
exit(1)
while True:
ret, frame = cap.read()
if not ret:
break
bboxes = proc.process_one_frame(frame)
for bbox in bboxes:
x, y, w, h = bbox
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 0xff), 1)
cv2.imshow("Video", frame)
key = cv2.waitKey(10)
if key == ord(" "):
k1 = cv2.waitKey()
cap.release()
cv2.destroyAllWindows()
四张惹、效果
能夠穩(wěn)定地對(duì)于目標(biāo)進(jìn)行檢測(cè)舀锨,且檢測(cè)框不易丟失。
五宛逗、環(huán)境配置
本例代碼運(yùn)行于macOS 11.4 Big Sur上坎匿,該系統(tǒng)運(yùn)行于arm平臺(tái)的M1芯片上,其中OpenCV若需要編譯則需要在配置cmake時(shí)配置好python2或python3的各個(gè)選項(xiàng)雷激。如果使用PyCharm軟件則在其內(nèi)置的軟件倉(cāng)庫(kù)中直接安裝即可使用替蔬,兼容性沒(méi)有問(wèn)題。