opencv調用yolov3模型深度學習目標檢測實例詳解

引言

opencv調用yolov3模型進行深度學習目標檢測,以實例進行代碼詳解

對於yolo v3已經訓練好的模型,opencv提供瞭加載相關文件,進行圖片檢測的類dnn。 下面對怎麼通過opencv調用yolov3模型進行目標檢測方法進行詳解,付源代碼

建立相關目錄

在訓練結果backup文件夾下,找到模型權重文件,拷到win的工程文件夾下 在cfg文件夾下,找到模型配置文件,yolov3-voc.cfg拷到win的工程文件夾下 在data文件夾下,找到voc.names,類別標簽文件,拷到win的工程文件夾下

代碼詳解

weightsPath='E:\deep_learn\yolov3_modeFile\yolov3-voc_25000.weights'# 模型權重文件
configPath="E:\deep_learn\yolov3_modeFile\yolov3-voc.cfg"# 模型配置文件
labelsPath = "E:\\deep_learn\\yolov3_modeFile\\voc.names"# 模型類別標簽文件

引入模型的相關文件,這裡需要使用yolo v3訓練模型的三個文件

(1)模型權重文件 name.weights

(2)訓練模型時的配置文件 yolov3-voc.cfg(一定和訓練時一致,後面會提原因)

(3)模型類別的標簽文件 voc.names

LABELS = open(labelsPath).read().strip().split("\n")

從voc.names中得到標簽的數組LABELS 我的模型識別的是車和人 voc,names文件內容

LABELS數組內容

COLORS = np.random.randint(0, 255, size=(len(LABELS), 3),dtype="uint8")#顏色  隨機生成顏色框

根據類別個數隨機生成幾個顏色 ,用來後期畫矩形框 [[ 33 124 191] [211 63 59]]

boxes = []
confidences = []
classIDs = []

聲明三個數組 (1)boxes 存放矩形框信息 (2)confidences 存放框的置信度 (3)classIDs 存放框的類別標簽 三個數組元素一一對應,即boxes[0]、confidences[0]、classIDs[0]對應一個識別目標的信息,後期根據該信息在圖片中畫出識別目標的矩形框

net = cv2.dnn.readNetFromDarknet(configPath,weightsPath)

加載 網絡配置與訓練的權重文件 構建網絡 註意此處opencv2.7不行 ,沒有dnn這個類,最好opencv版本在4.0以上,對應python用3.0以上版本

image = cv2.imread('E:\deep_learn\yolov3_detection_image\R1_WH_ZW_40_80_288.jpg')
(H,W) = image.shape[0:2]

讀入待檢測的圖片,得到圖像的高和寬

ln = net.getLayerNames()

得到 YOLO各層的名稱,之後從各層名稱中找到輸出層

可以看到yolo的各層非常多,紅框圈的'yolo_94'、'yolo_106'即為輸出層,下面就需要通過代碼找到這三個輸出層,為什麼是三個?跟yolo的框架結構有關,yolo有三個輸出。對應的我們在訓練模型時修改 yolov3-voc.cfg文件,修改的filters、classes也是三處,詳細參考darknet YOLOv3數據集訓練預測8. 修改./darknet/cfg/yolov3-voc.cfg文件

下面就是在yolo的所有層名稱ln中找出三個輸出層,代碼如下

out = net.getUnconnectedOutLayers()#得到未連接層得序號
x = []
for i in out:   # i=[200]
    x.append(ln[i[0]-1])    # i[0]-1    取out中的數字  [200][0]=200  ln(199)= 'yolo_82'
ln=x

yolo的輸出層是未連接層的前一個元素,通過net.getUnconnectedOutLayers()找到未連接層的序號out= [[200] /n [267] /n [400] ],循環找到所有的輸出層,賦值給ln 最終ln = ['yolo_82', 'yolo_94', 'yolo_106'] 接下來就是將圖像轉化為輸入的標準格式

blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416),swapRB=True, crop=False)

用需要檢測的原始圖像image構造一個blob圖像,對原圖像進行像素歸一化1 / 255.0,縮放尺寸 (416, 416),,對應訓練模型時cfg的文件 交換瞭R與G通道

交換R與G通道通道是opencv在打開圖片時交換瞭一次,此處交換即又換回來瞭 此時blob.shape=(1, 3, 416, 416),四維。 可以用numpy裡的squeeze()函數把秩為1的維度去掉,然後顯示圖片出來看看

image_blob = np.squeeze(blob)
cv2.namedWindow('image_blob', cv2.WINDOW_NORMAL)
cv2.imshow('image_blob',np.transpose(image_blob,[1,2,0]))
cv2.waitKey(0)

net.setInput(blob) #將blob設為輸入
layerOutputs = net.forward(ln)  #ln此時為輸出層名稱  ,向前傳播  得到檢測結果

將blob設為輸入 ln此時為輸出層名稱 ,向前傳播 得到檢測結果。 此時layerOutputs即三個輸出的檢測結果,

layerOutputs是一個含有三個矩陣的列表變量,三個矩陣對應三個層的檢測結果,其中一層的檢測結果矩陣如下圖

是個507*7的矩陣,這個矩陣代表著檢測結果,其中507就是這層檢測到瞭507個結果(即507個矩形框),其中7就是矩形框的信息,為什麼是7呢,這裡解釋下,7=5+2,5是矩形框(x,y,w,h,c)2是2個類別分別的置信度(class0、class1). 所以每一行代表一個檢測結果。

接下來就是對檢測結果進行處理與顯示 在檢測結果中會有很多每個類的置信度為0的矩形框,要把這些與置信度較低的框去掉

#接下來就是對檢測結果進行處理
for output in layerOutputs:  #對三個輸出層 循環
    for detection in output:  #對每個輸出層中的每個檢測框循環
        scores=detection[5:]  #detection=[x,y,h,w,c,class1,class2]
        classID = np.argmax(scores)#np.argmax反饋最大值的索引
        confidence = scores[classID]
        if confidence >0.5:#過濾掉那些置信度較小的檢測結果
            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)

現在就將網絡的檢測結果提取瞭出來,框、置信度、類別。 可以先畫一下看下效果

a=0
for box in  boxes:#將每個框畫出來
    a=a+1
    (x,y)=(box[0],box[1])#框左上角
    (w,h)=(box[2],box[3])#框寬高
    if classIDs[a-1]==0: #根據類別設定框的顏色
        color = [0,0,255]
    else:
        color = [0, 255, 0]
    cv2.rectangle(image, (x, y), (x + w, y + h), color, 2) #畫框
    text = "{}: {:.4f}".format(LABELS[classIDs[a-1]], confidences[a-1])
    cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.3, color, 1)#寫字
cv2.namedWindow('Image', cv2.WINDOW_NORMAL)
cv2.imshow("Image", image)
cv2.waitKey(0)

結果:

可以看到對於同一目標有幾個矩形框,這需要對框進行非極大值抑制處理。 進行非極大值抑制的操作,opencv的dnn有個直接的函數 NMSBoxes(bboxes, scores, score_threshold, nms_threshold, eta=None, top_k=None) bboxes需要操作的各矩形框對應咱程序的boxes scores矩形框對應的置信度對應咱程序的confidences score_threshold置信度的閾值,低於這個閾值的框直接刪除 nms_threshold nms的閾值 非極大值的原理沒有理解的話,裡面的參數不好設置。 下面簡單說下非極大值抑制的原理

1)先對輸入檢測框按置信度由高到低排序

2)挑選第一個檢測框(即最高置信度,記為A)和其它檢測框(記為B)進行iou計算

3)如果iou大於nmsThreshold, 那就將B清除掉

4)跳轉到2)從剩餘得框集裡面找置信度最大得框和其它框分別計算iou

5)直到所有框都過濾完 NMSBoxes()函數返回值為最終剩下的按置信度由高到低的矩形框的序列號 進行非極大值抑制後,顯示部分代碼改一部分即可。

直接給出代碼

idxs=cv2.dnn.NMSBoxes(boxes, confidences, 0.2,0.3)
box_seq = idxs.flatten()#[ 2  9  7 10  6  5  4]
if len(idxs)>0:
    for seq in box_seq:
        (x, y) = (boxes[seq][0], boxes[seq][1])  # 框左上角
        (w, h) = (boxes[seq][2], boxes[seq][3])  # 框寬高
        if classIDs[seq]==0: #根據類別設定框的顏色
            color = [0,0,255]
        else:
            color = [0,255,0]
        cv2.rectangle(image, (x, y), (x + w, y + h), color, 2)  # 畫框
        text = "{}: {:.4f}".format(LABELS[classIDs[seq]], confidences[seq])
        cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.3, color, 1)  # 寫字
cv2.namedWindow('Image', cv2.WINDOW_NORMAL)
cv2.imshow("Image", image)
cv2.waitKey(0)

最終的檢測結果

至此及用opencv加載yolo v3的模型,進行瞭一次圖片的檢測。

附源代碼

#coding:utf-8
import numpy as np
import cv2
import os
weightsPath='E:\deep_learn\yolov3_modeFile\yolov3-voc_25000.weights'# 模型權重文件
configPath="E:\deep_learn\yolov3_modeFile\yolov3-voc.cfg"# 模型配置文件
labelsPath = "E:\\deep_learn\\yolov3_modeFile\\voc.names"# 模型類別標簽文件
#初始化一些參數
LABELS = open(labelsPath).read().strip().split("\n")
boxes = []
confidences = []
classIDs = []
#加載 網絡配置與訓練的權重文件 構建網絡
net = cv2.dnn.readNetFromDarknet(configPath,weightsPath)  
#讀入待檢測的圖像
image = cv2.imread('E:\deep_learn\yolov3_detection_image\R1_WH_ZW_40_80_288.jpg')
#得到圖像的高和寬
(H,W) = image.shape[0:2]
# 得到 YOLO需要的輸出層
ln = net.getLayerNames()
out = net.getUnconnectedOutLayers()#得到未連接層得序號  [[200] /n [267]  /n [400] ]
x = []
for i in out:   # 1=[200]
    x.append(ln[i[0]-1])    # i[0]-1    取out中的數字  [200][0]=200  ln(199)= 'yolo_82'
ln=x
# ln  =  ['yolo_82', 'yolo_94', 'yolo_106']  得到 YOLO需要的輸出層
#從輸入圖像構造一個blob,然後通過加載的模型,給我們提供邊界框和相關概率
#blobFromImage(image, scalefactor=None, size=None, mean=None, swapRB=None, crop=None, ddepth=None)
blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416),swapRB=True, crop=False)#構造瞭一個blob圖像,對原圖像進行瞭圖像的歸一化,縮放瞭尺寸 ,對應訓練模型
net.setInput(blob) #將blob設為輸入??? 具體作用還不是很清楚
layerOutputs = net.forward(ln)  #ln此時為輸出層名稱  ,向前傳播  得到檢測結果
for output in layerOutputs:  #對三個輸出層 循環
    for detection in output:  #對每個輸出層中的每個檢測框循環
        scores=detection[5:]  #detection=[x,y,h,w,c,class1,class2] scores取第6位至最後
        classID = np.argmax(scores)#np.argmax反饋最大值的索引
        confidence = scores[classID]
        if confidence >0.5:#過濾掉那些置信度較小的檢測結果
            box = detection[0:4] * np.array([W, H, W, H])
            #print(box)
            (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)
idxs=cv2.dnn.NMSBoxes(boxes, confidences, 0.2,0.3)
box_seq = idxs.flatten()#[ 2  9  7 10  6  5  4]
if len(idxs)>0:
    for seq in box_seq:
        (x, y) = (boxes[seq][0], boxes[seq][1])  # 框左上角
        (w, h) = (boxes[seq][2], boxes[seq][3])  # 框寬高
        if classIDs[seq]==0: #根據類別設定框的顏色
            color = [0,0,255]
        else:
            color = [0,255,0]
        cv2.rectangle(image, (x, y), (x + w, y + h), color, 2)  # 畫框
        text = "{}: {:.4f}".format(LABELS[classIDs[seq]], confidences[seq])
        cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.3, color, 1)  # 寫字
cv2.namedWindow('Image', cv2.WINDOW_NORMAL)
cv2.imshow("Image", image)
cv2.waitKey(0)

以上就是opencv調用yolov3模型深度學習目標檢測實例詳解的詳細內容,更多關於opencv調用yolov3目標檢測的資料請關註WalkonNet其它相關文章!

推薦閱讀: