摘要: 本文介紹使用opencv和yolo完成圖像目標檢測,代碼解釋詳細,附源碼,上手快。python
計算機視覺領域中,目標檢測一直是工業應用上比較熱門且成熟的應用領域,好比人臉識別、行人檢測等,國內的曠視科技、商湯科技等公司在該領域佔據行業領先地位。相對於圖像分類任務而言,目標檢測會更加複雜一些,不只須要知道這是哪一類圖像,並且要知道圖像中所包含的內容有什麼及其在圖像中的位置,所以,其工業應用比較普遍。那麼,今天將向讀者介紹該領域中表現優異的一種算算法——「你只須要看一次」(you only look once,yolo),提出該算法的做者風趣幽默可愛,其我的主頁及論文風格顯示了其性情,目前該算法已經是第三個版本,簡稱YoLo V3。閒話少敘,下面進入教程的主要內容。
在本教程中,將學習如何使用YOLO、OpenCV和Python檢測圖像和視頻流中的對象。主要內容有:git
圖1: YOLO目標檢測器簡化示意圖github
當涉及基於深度學習的對象檢測時,經常使用的三類算法有:算法
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命令,能夠很方便快捷地生成目標樹:app
$ 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腳本。
目錄(按重要性順序)是:dom
yolo - coco /
:YOLOv3對象檢測器預先(在COCO數據集上)訓練獲得最終的權重文件,能夠在Darknet團隊主頁找到對應的文件;images /
:此文件夾包含四個靜態圖像,以後將執行對象檢測以進行測試和評估;videos/
:使用YOLO對圖像進行目標檢測器後,將實時處理視頻。該文件夾中包含四個示例視頻可供測試;輸出/
:輸出已由YOLO處理並帶有邊界框和類名稱註釋的視頻能夠放在此文件夾中;此外還有兩個Python腳本——yolo .py
和 yolo_video.py
,第一個腳本用於圖像處理,第二個腳本用於視頻處理。下面進入實戰內容,你準備好了嗎?ide
首先將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。如今解析四個命令行參數,命令行參數在運行時處理,容許咱們從終端更改腳本的輸入。若是你對其不熟悉,建議閱讀相關的內容。命令行參數包括:oop
-- 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))
在該代碼中:
若是你對blob和cv2.dnn.blobFromImage
有疑問,能夠看這篇博客進一步的瞭解。
當blob準備好了後,咱們就會
# initialize our lists of detected bounding boxes, confidences, and # class IDs, respectively boxes = [] confidences = [] classIDs = []
這些列表包括:
boxes
:對象的邊界框。confidences
:YOLO分配給對象的置信度值,較低的置信度值表示該對象可能不是網絡認爲的對象。上面的命令行參數中將過濾掉不大於 0.5閾值的對象。classIDs
:檢測到的對象的類標籤。下面用YOLO layerOutput
s中的數據填充這些列表 :
# 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
濾除弱檢測;過濾掉了不須要的檢測結果後,咱們將:
boxes
, confidences
,classIDs
列表。# 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算法的總結。