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

圖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

圖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: 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: YOLO用於檢測餐桌
雖然YOLO正確檢測到葡萄酒瓶、餐桌和花瓶,但只有兩個酒杯中的一個被正確檢測到。
下面嘗試最后一幅圖像:
$ python yolo.py --image images/soccer.jpg --yolo yolo-coco
[INFO] loading YOLO from disk... [INFO] YOLO took 0.345656 seconds

圖5:使用YOLO檢測足球運動員和足球
YOLO能夠正確地檢測球場上的每個球員,包括足球本身。請注意,盡管區域高度模糊且部分遮擋,但仍會檢測到背景中的人。
以上內容就是圖像檢測部分的全部內容,下一節將介紹視頻流中對象檢測以及YOLO算法的總結。
原文鏈接
本文為雲棲社區原創內容,未經允許不得轉載。
