動(dòng)手實(shí)踐系列:CV語(yǔ)義分割!
圖像分割是計(jì)算機(jī)視覺(jué)中除了分類和檢測(cè)外的另一項(xiàng)基本任務(wù),它意味著要將圖片根據(jù)內(nèi)容分割成不同的塊。相比圖像分類和檢測(cè),分割是一項(xiàng)更精細(xì)的工作,因?yàn)樾枰獙?duì)每個(gè)像素點(diǎn)分類。
如下圖的街景分割,由于對(duì)每個(gè)像素點(diǎn)都分類,物體的輪廓是精準(zhǔn)勾勒的,而不是像檢測(cè)那樣給出邊界框。

圖像分割可以分為以下三個(gè)子領(lǐng)域:語(yǔ)義分割、實(shí)例分割、全景分割。

由對(duì)比圖可發(fā)現(xiàn),語(yǔ)義分割是從像素層次來(lái)識(shí)別圖像,為圖像中的每個(gè)像素制定類別標(biāo)記,目前廣泛應(yīng)用于醫(yī)學(xué)圖像和無(wú)人駕駛等;實(shí)例分割相對(duì)更具有挑戰(zhàn)性,不僅需要正確檢測(cè)圖像中的目標(biāo),同時(shí)還要精確的分割每個(gè)實(shí)例;全景分割綜合了兩個(gè)任務(wù),要求圖像中的每個(gè)像素點(diǎn)都必須被分配給一個(gè)語(yǔ)義標(biāo)簽和一個(gè)實(shí)例id。
01 語(yǔ)義分割中的關(guān)鍵步驟
在進(jìn)行網(wǎng)絡(luò)訓(xùn)練時(shí),時(shí)常需要對(duì)語(yǔ)義標(biāo)簽圖或是實(shí)例分割圖進(jìn)行預(yù)處理。如對(duì)于一張彩色的標(biāo)簽圖,通過(guò)顏色映射表得到每種顏色所代表的類別,再將其轉(zhuǎn)換成相應(yīng)的掩膜或Onehot編碼完成訓(xùn)練。這里將會(huì)對(duì)于其中的關(guān)鍵步驟進(jìn)行講解。
首先,以語(yǔ)義分割任務(wù)為例,介紹標(biāo)簽的不同表達(dá)形式。
1.1 語(yǔ)義標(biāo)簽圖
語(yǔ)義分割數(shù)據(jù)集中包括原圖和語(yǔ)義標(biāo)簽圖,兩者的尺寸大小相同,均為RGB圖像。
在標(biāo)簽圖像中,白色和黑色分別代表邊框和背景,而其他不同顏色代表不同的類別:

1.2 單通道掩膜
每個(gè)標(biāo)簽的RGB值與各自的標(biāo)注類別對(duì)應(yīng),則可以很容易地查找標(biāo)簽中每個(gè)像素的類別索引,生成單通道掩膜Mask。
如下面這種圖,標(biāo)注類別包括:Person、Purse、Plants、Sidewalk、Building。將語(yǔ)義標(biāo)簽圖轉(zhuǎn)換為單通道掩膜后為右圖所示,尺寸大小不變,但通道數(shù)由3變?yōu)?。

每個(gè)像素點(diǎn)位置一一對(duì)應(yīng)。

1.3 Onehot編碼
Onehot作為一種編碼方式,可以對(duì)每一個(gè)單通道掩膜進(jìn)行編碼。
比如對(duì)于上述掩膜圖Mask,圖像尺寸為,標(biāo)簽類別共有5類,我們需要將這個(gè)Mask變?yōu)橐粋€(gè)5個(gè)通道的Onehot輸出,尺寸為,也就是將掩膜中值全為1的像素點(diǎn)抽取出生成一個(gè)圖,相應(yīng)位置置為1,其余為0。再將全為2的抽取出再生成一個(gè)圖,相應(yīng)位置置為1,其余為0,以此類推。

02 語(yǔ)義分割實(shí)踐
接下來(lái)以Pascal VOC 2012語(yǔ)義分割數(shù)據(jù)集為例,介紹不同表達(dá)形式之間應(yīng)該如何相互轉(zhuǎn)換。
實(shí)踐采用的是Pascal VOC 2012語(yǔ)義分割數(shù)據(jù)集,它是語(yǔ)義分割任務(wù)中十分重要的數(shù)據(jù)集,有 20 類目標(biāo),這些目標(biāo)包括人類、機(jī)動(dòng)車(chē)類以及其他類,可用于目標(biāo)類別或背景的分割。
數(shù)據(jù)集開(kāi)源地址:
https://gas.graviti.cn/dataset/yluy/VOC2012Segmentation
2.1 數(shù)據(jù)集讀取
本次使用格物鈦數(shù)據(jù)平臺(tái)服務(wù)來(lái)完成數(shù)據(jù)集的在線讀取,平臺(tái)支持多種數(shù)據(jù)集類型,且提供了很多公開(kāi)數(shù)據(jù)集便于使用。在使用之前先進(jìn)行一些必要的準(zhǔn)備工作:
Fork數(shù)據(jù)集:如果需要使用公開(kāi)數(shù)據(jù)集,則需要將其先f(wàn)ork到自己的賬戶。 獲取AccessKey:獲取使用SDK與格物鈦數(shù)據(jù)平臺(tái)交互所需的密鑰,鏈接為https://gas.graviti.cn/tensorbay/developer 理解Segment:數(shù)據(jù)集的進(jìn)一步劃分,如VOC數(shù)據(jù)集分成了“train”和“test”兩個(gè)部分。
import?os
from?tensorbay?import?GAS
from?tensorbay.dataset?import?Data,?Dataset
from?tensorbay.label?import?InstanceMask,?SemanticMask
from?PIL?import?Image
import?numpy?as?np
import?torchvision
import?matplotlib.pyplot?as?plt
ACCESS_KEY?=?""
gas?=?GAS(ACCESS_KEY)
def?read_voc_images(is_train=True,?index=0):
????"""
????read?voc?image?using?tensorbay
????"""
????dataset?=?Dataset("VOC2012Segmentation",?gas)
????if?is_train:
????????segment?=?dataset["train"]
????else:
????????segment?=?dataset["test"]
????data?=?segment[index]
????feature?=?Image.open(data.open()).convert("RGB")
????label?=?Image.open(data.label.semantic_mask.open()).convert("RGB")
????visualize(feature,?label)
????return?feature,?label??#?PIL?Image
def?visualize(feature,?label):
????"""
????visualize?feature?and?label
????"""
????fig?=?plt.figure()
????ax?=?fig.add_subplot(121)??#?第一個(gè)子圖
????ax.imshow(feature)
????ax2?=?fig.add_subplot(122)??#?第二個(gè)子圖
????ax2.imshow(label)
????plt.show()
train_feature,?train_label?=?read_voc_images(is_train=True,?index=10)
train_label?=?np.array(train_label)?#?(375,?500,?3)

2.2 顏色映射表
在得到彩色語(yǔ)義標(biāo)簽圖后,則可以構(gòu)建一個(gè)顏色表映射,列出標(biāo)簽中每個(gè)RGB顏色的值及其標(biāo)注的類別。
def?colormap_voc():
????"""
????create?a?colormap
????"""
????colormap?=?[[0,?0,?0],?[128,?0,?0],?[0,?128,?0],?[128,?128,?0],
????????????????????[0,?0,?128],?[128,?0,?128],?[0,?128,?128],?[128,?128,?128],
????????????????????[64,?0,?0],?[192,?0,?0],?[64,?128,?0],?[192,?128,?0],
????????????????????[64,?0,?128],?[192,?0,?128],?[64,?128,?128],?[192,?128,?128],
????????????????????[0,?64,?0],?[128,?64,?0],?[0,?192,?0],?[128,?192,?0],
????????????????????[0,?64,?128]]
????classes?=?['background',?'aeroplane',?'bicycle',?'bird',?'boat',
???????????????????'bottle',?'bus',?'car',?'cat',?'chair',?'cow',
???????????????????'diningtable',?'dog',?'horse',?'motorbike',?'person',
???????????????????'potted?plant',?'sheep',?'sofa',?'train',?'tv/monitor']
????return?colormap,?classes
2.3 Label與Onehot轉(zhuǎn)換
根據(jù)映射表,實(shí)現(xiàn)語(yǔ)義標(biāo)簽圖與Onehot編碼的相互轉(zhuǎn)換:
def?label_to_onehot(label,?colormap):
????"""
????Converts?a?segmentation?label?(H,?W,?C)?to?(H,?W,?K)?where?the?last?dim?is?a?one
????hot?encoding?vector,?C?is?usually?1?or?3,?and?K?is?the?number?of?class.
????"""
????semantic_map?=?[]
????for?colour?in?colormap:
????????equality?=?np.equal(label,?colour)
????????class_map?=?np.all(equality,?axis=-1)
????????semantic_map.append(class_map)
????semantic_map?=?np.stack(semantic_map,?axis=-1).astype(np.float32)
????return?semantic_map
def?onehot_to_label(semantic_map,?colormap):
????"""
????Converts?a?mask?(H,?W,?K)?to?(H,?W,?C)
????"""
????x?=?np.argmax(semantic_map,?axis=-1)
????colour_codes?=?np.array(colormap)
????label?=?np.uint8(colour_codes[x.astype(np.uint8)])
????return?label
colormap,?classes?=?colormap_voc()
semantic_map?=?mask_to_onehot(train_label,?colormap)
print(semantic_map.shape)??#?[H,?W,?K]?=?[375,?500,?21]?包括背景共21個(gè)類別
label?=?onehot_to_label(semantic_map,?colormap)
print(label.shape)?#?[H,?W,?K]?=?[375,?500,?3]
2.4 Onehot與Mask轉(zhuǎn)換
同樣,借助映射表,實(shí)現(xiàn)單通道掩膜Mask與Onehot編碼的相互轉(zhuǎn)換:
def?onehot2mask(semantic_map):
????"""
????Converts?a?mask?(K,?H,?W)?to?(H,W)
????"""
????_mask?=?np.argmax(semantic_map,?axis=0).astype(np.uint8)
????return?_mask
def?mask2onehot(mask,?num_classes):
????"""
????Converts?a?segmentation?mask?(H,W)?to?(K,H,W)?where?the?last?dim?is?a?one
????hot?encoding?vector
????"""
????semantic_map?=?[mask?==?i?for?i?in?range(num_classes)]
????return?np.array(semantic_map).astype(np.uint8)
mask?=?onehot2mask(semantic_map.transpose(2,0,1))
print(np.unique(mask))?#?[?0??1?15]?索引相對(duì)應(yīng)的是背景、飛機(jī)、人
print(mask.shape)?#?(375,?500)
semantic_map?=?mask2onehot(mask,?len(colormap))
print(semantic_map.shape)?#?(21,?375,?500)游璐穎
福州大學(xué),datawhale成員
個(gè)人博客:https://sonatau.github.io

