一、前言
yolo3也是目標檢測的新興算法之一。它的發展是基於HOG->CNN ->RCNN->YOLO
。
(圖源於網絡,侵刪)
圖像檢測發展史:
- HOG階段(一步法):邊緣檢測+卷積神經網絡。
- CNN ~ R-CNN階段(兩步法):基於圖像分類加上滑動窗口。
- YOLO:區域推薦(RP)。
1 , 2 方法缺點:計算量比較大,導致性能低下無法實時;YOLO採樣了一種完全不同的方法,達到對圖像每個區域只計算一次(You Look at Once - YOLO)。
YOLO3實現過程:
圖1:YOLO把圖像分爲13x13的Cell(網格);
圖2:每個Cell預測5個BOX,同時YOLO也會生成一個置信分數,告訴每個BOX包含某個對象的可能性是多少;
(注意置信分數不會直接說明BOX內是檢測到何種對象,最終那些得分高的BOX被加粗顯示)
圖3:對於每個BOX來說,Cell會預測檢測對象類別,這部分的工作就像是一個分類器一樣;
(基於VOC數據集20中對象檢測,YOLO結合分數與分類信息對每個BOX給出一個最終可能對象類型的可能性值,黃色標註區域85%可能性是狗)
因爲總數是13x13的網格,每個網格預言5個BOX,所以最終有854個BOX,證據表明絕大多數的BOX得分會很低,我們只要保留30%BOX即可(取決於你自己的閾值設置),最終輸出:
此部分引用出處:
OpenCV DNN之YOLO實時對象檢測
YOLO3訓練原理:
參考:YOLOv3 深入理解
後面我會標註訓練自己的手語數據集,這裏不擴展。
二、入門實驗coco數據集
1.下載
現在用官網示例的模型進行實驗:
-
下載:
https://pjreddie.com/media/files/yolov3.weights
(官網很慢的,可以在 百度網盤 中下載,好心人一生平安!)https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg
https://github.com/pjreddie/darknet/blob/master/data/coco.names
-
說明:
-
coco.names:訓練模型的所有類別名。
-
yolov3.weights:預訓練的權重。
-
yolov3.cfg:配置文件。
-
-
coco.names包含:
人 自行車 汽車 摩托車 飛機 巴士 火車 卡車 船 紅綠燈 消防栓 站牌 停車咪表 板凳 鳥 貓 狗 馬 羊 牛 象 熊 斑馬 長頸鹿 揹包 雨傘 手袋 領帶 手提箱 飛碟 滑雪 單板滑雪 運動的球 風箏 棒球棒 棒球手套 滑板 衝浪板 網球拍 瓶 酒杯 杯 叉 刀 勺 碗 香蕉 蘋果 三明治 橙 花椰菜 胡蘿蔔 熱狗 披薩 甜甜圈 蛋糕 椅子 沙發 盆栽植物 牀 餐桌 廁所 電視 筆記本 鼠標 遙控 鍵盤 手機 微波 烤箱 烤麪包 片 冰箱 本書 時鐘 花瓶 剪刀 泰迪熊 吹風機 牙刷
-
參考教程:
https://www.learnopencv.com/deep-learning-based-object-detection-using-yolov3-with-opencv-python-c/
2.食用
單圖測試:識別率99.9%,驚人!
測試圖2:
狗:98%、貓:67%(識別錯誤,這應該是狗狗)
鳥:57%、
其他動物:太小,角度等,未能識別,這都是我們在製作模型,標註圖像需要注意的地方
視頻檢測:
原諒我睡衣出境,不過person和cellphone的檢測效果挺好的。
3.源碼
相關筆記:
https://gitee.com/cungudafa/Python-notes/blob/master/yolov3/yolotest.ipynb
全部源碼:
- 圖片部分
import numpy as np
import cv2 as cv
import os
import time
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用來正常顯示中文標籤
plt.rcParams['axes.unicode_minus']=False #用來正常顯示負號
#參數
yolo_dir = 'D:/myworkspace/dataset/yolov3/' # 你下載YOLO權重的文件路徑
CONFIDENCE = 0.5 # 過濾弱檢測的最小概率
THRESHOLD = 0.4 # 非最大值抑制閾值
def yolov3(imgPath,yolo_dir,CONFIDENCE,THRESHOLD):
weightsPath = os.path.join(yolo_dir, 'yolov3.weights') # 權重文件
configPath = os.path.join(yolo_dir, 'yolov3.cfg') # 配置文件
labelsPath = os.path.join(yolo_dir, 'coco.names') # label名稱
# 加載網絡、配置權重
net = cv.dnn.readNetFromDarknet(configPath, weightsPath) ## 利用下載的文件
print("[INFO] loading YOLO from disk...") ## 可以打印下信息
# 加載圖片、轉爲blob格式、送入網絡輸入層
img = cv.imread(imgPath)
blobImg = cv.dnn.blobFromImage(img, 1.0/255.0, (416, 416), None, True, False) ## net需要的輸入是blob格式的,用blobFromImage這個函數來轉格式
net.setInput(blobImg) ## 調用setInput函數將圖片送入輸入層
# 獲取網絡輸出層信息(所有輸出層的名字),設定並前向傳播
outInfo = net.getUnconnectedOutLayersNames() ## 前面的yolov3架構也講了,yolo在每個scale都有輸出,outInfo是每個scale的名字信息,供net.forward使用
start = time.time()
layerOutputs = net.forward(outInfo) # 得到各個輸出層的、各個檢測框等信息,是二維結構。
end = time.time()
print("[INFO] YOLO took {:.6f} seconds".format(end - start)) ## 可以打印下信息
# 拿到圖片尺寸
(H, W) = img.shape[:2]
# 過濾layerOutputs
# layerOutputs的第1維的元素內容: [center_x, center_y, width, height, objectness, N-class score data]
# 過濾後的結果放入:
boxes = [] # 所有邊界框(各層結果放一起)
confidences = [] # 所有置信度
classIDs = [] # 所有分類ID
# # 1)過濾掉置信度低的框框
for out in layerOutputs: # 各個輸出層
for detection in out: # 各個框框
# 拿到置信度
scores = detection[5:] # 各個類別的置信度
classID = np.argmax(scores) # 最高置信度的id即爲分類id
confidence = scores[classID] # 拿到置信度
# 根據置信度篩查
if confidence > CONFIDENCE:
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))
boxes.append([x, y, int(width), int(height)])
confidences.append(float(confidence))
classIDs.append(classID)
# # 2)應用非最大值抑制(non-maxima suppression,nms)進一步篩掉
idxs = cv.dnn.NMSBoxes(boxes, confidences, CONFIDENCE, THRESHOLD) # boxes中,保留的box的索引index存入idxs
# 得到labels列表
with open(labelsPath, 'rt') as f:
labels = f.read().rstrip('\n').split('\n')
# 應用檢測結果
np.random.seed(42)
COLORS = np.random.randint(0, 255, size=(len(labels), 3), dtype="uint8") # 框框顯示顏色,每一類有不同的顏色,每種顏色都是由RGB三個值組成的,所以size爲(len(labels), 3)
if len(idxs) > 0:
for i in idxs.flatten(): # indxs是二維的,第0維是輸出層,所以這裏把它展平成1維
(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]]]
cv.rectangle(img, (x, y), (x+w, y+h), color, 2) # 線條粗細爲2px
text = "{}: {:.4f}".format(labels[classIDs[i]], confidences[i])
print("[INFO] predect result is: ",text)
(text_w, text_h), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)
cv2.rectangle(img, (x, y-text_h-baseline), (x + text_w, y), color, -1)
cv.putText(img, text, (x, y-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,0), 2) # cv.FONT_HERSHEY_SIMPLEX字體風格、0.5字體大小、粗細2px
# cv.imshow('detected image', img)
# cv.waitKey(0)
plt.figure(figsize=[10, 10])
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.axis("off")
plt.show()
imgPath = os.path.join(yolo_dir, 'test.jpg') # 測試圖像
yolov3(imgPath,yolo_dir,CONFIDENCE,THRESHOLD)
imgPath = os.path.join(yolo_dir, 'test2.jpg') # 測試圖像
yolov3(imgPath,yolo_dir,CONFIDENCE,THRESHOLD)
- 視頻部分
import numpy as np
import cv2 as cv
import os
import time
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用來正常顯示中文標籤
plt.rcParams['axes.unicode_minus']=False #用來正常顯示負號
#參數
yolo_dir = 'D:/myworkspace/dataset/yolov3/' # YOLO文件路徑
CONFIDENCE = 0.5 # 過濾弱檢測的最小概率
THRESHOLD = 0.4 # 非最大值抑制閾值
def yolov3_vedio(img,yolo_dir,CONFIDENCE,THRESHOLD):
weightsPath = os.path.join(yolo_dir, 'yolov3.weights') # 權重文件
configPath = os.path.join(yolo_dir, 'yolov3.cfg') # 配置文件
labelsPath = os.path.join(yolo_dir, 'coco.names') # label名稱
# 加載網絡、配置權重
net = cv.dnn.readNetFromDarknet(configPath, weightsPath) ## 利用下載的文件
print("[INFO] loading YOLO from disk...") ## 可以打印下信息
blobImg = cv.dnn.blobFromImage(img, 1.0/255.0, (416, 416), None, True, False) ## net需要的輸入是blob格式的,用blobFromImage這個函數來轉格式
net.setInput(blobImg) ## 調用setInput函數將圖片送入輸入層
# 獲取網絡輸出層信息(所有輸出層的名字),設定並前向傳播
outInfo = net.getUnconnectedOutLayersNames() ## 前面的yolov3架構也講了,yolo在每個scale都有輸出,outInfo是每個scale的名字信息,供net.forward使用
start = time.time()
layerOutputs = net.forward(outInfo) # 得到各個輸出層的、各個檢測框等信息,是二維結構。
end = time.time()
print("[INFO] YOLO took {:.6f} seconds".format(end - start)) ## 可以打印下信息
# 拿到圖片尺寸
(H, W) = img.shape[:2]
# 過濾layerOutputs
# layerOutputs的第1維的元素內容: [center_x, center_y, width, height, objectness, N-class score data]
# 過濾後的結果放入:
boxes = [] # 所有邊界框(各層結果放一起)
confidences = [] # 所有置信度
classIDs = [] # 所有分類ID
# # 1)過濾掉置信度低的框框
for out in layerOutputs: # 各個輸出層
for detection in out: # 各個框框
# 拿到置信度
scores = detection[5:] # 各個類別的置信度
classID = np.argmax(scores) # 最高置信度的id即爲分類id
confidence = scores[classID] # 拿到置信度
# 根據置信度篩查
if confidence > CONFIDENCE:
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))
boxes.append([x, y, int(width), int(height)])
confidences.append(float(confidence))
classIDs.append(classID)
# # 2)應用非最大值抑制(non-maxima suppression,nms)進一步篩掉
idxs = cv.dnn.NMSBoxes(boxes, confidences, CONFIDENCE, THRESHOLD) # boxes中,保留的box的索引index存入idxs
# 得到labels列表
with open(labelsPath, 'rt') as f:
labels = f.read().rstrip('\n').split('\n')
# 應用檢測結果
np.random.seed(42)
COLORS = np.random.randint(0, 255, size=(len(labels), 3), dtype="uint8") # 框框顯示顏色,每一類有不同的顏色,每種顏色都是由RGB三個值組成的,所以size爲(len(labels), 3)
if len(idxs) > 0:
for i in idxs.flatten(): # indxs是二維的,第0維是輸出層,所以這裏把它展平成1維
(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]]]
cv.rectangle(img, (x, y), (x+w, y+h), color, 2) # 線條粗細爲2px
text = "{}: {:.4f}".format(labels[classIDs[i]], confidences[i])
print("[INFO] predect result is: ",text)
(text_w, text_h), baseline = cv.getTextSize(text, cv.FONT_HERSHEY_SIMPLEX, 0.5, 2)
cv.rectangle(img, (x, y-text_h-baseline), (x + text_w, y), color, -1)
cv.putText(img, text, (x, y-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,0), 2) # cv.FONT_HERSHEY_SIMPLEX字體風格、0.5字體大小、粗細2px
return img
cap = cv.VideoCapture(0)
while(True):
ret, frame = cap.read() #讀取幀
yolov3_vedio(frame,yolo_dir,CONFIDENCE,THRESHOLD)
cv.imshow('frame',frame)
if cv.waitKey(1) & 0xFF == ord('q'): #按‘q’退出
break
#釋放資源並關閉窗口
cap.release()
cv.destroyAllWindows()