欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于OpenCV目標(biāo)跟蹤實(shí)現(xiàn)人員計(jì)數(shù)器

 更新時(shí)間:2022年03月09日 14:42:51   作者:求則得之,舍則失之  
這篇文章主要介紹了如何利用Python OpenCV這兩者來創(chuàng)建更準(zhǔn)確的人員計(jì)數(shù)器,文中的示例代碼講解詳細(xì),感興趣的小伙伴快來跟隨小編學(xué)習(xí)一下吧

在本教程中,您將學(xué)習(xí)如何使用 OpenCV 和 Python 構(gòu)建人員計(jì)數(shù)器。使用 OpenCV,我們將實(shí)時(shí)計(jì)算進(jìn)或出百貨商店的人數(shù)。

在今天博客文章的第一部分,我們將討論如何利用兩者來創(chuàng)建更準(zhǔn)確的人員計(jì)數(shù)器。之后,我們將查看項(xiàng)目的目錄結(jié)構(gòu),然后實(shí)施整個(gè)人員計(jì)數(shù)項(xiàng)目。最后,我們將檢查將 OpenCV 的人數(shù)統(tǒng)計(jì)應(yīng)用到實(shí)際視頻中的結(jié)果。

1.了解對(duì)象檢測(cè)與對(duì)象跟蹤

在繼續(xù)本教程的其余部分之前,您必須了解對(duì)象檢測(cè)和對(duì)象跟蹤之間的根本區(qū)別。

當(dāng)我們應(yīng)用對(duì)象檢測(cè)時(shí),我們是在確定一個(gè)對(duì)象在圖像/幀中的位置。與目標(biāo)跟蹤算法相比,目標(biāo)檢測(cè)器通常在計(jì)算上更昂貴,因此也更慢。目標(biāo)檢測(cè)算法的例子包括Haar級(jí)聯(lián)、HOG +線性支持向量機(jī)(HOG + Linear SVM)和基于深度學(xué)習(xí)的目標(biāo)檢測(cè)器,如Faster R-CNN、YOLO和Single Shot檢測(cè)器(SSD)。

另一方面,對(duì)象跟蹤器將接受對(duì)象在圖像中位置的輸入 (x, y) 坐標(biāo),并將:

1.為該特定對(duì)象分配唯一 ID

2.在對(duì)象圍繞視頻流移動(dòng)時(shí)跟蹤對(duì)象,根據(jù)幀的各種屬性(梯度、光流等)預(yù)測(cè)下一幀中的新對(duì)象位置

對(duì)象跟蹤算法的示例包括 MedianFlow、MOSSE、GOTURN、核化相關(guān)濾波器和判別相關(guān)濾波器等。

2.結(jié)合對(duì)象檢測(cè)和對(duì)象跟蹤

高精度目標(biāo)跟蹤器將目標(biāo)檢測(cè)和目標(biāo)跟蹤的概念結(jié)合到一個(gè)算法中,通常分為兩個(gè)階段:

1.階段1 檢測(cè):在檢測(cè)階段,我們正在運(yùn)行計(jì)算成本更高的對(duì)象跟蹤器,以 (1) 檢測(cè)是否有新對(duì)象進(jìn)入我們的視野,以及 (2) 看看我們是否可以找到在跟蹤階段“丟失”的對(duì)象。對(duì)于每個(gè)檢測(cè)到的對(duì)象,我們使用新的邊界框坐標(biāo)創(chuàng)建或更新對(duì)象跟蹤器。由于我們的目標(biāo)檢測(cè)器的計(jì)算成本更高,我們每 N 幀只運(yùn)行一次此階段。

2.階段2 跟蹤:當(dāng)我們不處于“檢測(cè)”階段時(shí),我們處于“跟蹤”階段。對(duì)于我們檢測(cè)到的每個(gè)對(duì)象,我們創(chuàng)建一個(gè)對(duì)象跟蹤器來跟蹤對(duì)象在框架周圍的移動(dòng)。我們的目標(biāo)跟蹤器應(yīng)該比目標(biāo)檢測(cè)器更快、更高效。我們將繼續(xù)跟蹤,直到我們到達(dá)第 N 幀,然后重新運(yùn)行我們的目標(biāo)檢測(cè)器。然后重復(fù)整個(gè)過程。

這種混合方法的好處是我們可以應(yīng)用高度準(zhǔn)確的對(duì)象檢測(cè)方法,而無需太多的計(jì)算負(fù)擔(dān)。我們將實(shí)施這樣一個(gè)跟蹤系統(tǒng)來建立我們的人員計(jì)數(shù)器。

3.項(xiàng)目結(jié)構(gòu)

讓我們回顧一下今天博客文章的項(xiàng)目結(jié)構(gòu)。獲取代碼后,您可以使用 tree 命令檢查目錄結(jié)構(gòu):

最重要的兩個(gè)目錄:

1.pyimagesearch/:該模塊包含質(zhì)心跟蹤算法。 “組合對(duì)象跟蹤算法”部分介紹了質(zhì)心跟蹤算法。

2.mobilenet_ssd/:包含 Caffe 深度學(xué)習(xí)模型文件。

今天項(xiàng)目的核心包含在 people_counter.py 腳本中——這是我們將花費(fèi)大部分時(shí)間的地方。今天我們還將回顧 trackableobject.py 腳本。

4.結(jié)合對(duì)象跟蹤算法

為了實(shí)現(xiàn)我們的人員計(jì)數(shù)器,我們將同時(shí)使用 OpenCV 和 dlib。我們將 OpenCV 用于標(biāo)準(zhǔn)的計(jì)算機(jī)視覺/圖像處理功能,以及用于人數(shù)統(tǒng)計(jì)的深度學(xué)習(xí)對(duì)象檢測(cè)器。

然后我們將使用 dlib 來實(shí)現(xiàn)相關(guān)過濾器。我們也可以在這里使用 OpenCV;但是,對(duì)于這個(gè)項(xiàng)目,dlib 對(duì)象跟蹤實(shí)現(xiàn)更容易使用。

除了 dlib 的對(duì)象跟蹤實(shí)現(xiàn),我們還將使用質(zhì)心跟蹤實(shí)現(xiàn)?;仡櫿麄€(gè)質(zhì)心跟蹤算法超出了這篇博文的范圍,但我在下面提供了一個(gè)簡(jiǎn)短的概述。

在步驟#1,我們接受一組邊界框并計(jì)算它們對(duì)應(yīng)的質(zhì)心(即邊界框的中心):

要使用 Python 通過質(zhì)心腳本構(gòu)建簡(jiǎn)單的對(duì)象跟蹤,第一步是接受邊界框坐標(biāo)并使用它們來計(jì)算質(zhì)心。

邊界框本身可以由以下任一方式提供:

1.目標(biāo)檢測(cè)器(如 HOG + Linear SVM、Faster R-CNN、SSDs 等)

2.或?qū)ο蟾櫰鳎ɡ缦嚓P(guān)過濾器)

在上圖中,您可以看到我們?cè)谒惴ǖ某跏嫉杏袃蓚€(gè)對(duì)象要跟蹤。

在步驟#2中,我們計(jì)算任何新質(zhì)心(黃色)和現(xiàn)有質(zhì)心(紫色)之間的歐幾里得距離:

此圖像中存在三個(gè)對(duì)象。我們需要計(jì)算每對(duì)原始質(zhì)心(紫色)和新質(zhì)心(黃色)之間的歐幾里得距離。

質(zhì)心跟蹤算法假設(shè)它們之間具有最小歐幾里德距離的質(zhì)心對(duì)必須是相同的對(duì)象 ID。

在上面的示例圖像中,我們有兩個(gè)現(xiàn)有的質(zhì)心(紫色)和三個(gè)新的質(zhì)心(黃色),這意味著已經(jīng)檢測(cè)到一個(gè)新對(duì)象(因?yàn)榕c舊質(zhì)心相比,還有一個(gè)新質(zhì)心)。

然后箭頭表示計(jì)算所有紫色質(zhì)心和所有黃色質(zhì)心之間的歐幾里得距離。一旦我們有了歐幾里得距離,我們就會(huì)在步驟#3 中嘗試關(guān)聯(lián)對(duì)象 ID:

您可以看到我們的質(zhì)心跟蹤器已選擇關(guān)聯(lián)使它們各自的歐幾里得距離最小化的質(zhì)心。但是左下角的點(diǎn)呢?它沒有與任何東西相關(guān)聯(lián)——我們?cè)撛趺崔k? 要回答這個(gè)問題,我們需要執(zhí)行步驟#4,注冊(cè)新對(duì)象:

注冊(cè)意味著我們通過以下方式將新對(duì)象添加到我們的跟蹤對(duì)象列表中:

1.為其分配一個(gè)新的對(duì)象 ID

2.存儲(chǔ)新對(duì)象的邊界框坐標(biāo)的質(zhì)心

如果對(duì)象丟失或離開視野,我們可以簡(jiǎn)單地取消注冊(cè)對(duì)象(步驟#5)。

5.創(chuàng)建可追蹤對(duì)象

為了跟蹤和計(jì)算視頻流中的對(duì)象,我們需要一種簡(jiǎn)單的方法來存儲(chǔ)有關(guān)對(duì)象本身的信息,包括:

  • 對(duì)象ID
  • 以前的質(zhì)心(所以我們可以很容易地計(jì)算出物體移動(dòng)的方向)
  • 對(duì)象是否已被計(jì)數(shù)

為了實(shí)現(xiàn)所有這些目標(biāo),我們可以定義一個(gè) TrackableObject 實(shí)例——打開 trackableobject.py 文件并插入以下代碼:

class TrackableObject:
    def __init__(self, objectID, centroid):
        # store the object ID, then initialize a list of centroids
        # using the current centroid
        self.objectID = objectID
        self.centroids = [centroid]
        # initialize a boolean used to indicate if the object has
        # already been counted or not
        self.counted = False

TrackableObject 構(gòu)造函數(shù)接受 objectID + centroid 并存儲(chǔ)它們。 centroids 變量是一個(gè)列表,因?yàn)樗鼘瑢?duì)象的質(zhì)心位置歷史記錄。 構(gòu)造函數(shù)還將 counted 初始化為 False ,表示該對(duì)象還沒有被計(jì)數(shù)。

6.使用 OpenCV + Python 實(shí)現(xiàn)我們的人員計(jì)數(shù)器

# import the necessary packages
from pyimagesearch.centroidtracker import CentroidTracker
from pyimagesearch.trackableobject import TrackableObject
from imutils.video import VideoStream
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import time
import dlib
import cv2

我們首先導(dǎo)入必要的包:

  • 從 pyimagesearch 模塊,我們導(dǎo)入自定義的 CentroidTracker 和 TrackableObject 類。
  • imutils.video 中的 VideoStream 和 FPS 模塊將幫助我們使用網(wǎng)絡(luò)攝像頭并計(jì)算估計(jì)的每秒幀數(shù) (FPS) 吞吐率。
  • 我們需要 imutils 的 OpenCV 便利功能。
  • dlib 庫(kù)將用于其相關(guān)跟蹤器實(shí)現(xiàn)。
  • OpenCV 將用于深度神經(jīng)網(wǎng)絡(luò)推理、打開視頻文件、寫入視頻文件以及在我們的屏幕上顯示輸出幀。

現(xiàn)在所有工具都觸手可及,讓我們解析命令行參數(shù):

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", required=True,
    help="path to Caffe 'deploy' prototxt file")
ap.add_argument("-m", "--model", required=True,
    help="path to Caffe pre-trained model")
ap.add_argument("-i", "--input", type=str,
    help="path to optional input video file")
ap.add_argument("-o", "--output", type=str,
    help="path to optional output video file")
ap.add_argument("-c", "--confidence", type=float, default=0.4,
    help="minimum probability to filter weak detections")
ap.add_argument("-s", "--skip-frames", type=int, default=30,
    help="# of skip frames between detections")
args = vars(ap.parse_args())

我們有六個(gè)命令行參數(shù),它們?cè)试S我們?cè)谶\(yùn)行時(shí)從終端將信息傳遞給我們的人員計(jì)數(shù)器腳本:

  • --prototxt :Caffe 部署 prototxt 文件的路徑。
  • --model :Caffe 預(yù)訓(xùn)練 CNN 模型的路徑。
  • --input : 可選的輸入視頻文件路徑。如果未指定路徑,將使用您的網(wǎng)絡(luò)攝像頭。
  • --output :可選的輸出視頻路徑。如果未指定路徑,則不會(huì)錄制視頻。
  • --confidence :默認(rèn)值為 0.4 ,這是有助于過濾掉弱檢測(cè)的最小概率閾值。
  • --skip-frames :在跟蹤對(duì)象上再次運(yùn)行我們的 DNN 檢測(cè)器之前要跳過的幀數(shù)。請(qǐng)記住,對(duì)象檢測(cè)的計(jì)算成本很高,但它確實(shí)有助于我們的跟蹤器重新評(píng)估幀中的

對(duì)象。默認(rèn)情況下,我們?cè)谑褂?OpenCV DNN 模塊和我們的 CNN 單次檢測(cè)器模型檢測(cè)對(duì)象之間跳過 30 幀。

現(xiàn)在我們的腳本可以在運(yùn)行時(shí)動(dòng)態(tài)處理命令行參數(shù),讓我們準(zhǔn)備我們的 SSD:

# initialize the list of class labels MobileNet SSD was trained to detect
CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
    "bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
    "dog", "horse", "motorbike", "person", "pottedplant", "sheep",
    "sofa", "train", "tvmonitor"]
# load our serialized model from disk
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])

首先,我們將初始化 CLASSES——SSD 支持的類列表。我們只對(duì)“人”類感興趣,但您也可以計(jì)算其他移動(dòng)對(duì)象。

我們加載用于檢測(cè)對(duì)象的預(yù)訓(xùn)練 MobileNet SSD(但同樣,我們只對(duì)檢測(cè)和跟蹤人感興趣,而不是任何其他類)。

我們可以初始化我們的視頻流:

# if a video path was not supplied, grab a reference to the webcam
if not args.get("input", False):
    print("[INFO] starting video stream...")
    vs = VideoStream(src=0).start()
    time.sleep(2.0)
# otherwise, grab a reference to the video file
else:
    print("[INFO] opening video file...")
    vs = cv2.VideoCapture(args["input"])

首先,我們處理使用網(wǎng)絡(luò)攝像頭視頻流的情況。否則,我們將從視頻文件中捕獲幀。在開始循環(huán)幀之前,我們還有一些初始化要執(zhí)行:

# initialize the video writer (we'll instantiate later if need be)
writer = None
# initialize the frame dimensions (we'll set them as soon as we read
# the first frame from the video)
W = None
H = None
# instantiate our centroid tracker, then initialize a list to store
# each of our dlib correlation trackers, followed by a dictionary to
# map each unique object ID to a TrackableObject
ct = CentroidTracker(maxDisappeared=40, maxDistance=50)
trackers = []
trackableObjects = {}
# initialize the total number of frames processed thus far, along
# with the total number of objects that have moved either up or down
totalFrames = 0
totalDown = 0
totalUp = 0
# start the frames per second throughput estimator
fps = FPS().start()

其余的初始化包括:

  • writer:我們的視頻寫入器。如果我們正在寫入視頻,我們稍后會(huì)實(shí)例化這個(gè)對(duì)象。
  • W 和 H:我們的幀尺寸。我們需要將這些插入到 cv2.VideoWriter 中。
  • ct:我們的 CentroidTracker。
  • trackers :存儲(chǔ) dlib 相關(guān)跟蹤器的列表。
  • trackableObjects :將 objectID 映射到 TrackableObject 的字典。
  • totalFrames :處理的幀總數(shù)。
  • totalDown 和 totalUp :向下或向上移動(dòng)的對(duì)象/人的總數(shù)。
  • fps :我們用于基準(zhǔn)測(cè)試的每秒幀數(shù)估計(jì)器。

現(xiàn)在我們所有的初始化都處理好了,讓我們循環(huán)傳入的幀:

# loop over frames from the video stream
while True:
    # grab the next frame and handle if we are reading from either
    # VideoCapture or VideoStream
    frame = vs.read()
    frame = frame[1] if args.get("input", False) else frame
    # if we are viewing a video and we did not grab a frame then we
    # have reached the end of the video
    if args["input"] is not None and frame is None:
        break
    # resize the frame to have a maximum width of 500 pixels (the
    # less data we have, the faster we can process it), then convert
    # the frame from BGR to RGB for dlib
    frame = imutils.resize(frame, width=500)
    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    # if the frame dimensions are empty, set them
    if W is None or H is None:
        (H, W) = frame.shape[:2]
    # if we are supposed to be writing a video to disk, initialize
    # the writer
    if args["output"] is not None and writer is None:
        fourcc = cv2.VideoWriter_fourcc(*"MJPG")
        writer = cv2.VideoWriter(args["output"], fourcc, 30,
            (W, H), True)

我們開始循環(huán)。在循環(huán)的頂部,我們抓取下一幀。如果我們已經(jīng)到達(dá)視頻的結(jié)尾,我們將跳出循環(huán)。

幀進(jìn)行預(yù)處理。這包括調(diào)整大小和交換顏色通道,因?yàn)?dlib 需要 rgb 圖像。我們?yōu)橐曨l編寫器獲取幀的尺寸。 如果通過命令行參數(shù)提供了輸出路徑,我們將從那里實(shí)例化視頻編寫器。

現(xiàn)在讓我們使用 SSD檢測(cè)人:

    # initialize the current status along with our list of bounding
    # box rectangles returned by either (1) our object detector or
    # (2) the correlation trackers
    status = "Waiting"
    rects = []
    # check to see if we should run a more computationally expensive
    # object detection method to aid our tracker
    if totalFrames % args["skip_frames"] == 0:
        # set the status and initialize our new set of object trackers
        status = "Detecting"
        trackers = []
        # convert the frame to a blob and pass the blob through the
        # network and obtain the detections
        blob = cv2.dnn.blobFromImage(frame, 0.007843, (W, H), 127.5)
        net.setInput(blob)
        detections = net.forward()

我們將狀態(tài)初始化為Waiting??赡艿臓顟B(tài)包括:

  • Waiting:在這種狀態(tài)下,我們正在等待檢測(cè)和跟蹤人員。
  • Detecting:我們正在使用 MobileNet SSD 檢測(cè)人員。
  • Tracking:人們?cè)趲斜桓櫍覀冋谟?jì)算 totalUp 和 totalDown 。

我們的 rects 列表將通過檢測(cè)或跟蹤來填充。我們繼續(xù)初始化rects 。

重要的是要了解深度學(xué)習(xí)對(duì)象檢測(cè)器的計(jì)算成本非常高,尤其是當(dāng)您在 CPU 上運(yùn)行它們時(shí)。

為了避免在每一幀上運(yùn)行我們的目標(biāo)檢測(cè)器,并加快我們的跟蹤管道,我們將跳過 N 幀(由命令行參數(shù)設(shè)置 --skip-frames ,其中 30 是默認(rèn)值)。只有每 N 幀,我們才會(huì)使用 SSD 進(jìn)行對(duì)象檢測(cè)。否則,我們將只是跟蹤中間的移動(dòng)對(duì)象。

使用模運(yùn)算符,我們確保每 N 幀執(zhí)行一次 if 語句中的代碼。 進(jìn)入if語句后,我們會(huì)將狀態(tài)更新為Detecting。 然后我們初始化新的跟蹤器列表。

接下來,我們將通過對(duì)象檢測(cè)進(jìn)行推理。我們首先從圖像中創(chuàng)建一個(gè) blob,然后將該 blob 通過網(wǎng)絡(luò)傳遞以獲得檢測(cè)。 現(xiàn)在我們將遍歷每個(gè)檢測(cè),希望找到屬于person類的對(duì)象:

        # loop over the detections
        for i in np.arange(0, detections.shape[2]):
            # extract the confidence (i.e., probability) associated
            # with the prediction
            confidence = detections[0, 0, i, 2]
            # filter out weak detections by requiring a minimum
            # confidence
            if confidence > args["confidence"]:
                # extract the index of the class label from the
                # detections list
                idx = int(detections[0, 0, i, 1])
                # if the class label is not a person, ignore it
                if CLASSES[idx] != "person":
                    continue

循環(huán)檢測(cè),我們繼續(xù)獲取置信度并過濾掉那些不屬于人類的結(jié)果。

現(xiàn)在我們可以為每個(gè)人計(jì)算一個(gè)邊界框并開始相關(guān)性跟蹤:

                # compute the (x, y)-coordinates of the bounding box
                # for the object
                box = detections[0, 0, i, 3:7] * np.array([W, H, W, H])
                (startX, startY, endX, endY) = box.astype("int")
                # construct a dlib rectangle object from the bounding
                # box coordinates and then start the dlib correlation
                # tracker
                tracker = dlib.correlation_tracker()
                rect = dlib.rectangle(startX, startY, endX, endY)
                tracker.start_track(rgb, rect)
                # add the tracker to our list of trackers so we can
                # utilize it during skip frames
                trackers.append(tracker)

計(jì)算我們的box。 然后實(shí)例化我們的 dlib 相關(guān)跟蹤器,然后將對(duì)象的邊界框坐標(biāo)傳遞給 dlib.rectangle,將結(jié)果存儲(chǔ)為 rect。 隨后,我們開始跟蹤,并將跟蹤器附加到跟蹤器列表中。 這是我們每 N 個(gè)跳幀執(zhí)行的所有操作的封裝! 讓我們處理在 else 塊中進(jìn)行跟蹤的典型操作:

    # otherwise, we should utilize our object *trackers* rather than
    # object *detectors* to obtain a higher frame processing throughput
    else:
        # loop over the trackers
        for tracker in trackers:
            # set the status of our system to be 'tracking' rather
            # than 'waiting' or 'detecting'
            status = "Tracking"
            # update the tracker and grab the updated position
            tracker.update(rgb)
            pos = tracker.get_position()
            # unpack the position object
            startX = int(pos.left())
            startY = int(pos.top())
            endX = int(pos.right())
            endY = int(pos.bottom())
            # add the bounding box coordinates to the rectangles list
            rects.append((startX, startY, endX, endY))

大多數(shù)時(shí)候,并沒有發(fā)生在跳幀倍數(shù)上。在此期間,我們將利用跟蹤器來跟蹤對(duì)象,而不是應(yīng)用檢測(cè)。 我們開始遍歷可用跟蹤器。 我們繼續(xù)將狀態(tài)更新為Tracking并獲取對(duì)象位置。 我們提取位置坐標(biāo),然后在我們的 rects 列表中填充信息。 現(xiàn)在讓我們畫一條水平可視化線(人們必須穿過它才能被跟蹤)并使用質(zhì)心跟蹤器來更新我們的對(duì)象質(zhì)心:

    # draw a horizontal line in the center of the frame -- once an
    # object crosses this line we will determine whether they were
    # moving 'up' or 'down'
    cv2.line(frame, (0, H // 2), (W, H // 2), (0, 255, 255), 2)
    # use the centroid tracker to associate the (1) old object
    # centroids with (2) the newly computed object centroids
    objects = ct.update(rects)

我們畫一條水平線,我們將用它來可視化人們“越過”——一旦人們?cè)竭^這條線,我們將增加各自的計(jì)數(shù)器 然后,我們利用 CentroidTracker 實(shí)例化來接受 rects 列表,無論它們是通過對(duì)象檢測(cè)還是對(duì)象跟蹤生成的。我們的質(zhì)心跟蹤器會(huì)將對(duì)象 ID 與對(duì)象位置相關(guān)聯(lián)。 在下一個(gè)代碼塊中,我們將回顧一個(gè)人在幀中向上或向下移動(dòng)的邏輯:

    # loop over the tracked objects
    for (objectID, centroid) in objects.items():
        # check to see if a trackable object exists for the current
        # object ID
        to = trackableObjects.get(objectID, None)
        # if there is no existing trackable object, create one
        if to is None:
            to = TrackableObject(objectID, centroid)
        # otherwise, there is a trackable object so we can utilize it
        # to determine direction
        else:
            # the difference between the y-coordinate of the *current*
            # centroid and the mean of *previous* centroids will tell
            # us in which direction the object is moving (negative for
            # 'up' and positive for 'down')
            y = [c[1] for c in to.centroids]
            direction = centroid[1] - np.mean(y)
            to.centroids.append(centroid)
            # check to see if the object has been counted or not
            if not to.counted:
                # if the direction is negative (indicating the object
                # is moving up) AND the centroid is above the center
                # line, count the object
                if direction < 0 and centroid[1] < H // 2:
                    totalUp += 1
                    to.counted = True
                # if the direction is positive (indicating the object
                # is moving down) AND the centroid is below the
                # center line, count the object
                elif direction > 0 and centroid[1] > H // 2:
                    totalDown += 1
                    to.counted = True
        # store the trackable object in our dictionary
        trackableObjects[objectID] = to

我們首先遍歷更新后的對(duì)象id的邊界框坐標(biāo)。我們嘗試為當(dāng)前的objectID獲取TrackableObject。如果objectID的TrackableObject不存在,我們就創(chuàng)建一個(gè)。否則,已經(jīng)存在一個(gè) TrackableObject ,所以我們需要弄清楚對(duì)象(人)是向上還是向下移動(dòng)。

為此,我們獲取給定對(duì)象之前所有質(zhì)心位置的y坐標(biāo)值。然后,通過取當(dāng)前質(zhì)心位置與之前所有質(zhì)心位置的平均值之間的差來計(jì)算方向。

我們?nèi)【档脑蚴菫榱舜_保我們的方向跟蹤更穩(wěn)定。如果我們只存儲(chǔ)這個(gè)人之前的質(zhì)心位置,我們就有可能出現(xiàn)錯(cuò)誤的方向計(jì)數(shù)。記住,目標(biāo)檢測(cè)和目標(biāo)跟蹤算法不是“魔術(shù)”——有時(shí)它們會(huì)預(yù)測(cè)出可能稍微偏離你預(yù)期的邊界盒;因此,通過取均值,我們可以讓我們的人計(jì)算得更準(zhǔn)確。

如果TrackableObject還沒有被計(jì)數(shù),我們需要確定它是否已經(jīng)準(zhǔn)備好被計(jì)數(shù),通過:

1.檢查direction是否為負(fù)(表示對(duì)象向上移動(dòng))并且質(zhì)心在中心線上方。在這種情況下,我們?cè)黾?totalUp。

2.或者檢查direction是否為正(表示物體正在向下移動(dòng))且質(zhì)心在中心線以下。如果這是真的,我們?cè)黾觮otalDown。

最后,我們將TrackableObject存儲(chǔ)在trackableObjects字典中,這樣我們就可以在捕獲下一幀時(shí)獲取并更新它。

接下來的三個(gè)代碼塊處理:

  • 顯示(繪圖并向幀寫入文本)
  • 將幀寫入磁盤上的視頻文件(如果存在--output命令行參數(shù))
  • 捕獲按鍵
  • 清理

首先,我們將在框架上繪制一些信息以進(jìn)行可視化:

        # draw both the ID of the object and the centroid of the
        # object on the output frame
        text = "ID {}".format(objectID)
        cv2.putText(frame, text, (centroid[0] - 10, centroid[1] - 10),
            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
        cv2.circle(frame, (centroid[0], centroid[1]), 4, (0, 255, 0), -1)
    # construct a tuple of information we will be displaying on the
    # frame
    info = [
        ("Up", totalUp),
        ("Down", totalDown),
        ("Status", status),
    ]
    # loop over the info tuples and draw them on our frame
    for (i, (k, v)) in enumerate(info):
        text = "{}: {}".format(k, v)
        cv2.putText(frame, text, (10, H - ((i * 20) + 20)),
            cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)

在這里,我們?cè)趲细采w以下數(shù)據(jù):

  • ObjectID :每個(gè)對(duì)象的ID。
  • centroid :對(duì)象的中心將由一個(gè)點(diǎn)表示,該點(diǎn)是通過填充一個(gè)圓圈而創(chuàng)建的。
  • info : 包括 totalUp 、 totalDown 和 status

然后我們將把幀寫入視頻文件(如果需要的話)并處理按鍵:

    # check to see if we should write the frame to disk
    if writer is not None:
        writer.write(frame)
    # show the output frame
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1) & 0xFF
    # if the `q` key was pressed, break from the loop
    if key == ord("q"):
        break
    # increment the total number of frames processed thus far and
    # then update the FPS counter
    totalFrames += 1
    fps.update()

在這個(gè)代碼塊中我們:

  • 如果需要,將幀寫入輸出視頻文件
  • 顯示幀并處理按鍵。如果q被按下,我們將跳出幀處理循環(huán)。
  • 更新我們的fps計(jì)數(shù)器

現(xiàn)在是時(shí)候清理了:

# stop the timer and display FPS information
fps.stop()
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
# check to see if we need to release the video writer pointer
if writer is not None:
    writer.release()
# if we are not using a video file, stop the camera video stream
if not args.get("input", False):
    vs.stop()
# otherwise, release the video file pointer
else:
    vs.release()
# close any open windows
cv2.destroyAllWindows()

為了完成腳本,我們向終端顯示 FPS 信息,釋放所有指針,并關(guān)閉所有打開的窗口。

7.完整代碼

people_counter.py

from pyimagesearch.centroidtracker import CentroidTracker
from pyimagesearch.trackableobject import TrackableObject
from imutils.video import VideoStream
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import time
import dlib
import cv2


# 構(gòu)造參數(shù)解析并解析參數(shù)
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", required=True,
	help="path to Caffe 'deploy' prototxt file")
ap.add_argument("-m", "--model", required=True,
	help="path to Caffe pre-trained model")
ap.add_argument("-i", "--input", type=str,
	help="path to optional input video file")
ap.add_argument("-o", "--output", type=str,
	help="path to optional output video file")
ap.add_argument("-c", "--confidence", type=float, default=0.4,
	help="minimum probability to filter weak detections")
ap.add_argument("-s", "--skip-frames", type=int, default=30,
	help="# of skip frames between detections")
args = vars(ap.parse_args())

# 初始化類標(biāo)簽列表
CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
	"bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
	"dog", "horse", "motorbike", "person", "pottedplant", "sheep",
	"sofa", "train", "tvmonitor"]
# 從磁盤加載我們的序列化模型
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])

# 如果未提供視頻路徑,請(qǐng)獲取網(wǎng)絡(luò)攝像頭的引用
if not args.get("input", False):
	print("[INFO] starting video stream...")
	vs = VideoStream(src=0).start()
	time.sleep(2.0)
# 否則,獲取對(duì)視頻文件的引用
else:
	print("[INFO] opening video file...")
	vs = cv2.VideoCapture(args["input"])

# 初始化視頻寫入器(如果需要,我們稍后將進(jìn)行實(shí)例化)
writer = None
# 初始化幀尺寸(我們將在從視頻中讀取第一幀后立即設(shè)置它們)
W = None
H = None
# 實(shí)例化我們的質(zhì)心跟蹤器,然后初始化一個(gè)列表來存儲(chǔ)每個(gè)dlib相關(guān)跟蹤器,
# 然后是一個(gè)字典來將每個(gè)唯一的對(duì)象ID映射到TrackableObject
ct = CentroidTracker(maxDisappeared=40, maxDistance=50)
trackers = []
trackableObjects = {}
# 初始化到目前為止處理的幀總數(shù),以及向上或向下移動(dòng)的對(duì)象總數(shù)
totalFrames = 0
totalDown = 0
totalUp = 0
# 啟動(dòng)FPS評(píng)估器
fps = FPS().start()

# 循環(huán)視頻流中的幀
while True:
	# 如果我們正在從 VideoCapture 或 VideoStream 讀取數(shù)據(jù),則抓取下一幀并處理
	frame = vs.read()
	frame = frame[1] if args.get("input", False) else frame
	# 如果我們正在觀看視頻并且我們沒有抓取幀,那么我們已經(jīng)到了視頻的結(jié)尾
	if args["input"] is not None and frame is None:
		break
	# 調(diào)整幀的最大寬度為 500 像素(我們擁有的數(shù)據(jù)越少,我們處理它的速度就越快),
	# 然后將幀從 BGR 轉(zhuǎn)換為 RGB 用于 dlib
	frame = imutils.resize(frame, width=500)
	rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
	# 如果幀尺寸為空,則設(shè)置它們
	if W is None or H is None:
		(H, W) = frame.shape[:2]
	# 如果我們應(yīng)該將視頻寫入磁盤,請(qǐng)初始化寫入器
	if args["output"] is not None and writer is None:
		fourcc = cv2.VideoWriter_fourcc(*"MJPG")
		writer = cv2.VideoWriter(args["output"], fourcc, 30,
			(W, H), True)


	# 初始化當(dāng)前狀態(tài)以及由(1)我們的對(duì)象檢測(cè)器或(2)相關(guān)跟蹤器返回的邊界框矩形列表
	status = "Waiting"
	rects = []
	# 檢查我們是否應(yīng)該運(yùn)行計(jì)算量更大的目標(biāo)檢測(cè)方法來幫助我們的跟蹤器
	if totalFrames % args["skip_frames"] == 0:
		# 設(shè)置狀態(tài)并初始化我們的新對(duì)象跟蹤器集
		status = "Detecting"
		trackers = []
		# 將幀轉(zhuǎn)換為 blob 并通過網(wǎng)絡(luò)傳遞 blob 并獲得檢測(cè)結(jié)果
		blob = cv2.dnn.blobFromImage(frame, 0.007843, (W, H), 127.5)
		net.setInput(blob)
		detections = net.forward()

		# 循環(huán)檢測(cè)結(jié)果
		for i in np.arange(0, detections.shape[2]):
			# 提取與預(yù)測(cè)相關(guān)的置信度(即概率)
			confidence = detections[0, 0, i, 2]
			# 通過要求最小置信度過濾掉弱檢測(cè)
			if confidence > args["confidence"]:
				# 從檢測(cè)列表中提取類標(biāo)簽的索引
				idx = int(detections[0, 0, i, 1])
				# 如果類標(biāo)簽不是人,則忽略它
				if CLASSES[idx] != "person":
					continue
				# 計(jì)算對(duì)象邊界框的 (x, y) 坐標(biāo)
				box = detections[0, 0, i, 3:7] * np.array([W, H, W, H])
				(startX, startY, endX, endY) = box.astype("int")
				# 利用邊界框坐標(biāo)構(gòu)造一個(gè) dlib 矩形對(duì)象,然后啟動(dòng) dlib 相關(guān)跟蹤器
				tracker = dlib.correlation_tracker()
				rect = dlib.rectangle(startX, startY, endX, endY)
				tracker.start_track(rgb, rect)
				# 將跟蹤器添加到我們的跟蹤器列表中,以便我們可以在跳幀期間使用它
				trackers.append(tracker)

	# 否則,我們應(yīng)該利用目標(biāo)跟蹤器而不是目標(biāo)檢測(cè)器來獲得更高的FPS
	else:
		# 遍歷跟蹤器
		for tracker in trackers:
			# 將系統(tǒng)的狀態(tài)設(shè)置為“跟蹤”而不是“等待”或“檢測(cè)”
			status = "Tracking"
			# 更新跟蹤器并獲取更新的位置
			tracker.update(rgb)
			pos = tracker.get_position()
			# 解包位置對(duì)象
			startX = int(pos.left())
			startY = int(pos.top())
			endX = int(pos.right())
			endY = int(pos.bottom())
			# 將邊界框坐標(biāo)添加到矩形列表
			rects.append((startX, startY, endX, endY))

	# 在幀中心畫一條水平線——一旦一個(gè)物體穿過這條線,我們將確定他們是在“向上”還是“向下”移動(dòng)。
	cv2.line(frame, (0, H // 2), (W, H // 2), (0, 255, 255), 2)
	# 使用質(zhì)心跟蹤器將 (1) 舊對(duì)象質(zhì)心與 (2) 新計(jì)算的對(duì)象質(zhì)心相關(guān)聯(lián)
	objects = ct.update(rects)

	# 循環(huán)遍歷被跟蹤的對(duì)象
	for (objectID, centroid) in objects.items():
		# 檢查當(dāng)前對(duì)象 ID 是否存在可跟蹤對(duì)象
		to = trackableObjects.get(objectID, None)
		# 如果沒有現(xiàn)有的可跟蹤對(duì)象,則創(chuàng)建一個(gè)
		if to is None:
			to = TrackableObject(objectID, centroid)
		# 否則,有一個(gè)可追蹤的物體,所以我們可以利用它來確定方向
		else:
			# *當(dāng)前*質(zhì)心的 y 坐標(biāo)與 *previous* 質(zhì)心的平均值之間的差異
			# 將告訴我們物體在哪個(gè)方向移動(dòng)(“向上”為負(fù),“向下”為正)
			y = [c[1] for c in to.centroids]
			direction = centroid[1] - np.mean(y)
			to.centroids.append(centroid)
			# 檢查對(duì)象是否已被計(jì)數(shù)
			if not to.counted:
				# 如果方向?yàn)樨?fù)(表示物體向上移動(dòng))且質(zhì)心在中線以上,則計(jì)算物體
				if direction < 0 and centroid[1] < H // 2:
					totalUp += 1
					to.counted = True
				# 如果方向?yàn)檎ū硎疚矬w正在向下移動(dòng))并且質(zhì)心低于中心線,則計(jì)算物體
				elif direction > 0 and centroid[1] > H // 2:
					totalDown += 1
					to.counted = True
		# 將可跟蹤對(duì)象存儲(chǔ)在我們的字典中
		trackableObjects[objectID] = to

		# 在輸出幀上繪制對(duì)象的 ID 和對(duì)象的質(zhì)心
		text = "ID {}".format(objectID)
		cv2.putText(frame, text, (centroid[0] - 10, centroid[1] - 10),
			cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
		cv2.circle(frame, (centroid[0], centroid[1]), 4, (0, 255, 0), -1)
	# 構(gòu)建我們將在幀上顯示的信息元組
	info = [
		("Up", totalUp),
		("Down", totalDown),
		("Status", status),
	]
	# 遍歷信息元組并將它們繪制在我們的幀上
	for (i, (k, v)) in enumerate(info):
		text = "{}: {}".format(k, v)
		cv2.putText(frame, text, (10, H - ((i * 20) + 20)),
			cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)



	# 檢查我們是否應(yīng)該將幀寫入磁盤
	if writer is not None:
		writer.write(frame)
	# 顯示輸出幀
	cv2.imshow("Frame", frame)
	key = cv2.waitKey(1) & 0xFF
	# 如果' q '鍵被按下,中斷循環(huán)
	if key == ord("q"):
		break
	# 增加到目前為止處理的幀總數(shù),然后更新 FPS 計(jì)數(shù)器
	totalFrames += 1
	fps.update()

# 停止定時(shí)器,顯示FPS信息
fps.stop()
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
# 檢查我們是否需要釋放視頻寫入器指針
if writer is not None:
	writer.release()
# 如果我們不使用視頻文件,請(qǐng)停止攝像頭視頻流
if not args.get("input", False):
	vs.stop()
# 否則,釋放視頻文件指針
else:
	vs.release()
# 關(guān)閉所有打開的窗口
cv2.destroyAllWindows()

centroidtracker.py

(1)質(zhì)心跟蹤器是最可靠的跟蹤器之一。

(2)為了簡(jiǎn)單起見,質(zhì)心跟蹤器計(jì)算包圍框的質(zhì)心。

(3)也就是說,邊界框是圖像中對(duì)象的(x, y)坐標(biāo)。

(4)一旦我們的SSD獲得了坐標(biāo),跟蹤器就會(huì)計(jì)算包圍框的質(zhì)心(中心)。換句話說,就是物體的中心。

(5)然后為每一個(gè)被檢測(cè)到的特定對(duì)象分配一個(gè)唯一的ID,用于跟蹤幀序列。

from scipy.spatial import distance as dist
from collections import OrderedDict
import numpy as np

class CentroidTracker:
	def __init__(self, maxDisappeared=50, maxDistance=50):
		# 初始化下一個(gè)唯一的對(duì)象ID,并使用兩個(gè)有序字典來跟蹤給定對(duì)象ID到其質(zhì)心的映射,
		# 以及它被標(biāo)記為“消失”的連續(xù)幀數(shù)
		self.nextObjectID = 0
		self.objects = OrderedDict()
		self.disappeared = OrderedDict()

		# 存儲(chǔ)一個(gè)給定對(duì)象允許被標(biāo)記為“消失”的最大連續(xù)幀數(shù),直到我們需要從跟蹤中注銷該對(duì)象
		self.maxDisappeared = maxDisappeared

		# 存儲(chǔ)質(zhì)心之間的最大距離以關(guān)聯(lián)對(duì)象——如果距離大于這個(gè)最大距離,我們開始將對(duì)象標(biāo)記為“消失”
		self.maxDistance = maxDistance

	def register(self, centroid):
		# 注冊(cè)對(duì)象時(shí),我們使用下一個(gè)可用的對(duì)象 ID 來存儲(chǔ)質(zhì)心
		self.objects[self.nextObjectID] = centroid
		self.disappeared[self.nextObjectID] = 0
		self.nextObjectID += 1

	def deregister(self, objectID):
		# 要注銷對(duì)象 ID,我們從各自的字典中刪除對(duì)象 ID
		del self.objects[objectID]
		del self.disappeared[objectID]

	def update(self, rects):
		# 檢查輸入邊界框矩形列表是否為空
		if len(rects) == 0:
			# 循環(huán)遍歷任何現(xiàn)有的跟蹤對(duì)象并將它們標(biāo)記為消失
			for objectID in list(self.disappeared.keys()):
				self.disappeared[objectID] += 1

				# 如果我們已經(jīng)達(dá)到給定對(duì)象被標(biāo)記為消失的最大連續(xù)幀數(shù),則取消注冊(cè)它
				if self.disappeared[objectID] > self.maxDisappeared:
					self.deregister(objectID)

			# 早點(diǎn)返回,因?yàn)闆]有要更新的質(zhì)心或跟蹤信息
			return self.objects

		# 初始化當(dāng)前幀的輸入質(zhì)心數(shù)組
		inputCentroids = np.zeros((len(rects), 2), dtype="int")

		# 循環(huán)邊界框矩形
		for (i, (startX, startY, endX, endY)) in enumerate(rects):
			# 使用邊界框坐標(biāo)推導(dǎo)出質(zhì)心
			cX = int((startX + endX) / 2.0)
			cY = int((startY + endY) / 2.0)
			inputCentroids[i] = (cX, cY)

		# 如果我們當(dāng)前沒有跟蹤任何對(duì)象,則獲取輸入質(zhì)心并注冊(cè)它們中的每一個(gè)
		if len(self.objects) == 0:
			for i in range(0, len(inputCentroids)):
				self.register(inputCentroids[i])

		# 否則,我們目前正在跟蹤對(duì)象,因此我們需要嘗試將輸入質(zhì)心與現(xiàn)有對(duì)象質(zhì)心匹配
		else:
			# 獲取一組對(duì)象 ID 和相應(yīng)的質(zhì)心
			objectIDs = list(self.objects.keys())
			objectCentroids = list(self.objects.values())

			# 分別計(jì)算每對(duì)對(duì)象質(zhì)心和輸入質(zhì)心之間的距離——我們的目標(biāo)是將輸入質(zhì)心與現(xiàn)有對(duì)象質(zhì)心匹配
			D = dist.cdist(np.array(objectCentroids), inputCentroids)

			# 為了執(zhí)行這種匹配,我們必須 (1) 找到每一行中的最小值,
			# 然后 (2) 根據(jù)它們的最小值對(duì)行索引進(jìn)行排序,以便具有最小值的行位于索引列表的 *front*
			rows = D.min(axis=1).argsort()

			# 接下來,我們對(duì)列執(zhí)行類似的處理,方法是在每個(gè)列中找到最小的值,
			# 然后使用之前計(jì)算的行索引列表進(jìn)行排序
			cols = D.argmin(axis=1)[rows]

			# 為了確定我們是否需要更新、注冊(cè)或取消注冊(cè)一個(gè)對(duì)象,我們需要跟蹤我們已經(jīng)檢查過的行和列索引
			usedRows = set()
			usedCols = set()

			# 循環(huán)遍歷(行,列)索引元組的組合
			for (row, col) in zip(rows, cols):
				# 如果我們之前已經(jīng)檢查過行值或列值,請(qǐng)忽略它
				if row in usedRows or col in usedCols:
					continue

				# 如果質(zhì)心之間的距離大于最大距離,則不要將兩個(gè)質(zhì)心關(guān)聯(lián)到同一個(gè)對(duì)象
				if D[row, col] > self.maxDistance:
					continue

				# 否則,獲取當(dāng)前行的對(duì)象 ID,設(shè)置其新質(zhì)心,并重置消失的計(jì)數(shù)器
				objectID = objectIDs[row]
				self.objects[objectID] = inputCentroids[col]
				self.disappeared[objectID] = 0

				# 表明我們已經(jīng)分別檢查了每個(gè)行和列索引
				usedRows.add(row)
				usedCols.add(col)

			# 計(jì)算我們尚未檢查的行和列索引
			unusedRows = set(range(0, D.shape[0])).difference(usedRows)
			unusedCols = set(range(0, D.shape[1])).difference(usedCols)

			# 如果對(duì)象質(zhì)心的數(shù)量等于或大于輸入質(zhì)心的數(shù)量,
			# 我們需要檢查并查看其中一些對(duì)象是否可能已經(jīng)消失
			if D.shape[0] >= D.shape[1]:
				# 循環(huán)未使用的行索引
				for row in unusedRows:
					# 獲取相應(yīng)行索引的對(duì)象 ID 并增加消失的計(jì)數(shù)器
					objectID = objectIDs[row]
					self.disappeared[objectID] += 1

					# 檢查對(duì)象的連續(xù)幀數(shù)是否被標(biāo)記為“消失”,以注銷該對(duì)象
					if self.disappeared[objectID] > self.maxDisappeared:
						self.deregister(objectID)

			# 否則,如果輸入質(zhì)心的數(shù)量大于現(xiàn)有對(duì)象質(zhì)心的數(shù)量,我們需要將每個(gè)新的輸入質(zhì)心注冊(cè)為可跟蹤對(duì)象
			else:
				for col in unusedCols:
					self.register(inputCentroids[col])

		# 返回可跟蹤對(duì)象的集合
		return self.objects

trackableobject.py

class TrackableObject:
	def __init__(self, objectID, centroid):
		# 存儲(chǔ)對(duì)象 ID,然后使用當(dāng)前質(zhì)心初始化質(zhì)心列表
		self.objectID = objectID
		self.centroids = [centroid]

		# 初始化一個(gè)布爾值,用于指示對(duì)象是否已被計(jì)數(shù)
		self.counted = False

8.運(yùn)行結(jié)果

打開終端,執(zhí)行以下命令:

python people_counter.py --prototxt mobilenet_ssd/MobileNetSSD_deploy.prototxt \
    --model mobilenet_ssd/MobileNetSSD_deploy.caffemodel \
    --input videos/example_01.mp4 --output output/output_01.avi

我們的人員計(jì)數(shù)正在計(jì)算以下人數(shù):

  • 正進(jìn)入百貨商店(下)
  • 離開的人數(shù)(上)

在第一個(gè)視頻的最后,你會(huì)看到有7個(gè)人進(jìn)入,3個(gè)人離開。

此外,檢查終端輸出,你會(huì)發(fā)現(xiàn)我們的人計(jì)數(shù)器能夠?qū)崟r(shí)運(yùn)行,達(dá)到34幀每秒。盡管我們正在使用深度學(xué)習(xí)對(duì)象檢測(cè)器來更準(zhǔn)確地檢測(cè)人。

我們的 34 FPS 幀率是通過我們的兩個(gè)階段過程實(shí)現(xiàn)的: 每 30 幀檢測(cè)一次人 然后在其間的所有幀中應(yīng)用更快、更有效的對(duì)象跟蹤算法。

9.改進(jìn)我們的人員計(jì)數(shù)器應(yīng)用程序

為了構(gòu)建我們的 OpenCV 人員計(jì)數(shù)器,我們使用了 dlib 的相關(guān)性跟蹤器。此方法易于使用,并且只需要很少的代碼。

然而,我們的實(shí)現(xiàn)有點(diǎn)低效——為了跟蹤多個(gè)對(duì)象,我們需要?jiǎng)?chuàng)建關(guān)聯(lián)跟蹤器對(duì)象的多個(gè)實(shí)例。然后當(dāng)我們需要在后續(xù)幀中計(jì)算對(duì)象的位置時(shí),我們需要遍歷所有 N 個(gè)對(duì)象跟蹤器并獲取更新的位置。

所有這些計(jì)算都將發(fā)生在我們腳本的主執(zhí)行線程中,從而降低了我們的 FPS 速率。

因此,提高性能的一種簡(jiǎn)單方法是使用dlib的多對(duì)象跟蹤器,以使我們的 FPS 速率提高 45%! 注意:OpenCV 也實(shí)現(xiàn)了多對(duì)象跟蹤,但不是多進(jìn)程(至少在撰寫本文時(shí))。 OpenCV 的多對(duì)象方法當(dāng)然更容易使用,但如果沒有多處理能力,在這種情況下它并沒有多大幫助。

最后,為了獲得更高的跟蹤精度(但在沒有快速 GPU 的情況下會(huì)犧牲速度),您可以研究基于深度學(xué)習(xí)的對(duì)象跟蹤器,例如 Deep SORT。

BONUS

前幾天在github上看見一個(gè)改進(jìn)版:

主要目標(biāo)是將項(xiàng)目用作業(yè)務(wù)視角,隨時(shí)可以擴(kuò)展。

用例:實(shí)時(shí)統(tǒng)計(jì)商店/大樓/購(gòu)物中心等的人數(shù)。

如果人數(shù)超過上限就會(huì)向工作人員發(fā)出警報(bào)。

自動(dòng)化特性并優(yōu)化實(shí)時(shí)流以獲得更好的性能(使用線程)。

作為一項(xiàng)措施,以進(jìn)行足跡分析,并在某種程度上應(yīng)對(duì)COVID-19。

到此這篇關(guān)于基于OpenCV目標(biāo)跟蹤實(shí)現(xiàn)人員計(jì)數(shù)器的文章就介紹到這了,更多相關(guān)OpenCV人員計(jì)數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論