利用OpenCV實(shí)現(xiàn)質(zhì)心跟蹤算法
目標(biāo)跟蹤的過程:
1、獲取對象檢測的初始集
2、為每個初始檢測創(chuàng)建唯一的ID
3、然后在視頻幀中跟蹤每個對象的移動,保持唯一ID的分配
本文使用OpenCV實(shí)現(xiàn)質(zhì)心跟蹤,這是一種易于理解但高效的跟蹤算法。
質(zhì)心跟蹤算法步驟
步驟1:接受邊界框坐標(biāo)并計算質(zhì)心
質(zhì)心跟蹤算法假設(shè)我們?yōu)槊恳粠械拿總€檢測到的對象傳入一組邊界框 (x, y) 坐標(biāo)。
這些邊界框可以由任何類型的對象檢測器(顏色閾值 + 輪廓提取、Haar 級聯(lián)、HOG + 線性 SVM、SSD、Faster R-CNN 等)生成,前提是它們是針對 該視頻。
通過邊界框坐標(biāo),就可以計算“質(zhì)心”(邊界框的中心 (x, y) 坐標(biāo))。 如上圖演示了通過一組邊界框坐標(biāo)計算出質(zhì)心。
給初始的邊界框分配ID。
步驟2:計算新邊界框和現(xiàn)有對象之間的歐幾里得距離
對于視頻流中的每個后續(xù)幀,首先執(zhí)行步驟1; 然后,我們首先需要確定是否可以將新的對象質(zhì)心(黃色)與舊的對象質(zhì)心(紫色)相關(guān)聯(lián),而不是為每個檢測到的對象分配一個新的唯一 ID(這會破壞對象跟蹤的目的)。 為了完成這個過程,我們計算每對現(xiàn)有對象質(zhì)心和輸入對象質(zhì)心之間的歐幾里得距離(用綠色箭頭突出顯示)。
上圖可以看出,我們這次在圖像中檢測到了三個對象。 靠近的兩對是兩個現(xiàn)有對象。
然后我們計算每對原始質(zhì)心(黃色)和新質(zhì)心(紫色)之間的歐幾里得距離。 但是我們?nèi)绾问褂眠@些點(diǎn)之間的歐幾里得距離來實(shí)際匹配它們并關(guān)聯(lián)它們呢?
答案在步驟3。
步驟 3:更新現(xiàn)有對象的 (x, y) 坐標(biāo)
質(zhì)心跟蹤算法的主要假設(shè)是給定對象可能會在后續(xù)幀之間移動,但幀 F_t 和 F_{t + 1} 的質(zhì)心之間的距離將小于對象之間的所有其他距離。
因此,如果我們選擇將質(zhì)心與后續(xù)幀之間的最小距離相關(guān)聯(lián),我們可以構(gòu)建我們的對象跟蹤器。
在上圖中,可以看到質(zhì)心跟蹤器算法如何選擇關(guān)聯(lián)質(zhì)心以最小化它們各自的歐幾里得距離。
但是左下角的孤獨(dú)點(diǎn)呢?
它沒有與任何東西相關(guān)聯(lián)——我們?nèi)绾翁幚硭?/p>
步驟4:注冊新對象
如果輸入檢測比跟蹤的現(xiàn)有對象多,我們需要注冊新對象。 “注冊”僅僅意味著我們通過以下方式將新對象添加到我們的跟蹤對象列表中:
為其分配一個新的對象 ID
存儲該對象的邊界框坐標(biāo)的質(zhì)心
然后我們可以返回到步驟2,重復(fù)執(zhí)行。
上圖演示了使用最小歐幾里得距離關(guān)聯(lián)現(xiàn)有對象 ID,然后注冊新對象的過程。
步驟5:注銷舊對象
當(dāng)舊的對象超出范圍時,注銷舊對象、
項(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() ? ? ? ? # 存儲給定對象被允許標(biāo)記為“消失”的最大連續(xù)幀數(shù),直到我們需要從跟蹤中注銷該對象 ? ? ? ? self.maxDisappeared = maxDisappeared
導(dǎo)入了所需的包和模塊:distance 、 OrderedDict 和 numpy 。
定義類 CentroidTracker 。構(gòu)造函數(shù)接受一個參數(shù)maxDisappeared,即給定對象必須丟失/消失的最大連續(xù)幀數(shù),如果超過這個數(shù)值就將這個對象刪除。
四個類變量:
nextObjectID :用于為每個對象分配唯一 ID 的計數(shù)器。如果一個對象離開幀并且沒有返回 maxDisappeared 幀,則將分配一個新的(下一個)對象 ID。
objects :使用對象 ID 作為鍵和質(zhì)心 (x, y) 坐標(biāo)作為值的字典。
disappeared:保持特定對象 ID(鍵)已被標(biāo)記為“丟失”的連續(xù)幀數(shù)(值)。
maxDisappeared :在我們?nèi)∠詫ο笾?,允許將對象標(biāo)記為“丟失/消失”的連續(xù)幀數(shù)。
讓我們定義負(fù)責(zé)向我們的跟蹤器添加新對象的 register 方法:
def register(self, centroid): ? ? ? ? # 注冊對象時,我們使用下一個可用的對象ID來存儲質(zhì)心 ? ? ? ? self.objects[self.nextObjectID] = centroid ? ? ? ? self.disappeared[self.nextObjectID] = 0 ? ? ? ? self.nextObjectID += 1 ? ? def deregister(self, objectID): ? ? ? ? # 要注銷注冊對象ID,我們從兩個字典中都刪除了該對象ID ? ? ? ? del self.objects[objectID] ? ? ? ? del self.disappeared[objectID]
register 方法接受一個質(zhì)心,然后使用下一個可用的對象 ID 將其添加到對象字典中。
對象消失的次數(shù)在消失字典中初始化為 0。
最后,我們增加 nextObjectID,這樣如果一個新對象進(jìn)入視野,它將與一個唯一的 ID 相關(guān)聯(lián)。
與我們的注冊方法類似,我們也需要一個注銷方法。
deregister 方法分別刪除對象和消失字典中的 objectID。
質(zhì)心跟蹤器實(shí)現(xiàn)的核心位于update方法中
?def update(self, rects): ? ? ? ? # 檢查輸入邊界框矩形的列表是否為空 ? ? ? ? if len(rects) == 0: ? ? ? ? ? ? # 遍歷任何現(xiàn)有的跟蹤對象并將其標(biāo)記為消失 ? ? ? ? ? ? for objectID in list(self.disappeared.keys()): ? ? ? ? ? ? ? ? self.disappeared[objectID] += 1 ? ? ? ? ? ? ? ? # 如果達(dá)到給定對象被標(biāo)記為丟失的最大連續(xù)幀數(shù),請取消注冊 ? ? ? ? ? ? ? ? if self.disappeared[objectID] > self.maxDisappeared: ? ? ? ? ? ? ? ? ? ? self.deregister(objectID) ? ? ? ? ? ? # 由于沒有質(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): ? ? ? ? ? ? # 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)前未跟蹤任何對象,請輸入輸入質(zhì)心并注冊每個質(zhì)心 ? ? ? ? if len(self.objects) == 0: ? ? ? ? ? ? for i in range(0, len(inputCentroids)): ? ? ? ? ? ? ? ? self.register(inputCentroids[i]) ? ? ? ? # 否則,當(dāng)前正在跟蹤對象,因此我們需要嘗試將輸入質(zhì)心與現(xiàn)有對象質(zhì)心進(jìn)行匹配 ? ? ? ? else: ? ? ? ? ? ? # 抓取一組對象ID和相應(yīng)的質(zhì)心 ? ? ? ? ? ? objectIDs = list(self.objects.keys()) ? ? ? ? ? ? objectCentroids = list(self.objects.values()) ? ? ? ? ? ? # 分別計算每對對象質(zhì)心和輸入質(zhì)心之間的距離-我們的目標(biāo)是將輸入質(zhì)心與現(xiàn)有對象質(zhì)心匹配 ? ? ? ? ? ? D = dist.cdist(np.array(objectCentroids), inputCentroids) ? ? ? ? ? ? # 為了執(zhí)行此匹配,我們必須(1)在每行中找到最小值,然后(2)根據(jù)行索引的最小值對行索引進(jìn)行排序,以使具有最小值的行位于索引列表的* front *處 ? ? ? ? ? ? rows = D.min(axis=1).argsort() ? ? ? ? ? ? # 接下來,我們在列上執(zhí)行類似的過程,方法是在每一列中找到最小值,然后使用先前計算的行索引列表進(jìn)行排序 ? ? ? ? ? ? cols = D.argmin(axis=1)[rows] ? ? ? ? ? ? # 為了確定是否需要更新,注冊或注銷對象,我們需要跟蹤已經(jīng)檢查過的行索引和列索引 ? ? ? ? ? ? usedRows = set() ? ? ? ? ? ? usedCols = set() ? ? ? ? ? ? # 循環(huán)遍歷(行,列)索引元組的組合 ? ? ? ? ? ? for (row, col) in zip(rows, cols): ? ? ? ? ? ? ? ? # 如果我們之前已經(jīng)檢查過行或列的值,請忽略它 ? ? ? ? ? ? ? ? if row in usedRows or col in usedCols: ? ? ? ? ? ? ? ? ? ? continue ? ? ? ? ? ? ? ? # 否則,獲取當(dāng)前行的對象ID,設(shè)置其新的質(zhì)心,然后重置消失的計數(shù)器 ? ? ? ? ? ? ? ? objectID = objectIDs[row] ? ? ? ? ? ? ? ? self.objects[objectID] = inputCentroids[col] ? ? ? ? ? ? ? ? self.disappeared[objectID] = 0 ? ? ? ? ? ? ? ? # 表示我們已經(jīng)分別檢查了行索引和列索引 ? ? ? ? ? ? ? ? usedRows.add(row) ? ? ? ? ? ? ? ? usedCols.add(col) ? ? ? ? ? ? # 計算我們尚未檢查的行和列索引 ? ? ? ? ? ? unusedRows = set(range(0, D.shape[0])).difference(usedRows) ? ? ? ? ? ? unusedCols = set(range(0, D.shape[1])).difference(usedCols) ? ? ? ? ? ? # 如果對象質(zhì)心的數(shù)量等于或大于輸入質(zhì)心的數(shù)量 ? ? ? ? ? ? # 我們需要檢查一下其中的某些對象是否已潛在消失 ? ? ? ? ? ? if D.shape[0] >= D.shape[1]: ? ? ? ? ? ? ? ? # loop over the unused row indexes ? ? ? ? ? ? ? ? for row in unusedRows: ? ? ? ? ? ? ? ? ? ? # 抓取相應(yīng)行索引的對象ID并增加消失的計數(shù)器 ? ? ? ? ? ? ? ? ? ? objectID = objectIDs[row] ? ? ? ? ? ? ? ? ? ? self.disappeared[objectID] += 1 ? ? ? ? ? ? ? ? ? ? # 檢查是否已將該對象標(biāo)記為“消失”的連續(xù)幀數(shù)以用于注銷該對象的手令 ? ? ? ? ? ? ? ? ? ? if self.disappeared[objectID] > self.maxDisappeared: ? ? ? ? ? ? ? ? ? ? ? ? self.deregister(objectID) ? ? ? ? ? ? # 否則,如果輸入質(zhì)心的數(shù)量大于現(xiàn)有對象質(zhì)心的數(shù)量,我們需要將每個新的輸入質(zhì)心注冊為可跟蹤對象 ? ? ? ? ? ? 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) 。
如果沒有檢測到,我們將遍歷所有對象 ID 并增加它們的消失計數(shù)。 我們還將檢查是否已達(dá)到給定對象被標(biāo)記為丟失的最大連續(xù)幀數(shù)。 如果是,我們需要將其從我們的跟蹤系統(tǒng)中刪除。 由于沒有要更新的跟蹤信息,我們將提前返回。
否則,我們將初始化一個 NumPy 數(shù)組來存儲每個 rect 的質(zhì)心。 然后,我們遍歷邊界框矩形并計算質(zhì)心并將其存儲在 inputCentroids 列表中。 如果沒有我們正在跟蹤的對象,我們將注冊每個新對象。
否則,我們需要根據(jù)最小化它們之間的歐幾里得距離的質(zhì)心位置更新任何現(xiàn)有對象 (x, y) 坐標(biāo)。
接下來我們在else中計算所有 objectCentroids 和 inputCentroids 對之間的歐幾里德距離:
獲取 objectID 和 objectCentroid 值。
計算每對現(xiàn)有對象質(zhì)心和新輸入質(zhì)心之間的距離。距離圖 D 的輸出 NumPy 數(shù)組形狀將是 (# of object centroids, # of input centroids) 。要執(zhí)行匹配,我們必須 (1) 找到每行中的最小值,以及 (2) 根據(jù)最小值對行索引進(jìn)行排序。我們對列執(zhí)行非常相似的過程,在每列中找到最小值,然后根據(jù)已排序的行對它們進(jìn)行排序。我們的目標(biāo)是在列表的前面具有最小對應(yīng)距離的索引值。
下一步是使用距離來查看是否可以關(guān)聯(lián)對象 ID:
初始化兩個集合以確定我們已經(jīng)使用了哪些行和列索引。
然后遍歷 (row, col) 索引元組的組合以更新我們的對象質(zhì)心:
如果我們已經(jīng)使用了此行或列索引,請忽略它并繼續(xù)循環(huán)。
否則,我們找到了一個輸入質(zhì)心:
- 到現(xiàn)有質(zhì)心的歐幾里得距離最小
- 并且沒有與任何其他對象匹配
- 在這種情況下,我們更新對象質(zhì)心(第 113-115 行)并確保將 row 和 col 添加到它們各自的 usedRows 和 usedCols 集合中。
在我們的 usedRows + usedCols 集合中可能有我們尚未檢查的索引:
所以我們必須確定哪些質(zhì)心索引我們還沒有檢查過,并將它們存儲在兩個新的方便集合(unusedRows 和 usedCols)中。
最終檢查會處理任何丟失或可能消失的對象:
如果對象質(zhì)心的數(shù)量大于或等于輸入質(zhì)心的數(shù)量:
我們需要循環(huán)遍歷未使用的行索引來驗(yàn)證這些對象是否丟失或消失。
在循環(huán)中,我們將:
1. 增加他們在字典中消失的次數(shù)。
2. 檢查消失計數(shù)是否超過 maxDisappeared 閾值,如果是,我們將注銷該對象。
否則,輸入質(zhì)心的數(shù)量大于現(xiàn)有對象質(zhì)心的數(shù)量,我們有新的對象要注冊和跟蹤.
循環(huán)遍歷未使用的Cols 索引并注冊每個新質(zhì)心。 最后,我們將可跟蹤對象集返回給調(diào)用方法。
實(shí)現(xiàn)對象跟蹤驅(qū)動程序腳本
已經(jīng)實(shí)現(xiàn)了 CentroidTracker 類,讓我們將其與對象跟蹤驅(qū)動程序腳本一起使用。
驅(qū)動程序腳本是您可以使用自己喜歡的對象檢測器的地方,前提是它會生成一組邊界框。 這可能是 Haar Cascade、HOG + 線性 SVM、YOLO、SSD、Faster R-CNN 等。
在這個腳本中,需要實(shí)現(xiàn)的功能:
1、使用實(shí)時 VideoStream 對象從網(wǎng)絡(luò)攝像頭中抓取幀
2、加載并使用 OpenCV 的深度學(xué)習(xí)人臉檢測器
3、實(shí)例化 CentroidTracker 并使用它來跟蹤視頻流中的人臉對象并顯示結(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)#最后一個是保存圖片的尺寸
導(dǎo)入需要的包。
定義最低置信度。
加載人臉檢測模型
初始化視頻流或者相機(jī)(設(shè)置成相機(jī)對應(yīng)的ID就會啟動相機(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)造一個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: ? ? ? ? ? ? # 計算對象邊界框的(x,y)坐標(biāo),然后更新邊界框矩形列表 ? ? ? ? ? ? box = detections[0, 0, i, 3:7] * np.array([W, H, W, H]) ? ? ? ? ? ? rects.append(box.astype("int")) ? ? ? ? ? ? # 在對象周圍畫一個邊界框,以便我們可視化它 ? ? ? ? ? ? (startX, startY, endX, endY) = box.astype("int") ? ? ? ? ? ? cv2.rectangle(frame, (startX, startY), (endX, endY), ? ? ? ? ? ? ? ? ? ? ? ? ? (0, 255, 0), 2) ? ? # 使用邊界框矩形的計算集更新質(zhì)心跟蹤器 ? ? objects = ct.update(rects) ? ? # 循環(huán)跟蹤對象 ? ? for (objectID, centroid) in objects.items(): ? ? ? ? # 在輸出幀上繪制對象的ID和對象的質(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)整為固定寬度(同時保持縱橫比)。
然后將幀通過 CNN 對象檢測器以獲得預(yù)測和對象位置(。
初始化一個 rects 列表,即邊界框矩形。
循環(huán)檢測detections,如果檢測超過我們的置信度閾值,表明檢測有效,然后計算邊框。
在質(zhì)心跟蹤器對象 ct 上調(diào)用 update 方法。
接下來在輸出幀上繪制對象的ID和質(zhì)心,將質(zhì)心顯示為一個實(shí)心圓圈和唯一的對象 ID 號文本。 現(xiàn)在我們將能夠可視化結(jié)果并通過將正確的 ID 與視頻流中的對象相關(guān)聯(lián)來檢查 CentroidTracker 是否正確地跟蹤對象。
限制和缺點(diǎn)
雖然質(zhì)心跟蹤器在這個例子中工作得很好,但這種對象跟蹤算法有兩個主要缺點(diǎn)。
1、它要求在輸入視頻的每一幀上運(yùn)行對象檢測步驟。對于非常快速的對象檢測器(即顏色閾值和 Haar 級聯(lián)),必須在每個輸入幀上運(yùn)行檢測器可能不是問題。但是,如果在 資源受限的設(shè)備上使用計算量大得多的對象檢測器,例如 HOG + 線性 SVM 或基于深度學(xué)習(xí)的檢測器,那么幀處理將大大減慢。
2、與質(zhì)心跟蹤算法本身的基本假設(shè)有關(guān)——質(zhì)心必須在后續(xù)幀之間靠得很近。
這個假設(shè)通常成立,但請記住,我們用 2D 幀來表示我們的 3D 世界——當(dāng)一個對象與另一個對象重疊時會發(fā)生什么?
答案是可能發(fā)生對象 ID 切換。如果兩個或多個對象相互重疊到它們的質(zhì)心相交的點(diǎn),而是與另一個相應(yīng)對象具有最小距離,則算法可能(在不知不覺中)交換對象 ID。重要的是要了解重疊/遮擋對象問題并非特定于質(zhì)心跟蹤 - 它也發(fā)生在許多其他對象跟蹤器中,包括高級對象跟蹤器。然而,質(zhì)心跟蹤的問題更為明顯,因?yàn)槲覀儑?yán)格依賴質(zhì)心之間的歐幾里得距離,并且沒有額外的度量、啟發(fā)式或?qū)W習(xí)模式。
以上就是利用OpenCV實(shí)現(xiàn)質(zhì)心跟蹤算法的詳細(xì)內(nèi)容,更多關(guān)于OpenCV質(zhì)心跟蹤算法的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python機(jī)器學(xué)習(xí)應(yīng)用之基于線性判別模型的分類篇詳解
線性判別分析(Linear?Discriminant?Analysis,?LDA)是一種監(jiān)督學(xué)習(xí)的降維方法,也就是說數(shù)據(jù)集的每個樣本是有類別輸出。和之前介紹的機(jī)器學(xué)習(xí)降維之主成分分析(PCA)方法不同,PCA是不考慮樣本類別輸出的無監(jiān)督學(xué)習(xí)方法2022-01-01OpenCV實(shí)現(xiàn)圖片編解碼實(shí)踐
在很多應(yīng)用中,經(jīng)常會直接把圖片的二進(jìn)制數(shù)據(jù)進(jìn)行交換,這就需要對普通進(jìn)行編碼解碼,那么怎么才能實(shí)現(xiàn),本文就來介紹一下2021-06-06Python使用PyQt5/PySide2編寫一個極簡的音樂播放器功能
這篇文章主要介紹了Python中使用PyQt5/PySide2編寫一個極簡的音樂播放器功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02python通過nmap掃描在線設(shè)備并嘗試AAA登錄(實(shí)例代碼)
這篇文章主要介紹了python通過nmap掃描在線設(shè)備并嘗試AAA登錄,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12Python實(shí)現(xiàn)?MK檢驗(yàn)示例代碼
這篇文章主要介紹了Python實(shí)現(xiàn)?MK檢驗(yàn),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-12-12用Python實(shí)現(xiàn)二叉樹、二叉樹非遞歸遍歷及繪制的例子
今天小編就為大家分享一篇用Python實(shí)現(xiàn)二叉樹、二叉樹非遞歸遍歷及繪制的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08Django實(shí)現(xiàn)登錄隨機(jī)驗(yàn)證碼的示例代碼
登錄驗(yàn)證碼是每個網(wǎng)站登錄時的基本標(biāo)配,這篇文章主要介紹了Django實(shí)現(xiàn)登錄隨機(jī)驗(yàn)證碼的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06