圖像語義分割簡介
圖像語義分割網絡結構-FCN
上采樣
代碼實現
import tensorflow as tf
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import glob
import os
# 顯存自適應分配
gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu,True)
gpu_ok = tf.test.is_gpu_available()
print("tf version:", tf.__version__)
print("use GPU", gpu_ok) # 判斷是否使用gpu進行訓練
例子
os.listdir("F:/py/ziliao/數據集/圖片定位與分割數據集/annotations/trimaps")[-5:]
# 讀取圖片
img = tf.io.read_file(r"F:/py/ziliao/數據集/圖片定位與分割數據集/annotations/trimaps/Abyssinian_2.png")
# 讀取圖片
img2 = tf.io.read_file(r"F:/py/ziliao/數據集/圖片定位與分割數據集/images/Abyssinian_2.jpg")
# 解碼
img = tf.image.decode_png(img)
img2 = tf.image.decode_jpeg(img2)
# 查看大小
img.shape
# 移除的所有大小為1的維度具有相同類型的張量
img = tf.squeeze(img)
img.shape
# 繪圖
plt.imshow(img)
img.numpy()
np.unique(img.numpy()) # 我們能看見改圖中只有3種像素 分別對應背景 邊框 身體
plt.imshow(img2)
完整代碼
# 讀取所有圖片路徑
images = glob.glob(r"F:/py/ziliao/數據集/圖片定位與分割數據集/images/*.jpg")
# 讀取對應的目標圖像
anno = glob.glob(r"F:/py/ziliao/數據集/圖片定位與分割數據集/annotations/trimaps/*.png")
# 對圖片進行隨機
np.random.seed(2020) #設置隨機數種子生成的隨機數將會是同一個
index = np.random.permutation(len(images))
images = np.array(images)[index]
anno = np.array(anno)[index]
# 創建數據集
dataset = tf.data.Dataset.from_tensor_slices((images,anno))
# 划分數據集
test_count = int(len(images)*0.2)
train_count = len(images)-test_count
test_count,train_count
# 分割數據集
data_train =dataset.skip(test_count) # 跳過test_count 的數據作為訓練數據
data_test = dataset.take(test_count) # 取出test_count的數據作為測試集
# 創建 jpg格式的解碼函數
def read_jpg(path):
img = tf.io.read_file(path)
img = tf.image.decode_jpeg(img,channels=3)
return img
# 創建png的解碼函數
def read_png(path):
img = tf.io.read_file(path)
img = tf.image.decode_png(img,channels=1)
return img
# 歸一化
def normal_img(input_images,input_anno):
input_images = tf.cast(input_images,tf.float32) # 改變數據類型為float32
input_images/127.5 - 1 # 歸一化到 -1 到 1 之間
input_anno -= 1 # 因為目標圖像取值范圍是 1,2,3 我們 -1 修改為 0,1,2
return input_images,input_anno
# 加載函數
def load_images(input_images_path,input_anno_path):
input_image = read_jpg(input_images_path)
input_anno = read_png(input_anno_path)
input_image = tf.image.resize(input_image,(224,224))
input_anno = tf.image.resize(input_anno,(224,224))
return normal_img(input_image,input_anno)
# 使用加載函數
data_train = data_train.map(load_images,
num_parallel_calls=tf.data.experimental.AUTOTUNE)
data_test = data_test.map(load_images,
num_parallel_calls=tf.data.experimental.AUTOTUNE)
BATCH_SIZE = 16
# 對數據集進行亂序
data_train = data_train.repeat().shuffle(5912).batch(BATCH_SIZE)
data_test = data_test.batch(BATCH_SIZE)
for img,anno in data_train.take(1):
plt.subplot(1,2,1) # 1行2列
plt.imshow(tf.keras.preprocessing.image.array_to_img(img[0]))
plt.subplot(1,2,2) # 1行2列
plt.imshow(tf.keras.preprocessing.image.array_to_img(anno[0]))
# 使用預訓練網絡
conv_base = tf.keras.applications.VGG16(weights="imagenet", # 使用該模型image權重
input_shape=(224,224,3),
include_top = False)
conv_base.summary()
# 例: 獲得某一層的輸出 如 獲取 block5_conv3 的層的輸出
conv_base.get_layer("block5_conv3").output
# 例: 從預訓練網絡中創建子模型
sub_model = tf.keras.models.Model(inputs = conv_base.input,
outputs = conv_base.get_layer("block5_conv3").output
)
FCN跳階-獲取模型中間層的輸出
layer_names = [
"block5_conv3", # 14x14
"block4_conv3", # 28x28
"block3_conv3", # 56x56
"block5_pool"
] # 獲取輸出層的名字
# 創建特征提取模型
layers_output = [conv_base.get_layer(layer_name).output for layer_name in layer_names]
# 從預訓練網絡中創建子模型
multi_out_model = tf.keras.models.Model(inputs = conv_base.input,
outputs = layers_output
)
multi_out_model.trainable = False # 禁止訓練
#構建模型
inputs = tf.keras.layers.Input(shape=(224,224,3))
out_block5_conv3,out_block4_conv3,out_block3_conv3,out = multi_out_model(inputs)
反卷積 上采樣
#輸出512個卷積核 窗口大小3*3 圖片變大2倍填充方式same保證和原有圖像大小一樣 激活relu
x1 = tf.keras.layers.Conv2DTranspose(512,3,
strides=2,
padding="same",
activation="relu")(out) # 調用out層
x1 = tf.keras.layers.Conv2D(512,3,
padding="same",
activation="relu")(x1) # 在增加一層卷積
x2 = tf.add(x1,out_block5_conv3) # 層相加
x2.shape
x2 = tf.keras.layers.Conv2DTranspose(512,3,strides=2,padding="same",activation="relu")(x2)
x2 = tf.keras.layers.Conv2D(512,3,padding="same",activation="relu")(x2) # 在增加一層卷積
x3 = tf.add(x2,out_block4_conv3) # 層相加
x3.shape
x3 = tf.keras.layers.Conv2DTranspose(256,3,strides=2,padding="same",activation="relu")(x3)
x3 = tf.keras.layers.Conv2D(256,3,padding="same",activation="relu")(x3) # 在增加一層卷積進一步提取特征
x4 = tf.add(x3,out_block3_conv3)
x4.shape
x5 = tf.keras.layers.Conv2DTranspose(128,3,strides=2,padding="same",activation="relu")(x4)
x5 = tf.keras.layers.Conv2D(128,3,padding="same",activation="relu")(x5) # 在增加一層卷積進一步提取特征
prediction = tf.keras.layers.Conv2DTranspose(3, # 因為目標圖像取值是3個,所以我們輸出為3
3,
strides=2,
padding="same",
activation="softmax")(x5) # 上采樣
model = tf.keras.models.Model(
inputs=inputs,
outputs=prediction
)
# 模型編譯
model.compile(
optimizer="adam",
loss = "sparse_categorical_crossentropy",
metrics=["acc"]
)
# 訓練模型
history = model.fit(data_train,
epochs=5,
steps_per_epoch=train_count//BATCH_SIZE,
validation_data=data_test,
validation_steps=test_count//BATCH_SIZE,
)
loss = history.history["loss"]
val_loss = history.history["val_loss"]
epochs = range(15)
plt.figure()
plt.plot(epochs,loss,"r",label="Trainning loss")
plt.plot(epochs,val_loss,"bo",label="Validation loss")
plt.title("Training and Validation Loss")
plt.xlabel("epochs")
plt.ylabel("Loss Value")
plt.legend()
plt.show()
num = 3
for image, mask in data_test.take(1): # 從test數據取出一個batch
pred_mask = model.predict(image) # model.predict(image) 對圖片進行預測
pred_mask = tf.argmax(pred_mask, axis=-1) # 取出預測最大值
pred_mask = pred_mask[..., tf.newaxis] # 維度擴展 取前面所有維度
plt.figure(figsize=(10, 10))
for i in range(num):
plt.subplot(num, 3, i*num+1)
plt.imshow(tf.keras.preprocessing.image.array_to_img(image[i])) # 原圖
plt.subplot(num, 3, i*num+2)
plt.imshow(tf.keras.preprocessing.image.array_to_img(mask[i])) # 真實分割圖
plt.subplot(num, 3, i*num+3)
plt.imshow(tf.keras.preprocessing.image.array_to_img(pred_mask[i])) # 預測分割圖