1. 程式人生 > >先理解Mask R-CNN的工作原理,然後構建顏色填充器應用

先理解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 生成兩個輸出:

  1. anchor 類別:前景或背景(FG/BG)。前景類別意味著可能存在一個目標在 anchor box 中。

  2. 邊框精調:前景 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

  1. 類別:ROI 中的目標的類別。和 RPN 不同(兩個類別,前景或背景),這個網路更深並且可以將區域分類為具體的類別(人、車、椅子等)。它還可以生成一個背景類別,然後就可以棄用 ROI 了。

  2. 邊框精調:和 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