【圖像工程入門(mén)】OpenCV檢測(cè)器和跟蹤器(運(yùn)動(dòng)物體檢測(cè)跟蹤)

一宣决、原理

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)


循環(huán)中每次調(diào)用類方法(process_one_frame)

需要強(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è)框不易丟失。


目標(biāo)檢測(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)題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屎暇,一起剝皮案震驚了整個(gè)濱河市承桥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌根悼,老刑警劉巖凶异,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異番挺,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)屯掖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)玄柏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人贴铜,你說(shuō)我怎么就攤上這事粪摘∑偕梗” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵徘意,是天一觀的道長(zhǎng)苔悦。 經(jīng)常有香客問(wèn)我,道長(zhǎng)椎咧,這世上最難降的妖魔是什么玖详? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮勤讽,結(jié)果婚禮上蟋座,老公的妹妹穿的比我還像新娘。我一直安慰自己脚牍,他們只是感情好向臀,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著诸狭,像睡著了一般券膀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驯遇,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天芹彬,我揣著相機(jī)與錄音,去河邊找鬼妹懒。 笑死雀监,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的眨唬。 我是一名探鬼主播会前,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼匾竿!你這毒婦竟也來(lái)了瓦宜?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岭妖,失蹤者是張志新(化名)和其女友劉穎临庇,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體昵慌,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡假夺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斋攀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片已卷。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嗦董,到底是詐尸還是另有隱情隐圾,我是刑警寧澤掂林,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布絮缅,位于F島的核電站巷疼,受9級(jí)特大地震影響抱婉,放射性物質(zhì)發(fā)生泄漏寻拂。R本人自食惡果不足惜晌坤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一逢艘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泡仗,春花似錦埋虹、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至截亦,卻和暖如春爬泥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背崩瓤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工袍啡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人却桶。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓境输,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親颖系。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嗅剖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351