愷明大神 Mask R-CNN 超實用教程
- 2019 年 10 月 7 日
- 筆記
原標題 | Mask R-CNN with OpenCV
作 者 | Adrian Rosebrock
翻 譯 | 天字一號(鄭州大學)、李美麗(華南師範大學)、had_in(電子科技大學)、nengdaiper(北京科技大學)

在此教程中,你將學習如何在opencv中使用Mask R-CNN。
使用Mask R-CNN,你可以自動分割和構建影像中每個對象的像素級MASK。我們將應用Mask R-CNN到影像和影片流。
在上周的部落格文章中,你學習了如何使用Yolo物體探測器來檢測影像中物體(https://www.pyimagesearch.com/2018/11/12/yolo-object-detection-with-opencv/)。對象檢測器,如yolo、faster r-cnn和ssd,生成四組(x,y)坐標,表示影像中對象的邊界框。
從獲取對象的邊界框開始挺好的,但是邊界框本身並不能告訴我們(1)哪些像素屬於前景對象,(2)哪些像素屬於背景。
這就引出了一個問題:
是否可以為影像中的每個對象生成一個MASK,從而允許我們從背景分割前景對象? 這樣的方法可能嗎?
答案是肯定的:我們只需要使用Mask R-CNN架構執行實例分割。
要了解如何利用opencv的Mask R-CNN應用於影像和影片流,繼續看下去吧!
正在查找此部落格的源程式碼?直接跳到下載(https://www.pyimagesearch.com/2018/11/19/mask-r-cnn-with-opencv/#)。
Mask R-CNN with OpenCV
在本教程的第一部分中,我們將討論影像分類、對象檢測、實例分割和語義分割之間的區別。
這裡,我們將簡要回顧Mask R-CNN架構及其與Faster R-CNN的關係。
然後,我將向您展示如何在影像和影片流上應用Mask R-CNN與OpenCV。
開始吧!
實例分割 vs. 語義分割

圖1:影像分類(左上),目標檢測(右上),語義分割(左下),實例分割(右下)。在本教程中,我們將使用Mask R-CNN執行實例分割。(來源:https://arxiv.org/abs/1704.06857)
解釋傳統的影像分類、目標檢測、語義分割和實例分割之間的區別,最好是用可視化方法。
在執行傳統的影像分類時,我們的目標是預測一組標籤來表示輸入影像的內容(左上角)。
目標檢測建立在影像分類的基礎上,但這一次需要我們對影像中每個對象定位。影像的表徵如下:
- 每個目標邊界框的坐標(x, y)
- 每個邊界框關聯的類別標籤
左下角是一個語義分割的例子。語義分割演算法要求我們將輸入影像中的每個像素與一個類別標籤(包括一個用於背景的類標籤)關聯起來。
注意關注我們語義分割的可視化——注意每個目標是如何分割的,但每個「cube」目標都有相同的顏色。
雖然語義分割演算法能夠對影像中的所有目標進行標記,但它們無法區分同一類的兩個對象。
特別是同一個類別的兩個目標是相互遮擋時,問題更加明顯,我們不知道一個對象的邊界在哪裡結束以及哪裡開始,如圖上兩個紫色立方體所示,我們無法說清楚一個立方體邊界的開始和結束。
另一方面,實例分割演算法為影像中的每個對象計算像素級mask,即使對象具有相同的類別標籤(右下角)。在這裡,您可以看到每個立方體都有自己獨特的顏色,這意味著我們的實例分割演算法不僅定位了每個獨立的立方體,而且還預測了它們的邊界。
而在本教程,我們將要討論的Mask R-CNN架構就是一個實例分割演算法的示例。
什麼是 Mask R-CNN?
Mask R-CNN演算法是何凱明等人在2017年發表的論文中提出的,Mask R-CNN(https://arxiv.org/abs/1703.06870)。
Mask R-CNN是基於之前的目標檢測工作R-CNN(2013)、Fast R-CNN(2015)、Faster R-CNN(2015),均由Girshick等人完成。
為了理解Mask R-CNN,讓我們簡要回顧一下R-CNN的變體,從原始的R-CNN開始:

圖2:初始的R-CNN架構(來源:Girshick等人,2013)
最初的R-CNN演算法分為四個步驟:
步驟1:向網路輸入影像。
步驟2:提取區域proposals(即,可能包含對象的影像區域)演算法,如選擇性搜索演算法(http://www.huppelen.nl/publications/selectiveSearchDraft.pdf)。
步驟3:利用遷移學習進行特徵提取,使用預先訓練的CNN計算每個proposals的特徵(這實際上是一個ROI)。
步驟4:使用支援向量機(SVM)對提取的特徵進行分類。
這種方法之所以有效,是因為CNN學習的特徵的魯棒性和可鑒別性。
然而,R-CNN方法的問題在於它非常慢。此外,我們實際上並沒有學習如何通過深度神經網路進行定位,我們只是在有效地構建一個更高級的HOG +線性SVM檢測器(https://www.pyimagesearch.com/2014/11/10/histogram-oriented-gradients-object-detection/)。
為了改進原有的R-CNN, Girshick等人發表了Fast R-CNN演算法:

圖3:Fast R-CNN架構(來源:Girshick et al., 2015)。
與原始的R-CNN相似,Fast R-CNN仍然使用選擇性搜索來獲取區域建議;然而,本文的新貢獻是感興趣區域(ROI)池化模組。
ROI池化的工作原理是從特徵map中提取一個固定大小的窗口,並使用這些特徵獲得最終的類別標籤和邊界框。這樣做主要好處是,網路現在可以有效地端到端地進行訓練:
- 我們輸入一個影像和對應的實際的邊界框
- 提取影像的特徵map
- 應用ROI池化,得到ROI特徵向量
- 最後, 使用兩組全連接層來獲得(1)類別標籤預測(2)每個proposal的邊框位置。
雖然網路現在是可以端到端訓練的,但是由於依賴於選擇性搜索演算法,在推斷時性能仍受到了極大的影響。
為了使R-CNN的架構更快,我們需要直接利用R-CNN獲得區域proposal:

圖4:Faster R-CNN架構(來源:Girshick et al., 2015)
Girshick等人的Faster R-CNN論文將 區域proposals網路(RPN)引入到神經網路架構中,減少了對選擇性搜索演算法的需求。
總的來說,Faster R-CNN架構能夠以大約7-10幀每秒的速度運行,這是通過深度學習實現實時目標檢測的一大進步。
Mask R-CNN演算法建立在Faster R-CNN架構的基礎之上,主要有兩個貢獻:
- 用更精確的ROI align模組替換ROI Pooling模組
- 從ROI align模組中插入一個額外的分支
這個額外的分支的輸入為ROI align模組的輸出,然後將其輸入到兩個CONV層。
CONV層的輸出即是掩摸(mask)本身。
我們可以將Mask R-CNN架構可視化如下圖所示:

圖5:He等人的Mask R-CNN工作用一個更精確的ROI align模組替換ROI Polling模組。然後將ROI模組的輸出送入兩個CONV層。CONV層的輸出即是掩摸(mask)本身。
注意兩個CONV層的分支來自ROI Align模組——我們實際生成掩摸由該模組生成。
我們知道,Faster R-CNN/Mask R-CNN架構利用區域proposal網路(RPN)生成可能包含對象的影像區域。
這些區域都是根據它們的「可能是目標的評分」(即,給定區域可能包含目標的可能性),然後保留最可能的前N個目標區域。
在原來Faster R-CNN論文中,Girshick等人設置N= 2000,但在實踐中,我們可以用一個小得多的N,比如N={10,100, 200,300},仍然可以得到很好的結果。
He等人在他們的論文(https://arxiv.org/abs/1703.06870)中設置N=300,這也是我們這裡使用的值。
所選的300個ROIs中的每一個都要經過網路的三個並行分支:
- 類別標籤預測
- 邊界框預測
- 掩摸預測
上面的圖5顯示了這些分支。
在預測時,300個ROIs都會經過非極大值抑制演算法(https://www.pyimagesearch.com/2014/11/17/non-maximum-suppression-object-detection-python/),然後僅保存可能性前100的檢測框,使得最終得到一個四維的100 x L x 15 x 15張量,L為數據幾種類別標籤的數量,15 x 15是每個類別L的掩摸(mask)的大小。
我們今天使用的掩模R-CNN是在COCO數據集上訓練的(http://cocodataset.org/#home),它有L=90個類,因此掩模R CNN掩模模組的最終體積大小是100 x 90 x 15 x 15。
Mask R-CNN的可視化過程,請看下圖:

圖6:Mask R-CNN過程的可視化,先生成一個15 x 15的掩摸,遮罩改變到影像的原始尺寸,最後將掩摸覆蓋到原始影像上。(來源:Python深度學習電腦視覺,ImageNet Bundle)
這裡你可以看到,我們從我們的輸入影像開始,並通過我們的Mask R-CNN網路,最終獲得我們的掩摸預測。
預測的掩模只有15 x 15的像素,因此我們將掩模的大小調整回原始輸入影像的尺寸。
最後,調整大小後的掩模可以覆蓋在原始輸入影像上。要了解更多關於Mask R-CNN工作原理的詳細討論,請參考:
- 由何等人發表的Mask R-CNN論文(https://arxiv.org/abs/1703.06870)
- 我的書, Deep Learning for Computer Vision with Python(https://www.pyimagesearch.com/deep-learning-computer-vision-python-book/),在這本書里,我將更詳細地討論Mask R-CNNs,包括如何根據自己的數據從零開始訓練自己的Mask R-CNNs。
項目結構
我們今天的項目主要由兩個腳本組成,還有其他幾個重要的文件。
我已經按照如下方式構建了這個項目(直接在終端上的tree命令輸出):
基於OpenCV的Mask R-CNN—-Shell
我們的項目包括四個目錄:
- mask-rcnn-coco/ : Mask R-CNN的模型文件。有四個文件:
- frozen_inference_graph.pb : Mask R-CNN模型的權重文件,是基於COCO數據集上預訓練的權重。
- mask_rcnn_inception_v2_coco_2018_01_28.pbtxt : Mask R-CNN模型的配置文件。如果你想在你自己的標註的數據上建立並訓練你自己的模型, 參考 Deep Learning for Computer Vision with Python(https://www.pyimagesearch.com/deep-learning-computer-vision-python-book/).
- object_detection_classes_coco.txt : 所有90個類別都列在這個文本文件中,每行一個。在文本編輯器中打開它,查看模型可以識別哪些對象。
- colors.txt : 這個文本文件包含六種顏色,可以隨機分配給影像中檢測到的目標。
- images/ : 我在「Downloads」中提供了三個測試影像。請隨意添加您自己的影像進行測試
- videos/ : 這是一個空目錄。實際上,我用從YouTube上搜集的大型影片進行了測試(Credits下面,就在「Summary」部分的上面)。我更傾向於建議您可以在YouTube上找到一些影片下載並測試,而不是提供一個真正大的zip文件。或者用你的手機拍一些影片,然後回到你的電腦前使用它們!
- output/ :另一個空目錄,將保存處理過的影片(假設您將命令行參數設置為輸出到此目錄)。
今天我們將回顧兩個腳本:
- mask_rcnn.py : 這個腳本將執行實例分割並對影像應用一個掩碼,這樣您就可以看到Mask R-CNN檢測出的對象在哪裡,精細到像素。
- mask_rcnn_video.py : 這個影片處理腳本使用相同的Mask R-CNN,並將模型應用於影片文件的每一幀。然後腳本將輸出幀寫回磁碟上的影片文件中。
基於OpenCV的關於影像的Mask R-CNN
現在,我們已經回顧了Mask R-CNNs的工作原理,讓我們動手寫一些Python程式碼。
在開始之前,請確保您的Python環境已經安裝了OpenCV 3.4.2/3.4.3或更高版本。您可以按照我的OpenCV安裝教程(https://www.pyimagesearch.com/opencv-tutorials-resources-guides/)來升級/安裝OpenCV。如果您想在5分鐘或更短的時間內啟動和運行,可以考慮使用pip安裝OpenCV(https://www.pyimagesearch.com/2018/09/19/pip-install-opencv/)。如果您有其他一些需求,您可能希望從源程式碼編譯OpenCV。
請確保您已經從本部落格文章的「Downloads」部分下載了源程式碼、訓練過的Mask R-CNN以及示例影像。
然後,打開mask_rcnn.py文件並插入以下程式碼:
Mask R-CNN with OpenCV—Python
# import the necessary packages import numpy as np import argparse import random import time import cv2 import os
首先,我們將在第2-7行導入所需的包。值得注意的是,我們正在導入NumPy和OpenCV包。大多數Python安裝都默認安裝了上所需的其他的包。
現在我們開始解析我們的命令行參數(https://www.pyimagesearch.com/2018/03/12/python-argparse-command-line-arguments/):
Mask R-CNN with OpenCV---Python # 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("-m", "--mask-rcnn", required=True, help="base path to mask-rcnn directory") ap.add_argument("-v", "--visualize", type=int, default=0, help="whether or not we are going to visualize each instance") 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="minimum threshold for pixel-wise mask segmentation") args = vars(ap.parse_args())
我們的腳本在終端中運行需要傳遞命令行參數標誌以及參數。我們的參數在第10-21行進行解析,其中前兩行是必需的,其餘的是可選的:
- –image : 輸入影像的路徑。
- –mask-rnn : Mask R-CNN文件的根路徑 .
- –visualize (可選): 正值表示想要可視化如何在螢幕上提取屏蔽區域。無論哪種方式,我們都將在螢幕上顯示最終的輸出。
- –confidence (optional): 您可以選擇0-0.5的概率值,該值用於過濾概率較低的檢測區域。
- –threshold (可選): 我們將為影像中的每個對象創建一個二進位掩碼,這個閾值將幫助我們過濾掉概率較低的掩碼。我發現默認值0.3時效果較好。
現在我們的命令行參數存儲在args字典中,讓我們載入標籤和顏色:
Mask R-CNN with OpenCV—Python
# load the COCO class labels our Mask R-CNN was trained on labelsPath = os.path.sep.join([args["mask_rcnn"], "object_detection_classes_coco.txt"]) LABELS = open(labelsPath).read().strip().split("n") # load the set of colors that will be used when visualizing a given # instance segmentation colorsPath = os.path.sep.join([args["mask_rcnn"], "colors.txt"]) COLORS = open(colorsPath).read().strip().split("n") COLORS = [np.array(c.split(",")).astype("int") for c in COLORS] COLORS = np.array(COLORS, dtype="uint8")
第24-26行載入COCO對象類別標籤。現在的Mask R-CNN能夠識別90個類,包括人,車輛,標誌,動物,日常用品,體育用品,廚房用品,食物等等!我建議您查看object_detection_classes_cocoa .txt,以查看可用的類別。
這裡我們從路徑載入顏色文件,並執行一些數組轉換操作(第30-33行)。
現在載入我們的模型:
Mask R-CNN with OpenCV—Python
# derive the paths to the Mask R-CNN weights and model configuration weightsPath = os.path.sep.join([args["mask_rcnn"], "frozen_inference_graph.pb"]) configPath = os.path.sep.join([args["mask_rcnn"], "mask_rcnn_inception_v2_coco_2018_01_28.pbtxt"]) # load our Mask R-CNN trained on the COCO dataset (90 classes) # from disk print("[INFO] loading Mask R-CNN from disk...") net = cv2.dnn.readNetFromTensorflow(weightsPath, configPath)
首先,我們構建權重和配置路徑(第36-39行),然後通過這些路徑載入模型(第44行)。
在下一個程式碼塊中,我們將載入Mask R-CNN神經網路,輸入一張影像:
Mask R-CNN with OpenCV—Python
# load our input image and grab its spatial dimensions image = cv2.imread(args["image"]) (H, W) = image.shape[:2] # construct a blob from the input image and then perform a forward # pass of the Mask R-CNN, giving us (1) the bounding box coordinates # of the objects in the image along with (2) the pixel-wise segmentation # for each specific object blob = cv2.dnn.blobFromImage(image, swapRB=True, crop=False) net.setInput(blob) start = time.time() (boxes, masks) = net.forward(["detection_out_final", "detection_masks"]) end = time.time() # show timing information and volume information on Mask R-CNN print("[INFO] Mask R-CNN took {:.6f} seconds".format(end - start)) print("[INFO] boxes shape: {}".format(boxes.shape)) print("[INFO] masks shape: {}".
這裡我們進行了以下操作:
- Load the input image and extract dimensions for scaling purposes later (Lines 47 and 48).
- Construct a blob via cv2.dnn.blobFromImage (Line 54). You can learn why and how to use this function in my previous tutorial(https://www.pyimagesearch.com/2017/11/06/deep-learning-opencvs-blobfromimage-works/).
- Perform a forward pass of the blob through the net while collecting timestamps (Lines 55-58). The results are contained in two important variables: boxes and masks .
現在我們已經在影像上執行了口罩R-CNN的正向傳遞,我們想要過濾+可視化我們的結果。這正是下一個for循環要完成的。它很長,所以我把它分成五個程式碼塊,從這裡開始:
# loop over the number of detected objects for i in range(0, boxes.shape[2]): # extract the class ID of the detection along with the confidence # (i.e., probability) associated with the prediction classID = int(boxes[0, 0, i, 1]) confidence = boxes[0, 0, i, 2] # filter out weak predictions by ensuring the detected probability # is greater than the minimum probability if confidence > args["confidence"]: # clone our original image so we can draw on it clone = image.copy() # scale the bounding box coordinates back relative to the # size of the image and then compute the width and the height # of the bounding box box = boxes[0, 0, i, 3:7] * np.array([W, H, W, H]) (startX, startY, endX, endY) = box.astype("int") boxW = endX - startX boxH = endY - startY
在這個程式碼塊中,我們開啟了一個訓練,不斷根據置信度過濾/並進行可視化(第66行)。
我們繼續提取特定檢測對象的分類和置信度(第69行和第70行)。
在此基礎之上,我們通過將置信度與命令行參數置信度值進行比較,從而過濾掉置信度較低的預測結果,確保超過該值(第74行)。
然後我們縮放對象的邊界框,並計算框的大小(第81-84行)。
影像分割要求我們找到目標所在的所有像素。因此,我們將在對象的頂部放置一個透明的層,以查看我們的演算法執行的效果。為了做到這一點,我們將計算一個掩模:
Mask R-CNN with OpenCV—Python
# extract the pixel-wise segmentation for the object, resize # the mask such that it's the same dimensions of the bounding # box, and then finally threshold to create a *binary* mask mask = masks[i, classID] mask = cv2.resize(mask, (boxW, boxH), interpolation=cv2.INTER_NEAREST) mask = (mask > args["threshold"]) # extract the ROI of the image roi = clone[startY:endY, startX:endX]
在第89-91行,我們提取了對象的像素級分割,並將其調整為原始影像的尺寸。最後,我們設置掩碼的閾值,使其成為二進位數組/影像(第92行)。
我們還提取了對象所在的感興趣區域(第95行)。
在本文後面的圖8中可以看到遮罩和roi的可視化結果。
為了方便起見,下一個程式碼塊實現了掩碼、roi和分割實例的可視化,如果通過命令行設置了參數 –visualize的話。
Mask R-CNN with OpenCV—Python
# check to see if are going to visualize how to extract the # masked region itself if args["visualize"] > 0: # convert the mask from a boolean to an integer mask with # to values: 0 or 255, then apply the mask visMask = (mask * 255).astype("uint8") instance = cv2.bitwise_and(roi, roi, mask=visMask) # show the extracted ROI, the mask, along with the # segmented instance cv2.imshow("ROI", roi) cv2.imshow("Mask", visMask) cv2.imshow("Segmented", instance)
這個程式碼塊中我們進行了以下操作:
- 檢查是否應該可視化ROI、掩模和分割實例(第99行)。
- 將掩模從布爾值轉換為整數,其中值「0」表示背景,「255」表示前景(第102行)。
- 執行按位掩模以僅僅可視化分割實例本身(第103行)。
- 顯示三個結果影像(第107-109行)。
同樣,只有通過可選的命令行設置參數 –visualize 標誌時,才會顯示這些可視化影像(默認情況下不會顯示這些影像)。
現在讓我們繼續可視化:
Mask R-CNN with OpenCV—Python
# now, extract *only* the masked region of the ROI by passing # in the boolean mask array as our slice condition roi = roi[mask] # randomly select a color that will be used to visualize this # particular instance segmentation then create a transparent # overlay by blending the randomly selected color with the ROI color = random.choice(COLORS) blended = ((0.4 * color) + (0.6 * roi)).astype("uint8") # store the blended ROI in the original image clone[startY:endY, startX:endX][mask] = blended
第113行通過將布爾掩模數組作為切片條件傳遞,只提取ROI的掩模區域。
然後我們將隨機選擇六種顏色中的一種,將透明的覆蓋層應用到對象上(第118行)。
隨後,我們將用roi混合掩模區域(第119行),然後將該混合區域放入影像clone中(第122行)。
最後,我們將在影像上繪製矩形和文本類別標籤+ 置信度的值,並顯示結果!
Mask R-CNN with OpenCV—Python
# draw the bounding box of the instance on the image color = [int(c) for c in color] cv2.rectangle(clone, (startX, startY), (endX, endY), color, 2) # draw the predicted label and associated probability of the # instance segmentation on the image text = "{}: {:.4f}".format(LABELS[classID], confidence) cv2.putText(clone, text, (startX, startY - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) # show the output image cv2.imshow("Output", clone) cv2.waitKey(0)
最後,我們進行以下操作:
在對象周圍繪製一個彩色邊框(第125行和第126行)。
構建我們的類別標籤+置信度文本,並在邊界框上面繪製文本(第130-132行)。
顯示影像,直到按下任意鍵(第135行和第136行)。
現在讓我們來試試我們的Mask R-CNN程式碼!
確保您已經從本教程的「Downloads」部分下載了源程式碼、訓練過的Mask R-CNN和示例影像。然後,打開您的終端並執行以下操作:
Mask R-CNN with OpenCV—Shell
$ python mask_rcnn.py --mask-rcnn mask-rcnn-coco --image images/example_01.jpg [INFO] loading Mask R-CNN from disk... [INFO] Mask R-CNN took 0.761193 seconds [INFO] boxes shape: (1, 1, 3, 7) [INFO] masks shape: (100, 90, 15, 15)

圖7:一個用於汽車場景的口罩R-CNN。Python和OpenCV用於生成掩碼。
在上面的圖片中,你可以看到我們的Mask R-CNN不僅定位了圖片中的每一輛車,還構建了一個像素級掩模,允許我們從圖片中分割每一輛車。
如果我們運行相同的命令,這次提供–visualize參數標誌,我們還可以可視化ROI、掩模和實例:

圖8:使用–visuatize標誌參數,我們可以查看用Python和OpenCV構建的mask R-CNN流程的ROI、掩模、分割的中間步驟。
讓我們再看另一個例子:
Mask R-CNN with OpenCV—Shell
$ python mask_rcnn.py --mask-rcnn mask-rcnn-coco --image images/example_02.jpg --confidence 0.6 [INFO] loading Mask R-CNN from disk... [INFO] Mask R-CNN took 0.676008 seconds [INFO] boxes shape: (1, 1, 8, 7) [INFO] masks shape: (100, 90, 15, 15)

圖9:使用Python和OpenCV,我們可以使用Mask R-CNN執行實例分割。
我們的Mask R-CNN從影像中正確地檢測並分割了人、狗、馬和卡車。
在我們開始在影片中使用Mask R-CNNs之前,還有最後一個例子:
Mask R-CNN with OpenCV—Shell
$ python mask_rcnn.py --mask-rcnn mask-rcnn-coco --image images/example_03.jpg [INFO] loading Mask R-CNN from disk... [INFO] Mask R-CNN took 0.680739 seconds [INFO] boxes shape: (1, 1, 3, 7) [INFO] masks shape: (100, 90, 15, 15)

圖10:在這裡,您可以看到我正在喂比格爾家的小獵犬傑瑪。每個被標識對象的像素級映射都被屏蔽,並透明地覆蓋在對象上。這幅影像是使用OpenCV和Python使用一個預訓練的Mask R-CNN模型生成的。
在這張圖片中,你可以看到我和傑瑪的照片,這是我家的小獵犬。
我們的面具R-CNN能夠以比較高的置信度檢測和定位我,傑瑪和椅子。
OpenCV和Mask RCNN在影片流中的應用
我們已經學會了怎麼將Mask RCNN應用於影像上,現在我們進一步學習如何在影片上應用Mask RCNN.
打開文件 mask_rcnn_video.py,插入下列程式碼:
# import the necessary packages import numpy as np import argparse import imutils import time import cv2 import os # construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-i", "--input", required=True, help="path to input video file") ap.add_argument("-o", "--output", required=True, help="path to output video file") ap.add_argument("-m", "--mask-rcnn", required=True, help="base path to mask-rcnn 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="minimum threshold for pixel-wise mask segmentation") args = vars(ap.parse_args())
首先,我們插導入必要的包,設置聲明的參數,緊接著的兩行程式碼是放置需要檢測的影像。
載入類別,顏色和Mask RCNN模型:
# load the COCO class labels our Mask R-CNN was trained on labelsPath = os.path.sep.join([args["mask_rcnn"], "object_detection_classes_coco.txt"]) 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") # derive the paths to the Mask R-CNN weights and model configuration weightsPath = os.path.sep.join([args["mask_rcnn"], "frozen_inference_graph.pb"]) configPath = os.path.sep.join([args["mask_rcnn"], "mask_rcnn_inception_v2_coco_2018_01_28.pbtxt"]) # load our Mask R-CNN trained on the COCO dataset (90 classes) # from disk print("[INFO] loading Mask R-CNN from disk...") net = cv2.dnn.readNetFromTensorflow(weightsPath, configPath)
類別和顏色載入程式碼在第24-31行。
在載入MaskRCNN網路之前,需要先載入權重模型和配置文件(第34-42行)。
然後初始化影片流和影片寫入器。
# initialize the video stream and pointer to output video file vs = cv2.VideoCapture(args["input"]) writer = None # try to determine the total number of frames in the video file try: prop = cv2.cv.CV_CAP_PROP_FRAME_COUNT if imutils.is_cv2() else cv2.CAP_PROP_FRAME_COUNT total = int(vs.get(prop)) print("[INFO] {} total frames in video".format(total)) # an error occurred while trying to determine the total # number of frames in the video file except: print("[INFO] could not determine # of frames in video") total = -1
我們的影片流和影片
我們嘗試確定影片文件的幀數,並將總的幀數顯示出來。如果不成功的話,程式就會終止,但列印狀態資訊,或者我們將這個幀數設置成-1,忽略這一個步驟,不做任何處理。
讓我們開始對所有幀進行循環。
# loop over frames from the video file stream while True: # read the next frame from the file (grabbed, frame) = vs.read() # if the frame was not grabbed, then we have reached the end # of the stream if not grabbed: break # construct a blob from the input frame and then perform a # forward pass of the Mask R-CNN, giving us (1) the bounding box # coordinates of the objects in the image along with (2) the # pixel-wise segmentation for each specific object blob = cv2.dnn.blobFromImage(frame, swapRB=True, crop=False) net.setInput(blob) start = time.time() (boxes, masks) = net.forward(["detection_out_final", "detection_masks"]) end = time.time()
我們開始通過定義無限循環來循環所有的幀,並且捕獲第一幀(第 62-64行)。循環地處理影片,直到滿足退出條件(第68和69行)。
然後,我們從幀中構造一個 blob,並在計算通過神經網路的時間,以便我們可以計算完成所需時間(第 75-80 行)。檢測的結果同時包含了檢測框和蒙版。
現在,讓我們開始循環檢測物體:
# loop over the number of detected objects for i in range(0, boxes.shape[2]): # extract the class ID of the detection along with the # confidence (i.e., probability) associated with the # prediction classID = int(boxes[0, 0, i, 1]) confidence = boxes[0, 0, i, 2] # 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 frame and then compute the width and the # height of the bounding box (H, W) = frame.shape[:2] box = boxes[0, 0, i, 3:7] * np.array([W, H, W, H]) (startX, startY, endX, endY) = box.astype("int") boxW = endX - startX boxH = endY - startY # extract the pixel-wise segmentation for the object, # resize the mask such that it's the same dimensions of # the bounding box, and then finally threshold to create # a *binary* mask mask = masks[i, classID] mask = cv2.resize(mask, (boxW, boxH), interpolation=cv2.INTER_NEAREST) mask = (mask > args["threshold"]) # extract the ROI of the image but *only* extracted the # masked region of the ROI roi = frame[startY:endY, sta
首先我們過濾掉低置信度的檢測結果,然後確定檢測框的坐標和蒙版。
現在,讓我們繪製出邊界檢測框和類別的置信度。
# grab the color used to visualize this particular class, # then create a transparent overlay by blending the color # with the ROI color = COLORS[classID] blended = ((0.4 * color) + (0.6 * roi)).astype("uint8") # store the blended ROI in the original frame frame[startY:endY, startX:endX][mask] = blended # draw the bounding box of the instance on the frame color = [int(c) for c in color] cv2.rectangle(frame, (startX, startY), (endX, endY), color, 2) # draw the predicted label and associated probability of # the instance segmentation on the frame text = "{}: {:.4f}".format(LABELS[classID], confidence) cv2.putText(frame, text, (startX, startY - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, col
這裡,我們將興趣區域用顏色表示出來,儲存在原始的框架中。
然後我們繪製矩形框,並顯示類別的顏色和置信度。
最後寫入影片文件,清除快取。
# check if the video writer is None if writer is None: # initialize our video writer fourcc = cv2.VideoWriter_fourcc(*"MJPG") writer = cv2.VideoWriter(args["output"], fourcc, 30, (frame.shape[1], frame.shape[0]), True) # some information on processing single frame if total > 0: elap = (end - start) print("[INFO] single frame took {:.4f} seconds".format(elap)) print("[INFO] estimated total time to finish: {:.4f}".format( elap * total)) # write the output frame to disk writer.write(frame) # release the file pointers print("[INFO] cleaning up...") writer.release() vs.release()
我們影片中的第一個循環迭代
估計所用的處理時間將列印到終端上
我們循環的最後操作是通過編寫器將幀寫入磁碟。
你會注意到,我沒有在螢幕上顯示每個幀。顯示操作非常耗時,當腳本完成處理時,你仍可以使用任何媒體播放器查看輸出影片。
注意: 此外,OpenCV 不支援 NVIDIA GPU 的 dnn 模組。目前僅支援數量有限的 GPU,主要是英特爾 GPU。NVIDIA GPU 支援即將推出,但目前我們無法輕鬆地使用具有 OpenCV dnn 的 GPU.
最後,我們發布影片輸入和輸出文件指針。
現在,我們已經編碼了我們的Mask R-CNN和OpenCV腳本的影片流,你可以自己嘗試下!
確保你使用"下載"。
然後,你需要使用智慧手機或其他錄製設備收集你自己的影片。或者,你也可以像我一樣從 YouTube 下載影片。
現在可以打開終端,執行下面程式碼:
$ python mask_rcnn_video.py --input videos/cats_and_dogs.mp4 --output output/cats_and_dogs_output.avi --mask-rcnn mask-rcnn-coco [INFO] loading Mask R-CNN from disk... [INFO] 19312 total frames in video [INFO] single frame took 0.8585 seconds [INFO] estimated total time to finish: 16579.2047

圖11:在上面的影片中,你可以找到包含狗和貓的有趣影片剪輯,並Mask R-CNN應用在上面!(觀看這個影片:https://youtu.be/T_GXkW0BUyA)
下面是第二個例子,這裡應用OpenCV和Mask R-CNN檢測寒冷天氣下滑動的汽車。
$ python mask_rcnn_video.py --input videos/slip_and_slide.mp4 --output output/slip_and_slide_output.avi --mask-rcnn mask-rcnn-coco [INFO] loading Mask R-CNN from disk... [INFO] 17421 total frames in video [INFO] single frame took 0.9341 seconds [INFO] estimated total time to finish: 16272.9920

圖12 利用Python和Opencv將Mask RCNN應用於影片中的車輛檢測
你可以想像一下,將Mask RCNN應用於擁擠道路上,檢查道路擁擠、車禍和需要幫助的車輛。(觀看影片:https://www.youtube.com/watch?v=8nbzVARfosE)
文中影片和音頻的來源:
- 貓狗:
- 「Try Not To Laugh Challenge – Funny Cat & Dog Vines compilation 2017」on YouTube(https://www.youtube.com/watch?v=EtH9Yllzjcc)
- 「Happy rock」 on BenSound(https://www.bensound.com/royalty-free-music/track/happy-rock)
- Slip and Slide:
- 「Compilation of Ridiculous Car Crash and Slip & Slide Winter Weather – Part 1」 on YouTube(https://www.youtube.com/watch?v=i59v0p-gAtk)
- 「Epic」 on BenSound(https://www.bensound.com/royalty-free-music/track/epic)
我該怎樣訓練自己的ask R-CNN模型?

圖13:在我的書 Deep Learning for Computer Vision with Python中
Mask RCNN模型的預訓練權重模型是在COCO數據集上訓練得到的。
但是,如果你想在自定義數據集上訓練 Mask R-CNN呢?
在我的書Deep Learning for Computer Vision with Python中有詳細介紹。
- 我教你如何訓練一個Mask R-CNN自動檢測和分割癌性皮膚病變影像-第一步,建立一個自動癌症危險因素分類系統。
- 為您提供我最喜歡的影像標註工具,使您能夠為輸入影像創建蒙版。
- 向您展示如何在自定義數據集上訓練 Mask R-CNN。
- 在訓練自己的 Mask R-CNN 時,為您提供我的最佳實踐、提示和建議。
所有 Mask R-CNN 章節都包含演算法和程式碼的詳細說明,確保您能夠成功訓練自己的 Mask R-CNN。要了解有關我的書的更多資訊(並獲取免費的示例章節和目錄集),請查看:https://www.pyimagesearch.com/deep-learning-computer-vision-python-book/
總結
在這個教程中,你學到了在OpenCV和Python下用Mask R-CNN進行影像和影片流中的目標分割。
像YOLO,SSD和Faster R-CNN這樣的目標檢測方法僅能夠生成影像中目標的邊界框 — 我們並不能從它們的方法得知目標的實際形狀。
而用 Mask R-CNN 我們能得到有相對形狀的顏色塊,從而幫助我們把物體從背景中分離。
進一步說,Mask R-CNN可以幫助我們從傳統電腦視覺演算法無法實現的影像中分割出複雜的物體和形狀。
希望今天的教程能幫到你更好地了解OpenCV 和 Mask R-CNN!
via https://www.pyimagesearch.com/2018/11/19/mask-r-cnn-with-opencv/