公眾號編輯 wordpress魔貝課凡seo
計算機(jī)視覺和圖像處理
- Tensorflow入門
- 深度神經(jīng)網(wǎng)絡(luò)
- 圖像分類
- 目標(biāo)檢測
- 圖像分割
圖像分割
- 一、目標(biāo)分割
- 1.1 圖像分割的定義
- 1.2 任務(wù)類型
- 1.2.1 任務(wù)描述
- 1.2.2 任務(wù)類型
- 二、語義分割
- 2.1 FCN網(wǎng)絡(luò)
- 2.1.1網(wǎng)絡(luò)結(jié)構(gòu)
- 2.2 Unet網(wǎng)絡(luò)
- 三、UNet案例
- 3.1 數(shù)據(jù)集獲取
- 3.1.1 設(shè)置相關(guān)信息
- 3.1.2 圖像展示
- 3.1.3 數(shù)據(jù)集生成器
- 3.2 模型構(gòu)建
- 3.2.1 編碼部分
- 3.2.2 解碼部分
- 3.2.3 模型構(gòu)建
- 3.3 模型訓(xùn)練
- 3.3.1 數(shù)據(jù)集劃分
- 3.3.2 數(shù)據(jù)獲取
- 3.3.3 模型編譯
- 3.3.4 模型訓(xùn)練
- 3.4 模型預(yù)測
一、目標(biāo)分割
- 圖像分類旨在判斷該圖像所屬類別
- 目標(biāo)檢測是在圖像分類的基礎(chǔ)上,進(jìn)一步判斷圖像中的目標(biāo)具體在圖像的什么位置,通常是以外包矩陣的形式表示。
- 圖像分割是目標(biāo)檢測更進(jìn)階的任務(wù),目標(biāo)檢測只需要框出每個目標(biāo)的包圍盒,語義分割需要進(jìn)一步判斷圖像中哪些像素屬于哪個目標(biāo)。但是,語義分割不區(qū)分屬于相同類別的不同實例。
1.1 圖像分割的定義
在計算機(jī)視覺領(lǐng)域,圖像分割指的是將數(shù)字圖像細(xì)分為多個圖像子區(qū)域(像素的集合)的過程,并且同一個子區(qū)域內(nèi)的特征具有一定相似性,不同子區(qū)域的特征呈現(xiàn)較為明顯的差異。
1.2 任務(wù)類型
1.2.1 任務(wù)描述
我們的目標(biāo)是輸入一個RGB彩色圖片或者一個灰度圖,然后輸出一個包含各個像素類別標(biāo)簽的分割圖。
預(yù)測目標(biāo)可以采用one-hot編碼,即為每一個可能的類創(chuàng)建一個輸出通道。通過取每個像素點在各個通道的argmax可以得到最終的預(yù)測分割圖。
1.2.2 任務(wù)類型
目前的圖像分割任務(wù)主要有兩類: 語義分割和實例分割
- 語義分割就是把圖像中每個像素賦予一個類別標(biāo)簽
- 實例分割,相對于語義分割來講,不僅要區(qū)分不同類別的像素,還需要需要對同一類別的不同個體進(jìn)行區(qū)分。如下圖所示,不僅需要進(jìn)行類別的劃分,還要將各個個體劃分出來:羊1,羊2,羊3,羊4,羊5等。
二、語義分割
2.1 FCN網(wǎng)絡(luò)
FCN用于圖像語義分割,自從該網(wǎng)絡(luò)提出后,就成為語義分割的基本框架,后續(xù)算法基本都是在該網(wǎng)絡(luò)框架中改進(jìn)而來。
簡而言之,FCN和CNN的區(qū)別就是:CNN卷積層之后連接的是全連接層;FCN卷積層之后仍然連卷積層,輸出的是與輸入大小相同的特征圖。
2.1.1網(wǎng)絡(luò)結(jié)構(gòu)
FCN是一個端到端,像素對像素的全卷積網(wǎng)絡(luò),用于進(jìn)行圖像的語義分割。整體的網(wǎng)絡(luò)結(jié)構(gòu)分為兩個部分:全卷積部分和上采樣部分。
- 全卷積部分
全卷積部分使用經(jīng)典的CNN網(wǎng)絡(luò)(以AlexNet網(wǎng)絡(luò)為例),并把最后的全連接層換成1x1卷積,用于特征提取。 - 上采用部分
上采樣部分將最終得到的特征圖上采樣得到原圖像大小的語義分割結(jié)果。
在這里采用的上卷積方法是反卷積,也叫轉(zhuǎn)置卷積,反卷積是一種特殊的正向卷積,通俗的講,就是輸入補(bǔ)0+卷積。先按照一定的比例通過補(bǔ)0來擴(kuò)大輸入圖像的尺寸,再進(jìn)行正向卷積即可。
2.2 Unet網(wǎng)絡(luò)
Unet網(wǎng)絡(luò)是建立再FCNN網(wǎng)絡(luò)基礎(chǔ)上的。
整個網(wǎng)絡(luò)由編碼部分(左)和解碼部分(右)組成,類似于一個大大的u字母,具體介紹如下:
1.編碼部分是典型的卷積網(wǎng)絡(luò)架構(gòu)
編碼部分的主要功能是提取輸入圖像的特征。通過一系列的卷積層和池化層(通常是最大池化層),編碼部分逐漸減少特征圖的尺寸,同時增加特征圖的深度(即特征圖的數(shù)量)。
- 架構(gòu)中含有一種重復(fù)結(jié)構(gòu),每次重復(fù)中有2個3x3卷積層、非線性ReLU層和一個2x2 max pooling層(stride為2)。
- 每一次下采樣后我們都把特征通道的數(shù)量加倍。
下采樣(編碼部分):減少數(shù)據(jù)點的數(shù)量或降低數(shù)據(jù)的分辨率,用于減少數(shù)據(jù)量、簡化模型訓(xùn)練等
- 解碼部分也使用了類似的模式:
解碼部分的主要功能是恢復(fù)特征圖的空間分辨率,最終生成與輸入圖像相同尺寸的分割結(jié)果。解碼部分通過一系列的上采樣操作(如轉(zhuǎn)置卷積或上采樣層)和卷積操作來逐步恢復(fù)特征圖的尺寸。
- 每一步都首先使用反卷積,每次使用反卷積都將特征通道數(shù)量減半,特征圖大小加倍。
- 反卷積過后,將反卷積的結(jié)果與編碼部分中對應(yīng)步驟的特征圖拼接起來。
- 編碼部分中的特征圖尺寸稍大,將其修建過后進(jìn)行拼接。
- 對拼接后的map再進(jìn)行2次3x3的卷積。
- 最后一層的卷積核大小為1x1,將64通道的特征圖轉(zhuǎn)化為特定類比數(shù)量的結(jié)果。
上采樣(解碼部分):增加數(shù)據(jù)點的數(shù)量或提高數(shù)據(jù)的分辨率,用于恢復(fù)細(xì)節(jié)、改善圖像質(zhì)量等。
三、UNet案例
Oxford-IIIT Pet Dataset寵物圖像分割數(shù)據(jù)集,包含37種寵物類別,其中有12種貓的類別和25種狗的類別,每個類別大約有200張圖片,所有圖像都具有品種,頭部ROI和像素級分割的標(biāo)注,如下圖所示:
import os
from IPython.display import Image,display
from tensorflow.keras.preprocessing.image import load_img
import PIL
from PIL import ImageOps
3.1 數(shù)據(jù)集獲取
3.1.1 設(shè)置相關(guān)信息
# 圖像位置
input_dir = 'segdata/images/'
# 圖像路徑
input_img_path = sorted([os.path.join(input_dir,fname) for fname in os.listdir(input_dir) if fname.endswith('jpg')])
input_img_path
# 標(biāo)注信息
target_dir = 'segdata/annotations/trimaps/'
# 目標(biāo)值
target_img_path = sorted([os.path.join(target_dir,fname) for fname in os.listdir(target_dir) if fname.endswith('png') and not fname.startswith('.')])
target_img_path
# 圖像大小及類別信息
img_size = (160,160)
batch_size = 32
num_classes = 4
3.1.2 圖像展示
display(Image(input_img_path[10]))
img = PIL.ImageOps.autocontrast(load_img(target_img_path[10]))
display(img)
3.1.3 數(shù)據(jù)集生成器
from tensorflow import keras
import numpy as np
# 數(shù)據(jù)集獲取類
class OxfordPets(keras.utils.Sequence):# 初始化def __init__(self,batch_size,img_size,input_img_path,target_img_path):self.batch_size = batch_sizeself.img_size = img_sizeself.input_img_path = input_img_pathself.target_img_path = target_img_path# 迭代次數(shù)def __len__(self):return len(self.target_img_path)//self.batch_size # 或者batch數(shù)據(jù)def __getitem__(self,idx):# 當(dāng)前批次對應(yīng)的索引值i = idx * self.batch_size# 圖像數(shù)據(jù)batch_input_img_path = self.input_img_path[i:i+self.batch_size]# 標(biāo)簽數(shù)據(jù)batch_target_img_path = self.target_img_path[i:i+self.batch_size]# 構(gòu)建送入網(wǎng)絡(luò)中圖像數(shù)據(jù)x = np.zeros((self.batch_size,)+self.img_size+(3,),dtype='float32')for j,path in enumerate(batch_input_img_path):img = load_img(path,target_size=self.img_size)# 將PIL圖像對象轉(zhuǎn)換成Numpy數(shù)組x[j] = keras.preprocessing.image.img_to_array(img)y = np.zeros((self.target_size,) + self.img_size +(1,),dtype='uint8')for j,path in enumerate(batch_target_img_path):img = load_img(path,target_size=self.img_size,color_mode="grayscale")# 再數(shù)組的末尾增加一個維度y[j] = np.expand_dims(img,2)return x,y
3.2 模型構(gòu)建
import tensorflow as tf
import tensorflow.keras as keras
from tensorflow.keras.layers import Input,Conv2D,Conv2DTranspose
from tensorflow.keras.layers import MaxPooling2D,Cropping2D,Concatenate,ZeroPadding2D
from tensorflow.keras.layers import Lambda,Activation,BatchNormalization,Dropout
from tensorflow.keras.models import Model
3.2.1 編碼部分
# 下采樣:輸出張量,卷積核個數(shù)
def downsampling_block(input_tensor,filters):# 輸入層x = Conv2D(filters,kernel_size=(3,3),padding='same')(input_tensor)# BN層x = BatchNormalization()(x)# 激活層x = Activation('relu')(x)# 卷積x = Conv2D(filters,kernel_size=(3,3),padding='same')(x)# BN層x = BatchNormalization()(x)# 激活層x = Activation('relu')(x)# 返回return MaxPooling2D(pool_size=(2,2))(x),x
3.2.2 解碼部分
# 上采樣:輸入張量,特征融合張量(編碼部分產(chǎn)生的特征圖),卷積核個數(shù)
def upsampling_block(input_tensor,skip_tensor,filters):# 反卷積x = Conv2DTranspose(filters,kernel_size=(3,3),strides=2,padding='same')(input_tensor)# 獲取當(dāng)前特征圖的尺寸_,x_hight,x_width,_ = x.shape# 獲取特征融合圖的尺寸_,s_hight,s_width,_ = skip_tensor.shape# 獲取特征圖大小差異h_crop = x_hight - s_hightw_crop = x_width - s_width# 若特征圖大小相同則不進(jìn)行裁剪if h_crop == 0 and w_crop ==0:y = skip_tensorelse:cropping = ((h_crop//2,h_crop-h_crop//2),(w_crop//2,w_crop-w_crop//2))y = ZeroPadding2D(cropping=cropping)(skip_tensor)# 特征融合x = Concatenate()([x,y])# 卷積x = Conv2D(filters,kernel_size=(3,3),padding='same')(x)# BN層x = BatchNormalization()(x)# 激活層x = Activation('relu')(x)# 卷積x = Conv2D(filters,kernel_size=(3,3),padding='same')(x)# BN層x = BatchNormalization()(x)# 激活層x = Activation('relu')(x)return x
3.2.3 模型構(gòu)建
def unet(imagesize,classes,fetures=64,depth=3):# 定義輸入inputs = keras.Input(shape=(img_size)+(3,))x = inputs# ?來存放進(jìn)?特征融合的特征圖skips=[]# 構(gòu)建編碼部分for i in range(depth):# 下采樣x,x0 = downsampling_block(x,fetures)skips.append(x0)# 特征翻倍fetures *=2# 卷積x = Conv2D(fetures,kernel_size=(3,3),padding="same")(x)# BN層x = BatchNormalization()(x)# 激活層x = Activation("relu")(x)# 卷積x = Conv2D(fetures,kernel_size=(3,3),padding='same')(x)# BN層x = BatchNormalization()(x)# 激活層x = Activation("relu")(x)# 構(gòu)建解碼部分for i in reversed(range(depth)):# 深度增加,特征圖通道減半fetures //= 2# 下采樣x = upsampling_block(x,skips[i],fetures)# 卷積x = Conv2D(fetures,kernel_size=(1,1),padding="same")(x)# 激活outputs = Activation("softmax")(x)# 模型定義model = keras.Model(inputs,outputs)return model
model = unet(img_size,4)
model.summary()
3.3 模型訓(xùn)練
3.3.1 數(shù)據(jù)集劃分
import random
# 驗證集數(shù)量
val_sample = 500
# 將數(shù)據(jù)集打亂
random.Random(100).shuffle(input_img_path)
random.Random(100).shuffle(target_img_path)
# 訓(xùn)練集
train_input_img_path = input_img_path[:-val_sample]
train_target_img_path = target_img_path[:-val_sample]
# 驗證集
test_input_img_path = input_img_path[-val_sample:]
test_target_img_path = target_img_path[-val_sample:]
3.3.2 數(shù)據(jù)獲取
train_gen = OxfordPets(batch_size,img_size,train_input_img_path,train_target_img_path)
test_gen = OxfordPets(batch_size,img_size,test_input_img_path,test_target_img_path)
3.3.3 模型編譯
model.compile(optimizer=tf.keras.optimizers.RMSprop(),loss=tf.keras.losses.sparse_categorical_crossentropy)
3.3.4 模型訓(xùn)練
model.fit(train_gen,epochs=8,validation_data=test_gen,steps_per_epoch=1,validation_steps=1)
3.4 模型預(yù)測
# predict = model.predict(test_gen)
predictions = []# 獲取數(shù)據(jù)集的大小
steps = len(test_gen)# 分批進(jìn)行預(yù)測
for i in range(steps):# 獲取下一個批次的數(shù)據(jù)batch = next(iter(test_gen))# batch 是一個元組,第一個元素是輸入數(shù)據(jù),第二個元素是標(biāo)簽batch_inputs, batch_labels = batch# 使用輸入數(shù)據(jù)進(jìn)行預(yù)測batch_predictions = model.predict(batch_inputs)predictions.append(batch_predictions)# 合并所有的預(yù)測結(jié)果
final_predictions = np.concatenate(predictions, axis=0)
def display_mask(i):# 獲取第i個樣本預(yù)測結(jié)果mask = np.argmax(final_predictions[i],axis=-1)# 擴(kuò)展維度mask =np.expand_dims(mask,axis=-1)# 將掩碼轉(zhuǎn)換為PIL Image對象img = keras.preprocessing.image.array_to_img(mask)# 增強(qiáng)圖像對比度img = PIL.ImageOps.autocontrast(img)display(img)
display(Image(filename=test_input_img_path[5]))
img = PIL.ImageOps.autocontrast(load_img(test_target_img_path[5]))
display(img)
display_mask(5)
**這人工智能不學(xué)也罷,沒有個好電腦根本跑不出來,只能降低epoch,降低batch_size,分批次去預(yù)測模型,勉強(qiáng)可以訓(xùn)練預(yù)測模型,但結(jié)果就有點不敬人意了