利用OpenCV實(shí)現(xiàn)質(zhì)心跟蹤算法
目標(biāo)跟蹤的過程:
1、獲取對(duì)象檢測的初始集
2、為每個(gè)初始檢測創(chuàng)建唯一的ID
3、然后在視頻幀中跟蹤每個(gè)對(duì)象的移動(dòng),保持唯一ID的分配
本文使用OpenCV實(shí)現(xiàn)質(zhì)心跟蹤,這是一種易于理解但高效的跟蹤算法。
質(zhì)心跟蹤算法步驟
步驟1:接受邊界框坐標(biāo)并計(jì)算質(zhì)心

質(zhì)心跟蹤算法假設(shè)我們?yōu)槊恳粠械拿總€(gè)檢測到的對(duì)象傳入一組邊界框 (x, y) 坐標(biāo)。
這些邊界框可以由任何類型的對(duì)象檢測器(顏色閾值 + 輪廓提取、Haar 級(jí)聯(lián)、HOG + 線性 SVM、SSD、Faster R-CNN 等)生成,前提是它們是針對(duì) 該視頻。
通過邊界框坐標(biāo),就可以計(jì)算“質(zhì)心”(邊界框的中心 (x, y) 坐標(biāo))。 如上圖演示了通過一組邊界框坐標(biāo)計(jì)算出質(zhì)心。
給初始的邊界框分配ID。
步驟2:計(jì)算新邊界框和現(xiàn)有對(duì)象之間的歐幾里得距離

對(duì)于視頻流中的每個(gè)后續(xù)幀,首先執(zhí)行步驟1; 然后,我們首先需要確定是否可以將新的對(duì)象質(zhì)心(黃色)與舊的對(duì)象質(zhì)心(紫色)相關(guān)聯(lián),而不是為每個(gè)檢測到的對(duì)象分配一個(gè)新的唯一 ID(這會(huì)破壞對(duì)象跟蹤的目的)。 為了完成這個(gè)過程,我們計(jì)算每對(duì)現(xiàn)有對(duì)象質(zhì)心和輸入對(duì)象質(zhì)心之間的歐幾里得距離(用綠色箭頭突出顯示)。
上圖可以看出,我們這次在圖像中檢測到了三個(gè)對(duì)象。 靠近的兩對(duì)是兩個(gè)現(xiàn)有對(duì)象。
然后我們計(jì)算每對(duì)原始質(zhì)心(黃色)和新質(zhì)心(紫色)之間的歐幾里得距離。 但是我們?nèi)绾问褂眠@些點(diǎn)之間的歐幾里得距離來實(shí)際匹配它們并關(guān)聯(lián)它們呢?
答案在步驟3。
步驟 3:更新現(xiàn)有對(duì)象的 (x, y) 坐標(biāo)

質(zhì)心跟蹤算法的主要假設(shè)是給定對(duì)象可能會(huì)在后續(xù)幀之間移動(dòng),但幀 F_t 和 F_{t + 1} 的質(zhì)心之間的距離將小于對(duì)象之間的所有其他距離。
因此,如果我們選擇將質(zhì)心與后續(xù)幀之間的最小距離相關(guān)聯(lián),我們可以構(gòu)建我們的對(duì)象跟蹤器。
在上圖中,可以看到質(zhì)心跟蹤器算法如何選擇關(guān)聯(lián)質(zhì)心以最小化它們各自的歐幾里得距離。
但是左下角的孤獨(dú)點(diǎn)呢?
它沒有與任何東西相關(guān)聯(lián)——我們?nèi)绾翁幚硭?/p>
步驟4:注冊新對(duì)象

如果輸入檢測比跟蹤的現(xiàn)有對(duì)象多,我們需要注冊新對(duì)象。 “注冊”僅僅意味著我們通過以下方式將新對(duì)象添加到我們的跟蹤對(duì)象列表中:
為其分配一個(gè)新的對(duì)象 ID
存儲(chǔ)該對(duì)象的邊界框坐標(biāo)的質(zhì)心
然后我們可以返回到步驟2,重復(fù)執(zhí)行。
上圖演示了使用最小歐幾里得距離關(guān)聯(lián)現(xiàn)有對(duì)象 ID,然后注冊新對(duì)象的過程。
步驟5:注銷舊對(duì)象
當(dāng)舊的對(duì)象超出范圍時(shí),注銷舊對(duì)象、
項(xiàng)目結(jié)構(gòu)

使用 OpenCV 實(shí)現(xiàn)質(zhì)心跟蹤
新建 centroidtracker.py,寫入代碼:
# import the necessary packages from scipy.spatial import distance as dist from collections import OrderedDict import numpy as np class CentroidTracker(): ? ? def __init__(self, maxDisappeared=50): ? ? ? ? self.nextObjectID = 0 ? ? ? ? self.objects = OrderedDict() ? ? ? ? self.disappeared = OrderedDict() ? ? ? ? # 存儲(chǔ)給定對(duì)象被允許標(biāo)記為“消失”的最大連續(xù)幀數(shù),直到我們需要從跟蹤中注銷該對(duì)象 ? ? ? ? self.maxDisappeared = maxDisappeared
導(dǎo)入了所需的包和模塊:distance 、 OrderedDict 和 numpy 。
定義類 CentroidTracker 。構(gòu)造函數(shù)接受一個(gè)參數(shù)maxDisappeared,即給定對(duì)象必須丟失/消失的最大連續(xù)幀數(shù),如果超過這個(gè)數(shù)值就將這個(gè)對(duì)象刪除。
四個(gè)類變量:
nextObjectID :用于為每個(gè)對(duì)象分配唯一 ID 的計(jì)數(shù)器。如果一個(gè)對(duì)象離開幀并且沒有返回 maxDisappeared 幀,則將分配一個(gè)新的(下一個(gè))對(duì)象 ID。
objects :使用對(duì)象 ID 作為鍵和質(zhì)心 (x, y) 坐標(biāo)作為值的字典。
disappeared:保持特定對(duì)象 ID(鍵)已被標(biāo)記為“丟失”的連續(xù)幀數(shù)(值)。
maxDisappeared :在我們?nèi)∠詫?duì)象之前,允許將對(duì)象標(biāo)記為“丟失/消失”的連續(xù)幀數(shù)。
讓我們定義負(fù)責(zé)向我們的跟蹤器添加新對(duì)象的 register 方法:
def register(self, centroid): ? ? ? ? # 注冊對(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,我們從兩個(gè)字典中都刪除了該對(duì)象ID ? ? ? ? del self.objects[objectID] ? ? ? ? del self.disappeared[objectID]
register 方法接受一個(gè)質(zhì)心,然后使用下一個(gè)可用的對(duì)象 ID 將其添加到對(duì)象字典中。
對(duì)象消失的次數(shù)在消失字典中初始化為 0。
最后,我們增加 nextObjectID,這樣如果一個(gè)新對(duì)象進(jìn)入視野,它將與一個(gè)唯一的 ID 相關(guān)聯(lián)。
與我們的注冊方法類似,我們也需要一個(gè)注銷方法。
deregister 方法分別刪除對(duì)象和消失字典中的 objectID。
質(zhì)心跟蹤器實(shí)現(xiàn)的核心位于update方法中
?def update(self, rects): ? ? ? ? # 檢查輸入邊界框矩形的列表是否為空 ? ? ? ? if len(rects) == 0: ? ? ? ? ? ? # 遍歷任何現(xiàn)有的跟蹤對(duì)象并將其標(biāo)記為消失 ? ? ? ? ? ? for objectID in list(self.disappeared.keys()): ? ? ? ? ? ? ? ? self.disappeared[objectID] += 1 ? ? ? ? ? ? ? ? # 如果達(dá)到給定對(duì)象被標(biāo)記為丟失的最大連續(xù)幀數(shù),請(qǐng)取消注冊 ? ? ? ? ? ? ? ? if self.disappeared[objectID] > self.maxDisappeared: ? ? ? ? ? ? ? ? ? ? self.deregister(objectID) ? ? ? ? ? ? # 由于沒有質(zhì)心或跟蹤信息要更新,請(qǐng)盡早返回 ? ? ? ? ? ? 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): ? ? ? ? ? ? # use the bounding box coordinates to derive the centroid ? ? ? ? ? ? cX = int((startX + endX) / 2.0) ? ? ? ? ? ? cY = int((startY + endY) / 2.0) ? ? ? ? ? ? inputCentroids[i] = (cX, cY) ? ? ? ? # 如果我們當(dāng)前未跟蹤任何對(duì)象,請(qǐng)輸入輸入質(zhì)心并注冊每個(gè)質(zhì)心 ? ? ? ? if len(self.objects) == 0: ? ? ? ? ? ? for i in range(0, len(inputCentroids)): ? ? ? ? ? ? ? ? self.register(inputCentroids[i]) ? ? ? ? # 否則,當(dāng)前正在跟蹤對(duì)象,因此我們需要嘗試將輸入質(zhì)心與現(xiàn)有對(duì)象質(zhì)心進(jìn)行匹配 ? ? ? ? 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() ? ? ? ? ? ? # 接下來,我們在列上執(zhí)行類似的過程,方法是在每一列中找到最小值,然后使用先前計(jì)算的行索引列表進(jìn)行排序 ? ? ? ? ? ? cols = D.argmin(axis=1)[rows] ? ? ? ? ? ? # 為了確定是否需要更新,注冊或注銷對(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 ? ? ? ? ? ? ? ? # 否則,獲取當(dāng)前行的對(duì)象ID,設(shè)置其新的質(zhì)心,然后重置消失的計(jì)數(shù)器 ? ? ? ? ? ? ? ? objectID = objectIDs[row] ? ? ? ? ? ? ? ? self.objects[objectID] = inputCentroids[col] ? ? ? ? ? ? ? ? self.disappeared[objectID] = 0 ? ? ? ? ? ? ? ? # 表示我們已經(jīng)分別檢查了行索引和列索引 ? ? ? ? ? ? ? ? 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ì)象是否已潛在消失 ? ? ? ? ? ? if D.shape[0] >= D.shape[1]: ? ? ? ? ? ? ? ? # loop over the unused row indexes ? ? ? ? ? ? ? ? for row in unusedRows: ? ? ? ? ? ? ? ? ? ? # 抓取相應(yīng)行索引的對(duì)象ID并增加消失的計(jì)數(shù)器 ? ? ? ? ? ? ? ? ? ? objectID = objectIDs[row] ? ? ? ? ? ? ? ? ? ? self.disappeared[objectID] += 1 ? ? ? ? ? ? ? ? ? ? # 檢查是否已將該對(duì)象標(biāo)記為“消失”的連續(xù)幀數(shù)以用于注銷該對(duì)象的手令 ? ? ? ? ? ? ? ? ? ? if self.disappeared[objectID] > self.maxDisappeared: ? ? ? ? ? ? ? ? ? ? ? ? self.deregister(objectID) ? ? ? ? ? ? # 否則,如果輸入質(zhì)心的數(shù)量大于現(xiàn)有對(duì)象質(zhì)心的數(shù)量,我們需要將每個(gè)新的輸入質(zhì)心注冊為可跟蹤對(duì)象 ? ? ? ? ? ? else: ? ? ? ? ? ? ? ? for col in unusedCols: ? ? ? ? ? ? ? ? ? ? self.register(inputCentroids[col]) ? ? ? ? # return the set of trackable objects ? ? ? ? return self.objects
update方法接受邊界框矩形列表。 rects 參數(shù)的格式假定為具有以下結(jié)構(gòu)的元組: (startX, startY, endX, endY) 。
如果沒有檢測到,我們將遍歷所有對(duì)象 ID 并增加它們的消失計(jì)數(shù)。 我們還將檢查是否已達(dá)到給定對(duì)象被標(biāo)記為丟失的最大連續(xù)幀數(shù)。 如果是,我們需要將其從我們的跟蹤系統(tǒng)中刪除。 由于沒有要更新的跟蹤信息,我們將提前返回。
否則,我們將初始化一個(gè) NumPy 數(shù)組來存儲(chǔ)每個(gè) rect 的質(zhì)心。 然后,我們遍歷邊界框矩形并計(jì)算質(zhì)心并將其存儲(chǔ)在 inputCentroids 列表中。 如果沒有我們正在跟蹤的對(duì)象,我們將注冊每個(gè)新對(duì)象。
否則,我們需要根據(jù)最小化它們之間的歐幾里得距離的質(zhì)心位置更新任何現(xiàn)有對(duì)象 (x, y) 坐標(biāo)。
接下來我們在else中計(jì)算所有 objectCentroids 和 inputCentroids 對(duì)之間的歐幾里德距離:
獲取 objectID 和 objectCentroid 值。
計(jì)算每對(duì)現(xiàn)有對(duì)象質(zhì)心和新輸入質(zhì)心之間的距離。距離圖 D 的輸出 NumPy 數(shù)組形狀將是 (# of object centroids, # of input centroids) 。要執(zhí)行匹配,我們必須 (1) 找到每行中的最小值,以及 (2) 根據(jù)最小值對(duì)行索引進(jìn)行排序。我們對(duì)列執(zhí)行非常相似的過程,在每列中找到最小值,然后根據(jù)已排序的行對(duì)它們進(jìn)行排序。我們的目標(biāo)是在列表的前面具有最小對(duì)應(yīng)距離的索引值。
下一步是使用距離來查看是否可以關(guān)聯(lián)對(duì)象 ID:
初始化兩個(gè)集合以確定我們已經(jīng)使用了哪些行和列索引。
然后遍歷 (row, col) 索引元組的組合以更新我們的對(duì)象質(zhì)心:
如果我們已經(jīng)使用了此行或列索引,請(qǐng)忽略它并繼續(xù)循環(huán)。
否則,我們找到了一個(gè)輸入質(zhì)心:
- 到現(xiàn)有質(zhì)心的歐幾里得距離最小
- 并且沒有與任何其他對(duì)象匹配
- 在這種情況下,我們更新對(duì)象質(zhì)心(第 113-115 行)并確保將 row 和 col 添加到它們各自的 usedRows 和 usedCols 集合中。
在我們的 usedRows + usedCols 集合中可能有我們尚未檢查的索引:
所以我們必須確定哪些質(zhì)心索引我們還沒有檢查過,并將它們存儲(chǔ)在兩個(gè)新的方便集合(unusedRows 和 usedCols)中。
最終檢查會(huì)處理任何丟失或可能消失的對(duì)象:
如果對(duì)象質(zhì)心的數(shù)量大于或等于輸入質(zhì)心的數(shù)量:
我們需要循環(huán)遍歷未使用的行索引來驗(yàn)證這些對(duì)象是否丟失或消失。
在循環(huán)中,我們將:
1. 增加他們在字典中消失的次數(shù)。
2. 檢查消失計(jì)數(shù)是否超過 maxDisappeared 閾值,如果是,我們將注銷該對(duì)象。
否則,輸入質(zhì)心的數(shù)量大于現(xiàn)有對(duì)象質(zhì)心的數(shù)量,我們有新的對(duì)象要注冊和跟蹤.
循環(huán)遍歷未使用的Cols 索引并注冊每個(gè)新質(zhì)心。 最后,我們將可跟蹤對(duì)象集返回給調(diào)用方法。
實(shí)現(xiàn)對(duì)象跟蹤驅(qū)動(dòng)程序腳本
已經(jīng)實(shí)現(xiàn)了 CentroidTracker 類,讓我們將其與對(duì)象跟蹤驅(qū)動(dòng)程序腳本一起使用。
驅(qū)動(dòng)程序腳本是您可以使用自己喜歡的對(duì)象檢測器的地方,前提是它會(huì)生成一組邊界框。 這可能是 Haar Cascade、HOG + 線性 SVM、YOLO、SSD、Faster R-CNN 等。
在這個(gè)腳本中,需要實(shí)現(xiàn)的功能:
1、使用實(shí)時(shí) VideoStream 對(duì)象從網(wǎng)絡(luò)攝像頭中抓取幀
2、加載并使用 OpenCV 的深度學(xué)習(xí)人臉檢測器
3、實(shí)例化 CentroidTracker 并使用它來跟蹤視頻流中的人臉對(duì)象并顯示結(jié)果。
新建 object_tracker.py 插入代碼:
from Model.centroidtracker import CentroidTracker
import numpy as np
import imutils
import time
import cv2
# 定義最低置信度
confidence_t=0.5
# 初始化質(zhì)心跟蹤器和框架尺寸
ct = CentroidTracker()
(H, W) = (None, None)
# 加載檢測人臉的模型
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe("deploy.prototxt", "res10_300x300_ssd_iter_140000_fp16.caffemodel")
# 初始化視頻流并允許相機(jī)傳感器預(yù)熱
print("[INFO] starting video stream...")
vs = cv2.VideoCapture('11.mp4')
time.sleep(2.0)
fps = 30 ? ?#保存視頻的FPS,可以適當(dāng)調(diào)整
size=(600,1066)#寬高,根據(jù)frame的寬和高確定。
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
videoWriter = cv2.VideoWriter('3.mp4',fourcc,fps,size)#最后一個(gè)是保存圖片的尺寸導(dǎo)入需要的包。
定義最低置信度。
加載人臉檢測模型
初始化視頻流或者相機(jī)(設(shè)置成相機(jī)對(duì)應(yīng)的ID就會(huì)啟動(dòng)相機(jī))
接下來定義cv2.VideoWriter的參數(shù)。
# 循環(huán)播放圖像流中的幀
while True:
? ? # 從視頻流中讀取下一幀并調(diào)整其大小
? ? (grabbed, frame) ?= vs.read()
? ? if not grabbed:
? ? ? ? break
? ? frame = imutils.resize(frame, width=600)
? ? print(frame.shape)
? ? # 如果幀中尺寸為“無”,則抓住它們
? ? if W is None or H is None:
? ? ? ? (H, W) = frame.shape[:2]
? ? # 從幀中構(gòu)造一個(gè)Blob,將其通過網(wǎng)絡(luò),
? ? # 獲取輸出預(yù)測,并初始化邊界框矩形的列表
? ? blob = cv2.dnn.blobFromImage(frame, 1.0, (W, H),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(104.0, 177.0, 123.0))
? ? net.setInput(blob)
? ? detections = net.forward()
? ? rects = []
? ? # 循環(huán)檢測
? ? for i in range(0, detections.shape[2]):
? ? ? ? # 通過確保預(yù)測的概率大于最小閾值來過濾掉弱檢測
? ? ? ? if detections[0, 0, i, 2] >0.5:
? ? ? ? ? ? # 計(jì)算對(duì)象邊界框的(x,y)坐標(biāo),然后更新邊界框矩形列表
? ? ? ? ? ? box = detections[0, 0, i, 3:7] * np.array([W, H, W, H])
? ? ? ? ? ? rects.append(box.astype("int"))
? ? ? ? ? ? # 在對(duì)象周圍畫一個(gè)邊界框,以便我們可視化它
? ? ? ? ? ? (startX, startY, endX, endY) = box.astype("int")
? ? ? ? ? ? cv2.rectangle(frame, (startX, startY), (endX, endY),
? ? ? ? ? ? ? ? ? ? ? ? ? (0, 255, 0), 2)
? ? # 使用邊界框矩形的計(jì)算集更新質(zhì)心跟蹤器
? ? objects = ct.update(rects)
? ? # 循環(huán)跟蹤對(duì)象
? ? for (objectID, centroid) in objects.items():
? ? ? ? # 在輸出幀上繪制對(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)
? ? # 顯示輸出畫面
? ? cv2.imshow("Frame", frame)
? ? videoWriter.write(frame)
? ? key = cv2.waitKey(1) & 0xFF
? ? # 如果按下“ q”鍵,則退出循環(huán)
? ? if key == ord("q"):
? ? ? ? break
videoWriter.release()
cv2.destroyAllWindows()
vs.release()遍歷幀并將它們調(diào)整為固定寬度(同時(shí)保持縱橫比)。
然后將幀通過 CNN 對(duì)象檢測器以獲得預(yù)測和對(duì)象位置(。
初始化一個(gè) rects 列表,即邊界框矩形。
循環(huán)檢測detections,如果檢測超過我們的置信度閾值,表明檢測有效,然后計(jì)算邊框。
在質(zhì)心跟蹤器對(duì)象 ct 上調(diào)用 update 方法。
接下來在輸出幀上繪制對(duì)象的ID和質(zhì)心,將質(zhì)心顯示為一個(gè)實(shí)心圓圈和唯一的對(duì)象 ID 號(hào)文本。 現(xiàn)在我們將能夠可視化結(jié)果并通過將正確的 ID 與視頻流中的對(duì)象相關(guān)聯(lián)來檢查 CentroidTracker 是否正確地跟蹤對(duì)象。
限制和缺點(diǎn)
雖然質(zhì)心跟蹤器在這個(gè)例子中工作得很好,但這種對(duì)象跟蹤算法有兩個(gè)主要缺點(diǎn)。
1、它要求在輸入視頻的每一幀上運(yùn)行對(duì)象檢測步驟。對(duì)于非??焖俚膶?duì)象檢測器(即顏色閾值和 Haar 級(jí)聯(lián)),必須在每個(gè)輸入幀上運(yùn)行檢測器可能不是問題。但是,如果在 資源受限的設(shè)備上使用計(jì)算量大得多的對(duì)象檢測器,例如 HOG + 線性 SVM 或基于深度學(xué)習(xí)的檢測器,那么幀處理將大大減慢。
2、與質(zhì)心跟蹤算法本身的基本假設(shè)有關(guān)——質(zhì)心必須在后續(xù)幀之間靠得很近。
這個(gè)假設(shè)通常成立,但請(qǐng)記住,我們用 2D 幀來表示我們的 3D 世界——當(dāng)一個(gè)對(duì)象與另一個(gè)對(duì)象重疊時(shí)會(huì)發(fā)生什么?
答案是可能發(fā)生對(duì)象 ID 切換。如果兩個(gè)或多個(gè)對(duì)象相互重疊到它們的質(zhì)心相交的點(diǎn),而是與另一個(gè)相應(yīng)對(duì)象具有最小距離,則算法可能(在不知不覺中)交換對(duì)象 ID。重要的是要了解重疊/遮擋對(duì)象問題并非特定于質(zhì)心跟蹤 - 它也發(fā)生在許多其他對(duì)象跟蹤器中,包括高級(jí)對(duì)象跟蹤器。然而,質(zhì)心跟蹤的問題更為明顯,因?yàn)槲覀儑?yán)格依賴質(zhì)心之間的歐幾里得距離,并且沒有額外的度量、啟發(fā)式或?qū)W習(xí)模式。
以上就是利用OpenCV實(shí)現(xiàn)質(zhì)心跟蹤算法的詳細(xì)內(nèi)容,更多關(guān)于OpenCV質(zhì)心跟蹤算法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Django之PopUp的具體實(shí)現(xiàn)方法
今天小編就為大家分享一篇Django之PopUp的具體實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-08-08
PyTorch實(shí)現(xiàn)手寫數(shù)字的識(shí)別入門小白教程
這篇文章主要介紹了python實(shí)現(xiàn)手寫數(shù)字識(shí)別,非常適合小白入門學(xué)習(xí),本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
JavaScript實(shí)現(xiàn)一維數(shù)組轉(zhuǎn)化為二維數(shù)組
下面小編就為大家分享一篇JavaScript實(shí)現(xiàn)一維數(shù)組轉(zhuǎn)化為二維數(shù)組,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-04-04
python獲取當(dāng)前用戶的主目錄路徑方法(推薦)
下面小編就為大家?guī)硪黄猵ython獲取當(dāng)前用戶的主目錄路徑方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01
Python模擬登陸網(wǎng)頁的三種方法小結(jié)
如何使用Python模擬登陸網(wǎng)頁,尤其是在涉及到復(fù)雜的認(rèn)證機(jī)制時(shí)?這篇文章將詳細(xì)介紹Python模擬登陸網(wǎng)頁的三種方法,以及如何繞過一些常見的安全防護(hù)措施,需要的朋友可以參考下2024-01-01
media配置及把用戶頭像從數(shù)據(jù)庫展示到前端的操作方法
media配置可以讓用戶上傳的所有文件都固定的存放在某一個(gè)指定的文件夾下,接下來通過本文給大家介紹下media配置及把用戶頭像從數(shù)據(jù)庫展示到前端的操作,需要的朋友可以參考下2022-09-09

