如何使用Python和OpenCV進行實時目標檢測實例詳解
簡介
這段程序是一個Python腳本,它使用了OpenCV庫和預訓練的YOLOv3模型來實現(xiàn)實時視頻流的目標檢測。它首先從攝像頭捕獲視頻流,并使用線程池處理每一幀圖像。在每一幀中,程序都會檢測和識別不同的對象,并用不同的顏色顯示結果。使用了非極大值抑制技術刪除重復的檢測框,并利用了并發(fā)處理技術以提高性能。最后,他還顯示了每秒處理的幀數(shù)(FPS)。我們可以通過按'q'鍵來結束程序。這個程序展示了一種有效的使用深度學習模型進行實時視覺任務的方法。
代碼介紹
這段代碼是使用OpenCV和Python編寫的,主要用于實時視頻流處理和目標檢測。代碼的核心是使用訓練好的YOLOv3模型來識別和定位視頻幀中的對象。下面是對整個流程的詳細解釋:
導入庫:首先,導入所需的庫,包括
cv2(OpenCV),numpy(用于科學計算),time(時間管理),以及concurrent.futures(用于并發(fā)執(zhí)行)。初始化參數(shù):設置檢測參數(shù),包括置信度閾值(用于過濾掉那些置信度低于閾值的檢測結果),非極大值抑制(NMS)閾值(用于去除重復的檢測框),以及輸入圖像的寬和高。
加載模型和類別:通過使用
cv2.dnn.readNet加載預訓練的YOLOv3模型,并從coco.names文件中加載可以識別的類名列表。顏色列表:為每個可識別的類別創(chuàng)建一個隨機顏色列表,這用于在檢測到的對象周圍繪制彩色的邊界框。
視頻捕獲:打開攝像頭進行視頻流的捕獲。
多線程處理:初始化
ThreadPoolExecutor以并發(fā)執(zhí)行多個任務。這里定義了兩個函數(shù)fetch_frame和process_frame。fetch_frame用于從視頻流中獲取一幀圖像,而process_frame則用于處理圖像并執(zhí)行目標檢測。目標檢測流程:
- 轉換輸入幀:將輸入幀轉換為模型所需要的格式(blob),包括縮放、顏色空間轉換等。
- 執(zhí)行檢測:調用神經(jīng)網(wǎng)絡模型執(zhí)行前向傳播,獲取檢測結果。
- 過濾結果:根據(jù)置信度閾值和NMS閾值過濾掉一部分檢測結果,消除低置信度以及重復的檢測框。
- 繪制邊界框和類別標簽:在原圖上繪制檢測到的對象的邊界框,并顯示類別名稱和置信度。
顯示結果:在屏幕上實時顯示處理后的視頻幀,并計算顯示FPS(每秒幀數(shù))。
程序退出:等待我們按
q鍵退出,并在退出前釋放資源,包括攝像頭和窗口。
這段代碼的設計利用了異步處理技術(通過ThreadPoolExecutor)來提高處理視頻流的效率,使得幀的捕獲和處理能夠并行執(zhí)行,從而盡可能提高FPS。
為什么這樣做,主要是我的電腦沒有獨立GPU,所以,呃,只能動點這中方法了,但是說實話并沒有什么實質性的提升,難受了。
代碼拆解講解
我們將使用預訓練的 YOLOv3 模型進行目標檢測,并使用 Python 的 concurrent.futures 庫來并行處理視頻幀的讀取和模型推理,以優(yōu)化程序的執(zhí)行速度。
感謝YOLO,雖然現(xiàn)在已經(jīng)發(fā)展到v8了,但是我們這里使用v3還是足夠了。
1.首先,讓我們導入需要用到的庫:
import cv2 import numpy as np import time from concurrent.futures import ThreadPoolExecutor
2.然后,設置兩個閾值:conf_threshold 和 nms_threshold,以及圖片的寬度和高度:
conf_threshold = 0.5 nms_threshold = 0.4 Width = 416 Height = 416
3.接下來,我們加載預訓練的 YOLOv3 模型,并加載識別的類名:
net = cv2.dnn.readNet('../needFiles/yolov3.weights', '../needFiles/yolov3.cfg')
with open('../needFiles/coco.names', 'r') as f:
classes = f.read().strip().split('\n')
4.然后,我們創(chuàng)建一個顏色列表,以在最后的目標檢測結果中為不同的類別繪制不同的顏色:
color_list = np.random.uniform(0, 255, size=(len(classes), 3))
5.下一步,我們定義兩個函數(shù) fetch_frame 和 process_frame
fetch_frame 用于從視頻對象讀取一幀;而 process_frame 則將讀取到的幀輸入到 YOLOv3 模型中,然后處理獲得的輸出結果,并在幀上繪制物體檢測結果:
def fetch_frame(cap):
...
def process_frame(frame):
...
6.在主循環(huán)中,我們使用 ThreadPoolExecutor 實現(xiàn)了并行處理讀取幀和模型推理的操作。
這樣可以使讀取下一幀的操作和模型推理操作同時進行,從而顯著地加快了處理速度:
executor = ThreadPoolExecutor(max_workers=2)
frame_future = executor.submit(fetch_frame, cap)
while True:
...
ret, frame, height, width, channels = frame_future.result()
frame_future = executor.submit(fetch_frame, cap)
processed_frame = executor.submit(process_frame, frame).result()
...
7.退出程序
在這段代碼的最后,如果你按下 q 鍵退出主循環(huán),視頻讀取對象將會被釋放,所有的窗口也將被銷毀:
if cv2.waitKey(1) & 0xFF == ord('q'):
break
...
cap.release()
cv2.destroyAllWindows()
總體代碼
# 導入必要的庫
import cv2
import numpy as np
import time
from concurrent.futures import ThreadPoolExecutor
# 設置置信度閾值和非極大值抑制(NMS)閾值
conf_threshold = 0.5
nms_threshold = 0.4
Width = 416
Height = 416
# 加載預訓練的 YOLOv3 模型
net = cv2.dnn.readNet('../needFiles/yolov3.weights', '../needFiles/yolov3.cfg')
# 加載可識別的類名
with open('../needFiles/coco.names', 'r') as f:
classes = f.read().strip().split('\n')
# 為不同的類別創(chuàng)建一個顏色列表
color_list = np.random.uniform(0, 255, size=(len(classes), 3))
# 打開攝像頭進行視頻幀的捕獲
cap = cv2.VideoCapture(0)
# 初始化一個ThreadPoolExecutor用于多線程處理
executor = ThreadPoolExecutor(max_workers=2)
# 定義fetch_frame函數(shù),從視頻流中獲取視頻幀
def fetch_frame(cap):
ret, frame = cap.read() # 讀取一幀圖像
height, width, channels = frame.shape # 獲取圖像的尺寸和通道信息
return ret, frame, height, width, channels
# 定義process_frame函數(shù),處理每幀圖像并進行目標檢測
def process_frame(frame):
# 將幀轉換為模型的輸入格式
blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False)
net.setInput(blob)
output_layers = net.getUnconnectedOutLayersNames() # 獲取輸出層的名字
layer_outputs = net.forward(output_layers) # 進行前向傳播,獲取檢測結果
boxes = [] # 用于存儲檢測到的邊界框
confidences = [] # 用于存儲邊界框的置信度
class_ids = [] # 用于存儲邊界框的類別ID
# 循環(huán)每個輸出層的檢測結果
for output in layer_outputs:
for detection in output:
scores = detection[5:] # 獲取類別的得分
class_id = np.argmax(scores) # 獲取得分最高的類別ID
confidence = scores[class_id] # 獲取得分最高的置信度
# 過濾低置信度的檢測結果
if confidence > conf_threshold:
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
# 計算邊界框的位置和尺寸
x = int(center_x - w / 2)
y = int(center_y - h / 2)
# 將邊界框的位置、尺寸、置信度和類別ID添加到列表中
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# 使用非極大值抑制去除重疊的邊界框
indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold)
# 在原圖上繪制邊界框和類別標簽
for i in indices.flatten():
box = boxes[i]
x = box[0]
y = box[1]
w = box[2]
h = box[3]
label = str(classes[class_ids[i]])
color = color_list[class_ids[i]]
cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
cv2.putText(frame, label, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
return frame
# 在進入循環(huán)前,先讀取一幀以開始異步處理
frame_future = executor.submit(fetch_frame, cap)
# 主循環(huán)
while True:
start = time.time() # 記錄開始處理的時間點
# 獲取當前幀和相應信息
ret, frame, height, width, channels = frame_future.result()
# 異步讀取下一幀
frame_future = executor.submit(fetch_frame, cap)
# 如果當前幀讀取成功,則繼續(xù)處理
if ret:
# 使用線程池異步處理當前幀
processed_frame = executor.submit(process_frame, frame).result()
# 計算FPS
end = time.time()
fps = 1 / (end - start)
# 在處理好的幀上顯示FPS
cv2.putText(processed_frame, "FPS: " + str(round(fps, 2)), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0),
2)
# 顯示處理好的幀
cv2.imshow('frame', processed_frame)
# 如果我們按下 ‘q' 鍵,退出循環(huán)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break # 如果幀沒有被成功讀取,退出循環(huán)
# 釋放視頻捕獲對象和銷毀所有OpenCV窗口
cap.release()
cv2.destroyAllWindows()
效果展示

這個效果還是不錯的哈,就是這個硬件性能跟不上,要是有獨顯就好了。
使用GPU說明
當然了,為了在大學時期狠狠的獎勵自己四年游戲,很多同學應該都是購買的游戲本,那么恭喜你,你的硬件非常的完美,那么這里你可以看一下。
如何操作
如果你想利用 GPU 加速你的目標檢測代碼,主要更改會集中在如何讓 OpenCV 利用你的 GPU。不幸的是,OpenCV 的 dnn 模塊默認使用 CPU 進行計算。想要使用 GPU,首要條件是你需要有一個支持 CUDA 的 NVIDIA GPU。
從 OpenCV 4.2 版本開始,dnn 模塊添加了對 CUDA 的支持,但實現(xiàn)這一點需要確保你的 OpenCV 是用 CUDA 支持構建的。如果你自己編譯 OpenCV,確保在編譯時啟用了 CUDA 支持。
以下是如何更改你的代碼以利用 CUDA 的基本步驟:
1.在讀取模型時啟用 CUDA:
替換代碼中的 readNet 調用,使用 readNetFromDarknet 并添加代碼來設置網(wǎng)絡的首選后端和目標為 CUDA。
# 加載預訓練的 YOLOv3 模型
net = cv2.dnn.readNetFromDarknet('../needFiles/yolov3.cfg', '../needFiles/yolov3.weights')
# 啟用 CUDA
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
2.其它代碼不需要做太多修改:
使用CUDA優(yōu)化后,主要是模型推理過程(即net.forward()的調用)會更快。數(shù)據(jù)準備和后處理(例如非極大值抑制)的代碼不需要太多修改,因為它們仍然在 CPU 上執(zhí)行。
這些改動僅在你已有 OpenCV 版本支持 CUDA,并且你的系統(tǒng)擁有合適的 NVIDIA GPU 時有效。如果你的環(huán)境滿足這些條件,這樣的更改可以顯著加快模型推理的速度,尤其是在進行視頻流處理時。
最后,你一定要確定你的環(huán)境(包括 NVIDIA 驅動程序和 CUDA Toolkit)被正確設置以支持 GPU 加速。如果你對如何編譯支持 CUDA 的 OpenCV 或如何配置你的系統(tǒng)環(huán)境有任何疑問,我建議查閱 OpenCV 官方文檔和 NVIDIA 的 CUDA 安裝指導,因為我真的沒有仔細研究過。
總結
希望這篇博客對你有所幫助。最后我想說,雖然NVIDIA對我們的學習有幫助,但是我還是想說。
到此這篇關于如何使用Python和OpenCV進行實時目標檢測的文章就介紹到這了,更多相關Python和OpenCV實時目標檢測內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python使用datetime模塊處理日期時間及常用功能詳解
datetime模塊是Python標準庫中用于處理日期和時間的模塊,在本節(jié)中,我們將介紹datetime模塊的一些常用功能,并通過實例代碼詳細講解每個知識點,有需要的朋友可以借鑒參考下,希望能夠有所幫助2023-06-06
Python實現(xiàn)根據(jù)IP地址和子網(wǎng)掩碼算出網(wǎng)段的方法
這篇文章主要介紹了Python實現(xiàn)根據(jù)IP地址和子網(wǎng)掩碼算出網(wǎng)段的方法,涉及Python基于Linux平臺的字符串操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07
Python 根據(jù)數(shù)據(jù)模板創(chuàng)建shapefile的實現(xiàn)
今天小編就為大家分享一篇Python 根據(jù)數(shù)據(jù)模板創(chuàng)建shapefile的實現(xiàn),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11

