先理解Mask R-CNN的工作原理,然後構建顏色填充器應用
-
程式碼(包括作者構建的資料集和已訓練的模型):https://github.com/matterport/Mask_RCNN/tree/master/samples/balloon
什麼是例項分割?
例項分割是一種在畫素層面識別目標輪廓的任務,相比其他相關任務,例項分割是較難解決的計算機視覺任務之一:
-
分類:這張影象中有一個氣球。
-
語義分割:這些全是氣球畫素。
-
目標檢測:這張影象中的這些位置上有 7 個氣球。
-
例項分割:這些位置上有 7 個氣球,並且這些畫素分別屬於每個氣球。
Mask R-CNN
Mask R-CNN 是一個兩階段的框架,第一個階段掃描影象並生成提議(proposals,即有可能包含一個目標的區域),第二階段分類提議並生成邊界框和掩碼。Mask R-CNN 擴充套件自 Faster R-CNN,由同一作者在去年提出。Faster R-CNN 是一個流行的目標檢測框架,Mask R-CNN 將其擴充套件為例項分割框架。
Mask R-CNN 的主要構建模組:
1. 主幹架構
主幹網路的簡化圖示
這是一個標準的卷積神經網路(通常來說是 ResNet50 和 ResNet101),作為特徵提取器。底層檢測的是低階特徵(邊緣和角等),較高層檢測的是更高階的特徵(汽車、人、天空等)。
經過主幹網路的前向傳播,影象從 1024x1024x3(RGB)的張量被轉換成形狀為 32x32x2048 的特徵圖。該特徵圖將作為下一個階段的輸入。
-
程式碼提示:主幹網路在 resnet_graph() 函式中。程式碼支援 ResNet50 和 ResNet101。
特徵金字塔網路(FPN)
來源:Feature Pyramid Networks for Object Detection
上述的主幹網路還可以進一步提升。由 Mask R-CNN 的同一作者引入的特徵金字塔網路(FPN)是對該主幹網路的擴充套件,可以在多個尺度上更好地表徵目標。
FPN 通過新增第二個金字塔提升了標準特徵提取金字塔的效能,第二個金字塔可以從第一個金字塔選擇高階特徵並傳遞到底層上。通過這個過程,它允許每一級的特徵都可以和高階、低階特徵互相結合。
在我們的 Mask R-CNN 實現中使用的是 ResNet101+FPN 主幹網路。
-
程式碼提示:FPN 在 MaskRCNN.build() 中建立,位於構建 ResNet 的部分之後。FPN 引入了額外的複雜度:在 FPN 中第二個金字塔擁有一個包含每一級特徵的特徵圖,而不是標準主幹中的單個主幹特徵圖(即第一個金字塔中的最高層)。選用哪一級的特徵是由目標的尺寸動態地確定的。
2. 區域建議網路(RPN)
展示 49 個 anchor box 的簡化圖示
RPN 是一個輕量的神經網路,它用滑動視窗來掃描影象,並尋找存在目標的區域。
RPN 掃描的區域被稱為 anchor,這是在影象區域上分佈的矩形,如上圖所示。這只是一個簡化圖。實際上,在不同的尺寸和長寬比下,影象上會有將近 20 萬個 anchor,並且它們互相重疊以儘可能地覆蓋影象。
RPN 掃描這些 anchor 的速度有多快呢?非常快。滑動視窗是由 RPN 的卷積過程實現的,可以使用 GPU 並行地掃描所有區域。此外,RPN 並不會直接掃描影象,而是掃描主幹特徵圖。這使得 RPN 可以有效地複用提取的特徵,並避免重複計算。通過這些優化手段,RPN 可以在 10ms 內完成掃描(根據引入 RPN 的 Faster R-CNN 論文中所述)。在 Mask R-CNN 中,我們通常使用的是更高解析度的影象以及更多的 anchor,因此掃描過程可能會更久。
-
程式碼提示:RPN 在 rpn_graph() 中建立。anchor 的尺度和長寬比由 config.py 中的 RPN_ANCHOR_SCALES 和 RPN_ANCHOR_RATIOS 控制。
RPN 為每個 anchor 生成兩個輸出:
-
anchor 類別:前景或背景(FG/BG)。前景類別意味著可能存在一個目標在 anchor box 中。
-
邊框精調:前景 anchor(或稱正 anchor)可能並沒有完美地位於目標的中心。因此,RPN 評估了 delta 輸出(x、y、寬、高的變化百分數)以精調 anchor box 來更好地擬合目標。
使用 RPN 的預測,我們可以選出最好地包含了目標的 anchor,並對其位置和尺寸進行精調。如果有多個 anchor 互相重疊,我們將保留擁有最高前景分數的 anchor,並捨棄餘下的(非極大值抑制)。然後我們就得到了最終的區域建議,並將其傳遞到下一個階段。
-
程式碼提示:ProposalLayer 是一個自定義的 Keras 層,可以讀取 RPN 的輸出,選取最好的 anchor,並應用邊框精調。
3. ROI 分類器和邊界框迴歸器
這個階段是在由 RPN 提出的 ROI 上執行的。正如 RPN 一樣,它為每個 ROI 生成了兩個輸出:
階段 2 的圖示。來源:Fast R-CNN
-
類別:ROI 中的目標的類別。和 RPN 不同(兩個類別,前景或背景),這個網路更深並且可以將區域分類為具體的類別(人、車、椅子等)。它還可以生成一個背景類別,然後就可以棄用 ROI 了。
-
邊框精調:和 RPN 的原理類似,它的目標是進一步精調邊框的位置和尺寸以將目標封裝。
-
程式碼提示:分類器和邊框迴歸器已在 fpn_classifier_graph() 中建立。
ROI 池化
在我們繼續之前,需要先解決一些問題。分類器並不能很好地處理多種輸入尺寸。它們通常只能處理固定的輸入尺寸。但是,由於 RPN 中的邊框精調步驟,ROI 框可以有不同的尺寸。因此,我們需要用 ROI 池化來解決這個問題。
圖中展示的特徵圖來自較底層。
ROI 池化是指裁剪出特徵圖的一部分,然後將其重新調整為固定的尺寸。這個過程實際上和裁剪圖片並將其縮放是相似的(在實現細節上有所不同)。
Mask R-CNN 的作者提出了一種方法 ROIAlign,在特徵圖的不同點取樣,並應用雙線性插值。在我們的實現中,為簡單起見,我們使用 TensorFlow 的 crop_and_resize 函式來實現這個過程。
-
程式碼提示:ROI 池化在類 PyramidROIAlign 中實現。
4. 分割掩碼
到第 3 節為止,我們得到的正是一個用於目標檢測的 Faster R-CNN。而分割掩碼網路正是 Mask R-CNN 的論文引入的附加網路。
掩碼分支是一個卷積網路,取 ROI 分類器選擇的正區域為輸入,並生成它們的掩碼。其生成的掩碼是低解析度的:28x28 畫素。但它們是由浮點數表示的軟掩碼,相對於二進位制掩碼有更多的細節。掩碼的小尺寸屬性有助於保持掩碼分支網路的輕量性。在訓練過程中,我們將真實的掩碼縮小為 28x28 來計算損失函式,在推斷過程中,我們將預測的掩碼放大為 ROI 邊框的尺寸以給出最終的掩碼結果,每個目標有一個掩碼。
-
程式碼提示:掩碼分支網路在 build_fpn_mask_graph() 中。
建立一個顏色填充過濾器
和大多數影象編輯 app 中包含的過濾器不同,我們的過濾器更加智慧一些:它能自動找到目標。當你希望把它應用到視訊上而不是影象上時,這種技術更加有用。
訓練資料集
通常我會從尋找包含所需目標的公開資料集開始。但在這個案例中,我想向你展示這個專案的構建迴圈過程,因此我將介紹如何從零開始構建一個數據集。
我在 flickr 上搜索氣球圖片,並選取了 75 張圖片,將它們分成了訓練集和驗證集。找到圖片很容易,但標註階段才是困難的部分。
等等,我們不是需要數百萬張圖片來訓練深度學習模型嗎?實際上,有時候需要,有時候則不需要。我是考慮到以下兩點而顯著地減小了訓練集的規模:
首先,遷移學習。簡單來說,與其從零開始訓練一個新模型,我從已在 COCO 資料集(在 repo 中已提供下載)上訓練好的權重檔案開始。雖然 COCO 數劇集不包含氣球類別,但它包含了大量其它影象(約 12 萬張),因此訓練好的影象已經包含了自然影象中的大量常見特徵,這些特徵很有用。其次,由於這裡展示的應用案例很簡單,我並不需要令這個模型達到很高的準確率,很小的資料集就已足夠。
有很多工具可以用來標註影象。由於其簡單性,我最終使用了 VIA(VGG 影象標註器)。這是一個 HTML 檔案,你可以下載並在瀏覽器中開啟。標註最初幾張影象時比較慢,不過一旦熟悉了使用者介面,就能達到一分鐘一個目標的速度。
VGG 影象標註器工具的使用者介面
如果你不喜歡 VIA 工具,可以試試下列工具,我都測試過了:
-
LabelMe:最著名的標註工具之一,雖然其使用者介面有點慢,特別是縮放高清影象時。
-
RectLabel:簡單易用,只在 Mac 可用。
-
LabelBox:對於大型標記專案很合適,提供不同型別標記任務的選項。
-
COCO UI:用於標註 COCO 資料集的工具。
載入資料集
分割掩碼的儲存格式並沒有統一的標準。有些資料集中以 PNG 影象儲存,其它以多邊形點儲存等。為了處理這些案例,在我們的實現中提供了一個 Dataset 類,你可以通過重寫幾個函式來讀取任意格式的影象。
VIA 工具將標註儲存為 JSON 檔案,每個掩碼都是一系列多邊形點。
-
程式碼提示:通過複製 coco.py 並按你的需要修改是應用新資料集的簡單方法,我將新的檔案儲存為 ballons.py。
我的 BalloonDataset 類是這樣定義的:
load_balloons 讀取 JSON 檔案,提取標註,然後迭代地呼叫內部的 add_class 和 add_image 函式來構建資料集。
load_mask 通過畫出多邊形為影象中的每個目標生成點陣圖掩碼。
image_reference 返回鑑別影象的字串結果,以進行除錯。這裡返回的是影象檔案的路徑。
你可能已經注意到我的類不包含載入影象或返回邊框的函式。基礎的 Dataset 類中預設的 load_image 函式可以用於載入影象,邊框是通過掩碼動態地生成的。
驗證該資料集
為了驗證我的新程式碼可以正確地實現,我添加了這個 Jupyter notebook:inspect_balloon_data.ipynb。它載入了資料集,並可視化了掩碼、邊框,還可視化了 anchor 來驗證 anchor 的大小是否擬合了目標大小。以下是一個 good example。
來自 inspect_balloon_data notebook 的樣本
-
程式碼提示:為了建立這個 notebook 我複製了 inspect_data.ipynb(這是為 COCO 資料集寫的),然後修改了程式碼的初始部分來載入 Balloons 資料集。
配置
這個專案的配置和訓練 COCO 資料集的基礎配置很相似,因此我只需要修改 3 個值。正如我對 Dataset 類所設定的,我複製了基礎的 Config 類,然後添加了我的覆寫:
基礎的配置使用的是 1024x1024 px 的輸入影象尺寸以獲得最高的準確率。我保持了相同的配置,雖然影象相對較小,但模型可以自動地將它們重新縮放。
-
程式碼提示:基礎的 Config 類在 config.py 中,BalloonConfig 在 balloons.py 中。
訓練
Mask R-CNN 是一個規模很大的模型。尤其是在我們的實現中使用了 ResNet101 和 FPN,因此你需要一個 12GB 視訊記憶體的 GPU 才能訓練這個模型。我使用的是 Amazon P2 例項來訓練這個模型,在小規模的資料集上,訓練時間不到 1 個小時。
用以下命令開始訓練,以從 balloon 的目錄開始執行。這裡,我們需要指出訓練過程應該從預訓練的 COCO 權重開始。程式碼將從我們的 repo 中自動下載權重。
如果訓練停止了,用以下命令讓訓練繼續
-
程式碼提示:除了 balloon.py 以外,該 repo 還有兩個例子:train_shapes.ipynb,它訓練了一個小規模模型來檢測幾何形狀;coco.py,它是在 COCO 資料集上訓練的。
檢查結果
inspect_balloon_model notebook 展示了由訓練好的模型生成的結果。檢視該 notebook 可以獲得更多的視覺化選項,並一步一步檢查檢測流程。
-
程式碼提示:這個 notebook 是 inspect_model.ipynb 的簡化版本,包含視覺化選項和對 COCO 資料集程式碼的除錯。
顏色填充
現在我們已經得到了目標掩碼,讓我們將它們應用於顏色填充效果。方法很簡單:建立一個影象的灰度版本,然後在目標掩碼區域,將原始影象的顏色畫素複製上去。以下是一個 good example:
-
程式碼提示:應用填充效果的程式碼在 color_splash() 函式中。detect_and_color_splash() 可以實現載入影象、執行例項分割和應用顏色填充過濾器的完整流程。
FAQ 環節
Q:我希望瞭解更多該實現的細節,有什麼可讀的?
A:按這個順序閱讀論文:RCNN、Fast RCNN、Faster RCNN、FPN、Mask RCNN。
Q:我能在哪裡提更多的問題?
A:我們的 repo 的 Issue 頁面:https://github.com/matterport/Mask_RCNN/issues
原文連結:https://engineering.matterport.com/splash-of-color-instance-segmentation-with-mask-r-cnn-and-tensorflow-7c761e238b46