1. 程式人生 > >一文帶你學會使用YOLO及Opencv完成影象及視訊流目標檢測(上)|附原始碼

一文帶你學會使用YOLO及Opencv完成影象及視訊流目標檢測(上)|附原始碼

計算機視覺領域中,目標檢測一直是工業應用上比較熱門且成熟的應用領域,比如人臉識別、行人檢測等,國內的曠視科技、商湯科技等公司在該領域佔據行業領先地位。相對於影象分類任務而言,目標檢測會更加複雜一些,不僅需要知道這是哪一類影象,而且要知道影象中所包含的內容有什麼及其在影象中的位置,因此,其工業應用比較廣泛。那麼,今天將向讀者介紹該領域中表現優異的一種算演算法——“你只需要看一次”(you only look once,yolo),提出該演算法的作者風趣幽默可愛,其個人主頁及論文風格顯示了其性情,目前該演算法已是第三個版本,簡稱YoLo V3。閒話少敘,下面進入教程的主要內容。
在本教程中,將學習如何使用YOLO、OpenCV和Python檢測影象和視訊流中的物件。主要內容有:

  • 簡要討論YOLO演算法;
  • 使用YOLO、OpenCV、Python進行影象檢測;
  • 使用YOLO、OpenCV、Python進行視訊流檢測;
  • 討論YOLO演算法的優點和缺點;

什麼是YOLO?

 

2
圖1: YOLO目標檢測器簡化示意圖


當涉及基於深度學習的物件檢測時,常用的三類演算法有:

  • R-CNN家族系列演算法:R-CNN、fast R-CNN以及faster R-CNN;
  • 單發檢測器(SSD);
  • YOLO演算法;
    R-CNN演算法是最早的基於深度學習的目標檢測器之一,其結構是兩級網路:
  • 首先需要諸如選擇性搜尋之類的演算法來提出可能包含物件的候選邊界框;
  • 然後將這些區域傳遞到CNN演算法進行分類;

R-CNN演算法存在的問題在於其模擬很慢,並且不是完整的端到端的目標檢測器。
Fast R-CNN演算法對原始R-CNN進行了相當大的改進,即提高準確度並減少執行正向傳遞所花費的時間,但是,該模型仍然依賴於外部區域搜尋演算法。
直到2015年,[faster R-CNN]()才成為真正的端到端深度學習目標檢測器,刪除了選擇性搜尋的要求,而是依賴於(1)完全卷積的區域提議網路(RPN)和(2)可以預測物件邊界框和“物件”分數(量化它是一個區域的可能性的分數)。然後將RPN的輸出傳遞到R-CNN元件以進行最終分類和標記。
R-CNN系列演算法的檢測結果一般都非常準確,但R-CNN系列演算法最大的問題在模擬速度——非常慢,即使是在GPU上也僅獲得5 FPS。
為了提高基於深度學習的目標檢測器的速度,單次檢測器(SSD)和YOLO都使用單級檢測器策略(one stage)。這類演算法將物件檢測視為迴歸問題,獲取給定的輸入影象並同時學習邊界框座標和相應的類標籤概率。通常,單級檢測器往往不如兩級檢測器準確,但其速度明顯更快。YOLO是單級檢測器中一個很好的演算法。

YOLO演算法於2015年提出,在GPU上獲得了  45 FPS效能,此外,同時也提出了一個較小的變體稱為“Fast YOLO”,在GPU上達到155 FPS的效能。
YOLO經歷了許多次的迭代,包括YOLOv2,能夠檢測超過9,000個目標。直到最近提出的YOLOv3演算法,YOLOv3模型比之前的版本要複雜得多,但它是YOLO系列目標檢測器中最好的一款。
本文使用YOLOv3,並在COCO資料集上進行訓練。
COCO資料集由80個標籤組成,可以使用此連結找到YOLO在COCO資料集上訓練的內容的完整列表。

專案結構

在終端中使用tree命令,可以很方便快捷地生成目標樹:

$ tree
.
├── images
│   ├── baggage_claim.jpg
│   ├── dining_table.jpg
│   ├── living_room.jpg
│   └── soccer.jpg
├── output
│   ├── airport_output.avi
│   ├── car_chase_01_output.avi
│   ├── car_chase_02_output.avi
│   └── overpass_output.avi
├── videos
│   ├── airport.mp4
│   ├── car_chase_01.mp4
│   ├── car_chase_02.mp4
│   └── overpass.mp4
├── yolo-coco
│   ├── coco.names
│   ├── yolov3.cfg
│   └── yolov3.weights
├── yolo.py
└── yolo_video.py

從上面可以看出,專案包括4個資料夾和2個Python指令碼。
目錄(按重要性順序)是:

  • yolo - coco /  :YOLOv3物件檢測器預先(在COCO資料集上)訓練得到最終的權重檔案,可以在Darknet團隊主頁找到對應的檔案;
  • images / :此資料夾包含四個靜態影象,之後將執行物件檢測以進行測試和評估;
  • videos/ :使用YOLO對影象進行目標檢測器後,將實時處理視訊。該資料夾中包含四個示例視訊可供測試;
  • 輸出/  :輸出已由YOLO處理並帶有邊界框和類名稱註釋的視訊可以放在此資料夾中;

此外還有兩個Python指令碼——yolo .py和 yolo_video.py ,第一個指令碼用於影象處理,第二個指令碼用於視訊處理。下面進入實戰內容,你準備好了嗎?

將YOLO應用於影象物件檢測

首先將YOLO目標檢測器應用於影象中,首先開啟專案中的 yolo .py並插入以下程式碼:

# import the necessary packages
import numpy as np
import argparse
import time import cv2 import os # construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True, help="path to input image") ap.add_argument("-y", "--yolo", required=True, help="base path to YOLO directory") ap.add_argument("-c", "--confidence", type=float, default=0.5, help="minimum probability to filter weak detections") ap.add_argument("-t", "--threshold", type=float, default=0.3, help="threshold when applying non-maxima suppression") args = vars(ap.parse_args())

在使用之前,需要為此指令碼安裝 3.4.2+版本以上的OpenCV,可以直接使用
pip install opencv-python==3.4.2安裝,你也可以在這裡找到OpenCV安裝教程,這裡注意一點,OpenCV 4目前處於測試階段,這裡建議去安裝OpenCV 3.4.2+。
首先,匯入所需的資料包——OpenCV和NumPy。現在解析四個命令列引數,命令列引數在執行時處理,允許我們從終端更改指令碼的輸入。如果你對其不熟悉,建議閱讀相關的內容。命令列引數包括:

  • -- image  :輸入影象的路徑;
  • -- yolo  :YOLO檔案路徑,指令碼將載入所需的YOLO檔案,以便在影象上執行物件檢測;
  • -- confidence  :過濾弱檢測的最小概率,預設值設定為0.5,但該值也可以隨意設定;
  • -- threshold  :非最大值抑制閾值,預設值設定為 0.3,可以在此處閱讀有關非最大值抑制的更多資訊。

解析之後,args變數是一個包含命令列引數的鍵值對的字典。下面為每個標籤設定隨機顏色:

# load the COCO class labels our YOLO model was trained on
labelsPath = os.path.sep.join([args["yolo"], "coco.names"])
LABELS = open(labelsPath).read().strip().split("\n")
 
# initialize a list of colors to represent each possible class label np.random.seed(42) COLORS = np.random.randint(0, 255, size=(len(LABELS), 3), dtype="uint8")

上述載入所有類 LABELS,其型別是列表,儲存的是類別名稱,然後將隨機顏色分配給每個標籤  。下面設定YOLO權重和配置檔案的路徑,然後從磁碟載入YOLO檔案:

# derive the paths to the YOLO weights and model configuration
weightsPath = os.path.sep.join([args["yolo"], "yolov3.weights"])
configPath = os.path.sep.join([args["yolo"], "yolov3.cfg"]) # load our YOLO object detector trained on COCO dataset (80 classes) print("[INFO] loading YOLO from disk...") net = cv2.dnn.readNetFromDarknet(configPath, weightsPath)

從磁碟載入YOLO檔案後,並利用OpenCV中的cv2.dnn.readNetFromDarknet函式從中讀取網路檔案及權重引數,此函式需要兩個引數configPath 和 weightsPath,這裡再次強調,:OpenCV 的版本至少是3.4.2及以上才能執行此程式碼,因為它需要載入YOLO所需的更新的 dnn模組。
下面載入影象並處理:

# load our input image and grab its spatial dimensions
image = cv2.imread(args["image"])
(H, W) = image.shape[:2]
 
# determine only the *output* layer names that we need from YOLO
ln = net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()] # construct a blob from the input image and then perform a forward # pass of the YOLO object detector, giving us our bounding boxes and # associated probabilities blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416), swapRB=True, crop=False) net.setInput(blob) start = time.time() layerOutputs = net.forward(ln) end = time.time() # show timing information on YOLO print("[INFO] YOLO took {:.6f} seconds".format(end - start))

在該程式碼中:

  • 載入輸入 影象並獲得其尺寸;
  • 確定YOLO模型中的輸出圖層名稱;
  • 從影象構造一個 blob結構;

如果你對blob和cv2.dnn.blobFromImage有疑問,可以看這篇部落格進一步的瞭解。
當blob準備好了後,我們就會

  • 通過YOLO網路進行前向傳遞;
  • 顯示YOLO的推理時間;
    現在採取措施來過濾和視覺化最終的結果。首先,讓我們初步化一些處理過程中需要的列表:
# initialize our lists of detected bounding boxes, confidences, and
# class IDs, respectively
boxes = []
confidences = []
classIDs = []

這些列表包括:

  • boxes  :物件的邊界框。
  • confidences  :YOLO分配給物件的置信度值,較低的置信度值表示該物件可能不是網路認為的物件。上面的命令列引數中將過濾掉不大於 0.5閾值的物件。
  • classIDs  :檢測到的物件的類標籤。

下面用YOLO layerOutputs中的資料填充這些列表 :

# loop over each of the layer outputs
for output in layerOutputs:
    # loop over each of the detections
    for detection in output: # extract the class ID and confidence (i.e., probability) of # the current object detection scores = detection[5:] classID = np.argmax(scores) confidence = scores[classID] # filter out weak predictions by ensuring the detected # probability is greater than the minimum probability if confidence > args["confidence"]: # scale the bounding box coordinates back relative to the # size of the image, keeping in mind that YOLO actually # returns the center (x, y)-coordinates of the bounding # box followed by the boxes' width and height box = detection[0:4] * np.array([W, H, W, H]) (centerX, centerY, width, height) = box.astype("int") # use the center (x, y)-coordinates to derive the top and # and left corner of the bounding box x = int(centerX - (width / 2)) y = int(centerY - (height / 2)) # update our list of bounding box coordinates, confidences, # and class IDs boxes.append([x, y, int(width), int(height)]) confidences.append(float(confidence)) classIDs.append(classID)

在這個塊中:

  • 迴圈遍歷每個 layerOutputs
  • 迴圈每個detection中 output
  • 提取 classID和 confidence
  • 使用 confidence濾除弱檢測;

過濾掉了不需要的檢測結果後,我們將:

  • 縮放邊界框座標,以便我們可以在原始影象上正確顯示它們;
  • 提取邊界框的座標和尺寸,YOLO返回邊界框座標形式: (centerX ,centerY ,width,height);
  • 使用此資訊匯出邊界框的左上角(x,y)座標;
  • 更新boxes, confidences ,classIDs列表。
    有了這些資料後,將應用“非最大值抑制”(non-maxima suppression,nms):
# apply non-maxima suppression to suppress weak, overlapping bounding
# boxes
idxs = cv2.dnn.NMSBoxes(boxes, confidences, args["confidence"],
    args["threshold"])

YOLO演算法並沒有應用非最大值抑制,這裡需要說明一下。應用非最大值抑制可以抑制明顯重疊的邊界框,只保留最自信的邊界框,NMS還確保我們沒有任何冗餘或無關的邊界框。
利用OpenCV內建的NMS DNN模組實現即可實現非最大值抑制 ,所需要的引數是邊界 框、 置信度、以及置信度閾值和NMS閾值。
最後在影象上繪製檢測框和類文字:

# ensure at least one detection exists
if len(idxs) > 0:
    # loop over the indexes we are keeping
    for i in idxs.flatten(): # extract the bounding box coordinates (x, y) = (boxes[i][0], boxes[i][1]) (w, h) = (boxes[i][2], boxes[i][3]) # draw a bounding box rectangle and label on the image color = [int(c) for c in COLORS[classIDs[i]]] cv2.rectangle(image, (x, y), (x + w, y + h), color, 2) text = "{}: {:.4f}".format(LABELS[classIDs[i]], confidences[i]) cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) # show the output image cv2.imshow("Image", image) cv2.waitKey(0)

假設存在至少一個檢測結果,就迴圈用非最大值抑制確定idx 。然後,我們使用隨機類顏色在影象上繪製邊界框和文字 。最後,顯示結果影象,直到使用者按下鍵盤上的任意鍵。
下面進入測試環節,開啟一個終端並執行以下命令:

$ python yolo.py --image images/baggage_claim.jpg --yolo yolo-coco
[INFO] loading YOLO from disk...
[INFO] YOLO took 0.347815 seconds

 

22
圖2:YOLO用於檢測機場中的人員和行李


從上圖可以看到,YOLO不僅檢測了輸入影象中的每個人,還檢測了手提箱。此外,可以從影象的右上角看到,YOLO還檢測到女士肩上的手提包。
我們試試另一個例子:

$ python yolo.py --image images/living_room.jpg --yolo yolo-coco
[INFO] loading YOLO from disk...
[INFO] YOLO took 0.340221 seconds

 

3
圖3: YOLO用於檢測人、狗、電視和椅子


YOLO還可以檢測電視顯示器和椅子,令我驚訝的是YOLO能夠檢測到椅子,因為它是手工製作的老式“嬰兒高腳椅”。
有趣的是,YOLO認為我手中有一個遙控器,它實際上不是遙控器——玻璃反射的VHS錄,仔細盯著這個地方看,它實際上看起來非常像遙控器。
以下示例影象演示了YOLO物件檢測器的侷限性和弱點:

$ python yolo.py --image images/dining_table.jpg --yolo yolo-coco
[INFO] loading YOLO from disk...
[INFO] YOLO took 0.362369 seconds

 

4
圖4: YOLO用於檢測餐桌


雖然YOLO正確檢測到葡萄酒瓶、餐桌和花瓶,但只有兩個酒杯中的一個被正確檢測到。
下面嘗試最後一幅影象:

$ python yolo.py --image images/soccer.jpg --yolo yolo-coco
[INFO] loading YOLO from disk...
[INFO] YOLO took 0.345656 seconds

 

5
圖5:使用YOLO檢測足球運動員和足球


YOLO能夠正確地檢測球場上的每個球員,包括足球本身。請注意,儘管區域高度模糊且部分遮擋,但仍會檢測到背景中的人。
以上內容就是影象檢測部分的全部內容,下一節將介紹視訊流中物件檢測以及YOLO演算法的總結。


原文連結
本文為雲棲社群原創內容,未經允許不得轉載。