先理解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://www.jiqizhixin.com/articles/Mask_RCNN-tree-master-samples-balloon