全文共6625字,預計學習時長13分鐘或更長
劃重點!!!本文主要內容:
· Mask R-CNN是一種先進的算法框架,用於解決圖像分割問題
· 本文將採用逐步推進、層層推進的方式來剖析Mask R-CNN的工作原理
· 本文還將探析如何用Python實現Mask R-CNN及其在圖像處理的運用
自動駕駛系統所囊括的計算機視覺技術複雜巧妙,種類繁多,在數據科學家眼中,這種複雜性與混合性堪比夢中天籟,引人入勝。
因此,筆者開始研究這一系統,試圖理解自動駕駛賦予汽車的這雙「火眼金睛」的背後,究竟隱藏著何等玄機。僅憑一個物體檢測框架可能行不通,因為它僅能識別對象,並用某種固定的形狀將其標出。
然而若運用於現實中,這種方法風險很大。試想,倘若前方有一處急轉彎,系統只在道路上給出一個矩形標識,這樣以來車輛很有可能判斷不出是該避讓還是徑直上前,車禍一觸即發!
因此,對新技術的訴求應運而生,該技術須能識別具體路況,以指引車輛順利過彎。
到底什麼樣的黑科技能夠滿足這一需求呢—— Mask R-CNN閃亮登場!
本文將簡要闡釋何為圖像分割,並以此引入全文的核心主角—— Mask R-CNN算法框架。最後本文將探析如何用Python實現Mask R-CNN框架。
目錄
1. 圖像分割概覽
2. 剖析Mask R-CNN
3. 實現Mask R-CNN的步驟
4. 實現Mask R-CNN
1. 圖像分割概覽
讓我們先來簡單回顧一下圖像分割:圖像分割即為圖片的每個對象創建一個像素級的掩膜,該技術可使大家對圖像中的對象有更深入的了解。下圖將助你理解何為圖像分割。
如圖所示,每個對象(即該圖中的每個單元)已彼此分離,這就是圖像分割的工作原理。
圖像分割可分為兩種,語義分割與實例分割,具體實例詳見下文。
左圖五個對象均為人,因此語義分割會將這五個對象視為一個整體。而右圖同樣也有五個對象(亦均為人),但同一類別的不同對象在此被視為不同的實例,這就是實例分割。
2. 剖析Mask-CNN
Mask R-CNN可算作是Faster R-CNN的升級版。Faster R-CNN廣泛用於對象檢測。對於給定圖像,它會給圖中每個對象加上類別標籤與邊界框坐標。如果你輸入下圖:
Fast R-CNN將會返回如下結果:
Mask R-CNN框架是以Faster R-CNN為基礎而架構的。因此,針對給定圖像, Mask R-CNN不僅會給每個對象添加類標籤與邊界框坐標,還會返回其對象掩膜。
先來分析Faster R-CNN的工作原理,因為這也助於促進對Mask R-CNN的直觀感受。
· Faster R-CNN通過ConvNet從圖片中提取特徵圖
· 特徵圖通過區域候選網絡 (RPN) 予以傳遞,該網絡會返回候選邊界框
· 應用Rol池化層,將所有候選區域修正到同一尺寸
· 將候選區域傳遞到全連接層,將對象邊界框分類及輸出
一旦掌握了Faster R-CNN 的工作原理, Mask R-CNN 自然也就易如反掌。接下來將從輸入到預測類標籤、邊界框與對象掩膜,逐步剖析Mask R-CNN 的背後玄機。
骨幹模型 (Backbone Model)
與在Faster R-CNN 中從圖片提取特徵圖所用的ConvNet相似,在Mask R-CNN中提取特徵圖用的是殘差網絡 101結構。因此,先選取圖片,用殘差網絡 101結構提取特徵,這些特徵便是下一層的輸入數據。
區域候選網絡 (RPN)
將上一步提取的特徵圖應用於區域候選網絡 (RPN)。這基本能預測出一個對象是否位於該區域。該步驟可得到那些可能包含某些對象的區域或特徵圖。
感興趣區域 (RoI)
從RPN獲得的區域可能形狀不一。因此,須將所得區域應用於一個池化層,將所有區域轉換為同一形狀。隨後,這些區域通過全連接網絡,以此預測出類標籤與邊界框。
在該步驟之前,一切似乎與Faster R-CNN 並無二致。不過RoI便是這兩者的區別所在。除此之外,另一個區別在於,Mask R-CNN可創建掩膜。
我們先算出感興趣區域,以減少計算時間。在所有預測區域中,原標記框可用於計算交並比。計算公式如下:
交並比=交集區域/併集區域
只有當交並比大於等於0.5時,這一區域方為感興趣區域;若小於0.5,該區域將不予考慮。計算所有區域的交並比,並選出交並比大於0.5的區域。
以下圖為例:
紅框即該圖的原標記框,如下圖所示,通過RPN得出4個區域:
如圖所示,框1與框2的交並比應該小於0.5,而框3與框4的交並比約大於0.5。因此可判定框3與框4屬於該圖的感興趣區域,而框1及框2可棄之不用。
接下來便是Mask R-CNN操作流程的尾聲。
分割掩膜
基於交並比值獲得感興趣區域後,給已有框架加上一個掩膜分支,每個囊括特定對象的區域都會被賦予一個掩膜。每個區域都會被賦予一個28 X 28掩膜,並按比例放大以便推斷。
詳見下圖:
該圖分割掩膜效果如下:
到此,圖上所有對象已被分割。這就是Mask R-CNN給圖中所有對象預測掩膜的最後一步。
謹記, Mask R-CNN的訓練周期很長。筆者曾用享有盛名的COCO數據集訓練Mask R-CNN ,耗時長達一到兩天。受篇幅所限,本文不會討論Mask R-CNN的訓練詳情。
不過筆者會用到COCO數據集中Mask R-CNN的預訓練權值。在探析如何用Python編碼前,先來了解Mask R-CNN在執行實例分割時的所需步驟。
3. 實現Mask R-CNN的步驟
以下即圖像分割環節。該環節所用的Mask R-CNN框架 是由Facebook AI Research(FAIR) 的數據科學家與研究員聯手打造。
Mask R-CNN分割圖像所要遵循的步驟,詳見下文。
第1步:克隆知識庫
首先,克隆mask rcnn知識庫,其庫應具有Mask R-CNN架構。可利用以下命令克隆該知識庫:
git clone
克隆完成後,需要安裝Mask R-CNN所需的依賴庫。
第2步:安裝依賴庫
以下是Mask R-CNN的所有依賴庫:
· numpy
· scipy
· Pillow
· cython
· matplotlib
· scikit-image
· tensorflow>=1.3.0
· keras>=2.0.8
· opencv-python
· h5py
· imgaug
· IPython
使用Mask R-CNN框架前必須安裝所有依賴庫。
第3步:下載預訓練模型權值
接下來,需要下載預訓練模型權值。這些權值從MS COCO數據集所訓練的一種模型而來。下載完成後,粘貼此文件於第一步克隆的Mask_RCNN知識庫樣本文件夾中。
第4步:圖像預測
最後,採用Mask R-CNN架構和預訓練模型權值生成圖像預測。
四步完成後,開始轉入Jupyter Notebook。用python實現上述所有代碼並為圖像中各個對象生成掩膜、類和邊界框
4. 基於Python的Mask R-CNN實現
為了執行所有本節將談到的代碼塊,須創建一個新的Python筆記本,然後將其放入所克隆的Mask_RCNN 「樣本」文件夾中。
開始導入所需程序庫:
import os
import sys
import random
import math
import numpy as np
import skimage.io
import matplotlib
import matplotlib.pyplot as plt
# Root directory of the project
ROOT_DIR = os.path.abspath("../")
import warnings
warnings.filterwarnings("ignore")
# Import Mask RCNN
sys.path.append(ROOT_DIR) # To find local version of the library
from mrcnn import utils
import mrcnn.model as modellib
from mrcnn import visualize
# Import COCO config
sys.path.append(os.path.join(ROOT_DIR, "samples/coco/")) # To find local version
import coco
%matplotlib inline
接下來,將對預訓練權值及待分割的圖像定義路徑:
# Directory to save logs and trained model
MODEL_DIR = os.path.join(ROOT_DIR, "logs")
# Local path to trained weights file
COCO_MODEL_PATH = os.path.join('', "mask_rcnn_coco.h5")
# Download COCO trained weights from Releases if needed
if not os.path.exists(COCO_MODEL_PATH):
utils.download_trained_weights(COCO_MODEL_PATH)
# Directory of images to run detection on
IMAGE_DIR = os.path.join(ROOT_DIR, "images")
若未將權值放置於樣本文件夾,則會重新下載權值。現在,創建推理類,用於推斷Mask R-CNN模型:
class InferenceConfig(coco.CocoConfig):
# Set batch size to 1 since we'll be running inference on
# one image at a time. Batch size = GPU_COUNT * IMAGES_PER_GPU
GPU_COUNT = 1
IMAGES_PER_GPU = 1
config = InferenceConfig()
config.display()
由此匯總可推斷出什麼呢?可看到即將使用的Mask R-CNN模型的多項性能規格。
如上所述,該算法基幹是殘差網絡101。鑑於其訓練數據集為COCO,故返回的掩膜形狀為28×28。總計81類(包括背景)。
此外,還有其他多項數據如:
· 輸入形狀
· 待用圖形處理器編號
· 驗證步驟等
加載權值
接下來,創建模型,加載已下載的預訓練模型權值。確保預訓練權值與筆記本處在同一文件夾中,否則,必須給出該權值文件夾的位置:
# Create model object in inference mode.
model = modellib.MaskRCNN(mode="inference", model_dir='mask_rcnn_coco.hy', config=config)
# Load weights trained on MS-COCO
model.load_weights('mask_rcnn_coco.h5', by_name=True)
現在,定義COCO數據集類別,供預測階段使用:
# COCO Class names
class_names = ['BG', 'person', 'bicycle', 'car', 'motorcycle', 'airplane',
'bus', 'train', 'truck', 'boat', 'traffic light',
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird',
'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear',
'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie',
'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball',
'kite', 'baseball bat', 'baseball glove', 'skateboard',
'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup',
'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza',
'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed',
'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote',
'keyboard', 'cell phone', 'microwave', 'oven', 'toaster',
'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors',
'teddy bear', 'hair drier', 'toothbrush']
加載圖像,觀察模型運行情況。測試圖像任意選擇。
# Load a random image from the images folder
image = skimage.io.imread('sample.jpg')
# original image
plt.figure(figsize=(12,10))
skimage.io.imshow(image)
此即測試用圖。兩輛汽車(一輛在前,一輛在後)及一輛自行車清晰可辨。
進行預測
開始預測!使用帶有預訓練權值的Mask R-CNN模型,並觀察其圖像分割效果。首先對圖像進行預測,繪製結果並將其可視化:
# Run detection
results = model.detect([image], verbose=1)
# Visualize results
r = results[0]
visualize.display_instances(image, r['rois'], r['masks'], r['class_ids'], class_names, r['scores'])
有趣!該模型成功地將圖像中的汽車和自行車進行了分割。每個掩膜或所分割對象均可分開研究。其實現方式詳見下文。
首先將模型預測的所有掩膜存儲在掩膜變量中。現在,這些掩膜是布爾型(True和False),因此需要將其轉換為二進位(1和0):
mask = r['masks']
mask = mask.astype(int)
mask.shape
輸出:
(480,640,3)
此會得出一個0和1的數組,其中0表示在那個特定像素處無對象存在,1表示在該像素處有對象存在。注意,掩膜的形狀與原始圖像的形狀相似(這可以通過輸出原始圖像的形狀來進行驗證)
然而,掩膜形狀裡的3不表示路徑,相反,它代表模型所分割的對象數量。由於模型在上述樣本圖像中已識別了3個對象,所以掩模的形狀為 (480,640,3) 。如果有5個對象,則此形狀應為 (480,640,5) 。
有了原始圖像以及其掩膜數組。為了輸出或獲得各個圖像片段,將創建for loop函數,後將每個掩模與原始圖像相乘,以求分割後的各圖像片段:
for i in range(mask.shape[2]):
temp = skimage.io.imread('sample.jpg')
for j in range(temp.shape[2]):
temp[:,:,j] = temp[:,:,j] * mask[:,:,i]
plt.figure(figsize=(8,8))
plt.imshow(temp)
以上即繪製出圖片各掩膜或對象的操作方法。有許多有趣又使用的用例。圖像分割可以降低計算成本,因為如此一來不需要對整個圖像進行預處理,只需要預處理分割片段即可。
推斷
下面即Mask R-CNN模型的輸出結果:
Mask R-CNN圓滿完成圖像分割任務。太棒了!你已經成功地使用Mask R-CNN模型建立了屬於自己的圖像分割。
留言 點讚 關注
我們一起分享AI學習與發展的乾貨
如需轉載,請後臺留言,遵守轉載規範