YOLO的Python實現以及 OpenCV
Darknet 實現 YOLO
從頭開始開發 YOLO模型不容易,所以我們要使用預訓練模型在項目里進行目 標檢測。你可以在 https://pjreddie.com里到所有可用的預訓練模型。這是 Joseph C. Redmon的主頁,他是 Darknet的維護者。
注意????? Darknet是C和CUDA開發的開源神經網絡框架,它很快,很容易安裝,支持 CpU 和 GpU計算。
在子頁 ?(https://pjreddie.com/darknet/yolo/), ?你可以找到 YOLO算法的所有信息。你可以從這個網頁下載多個預訓練模型的權重。對于每一個模型,你需要二個文件 :
- 一個 .cfg文件,它包含網絡結構.
- 一個 .weights文件,它包含訓練后的權重
為了給你這些文件的內容, .cfg文件包含使用的層的信息等,示例如:
[convolutional] batch_normalize=1 filters=64
size=3
stride=1 pad=1
activation=leaky
這告 訴你有特殊的卷積層。最重要的信息是:
- Network architecture
- Anchor boxes
- Number of classes
- Learning rate and other parameters used
- Batch size
其它文件(.weights)包含預訓練權重以進行推斷。注意它們不能保存為Keras兼容的格式 (如 .h5文件),所以它們不能加載到 Keras模型,除非你先轉換。
有一些非標準的工具可以轉換這些文件,因為格式不是固定的 ( YOLOv2 和 YOLOv3有些不同)。如果你感興趣以 YOLO v2,你可以使用 YAD2K 庫 (另一個是 Darknet 2 Keras),可以在 https://github.com/allanzelener/YAD2K里找到。
注意這不能處理 YOLOv3 .cfg文件。相信我,我試過。但是如果你滿意 YOLOv2, 你可以用這個目錄的代碼轉換 .weight文件到Keras友好的格式。有個目錄實現了 YOLOv3 的轉換,在https://github.com/qqwweee/keras-yolo3。它有些局限,但是它是轉換的很好的起點。 但是有便好的方法,使用預訓練模型,那就是使用 OpenCV, 后面我們會看到。
用 Darknet檢測對象
如果你只是想對圖像進行分類,最容易的辦法是按 darknet網站的指示。我們看一下如何做。如果你使用Linux或 MacOS X系統,這些指令有用。 在Windows里,你要安裝 gcc和別的工具。如網站所述,安裝只需要幾行代碼:
git ?clone ?https://github.com/pjreddie/darknet cd darknet
make
wget??? ?https://pjreddie.com/media/files/yolov3.weights
這里你就可以進行目標檢測了:
./darknet detect cfg/yolov3.cfg yolov3.weights table.jpg
注意,權重文件很大,下載時要注意這一點,使用CPU這很慢,MacBook Pro 2018需要18秒來下載。見圖 7-2。
圖7-2. YOLOv3使用 darknet進行圖像檢測
黙認使用0.25的閾值。但是你可以使用不同的參數。你必須改變XYZ到你想要的值。
黙認使用0.25的閾值。但是你可以使用不同的參數。你必須改變XYZ到你想要的值。.
這個方法于于目標檢測 很好,但是很難在python項目里使用。要這樣做,你需要在你的代碼里使用預訓練的模型。有幾個方法,最容易的是使用opencv庫。如果你處理圖像,你很可能已經使用過這個庫。如果你沒有聽說過,建議你試一下,因為它是處理圖像很有名的庫。你可以看官網 https:// opencv.org.
你可以在網上下載本章的完整代碼,但是我們只簡單的討論重要的部分。
你需要安裝最新的opencv庫。 用下面的代碼檢測版本:
import cv2
print (cv2. ?version ?)
我們需要從https:// pjreddie.com 下載三個文件:
- coco.names
- yolov3.cfg
- yolov3.weights
coco.names 包含了預訓練模型能分類的標簽。yolov3.cfg 和yolov3.weights是模型配置參數 ?(前面已討論過)且我們需要使用 權重。為了你方便,yolov3.weights大約 240MB容量,你可以下載 ZIP 文件自 http://toe.lt/r。在代碼里我們需要指明文件在哪里。例如,你使用下面的代碼:
weightsPath ?= ?"yolo-coco/yolov3.weights" configPath? ?=? ?"yolo-coco/yolov3.cfg"
你要改變文件的位置。 OpenCV提供了函數來加載權重而不需要轉換它們:
net ?= ?cv2.dnn.readNetFromDarknet(configPath, ?weightsPath)
這很舒服,因為你不需要分析和寫加載函數。它返回模型對象供你后面推斷。如果你記得本章開始的討論,你要得到輸出層來得到所有需要的信息,像邊框和預測分類。我們用下面的代碼很容易做到:
ln ?= ?net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]
?getUnconnectedOutLayers()函數返回帶用unconnected輸出的層, 正是我們想要的。ln這量含有下面的層:
['yolo_82', 'yolo_94', 'yolo_106']
然后我們要改變圖像大小到 416x416并歸一化它:
blob? =? cv2.dnn.blobFromImage(image,? 1? / 255.0,? (416,? 416), swapRB=True, crop=False)
然后用它作為模型的輸入:
net.setInput(blob)
然后我們用 forward()函數向前傳遞給訓練模型:
layerOutputs? ?=? ?net.forward(ln)
我們還沒有完成,不要休息。我們要提取邊框,我們保存在列表里,然后是置信,然后是預測分類.
我們初始化列表:
boxes = [] confidences = [] classIDs = []
for output in layerOutputs: for ?detection ?in ?output:
現在分 數保存在第5個檢測變量的元素里,我們提取分類用np. argmax(scores):
scores? ?=? ?detection[5:] classID ?= ?np.argmax(scores)
置信度是預測分類的分值:
confidence? ?=? ?scores[classID]
我們想要預測置信度大于0的。我們選擇限度為0.15。預測邊框包含于檢測變量的前4個里:
box =? detection[0:4] *? np.array([W, H,? W,? H]) (centerX,? ?centerY,? ?width,? ?height)? ?=? ?box.astype("int")
如果你記得,YOLO預測邊框的中心點,所在我們要的提取左上角位置:
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)
然后我們使用 non-maxima 抑制 (上一節討論過)。 OpenCV 也提供了函數:
idxs = cv2.dnn.NMSBoxes(boxes, confidences, 0.6,0.2)
函數需要下面的參數:
- 邊框集合 (保存于 boxes變量)
- 置信度集合 (保存于confidences變量)
- 按分值過濾邊框的閾值 ?(前面代碼是0.6)
- ?non-maximum抑制的閾值 (前面的代碼是0.2)
然我們得到準確的位置:
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])
你可以在圖 7-3看到結果。
圖?7-3. YOLOv3用 OpenCV獲得的結果
這正是它應有的—與圖7-2一樣的結果。另外,我們有預測邊框的概率。你可以看到這是多么的容易。你只需要添加幾行代碼到你的項目。
這正是它應有的—與圖7-2一樣的結果。另外,我們有預測邊框的概率。你可以看到這是多么的容易。你只需要添加幾行代碼到你的項目。
記住我們使用預訓練模型只能檢測數據集里的對象。如果你要用不同的對象,你要微調模型,或重新訓練模型。從頭訓練模型超出了本書的范圍。但是下一節我會給你一些提示。