目標檢測就是對目標進行動態實時跟蹤定位,常見的目標檢測演算法有 R-CNN、Fast R-CNN、Faster R-CNN、SSD、Yolo 等,其中 Yolo 的速度和精確度都比較高,且只需訓練一次,使用起來比較方便。
這裡我們就使用官方現成的模型來檢測圖片,看一看效果,先學會使用流程,以後再訓練自己的模型。
注意
:opencv-python 目前只有
4.4.0
版本適配了 YOLOv4
匯入庫
import numpy as np
import time
import cv2
設定標籤和標註顏色
LABELS = open(“coco。names”)。read()。strip()。split(“\n”)
np。random。seed(666)
COLORS = np。random。randint(0, 255, size=(len(LABELS), 3), dtype=“uint8”)
coco。names 內包含了很多目標標籤,如 person、bicycle、car 等,且按一定順序排列,裡面基本包含了 Yolo 官方模型中可檢測的物件。該檔案可從以下連結中提取:
https://
gitee。com/public_sharin
g/ObjectDetection-YOLO/blob/master/coco。names
每個物件配備了不一樣的顏色,以便在圖片中標記時便於區分。
載入網路
# 匯入 YOLO 配置和權重檔案並載入網路:
net = cv2。dnn_DetectionModel(‘yolov4。cfg’, ‘yolov4。weights’)
# 獲取 YOLO 未連線的輸出圖層
layer = net。getUnconnectedOutLayersNames()
yolov4。cfg 和 yolov4。weights 檔案就是官方提供的模型,下載連結:
https://
pan。baidu。com/s/1XrcPHd
p2_4c-dKge2Guw4w
提取碼:xsxb 。如果失效,可以直接百度搜索
Yolov4模型下載
,有很多人都分享出來了。
cv2。dnn_DetectionModel 是 opencv 4。1。2 開始新增的方法,用於載入網路。以前是使用 cv2。dnn。readNetFromDarknet ,此處使用也可以達到同樣的效果。
getUnconnectedOutLayersNames() 用於提取輸出圖層的名稱,yolo 含有很多的圖層,可以使用 getLayerNames() 將所有圖層的名稱提取出來。但在這裡,我們只需要使用 yolo 的最後輸出圖層。
檢測圖片
# 匯入圖片
image = cv2。imread(‘timg。jpg’)
# 獲取圖片尺寸
(H, W) = image。shape[:2]
# 從輸入影象構造一個 blob,然後執行 YOLO 物件檢測器的前向傳遞,給我們邊界盒和相關機率
blob = cv2。dnn。blobFromImage(image, 1/255。0, (416, 416),
swapRB=True, crop=False)
net。setInput(blob)
start = time。time()
# 前向傳遞,獲得資訊
layerOutputs = net。forward(layer)
# 用於得出檢測時間
end = time。time()
print(“[INFO] YOLO took {:。6f} seconds”。format(end - start))
blobFromImage 用於對影象進行預處理
cv2。dnn。blobFromImage(image[, scalefactor[, size[, mean[, swapRB[, crop[, ddepth]]]]]])
image:輸入影象
scalefactor:影象各通道數值的縮放比例
size:輸出影象的空間尺寸
mean:用於各通道減去的值,以降低光照的影響
swapRB:交換 RB 通道,預設為 False
crop:影象裁剪,預設為 False。當值為 True 時,先按比例縮放,然後從中心裁剪成 size 尺寸
ddepth:輸出的影象深度,可選 CV_32F 或者 CV_8U
資料提取
boxes = []
confidences = []
classIDs = []
# 迴圈提取每個輸出層
for output in layerOutputs:
# 迴圈提取每個框
for detection in output:
# 提取當前目標的類 ID 和置信度
scores = detection[5:]
classID = np。argmax(scores)
confidence = scores[classID]
# 透過確保檢測機率大於最小機率來過濾弱預測
if confidence > 0。5:
# 將邊界框座標相對於影象的大小進行縮放,YOLO 返回的是邊界框的中心(x, y)座標,
# 後面是邊界框的寬度和高度
box = detection[0:4] * np。array([W, H, W, H])
(centerX, centerY, width, height) = box。astype(“int”)
# 轉換出邊框左上角座標
x = int(centerX - (width / 2))
y = int(centerY - (height / 2))
# 更新邊界框座標、置信度和類 id 的列表
boxes。append([x, y, int(width), int(height)])
confidences。append(float(confidence))
classIDs。append(classID)
3 個列表內儲存的內容:
boxes:物件的邊界框
confidences :YOLO 分配給物件的置信度值,較低的置信度值表示該物件可能不是網路認為的物件。上面的程式碼中將過濾掉小於 0。5 閾值的物件
classIDs:檢測到的物件的類標籤
這樣每個被提取出的物件,都確定了標籤和區域座標就、位置。接下來就是在圖片中標記出來,便於我們觀看。
標記顯示
# 非最大值抑制,確定唯一邊框
idxs = cv2。dnn。NMSBoxes(boxes, confidences, 0。5, 0。3)
# 確定每個物件至少有一個框存在
if len(idxs) > 0:
# 迴圈畫出儲存的邊框
for i in idxs。flatten():
# 提取座標和寬度
(x, y) = (boxes[i][0], boxes[i][1])
(w, h) = (boxes[i][2], boxes[i][3])
# 畫出邊框和標籤
color = [int(c) for c in COLORS[classIDs[i]]]
cv2。rectangle(image, (x, y), (x + w, y + h), color, 1, lineType=cv2。LINE_AA)
text = “{}: {:。4f}”。format(LABELS[classIDs[i]], confidences[i])
cv2。putText(image, text, (x, y - 5), cv2。FONT_HERSHEY_SIMPLEX,
0。5, color, 1, lineType=cv2。LINE_AA)
cv2。imshow(“Tag”, image)
cv2。waitKey(0)
對於每個物件,Yolo 會框出 3 個左右的區域,我們只需要顯示出最合適的區域。非最大值抑制,就是搜尋出局部最大值,將置信度最大的框儲存,其餘剔除。
cv2。dnn。NMSBoxes(bboxes, scores, score_threshold, nms_threshold, eta=None, top_k=None)
bboxes:一組邊框
scores:一組對應的置信度
score_threshold:置信度的閾值
nms_threshold:非最大抑制的閾值
之後將每個物件的方框和標籤都畫出來
結果展示:
完整程式碼
import numpy as np
import time
import cv2
LABELS = open(“coco。names”)。read()。strip()。split(“\n”)
np。random。seed(666)
COLORS = np。random。randint(0, 255, size=(len(LABELS), 3), dtype=“uint8”)
# 匯入 YOLO 配置和權重檔案並載入網路:
net = cv2。dnn_DetectionModel(‘yolov4。cfg’, ‘yolov4。weights’)
# 獲取 YOLO 未連線的輸出圖層
layer = net。getUnconnectedOutLayersNames()
image = cv2。imread(‘timg。jpg’)
# 獲取圖片尺寸
(H, W) = image。shape[:2]
# 從輸入影象構造一個 blob,然後執行 YOLO 物件檢測器的前向傳遞,給我們邊界盒和相關機率
blob = cv2。dnn。blobFromImage(image, 1/255。0, (416, 416),
swapRB=True, crop=False)
net。setInput(blob)
start = time。time()
# 前向傳遞,獲得資訊
layerOutputs = net。forward(layer)
# 用於得出檢測時間
end = time。time()
print(“YOLO took {:。6f} seconds”。format(end - start))
boxes = []
confidences = []
classIDs = []
# 迴圈提取每個輸出層
for output in layerOutputs:
# 迴圈提取每個框
for detection in output:
# 提取當前目標的類 ID 和置信度
scores = detection[5:]
classID = np。argmax(scores)
confidence = scores[classID]
# 透過確保檢測機率大於最小機率來過濾弱預測
if confidence > 0。5:
# 將邊界框座標相對於影象的大小進行縮放,YOLO 返回的是邊界框的中心(x, y)座標,
# 後面是邊界框的寬度和高度
box = detection[0:4] * np。array([W, H, W, H])
(centerX, centerY, width, height) = box。astype(“int”)
# 轉換出邊框左上角座標
x = int(centerX - (width / 2))
y = int(centerY - (height / 2))
# 更新邊界框座標、置信度和類 id 的列表
boxes。append([x, y, int(width), int(height)])
confidences。append(float(confidence))
classIDs。append(classID)
# 非最大值抑制,確定唯一邊框
idxs = cv2。dnn。NMSBoxes(boxes, confidences, 0。5, 0。3)
# 確定每個物件至少有一個框存在
if len(idxs) > 0:
# 迴圈畫出儲存的邊框
for i in idxs。flatten():
# 提取座標和寬度
(x, y) = (boxes[i][0], boxes[i][1])
(w, h) = (boxes[i][2], boxes[i][3])
# 畫出邊框和標籤
color = [int(c) for c in COLORS[classIDs[i]]]
cv2。rectangle(image, (x, y), (x + w, y + h), color, 1, lineType=cv2。LINE_AA)
text = “{}: {:。4f}”。format(LABELS[classIDs[i]], confidences[i])
cv2。putText(image, text, (x, y - 5), cv2。FONT_HERSHEY_SIMPLEX,
0。5, color, 1, lineType=cv2。LINE_AA)
cv2。imshow(“Tag”, image)
cv2。waitKey(0)
原始碼地址