语义分割-FCN8S(案例) 作者:马育民 • 2020-02-21 11:10 • 阅读:10209 # 代码 ### 导包 ``` import tensorflow as tf import glob import numpy as np import matplotlib.pyplot as plt from PIL import Image from IPython.display import display import os.path ``` ### 常量 ``` # 图片大小 IMG_WIDTH=224 AUTOTUNE=tf.data.experimental.AUTOTUNE # vgg16权重文件 vgg16_h5='/kaggle/input/vgg16-weights-tf-dim-ordering-tf-kernels-notop/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5' ``` ### 获取所有jpg图片路径 ``` # 有3个mat文件 img_paths=glob.glob("/kaggle/input/head-location/images/images/*.jpg") # png_paths=glob.glob("/kaggle/input/head-location/annotations/annotations/trimaps/*") print(img_paths[:3]) # print(png_paths[:3]) ``` ### 查看所有图片数量 ``` count_img=len(img_paths) # count_png=len(png_paths) print(count_img) # print(count_png) ``` ### 获取所有png图片路径 jpg图片和png图片可能不是一一对应的,所以需要根据jpg图片名字,获取png图片路径 ``` png_path="/kaggle/input/head-location/annotations/annotations/trimaps/" png_paths=[] def get_png_paths(): for item in img_paths: # print(item) name=os.path.basename(item).split(".")[0]+".png" # print(name) path=os.path.join(png_path,name) png_paths.append(path) # print(path) get_png_paths() print(png_paths[0]) print(len(png_paths)) ``` ### 测试:显示jpg和png图片 ``` display(Image.open(img_paths[0])) display(Image.open(png_paths[0])) ``` **注意:** png图片不能正常显示 ### 查看png图片内部数据(关键) ``` from collections import Counter def show_png_data(): arr=np.array(Image.open(png_paths[0])) print(type(arr)) print(arr) print("最小数:",np.min(arr)) print("最大数:",np.max(arr)) print(Counter(arr.flatten())) show_png_data() ``` 执行结果: ``` [[2 2 2 ... 2 2 2] [2 2 2 ... 2 2 2] [2 2 2 ... 2 2 2] ... [2 2 2 ... 2 2 2] [2 2 2 ... 2 2 2] [2 2 2 ... 2 2 2]] 最小数: 1 最大数: 3 Counter({2: 132176, 1: 37847, 3: 17477}) ``` 可知png图片的数据只有1,2,3 ### 用matplotlib图片 matplotlib显示特殊图片时,会自行调整,显示成人眼可识别的图片 ``` plt.imshow(Image.open(img_paths[0])) plt.show() plt.imshow(Image.open(png_paths[0])) plt.show() ``` ### 生成Dataset ``` ds=tf.data.Dataset.from_tensor_slices((img_paths,png_paths)) ``` 测试 ``` for jpg,png in ds.take(2): print(jpg) print(png) print('--') ``` ### 实现解析图片函数(关键) ``` def parse_jpg(path): img=tf.io.read_file(path) img=tf.image.decode_jpeg(img,channels=3) img=tf.image.resize(img,(IMG_WIDTH,IMG_WIDTH)) img=img/255 return img ``` 下面代码处理png图片时,要-1(这是laben) ``` def parse_png(path): img=tf.io.read_file(path) img=tf.image.decode_png(img,channels=1) img=tf.image.resize(img,(IMG_WIDTH,IMG_WIDTH)) img=img-1 return img ``` ``` def parse_img(jpg_path,png_path): jpg=parse_jpg(jpg_path) png=parse_png(png_path) return jpg,png ``` 执行函数 ``` ds2=ds.map(parse_img,num_parallel_calls=AUTOTUNE).shuffle(count_img) ``` ### 分割训练集和测试集 ``` train_count=int(count_img*0.7) train_ds=ds2.take(train_count) test_ds=ds2.skip(train_count) ``` 测试图片 ``` for jpg,png in train_ds.take(2): # print(jpg.numpy()) # arr=png.numpy() # print(collections.Counter(arr.flatten())) plt.imshow(jpg.numpy()) plt.show() png2=np.squeeze(png.numpy()) plt.imshow(png2) plt.show() ``` ### 准备训练集和测试集 ``` train_ds2=train_ds.shuffle(train_count).batch(8).prefetch(AUTOTUNE) test_ds2=test_ds.batch(8).prefetch(AUTOTUNE) train_ds ``` ### 加载vgg16模型 使用vgg16模型提取图片的特征,所以设置其不可训练 ``` app_vgg16=tf.keras.applications.VGG16(include_top=False,weights=vgg16_h5,input_shape=(IMG_WIDTH,IMG_WIDTH,3)) app_vgg16.trainable=False app_vgg16.summary() ``` ### 构建FCN模型(关键) ``` def build_fcn8_vgg16(): pool3=app_vgg16.get_layer("block3_pool").output print(pool3.shape) pool4=app_vgg16.get_layer("block4_pool").output print(pool4.shape) # 卷积层 print(app_vgg16.name,":",app_vgg16.output.shape) # 上采样,放大2倍 o=tf.keras.layers.Conv2DTranspose(512,2,2,padding="same",activation="relu",name="myblock8_conv_convtran")(app_vgg16.output) #padding="same", print(o.name,":",o.shape) # 对pool4进行卷积运算 o2=tf.keras.layers.Conv2D(512,1,1,activation="relu",padding="same",name="pool4_conv")(pool4) print(o2.shape) o=tf.add(o,o2,name="myblock8_conv_convtran_add_pool4_conv") # 上采样,放大2倍 o = tf.keras.layers.Conv2DTranspose(256, 2, 2, padding="same", activation="relu",name="sum_convtran")(o) # 对pool3进行卷积运算 o2=tf.keras.layers.Conv2D(256,1,1,activation="relu",padding="same",name="pool3_conv")(pool3) print(o2.shape) o=tf.add(o,o2,name="sum_convtran_add_pool3_conv") o=tf.keras.layers.Conv2D(64,3,1,padding="same",activation="relu")(o) print(o.shape) # 对最终结果放大8倍 o = tf.keras.layers.Conv2DTranspose(3, 8, 8, padding="same", activation="softmax",name="result")(o) model=tf.keras.Model(inputs=app_vgg16.input,outputs=[o],name="fcn8s_vgg16") print("|"*50) model.summary() return model model=build_fcn8_vgg16() ``` 执行结果: ``` (None, 28, 28, 256) (None, 14, 14, 512) vgg16 : (None, 7, 7, 512) myblock8_conv_convtran/Identity:0 : (None, 14, 14, 512) (None, 14, 14, 512) (None, 28, 28, 256) (None, 28, 28, 64) |||||||||||||||||||||||||||||||||||||||||||||||||| Model: "fcn8s_vgg16" __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_1 (InputLayer) [(None, 224, 224, 3) 0 __________________________________________________________________________________________________ block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 input_1[0][0] __________________________________________________________________________________________________ block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 block1_conv1[0][0] __________________________________________________________________________________________________ block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 block1_conv2[0][0] __________________________________________________________________________________________________ block2_conv1 (Conv2D) (None, 112, 112, 128 73856 block1_pool[0][0] __________________________________________________________________________________________________ block2_conv2 (Conv2D) (None, 112, 112, 128 147584 block2_conv1[0][0] __________________________________________________________________________________________________ block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 block2_conv2[0][0] __________________________________________________________________________________________________ block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 block2_pool[0][0] __________________________________________________________________________________________________ block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 block3_conv1[0][0] __________________________________________________________________________________________________ block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 block3_conv2[0][0] __________________________________________________________________________________________________ block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 block3_conv3[0][0] __________________________________________________________________________________________________ block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 block3_pool[0][0] __________________________________________________________________________________________________ block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 block4_conv1[0][0] __________________________________________________________________________________________________ block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 block4_conv2[0][0] __________________________________________________________________________________________________ block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 block4_conv3[0][0] __________________________________________________________________________________________________ block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 block4_pool[0][0] __________________________________________________________________________________________________ block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 block5_conv1[0][0] __________________________________________________________________________________________________ block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 block5_conv2[0][0] __________________________________________________________________________________________________ block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 block5_conv3[0][0] __________________________________________________________________________________________________ myblock8_conv_convtran (Conv2DT (None, 14, 14, 512) 1049088 block5_pool[0][0] __________________________________________________________________________________________________ pool4_conv (Conv2D) (None, 14, 14, 512) 262656 block4_pool[0][0] __________________________________________________________________________________________________ tf_op_layer_myblock8_conv_convt [(None, 14, 14, 512) 0 myblock8_conv_convtran[0][0] pool4_conv[0][0] __________________________________________________________________________________________________ sum_convtran (Conv2DTranspose) (None, 28, 28, 256) 524544 tf_op_layer_myblock8_conv_convtra __________________________________________________________________________________________________ pool3_conv (Conv2D) (None, 28, 28, 256) 65792 block3_pool[0][0] __________________________________________________________________________________________________ tf_op_layer_sum_convtran_add_po [(None, 28, 28, 256) 0 sum_convtran[0][0] pool3_conv[0][0] __________________________________________________________________________________________________ conv2d (Conv2D) (None, 28, 28, 64) 147520 tf_op_layer_sum_convtran_add_pool __________________________________________________________________________________________________ result (Conv2DTranspose) (None, 224, 224, 3) 12291 conv2d[0][0] ================================================================================================== Total params: 16,776,579 Trainable params: 2,061,891 Non-trainable params: 14,714,688 __________________________________________________________________________________________________ ``` ### 可视化模型 ``` tf.keras.utils.plot_model(model2,to_file="model.png",show_shapes=True) ``` [![](https://www.malaoshi.top/upload/0/0/1EF54WKU2GoM.png)](https://www.malaoshi.top/upload/0/0/1EF54WKU2GoM.png) ### 编译 和 训练 ``` def exe_model(): # model.compile(optimizer="adam",loss="sparse_categorical_crossentropy",metrics=["acc"]) model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.001),loss="sparse_categorical_crossentropy",metrics=["acc"]) history=model.fit(train_ds2,epochs=20,validation_data=test_ds2) return history history=exe_model() ``` ### 查看损失值 和 评价值 ``` plt.plot(history.epoch,history.history.get("loss"),label="loss") plt.plot(history.epoch,history.history.get("val_loss"),label="val_loss") plt.legend() plt.show() ``` [![](https://www.malaoshi.top/upload/0/0/1EF54WNCvy4T.png)](https://www.malaoshi.top/upload/0/0/1EF54WNCvy4T.png) ``` plt.plot(history.epoch,history.history.get("acc"),label="acc") plt.plot(history.epoch,history.history.get("val_acc"),label="val_acc") plt.legend() plt.show() ``` [![](https://www.malaoshi.top/upload/0/0/1EF54WNRMccW.png)](https://www.malaoshi.top/upload/0/0/1EF54WNRMccW.png) ### 保存模型 ``` model.save("model.h5") ``` 下接:https://www.malaoshi.top/show_1EF54Wt0sPnN.html 原文出处:http://malaoshi.top/show_1EF51UeOXjdv.html