【Yolo3】入門目標檢測實驗--Python+Opencv2+dnn

一、前言

yolo3也是目標檢測的新興算法之一。它的發展是基於HOG->CNN ->RCNN->YOLO
在這裏插入圖片描述
(圖源於網絡,侵刪)

圖像檢測發展史

  1. HOG階段(一步法):邊緣檢測+卷積神經網絡。
  2. CNN ~ R-CNN階段(兩步法):基於圖像分類加上滑動窗口。
  3. 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.下載

現在用官網示例的模型進行實驗:

  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

  2. 說明:

    • coco.names:訓練模型的所有類別名。

    • yolov3.weights:預訓練的權重。

    • yolov3.cfg:配置文件。

  3. coco.names包含:

    人 自行車 汽車 摩托車 飛機 巴士 火車 卡車 船 紅綠燈 消防栓 站牌 停車咪表 板凳 鳥 貓 狗 
    馬 羊 牛 象 熊 斑馬 長頸鹿 揹包 雨傘 手袋 領帶 手提箱 飛碟 滑雪 單板滑雪 運動的球 
    風箏 棒球棒 棒球手套 滑板 衝浪板 網球拍 瓶 酒杯 杯 叉 刀 勺 碗 香蕉 蘋果 三明治 橙 
    花椰菜 胡蘿蔔 熱狗 披薩 甜甜圈 蛋糕 椅子 沙發 盆栽植物  牀 餐桌  廁所 電視 筆記本
    鼠標 遙控 鍵盤 手機  微波 烤箱 烤麪包 片 冰箱 本書 時鐘 花瓶 剪刀 泰迪熊 吹風機 牙刷
    
  4. 參考教程:

    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

全部源碼:

  1. 圖片部分

參考:【教程】opencv-python+yolov3實現目標檢測

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)

  1. 視頻部分
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()


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章