├── LICENSE ├── Muiti_Class_deeplab_Mobile ├── dataset2 │ ├── README.MD │ └── train_data.txt ├── get_train_txt.py ├── img │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── img_out │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── logs │ └── README.md ├── model_data │ └── deeplabv3_mobilenetv2_tf_dim_ordering_tf_kernels.h5 ├── nets │ ├── __pycache__ │ │ ├── Xception.cpython-36.pyc │ │ ├── deeplab.cpython-36.pyc │ │ └── mobilenetV2.cpython-36.pyc │ ├── deeplab.py │ └── mobilenetV2.py ├── predict.py ├── test.py └── train.py ├── README.md ├── SegNet_Conv ├── dataset2 │ └── README.MD ├── img │ ├── 1.jpg │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── img_out │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── logs │ └── README.MD ├── nets │ ├── __pycache__ │ │ ├── convnet.cpython-36.pyc │ │ └── segnet.cpython-36.pyc │ ├── convnet.py │ └── segnet.py ├── predict.py ├── test.py └── train.py ├── SegNet_Mobile ├── dataset2 │ └── README.MD ├── img │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── img_out │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── logs │ └── README.MD ├── nets │ ├── __pycache__ │ │ ├── mobilenet.cpython-36.pyc │ │ ├── resnet50.cpython-36.pyc │ │ └── segnet.cpython-36.pyc │ ├── mobilenet.py │ └── segnet.py ├── predict.py ├── test.py └── train.py ├── SegNet_ResNet ├── dataset2 │ └── README.MD ├── img │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── img_out │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── logs │ └── README.MD ├── nets │ ├── __pycache__ │ │ ├── resnet50.cpython-36.pyc │ │ └── segnet.cpython-36.pyc │ ├── resnet50.py │ └── segnet.py ├── predict.py ├── test.py └── train.py ├── Unet_Mobile ├── dataset2 │ └── README.MD ├── img │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── img_out │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── logs │ └── README.MD ├── nets │ ├── __pycache__ │ │ ├── mobilenet.cpython-36.pyc │ │ ├── resnet50.cpython-36.pyc │ │ ├── segnet.cpython-36.pyc │ │ └── unet.cpython-36.pyc │ ├── mobilenet.py │ └── unet.py ├── predict.py ├── test.py └── train.py ├── deeplab_Mobile ├── dataset2 │ └── README.MD ├── img │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── img_out │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── logs │ └── README.MD ├── nets │ ├── __pycache__ │ │ ├── Xception.cpython-36.pyc │ │ ├── deeplab.cpython-36.pyc │ │ └── mobilenetV2.cpython-36.pyc │ ├── deeplab.py │ └── mobilenetV2.py ├── predict.py ├── test.py └── train.py ├── deeplab_Xception ├── dataset2 │ └── README.MD ├── img │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── img_out │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── logs │ └── README.MD ├── nets │ ├── Xception.py │ ├── __pycache__ │ │ ├── Xception.cpython-36.pyc │ │ ├── deeplab.cpython-36.pyc │ │ └── mobilenetV2.cpython-36.pyc │ └── deeplab.py ├── predict.py ├── test.py └── train.py ├── make_dataset ├── before │ ├── 1.jpg │ ├── 1.json │ ├── 2.jpg │ ├── 2.json │ ├── 3.jpg │ ├── 3.json │ └── class_name.txt ├── get_jpg_and_png.py ├── get_train_txt.py ├── jpg │ ├── 1.jpg │ ├── 2.jpg │ └── 3.jpg ├── json_to_dataset.py ├── output │ ├── 1_json │ │ ├── img.png │ │ ├── info.yaml │ │ ├── label.png │ │ ├── label_names.txt │ │ └── label_viz.png │ ├── 2_json │ │ ├── img.png │ │ ├── info.yaml │ │ ├── label.png │ │ ├── label_names.txt │ │ └── label_viz.png │ └── 3_json │ │ ├── img.png │ │ ├── info.yaml │ │ ├── label.png │ │ ├── label_names.txt │ │ └── label_viz.png ├── png │ ├── 1.png │ ├── 2.png │ └── 3.png └── train.txt ├── pspnet_Mobile ├── dataset2 │ └── README.MD ├── img │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── img_out │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── logs │ └── README.MD ├── nets │ ├── __pycache__ │ │ ├── mobilenet.cpython-36.pyc │ │ ├── pspnet.cpython-36.pyc │ │ ├── resnet50.cpython-36.pyc │ │ ├── segnet.cpython-36.pyc │ │ └── unet.cpython-36.pyc │ ├── mobilenet.py │ └── pspnet.py ├── predict.py ├── test.py └── train.py ├── pspnet_Multi_Mobile ├── dataset2 │ └── README.MD ├── img │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── img_out │ ├── timg.jpg │ ├── timg2.jpg │ ├── timg3.jpg │ ├── timg4.jpg │ └── timg5.jpg ├── logs │ └── README.MD ├── nets │ ├── __pycache__ │ │ ├── mobilenet.cpython-36.pyc │ │ ├── pspnet.cpython-36.pyc │ │ ├── resnet50.cpython-36.pyc │ │ ├── segnet.cpython-36.pyc │ │ └── unet.cpython-36.pyc │ ├── mobilenet.py │ └── pspnet.py ├── predict.py ├── test.py └── train.py └── 常见问题汇总.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 JiaQi Xu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/dataset2/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放dataset内的训练数据。 2 | This part is used to store the training data in the dataset. -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/get_train_txt.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | jpgs = os.listdir("./dataset2/jpg") 4 | pngs = os.listdir("./dataset2/png") 5 | 6 | with open("./dataset2/train.txt","w") as f: 7 | for jpg in jpgs: 8 | png = jpg.replace("jpg","png") 9 | # 判断jpg是否存在对应的png 10 | if png in pngs: 11 | f.write(jpg+";"+png+"\n") -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img/1.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img/2.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img/3.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img/timg.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img/timg2.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img/timg3.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img/timg4.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img/timg5.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img_out/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img_out/1.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img_out/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img_out/2.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img_out/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img_out/3.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img_out/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img_out/timg.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img_out/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img_out/timg2.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img_out/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img_out/timg3.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img_out/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img_out/timg4.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/img_out/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/img_out/timg5.jpg -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/logs/README.md: -------------------------------------------------------------------------------- 1 | 训练后的权重会出现在这里 -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/model_data/deeplabv3_mobilenetv2_tf_dim_ordering_tf_kernels.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/model_data/deeplabv3_mobilenetv2_tf_dim_ordering_tf_kernels.h5 -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/nets/__pycache__/Xception.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/nets/__pycache__/Xception.cpython-36.pyc -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/nets/__pycache__/deeplab.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/nets/__pycache__/deeplab.cpython-36.pyc -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/nets/__pycache__/mobilenetV2.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Muiti_Class_deeplab_Mobile/nets/__pycache__/mobilenetV2.cpython-36.pyc -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/nets/deeplab.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from keras import backend as K 3 | from keras import layers 4 | from keras.activations import relu 5 | from keras.applications.imagenet_utils import preprocess_input 6 | from keras.layers import (Activation, Add, BatchNormalization, Concatenate, 7 | Conv2D, DepthwiseConv2D, Dropout, 8 | GlobalAveragePooling2D, Input, Lambda, Reshape, 9 | Softmax, ZeroPadding2D) 10 | from keras.models import Model 11 | from keras.utils.data_utils import get_file 12 | 13 | from nets.mobilenetV2 import mobilenetV2 14 | 15 | 16 | def SepConv_BN(x, filters, prefix, stride=1, kernel_size=3, rate=1, depth_activation=False, epsilon=1e-3): 17 | if stride == 1: 18 | depth_padding = 'same' 19 | else: 20 | kernel_size_effective = kernel_size + (kernel_size - 1) * (rate - 1) 21 | pad_total = kernel_size_effective - 1 22 | pad_beg = pad_total // 2 23 | pad_end = pad_total - pad_beg 24 | x = ZeroPadding2D((pad_beg, pad_end))(x) 25 | depth_padding = 'valid' 26 | 27 | if not depth_activation: 28 | x = Activation('relu')(x) 29 | 30 | # 首先使用3x3的深度可分离卷积 31 | x = DepthwiseConv2D((kernel_size, kernel_size), strides=(stride, stride), dilation_rate=(rate, rate), 32 | padding=depth_padding, use_bias=False, name=prefix + '_depthwise')(x) 33 | x = BatchNormalization(name=prefix + '_depthwise_BN', epsilon=epsilon)(x) 34 | if depth_activation: 35 | x = Activation('relu')(x) 36 | 37 | # 利用1x1卷积进行通道数调整 38 | x = Conv2D(filters, (1, 1), padding='same', use_bias=False, name=prefix + '_pointwise')(x) 39 | x = BatchNormalization(name=prefix + '_pointwise_BN', epsilon=epsilon)(x) 40 | if depth_activation: 41 | x = Activation('relu')(x) 42 | 43 | return x 44 | 45 | def Deeplabv3(input_shape=(416, 416, 3), classes=21, alpha=1.): 46 | img_input = Input(shape=input_shape) 47 | 48 | # x 52, 52, 320 49 | # skip1 104, 104, 24 50 | x, skip1 = mobilenetV2(img_input, alpha) 51 | size_before = tf.keras.backend.int_shape(x) 52 | 53 | #---------------------------------------------------------------# 54 | # 全部求平均后,再利用expand_dims扩充维度 55 | # 52,52,320 -> 1,1,320 -> 1,1,320 56 | #---------------------------------------------------------------# 57 | b4 = GlobalAveragePooling2D()(x) 58 | b4 = Lambda(lambda x: K.expand_dims(x, 1))(b4) 59 | b4 = Lambda(lambda x: K.expand_dims(x, 1))(b4) 60 | b4 = Conv2D(256, (1, 1), padding='same', use_bias=False, name='image_pooling')(b4) 61 | b4 = BatchNormalization(name='image_pooling_BN', epsilon=1e-5)(b4) 62 | b4 = Activation('relu')(b4) 63 | # 1,1,256 -> 52,52,256 64 | b4 = Lambda(lambda x: tf.image.resize_images(x, size_before[1:3]))(b4) 65 | 66 | #---------------------------------------------------------------# 67 | # 调整通道 68 | #---------------------------------------------------------------# 69 | b0 = Conv2D(256, (1, 1), padding='same', use_bias=False, name='aspp0')(x) 70 | b0 = BatchNormalization(name='aspp0_BN', epsilon=1e-5)(b0) 71 | b0 = Activation('relu', name='aspp0_activation')(b0) 72 | 73 | # 52, 52, 256 + 52, 52, 256 -> 52, 52, 512 74 | x = Concatenate()([b4, b0]) 75 | 76 | # 利用1x1卷积调整通道数 77 | # 52, 52, 1280 -> 52,52,256 78 | x = Conv2D(256, (1, 1), padding='same', use_bias=False, name='concat_projection')(x) 79 | x = BatchNormalization(name='concat_projection_BN', epsilon=1e-5)(x) 80 | x = Activation('relu')(x) 81 | x = Dropout(0.1)(x) 82 | 83 | # 52,52,256 -> 104,104,2 -> 416,416,2 84 | size_before3 = tf.keras.backend.int_shape(img_input) 85 | x = Conv2D(classes, (1, 1), padding='same')(x) 86 | x = Lambda(lambda xx:tf.image.resize_images(xx, size_before3[1:3]))(x) 87 | 88 | x = Reshape((-1,classes))(x) 89 | x = Softmax()(x) 90 | 91 | model = Model(img_input, x, name='deeplabv3plus') 92 | return model 93 | 94 | -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/nets/mobilenetV2.py: -------------------------------------------------------------------------------- 1 | from keras import layers 2 | from keras.activations import relu 3 | from keras.layers import (Activation, Add, BatchNormalization, Concatenate, 4 | Conv2D, DepthwiseConv2D, Dropout, 5 | GlobalAveragePooling2D, Input, Lambda, ZeroPadding2D) 6 | from keras.models import Model 7 | 8 | 9 | def _make_divisible(v, divisor, min_value=None): 10 | if min_value is None: 11 | min_value = divisor 12 | new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) 13 | if new_v < 0.9 * v: 14 | new_v += divisor 15 | return new_v 16 | 17 | def relu6(x): 18 | return relu(x, max_value=6) 19 | 20 | def _inverted_res_block(inputs, expansion, stride, alpha, filters, block_id, skip_connection, rate=1): 21 | in_channels = inputs.shape[-1].value # inputs._keras_shape[-1] 22 | pointwise_filters = _make_divisible(int(filters * alpha), 8) 23 | prefix = 'expanded_conv_{}_'.format(block_id) 24 | 25 | x = inputs 26 | # 利用1x1卷积进行通道数的扩张 27 | if block_id: 28 | x = Conv2D(expansion * in_channels, kernel_size=1, padding='same', 29 | use_bias=False, activation=None, 30 | name=prefix + 'expand')(x) 31 | x = BatchNormalization(epsilon=1e-3, momentum=0.999, 32 | name=prefix + 'expand_BN')(x) 33 | x = Activation(relu6, name=prefix + 'expand_relu')(x) 34 | else: 35 | prefix = 'expanded_conv_' 36 | 37 | # 利用3x3深度可分离卷积提取特征 38 | x = DepthwiseConv2D(kernel_size=3, strides=stride, activation=None, 39 | use_bias=False, padding='same', dilation_rate=(rate, rate), 40 | name=prefix + 'depthwise')(x) 41 | x = BatchNormalization(epsilon=1e-3, momentum=0.999, 42 | name=prefix + 'depthwise_BN')(x) 43 | 44 | x = Activation(relu6, name=prefix + 'depthwise_relu')(x) 45 | 46 | # 利用1x1卷积进行通道数的下降 47 | x = Conv2D(pointwise_filters, 48 | kernel_size=1, padding='same', use_bias=False, activation=None, 49 | name=prefix + 'project')(x) 50 | x = BatchNormalization(epsilon=1e-3, momentum=0.999, 51 | name=prefix + 'project_BN')(x) 52 | 53 | if skip_connection: 54 | return Add(name=prefix + 'add')([inputs, x]) 55 | 56 | return x 57 | 58 | def mobilenetV2(inputs,alpha=1): 59 | first_block_filters = _make_divisible(32 * alpha, 8) 60 | # 416,416,3 -> 208,208,32 61 | x = Conv2D(first_block_filters, 62 | kernel_size=3, 63 | strides=(2, 2), padding='same', 64 | use_bias=False, name='Conv')(inputs) 65 | x = BatchNormalization(epsilon=1e-3, momentum=0.999, name='Conv_BN')(x) 66 | x = Activation(relu6, name='Conv_Relu6')(x) 67 | 68 | 69 | # 208,208,32 -> 208,208,16 70 | x = _inverted_res_block(x, filters=16, alpha=alpha, stride=1, 71 | expansion=1, block_id=0, skip_connection=False) 72 | 73 | # 208,208,16 -> 104,104,24 74 | x = _inverted_res_block(x, filters=24, alpha=alpha, stride=2, 75 | expansion=6, block_id=1, skip_connection=False) 76 | x = _inverted_res_block(x, filters=24, alpha=alpha, stride=1, 77 | expansion=6, block_id=2, skip_connection=True) 78 | skip1 = x 79 | 80 | # 104,104,24 -> 52,52,32 81 | x = _inverted_res_block(x, filters=32, alpha=alpha, stride=2, 82 | expansion=6, block_id=3, skip_connection=False) 83 | x = _inverted_res_block(x, filters=32, alpha=alpha, stride=1, 84 | expansion=6, block_id=4, skip_connection=True) 85 | x = _inverted_res_block(x, filters=32, alpha=alpha, stride=1, 86 | expansion=6, block_id=5, skip_connection=True) 87 | 88 | #---------------------------------------------------------------# 89 | # 52,52,32 -> 52,52,64 90 | x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1, 91 | expansion=6, block_id=6, skip_connection=False) 92 | x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1, rate=2, 93 | expansion=6, block_id=7, skip_connection=True) 94 | x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1, rate=2, 95 | expansion=6, block_id=8, skip_connection=True) 96 | x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1, rate=2, 97 | expansion=6, block_id=9, skip_connection=True) 98 | 99 | # 52,52,64 -> 52,52,96 100 | x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1, rate=2, 101 | expansion=6, block_id=10, skip_connection=False) 102 | x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1, rate=2, 103 | expansion=6, block_id=11, skip_connection=True) 104 | x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1, rate=2, 105 | expansion=6, block_id=12, skip_connection=True) 106 | 107 | # 52,52,96 -> 52,52,160 108 | x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1, rate=2, # 1! 109 | expansion=6, block_id=13, skip_connection=False) 110 | x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1, rate=4, 111 | expansion=6, block_id=14, skip_connection=True) 112 | x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1, rate=4, 113 | expansion=6, block_id=15, skip_connection=True) 114 | 115 | # 52,52,160 -> 52,52,320 116 | x = _inverted_res_block(x, filters=320, alpha=alpha, stride=1, rate=4, 117 | expansion=6, block_id=16, skip_connection=False) 118 | return x,skip1 119 | -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/predict.py: -------------------------------------------------------------------------------- 1 | import colorsys 2 | import copy 3 | import os 4 | import random 5 | 6 | import numpy as np 7 | from PIL import Image 8 | 9 | from nets.deeplab import Deeplabv3 10 | 11 | 12 | def letterbox_image(image, size): 13 | '''resize image with unchanged aspect ratio using padding''' 14 | iw, ih = image.size 15 | w, h = size 16 | scale = min(w/iw, h/ih) 17 | nw = int(iw*scale) 18 | nh = int(ih*scale) 19 | 20 | image = image.resize((nw,nh), Image.BICUBIC) 21 | new_image = Image.new('RGB', size, (0,0,0)) 22 | new_image.paste(image, ((w-nw)//2, (h-nh)//2)) 23 | return new_image,nw,nh 24 | 25 | def get_class_colors(num_classes): 26 | if num_classes <= 21: 27 | colors = [(0, 0, 0), (128, 0, 0), (0, 128, 0), (128, 128, 0), (0, 0, 128), (128, 0, 128), (0, 128, 128), 28 | (128, 128, 128), (64, 0, 0), (192, 0, 0), (64, 128, 0), (192, 128, 0), (64, 0, 128), (192, 0, 128), 29 | (64, 128, 128), (192, 128, 128), (0, 64, 0), (128, 64, 0), (0, 192, 0), (128, 192, 0), (0, 64, 128), (128, 64, 12)] 30 | else: 31 | # 画框设置不同的颜色 32 | hsv_tuples = [(x / len(num_classes), 1., 1.) 33 | for x in range(len(num_classes))] 34 | colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) 35 | colors = list( 36 | map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), 37 | colors)) 38 | return colors 39 | 40 | if __name__ == "__main__": 41 | #---------------------------------------------# 42 | # 定义输入图片的高和宽,以及种类数量 43 | #---------------------------------------------# 44 | HEIGHT = 512 45 | WIDTH = 512 46 | #---------------------------------------------# 47 | # 背景 + 需要去区分的类的个数 48 | #---------------------------------------------# 49 | NCLASSES = 21 50 | #---------------------------------------------------# 51 | # 根据种类的数量获取颜色 52 | #---------------------------------------------------# 53 | class_colors = get_class_colors(NCLASSES) 54 | 55 | #---------------------------------------------# 56 | # 载入模型 57 | #---------------------------------------------# 58 | model = Deeplabv3(classes=NCLASSES,input_shape=(HEIGHT,WIDTH,3)) 59 | #--------------------------------------------------# 60 | # 载入权重,训练好的权重会保存在logs文件夹里面 61 | # 我们需要将对应的权重载入 62 | # 修改model_path,将其对应我们训练好的权重即可 63 | # 下面只是一个示例 64 | #--------------------------------------------------# 65 | model.load_weights("model_data/deeplabv3_mobilenetv2_tf_dim_ordering_tf_kernels.h5") 66 | 67 | #--------------------------------------------------# 68 | # 对imgs文件夹进行一个遍历 69 | #--------------------------------------------------# 70 | imgs = os.listdir("./img/") 71 | for jpg in imgs: 72 | #--------------------------------------------------# 73 | # 打开imgs文件夹里面的每一个图片 74 | #--------------------------------------------------# 75 | img = Image.open("./img/"+jpg) 76 | 77 | old_img = copy.deepcopy(img) 78 | orininal_h = np.array(img).shape[0] 79 | orininal_w = np.array(img).shape[1] 80 | 81 | #--------------------------------------------------# 82 | # 对输入进来的每一个图片进行letterbox_image 83 | #--------------------------------------------------# 84 | img, nw, nh = letterbox_image(img, [WIDTH, HEIGHT]) 85 | img = np.array(img) / 127.5 - 1 86 | img = img.reshape(-1,HEIGHT,WIDTH,3) 87 | 88 | #--------------------------------------------------# 89 | # 将图像输入到网络当中进行预测 90 | #--------------------------------------------------# 91 | pr = model.predict(img)[0] 92 | pr = pr.reshape((HEIGHT,WIDTH,NCLASSES)).argmax(axis=-1) 93 | pr = pr[int((HEIGHT-nh)//2):int((HEIGHT-nh)//2+nh), int((WIDTH-nw)//2):int((WIDTH-nw)//2+nw)] 94 | 95 | #------------------------------------------------# 96 | # 创建一副新图,并根据每个像素点的种类赋予颜色 97 | #------------------------------------------------# 98 | seg_img = np.zeros((nh, nw,3)) 99 | for c in range(NCLASSES): 100 | seg_img[:,:,0] += ( (pr[:,: ] == c )*( class_colors[c][0] )).astype('uint8') 101 | seg_img[:,:,1] += ((pr[:,: ] == c )*( class_colors[c][1] )).astype('uint8') 102 | seg_img[:,:,2] += ((pr[:,: ] == c )*( class_colors[c][2] )).astype('uint8') 103 | 104 | seg_img = Image.fromarray(np.uint8(seg_img)).resize((orininal_w,orininal_h)) 105 | #------------------------------------------------# 106 | # 将新图片和原图片混合 107 | #------------------------------------------------# 108 | image = Image.blend(old_img,seg_img,0.5) 109 | 110 | image.save("./img_out/"+jpg) 111 | 112 | 113 | -------------------------------------------------------------------------------- /Muiti_Class_deeplab_Mobile/test.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | import numpy as np 3 | from matplotlib.colors import rgb_to_hsv, hsv_to_rgb 4 | def letterbox_image(image, size, type): 5 | '''resize image with unchanged aspect ratio using padding''' 6 | iw, ih = image.size 7 | w, h = size 8 | scale = min(w/iw, h/ih) 9 | nw = int(iw*scale) 10 | nh = int(ih*scale) 11 | 12 | image = image.resize((nw,nh), Image.BICUBIC) 13 | if(type=="jpg"): 14 | new_image = Image.new('RGB', size, (0,0,0)) 15 | elif(type=="png"): 16 | new_image = Image.new('RGB', size, (0,0,0)) 17 | new_image.paste(image, ((w-nw)//2, (h-nh)//2)) 18 | return new_image,nw,nh 19 | 20 | def rand(a=0, b=1): 21 | return np.random.rand()*(b-a) + a 22 | 23 | def get_random_data(image, label, input_shape, jitter=.2, hue=.2, sat=1.1, val=1.1): 24 | h, w = input_shape 25 | 26 | # resize image 27 | rand_jit1 = rand(1-jitter,1+jitter) 28 | rand_jit2 = rand(1-jitter,1+jitter) 29 | new_ar = w/h * rand_jit1/rand_jit2 30 | scale = rand(.6, 1.4) 31 | if new_ar < 1: 32 | nh = int(scale*h) 33 | nw = int(nh*new_ar) 34 | else: 35 | nw = int(scale*w) 36 | nh = int(nw/new_ar) 37 | image = image.resize((nw,nh), Image.BICUBIC) 38 | label = label.resize((nw,nh), Image.BICUBIC) 39 | # place image 40 | dx = int(rand(0, w-nw)) 41 | dy = int(rand(0, h-nh)) 42 | new_image = Image.new('RGB', (w,h), (0,0,0)) 43 | new_label = Image.new('RGB', (w,h), (0,0,0)) 44 | new_image.paste(image, (dx, dy)) 45 | new_label.paste(label, (dx, dy)) 46 | image = new_image 47 | label = new_label 48 | # flip image or not 49 | flip = rand()<.5 50 | if flip: 51 | image = image.transpose(Image.FLIP_LEFT_RIGHT) 52 | label = label.transpose(Image.FLIP_LEFT_RIGHT) 53 | 54 | # distort image 55 | hue = rand(-hue, hue) 56 | sat = rand(1, sat) if rand()<.5 else 1/rand(1, sat) 57 | val = rand(1, val) if rand()<.5 else 1/rand(1, val) 58 | x = rgb_to_hsv(np.array(image)/255.) 59 | x[..., 0] += hue 60 | x[..., 0][x[..., 0]>1] -= 1 61 | x[..., 0][x[..., 0]<0] += 1 62 | x[..., 1] *= sat 63 | x[..., 2] *= val 64 | x[x>1] = 1 65 | x[x<0] = 0 66 | image_data = hsv_to_rgb(x) 67 | return image_data,label 68 | 69 | jpg = Image.open("dataset2/jpg/2008_000003.jpg") 70 | png = Image.open("dataset2/png/2008_000003.png") 71 | 72 | jpg,_,_ = letterbox_image(jpg,[512,512],"jpg") 73 | png,_,_ = letterbox_image(png,[512,512],"jpg") 74 | jpg.show() 75 | 76 | jpg,png = get_random_data(jpg, png, [512,512]) 77 | Image.fromarray(np.uint8(jpg * 255)).show() 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Semantic-Segmentation语义分割模型在Keras当中的实现 2 | --- 3 | 4 | ## 大通知! 5 | PSPnet-Keras重制版如下: 6 | 源码路径:https://github.com/bubbliiiing/pspnet-keras 7 | 视频地址:https://www.bilibili.com/video/BV1bz4y1f77C 8 | 9 | PSPnet-Pytorch重制版如下: 10 | 源码路径:https://github.com/bubbliiiing/pspnet-pytorch 11 | 视频地址:https://www.bilibili.com/video/BV1zt4y1q7HH 12 | 13 | PSPnet-Tensorflow2重制版如下: 14 | 源码路径:https://github.com/bubbliiiing/pspnet-tf2 15 | 视频地址:https://www.bilibili.com/video/BV1Wh411f7NU 16 | 17 | Unet-Keras重制版如下: 18 | 源码路径:https://github.com/bubbliiiing/unet-keras 19 | 视频地址:https://www.bilibili.com/video/BV1St4y1r7hE 20 | 21 | Unet-Pytorch重制版如下: 22 | 源码路径:https://github.com/bubbliiiing/unet-pytorch 23 | 视频地址:https://www.bilibili.com/video/BV1rz4y117rR 24 | 25 | Unet-Tensorflow2重制版如下: 26 | 源码路径:https://github.com/bubbliiiing/unet-tf2 27 | 28 | Deeplab-Keras重制版如下: 29 | 源码路径:https://github.com/bubbliiiing/deeplabv3-plus-keras 30 | 31 | 32 | 33 | ## 目录 34 | 1. [所需环境 Environment](#所需环境) 35 | 2. [注意事项 Attention](#注意事项) 36 | 3. [数据集下载 Download](#数据集下载) 37 | 4. [训练步骤 How2train](#训练步骤) 38 | 5. [预测步骤 How2predict](#预测步骤) 39 | 6. [参考资料 Reference](#Reference) 40 | 41 | ## 所需环境 42 | tensorflow-gpu==1.13.1 43 | keras==2.1.5 44 | 45 | ## 注意事项 46 | 该代码是我早期整理的语义分割代码,尽管可以使用,但是存在许多缺点。大家尽量可以使用重制版的代码,因为重制版的代码里面增加了很多新内容,比如添加了Dice-loss,增加了更多参数的选择,提供了VOC预训练权重等。 47 | 48 | 在2021年1月28重新上传了该库,给代码添加了非常详细的注释,该库仍然可以作为一个语义分割的入门库进行使用。 49 | 50 | 在使用前一定要注意根目录与相对目录的选取,这样才能正常进行训练。 51 | 52 | ## 数据集下载 53 | 斑马线数据集: 54 | 链接:https://pan.baidu.com/s/1uzwqLaCXcWe06xEXk1ROWw 提取码:pp6w 55 | 56 | VOC数据集: 57 | 链接: https://pan.baidu.com/s/1Urh9W7XPNMF8yR67SDjAAQ 提取码: cvy2 58 | 59 | ## 训练步骤 60 | 1. 准备好训练数据集,如果想要进行简单尝试,可以通过如上的斑马线数据集进行尝试;如果想要进行自己的数据集训练,可以参考制作自己的数据集的视频,进行制作 61 | 2. 在完成数据集的准备后,利用pycharm或者vscode打开对应模型的文件夹,将数据集及其对应的train.txt文件复制到datasets2文件夹中 62 | 3. 然后运行train.py进行训练。 63 | 4. 大家关心的多分类的代码在Muiti_Class_deeplab_Mobile里。 64 | 65 | ## 预测步骤 66 | 1. 除去Muiti_Class_deeplab_Mobile可以直接运行predict.py进行预测外,其它的模型均需要先完成训练才可以预测。 67 | 2. 在完成训练后,将predict.py里面模型载入的权重更换成logs文件夹内的权值。 68 | 3. 将想要预测的图片放入img文件夹。 69 | 4. 运行predict.py即可开始预测。 70 | 71 | ## Reference 72 | [image-segmentation-keras](https://github.com/divamgupta/image-segmentation-keras) 73 | -------------------------------------------------------------------------------- /SegNet_Conv/dataset2/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放dataset内的训练数据。 2 | This part is used to store the training data in the dataset. -------------------------------------------------------------------------------- /SegNet_Conv/img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img/1.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img/timg.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img/timg2.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img/timg3.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img/timg4.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img/timg5.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img_out/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img_out/timg.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img_out/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img_out/timg2.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img_out/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img_out/timg3.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img_out/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img_out/timg4.jpg -------------------------------------------------------------------------------- /SegNet_Conv/img_out/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/img_out/timg5.jpg -------------------------------------------------------------------------------- /SegNet_Conv/logs/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放训练后的文件。 2 | This part is used to store post training documents. -------------------------------------------------------------------------------- /SegNet_Conv/nets/__pycache__/convnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/nets/__pycache__/convnet.cpython-36.pyc -------------------------------------------------------------------------------- /SegNet_Conv/nets/__pycache__/segnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Conv/nets/__pycache__/segnet.cpython-36.pyc -------------------------------------------------------------------------------- /SegNet_Conv/nets/convnet.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import keras.backend as K 3 | from keras.activations import * 4 | from keras.layers import * 5 | from keras.models import * 6 | 7 | IMAGE_ORDERING = 'channels_last' 8 | 9 | 10 | def get_convnet_encoder(input_height=416 ,input_width=416): 11 | 12 | img_input = Input(shape=(input_height,input_width,3)) 13 | 14 | # 416,416,3 -> 208,208,64 15 | x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(img_input) 16 | x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(x) 17 | x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x) 18 | f1 = x 19 | 20 | # 208,208,64 -> 104,104,128 21 | x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(x) 22 | x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(x) 23 | x = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x) 24 | f2 = x 25 | 26 | # 104,104,128 -> 52,52,256 27 | x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(x) 28 | x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(x) 29 | x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(x) 30 | x = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x) 31 | f3 = x 32 | 33 | # 52,52,256 -> 26,26,512 34 | x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(x) 35 | x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(x) 36 | x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(x) 37 | x = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x) 38 | f4 = x 39 | 40 | # 在视频中,这个f5并没有用到 41 | # 26,26,512 -> 13,13,512 42 | x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(x) 43 | x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(x) 44 | x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(x) 45 | x = MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')(x) 46 | f5 = x 47 | 48 | return img_input, [f1 , f2 , f3 , f4 , f5] 49 | 50 | -------------------------------------------------------------------------------- /SegNet_Conv/nets/segnet.py: -------------------------------------------------------------------------------- 1 | from keras.layers import * 2 | from keras.models import * 3 | 4 | from nets.convnet import get_convnet_encoder 5 | 6 | def segnet_decoder(f, n_classes, n_up=3): 7 | assert n_up >= 2 8 | 9 | o = f 10 | # 26,26,512 -> 26,26,512 11 | o = ZeroPadding2D((1,1))(o) 12 | o = Conv2D(512, (3, 3), padding='valid')(o) 13 | o = BatchNormalization()(o) 14 | 15 | # 进行一次UpSampling2D,此时hw变为原来的1/8 16 | # 26,26,512 -> 52,52,256 17 | o = UpSampling2D((2,2))(o) 18 | o = ZeroPadding2D((1,1))(o) 19 | o = Conv2D(256, (3, 3), padding='valid')(o) 20 | o = BatchNormalization()(o) 21 | 22 | # 进行一次UpSampling2D,此时hw变为原来的1/4 23 | # 52,52,256 -> 104,104,128 24 | for _ in range(n_up-2): 25 | o = UpSampling2D((2,2))(o) 26 | o = ZeroPadding2D((1,1))(o) 27 | o = Conv2D(128, (3, 3), padding='valid')(o) 28 | o = BatchNormalization()(o) 29 | 30 | # 进行一次UpSampling2D,此时hw变为原来的1/2 31 | # 104,104,128 -> 208,208,64 32 | o = UpSampling2D((2,2))(o) 33 | o = ZeroPadding2D((1,1))(o) 34 | o = Conv2D(64, (3, 3), padding='valid')(o) 35 | o = BatchNormalization()(o) 36 | 37 | # 此时输出为h_input/2,w_input/2,nclasses 38 | # 208,208,2 39 | o = Conv2D(n_classes, (3, 3), padding='same')(o) 40 | 41 | return o 42 | 43 | def _segnet(n_classes, encoder, input_height=416, input_width=416, encoder_level=3): 44 | # encoder通过主干网络 45 | img_input , levels = encoder(input_height=input_height, input_width=input_width ) 46 | 47 | # 获取hw压缩四次后的结果 48 | feat = levels[encoder_level] 49 | 50 | # 将特征传入segnet网络 51 | o = segnet_decoder(feat, n_classes, n_up=3 ) 52 | 53 | # 将结果进行reshape 54 | o = Reshape((int(input_height/2)*int(input_width/2), -1))(o) 55 | o = Softmax()(o) 56 | model = Model(img_input,o) 57 | 58 | return model 59 | 60 | def convnet_segnet(n_classes, input_height=224, input_width=224, encoder_level=3): 61 | model = _segnet(n_classes, get_convnet_encoder, input_height=input_height, input_width=input_width, encoder_level=encoder_level) 62 | model.model_name = "convnet_segnet" 63 | return model 64 | 65 | -------------------------------------------------------------------------------- /SegNet_Conv/predict.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | import random 4 | 5 | import numpy as np 6 | from PIL import Image 7 | 8 | from nets.segnet import convnet_segnet 9 | 10 | if __name__ == "__main__": 11 | #---------------------------------------------------# 12 | # 定义了输入图片的颜色,当我们想要去区分两类的时候 13 | # 我们定义了两个颜色,分别用于背景和斑马线 14 | # [0,0,0], [0,255,0]代表了颜色的RGB色彩 15 | #---------------------------------------------------# 16 | class_colors = [[0,0,0],[0,255,0]] 17 | #---------------------------------------------# 18 | # 定义输入图片的高和宽,以及种类数量 19 | #---------------------------------------------# 20 | HEIGHT = 416 21 | WIDTH = 416 22 | #---------------------------------------------# 23 | # 背景 + 斑马线 = 2 24 | #---------------------------------------------# 25 | NCLASSES = 2 26 | 27 | #---------------------------------------------# 28 | # 载入模型 29 | #---------------------------------------------# 30 | model = convnet_segnet(n_classes=NCLASSES,input_height=HEIGHT, input_width=WIDTH) 31 | #--------------------------------------------------# 32 | # 载入权重,训练好的权重会保存在logs文件夹里面 33 | # 我们需要将对应的权重载入 34 | # 修改model_path,将其对应我们训练好的权重即可 35 | # 下面只是一个示例 36 | #--------------------------------------------------# 37 | model_path = "logs/ep033-loss0.040-val_loss0.037.h5" 38 | model.load_weights(model_path) 39 | 40 | #--------------------------------------------------# 41 | # 对imgs文件夹进行一个遍历 42 | #--------------------------------------------------# 43 | imgs = os.listdir("./img/") 44 | for jpg in imgs: 45 | #--------------------------------------------------# 46 | # 打开imgs文件夹里面的每一个图片 47 | #--------------------------------------------------# 48 | img = Image.open("./img/"+jpg) 49 | 50 | old_img = copy.deepcopy(img) 51 | orininal_h = np.array(img).shape[0] 52 | orininal_w = np.array(img).shape[1] 53 | 54 | #--------------------------------------------------# 55 | # 对输入进来的每一个图片进行Resize 56 | # resize成[HEIGHT, WIDTH, 3] 57 | #--------------------------------------------------# 58 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 59 | img = np.array(img) / 255 60 | img = img.reshape(-1, HEIGHT, WIDTH, 3) 61 | 62 | #--------------------------------------------------# 63 | # 将图像输入到网络当中进行预测 64 | #--------------------------------------------------# 65 | pr = model.predict(img)[0] 66 | pr = pr.reshape((int(HEIGHT/2), int(WIDTH/2), NCLASSES)).argmax(axis=-1) 67 | 68 | #------------------------------------------------# 69 | # 创建一副新图,并根据每个像素点的种类赋予颜色 70 | #------------------------------------------------# 71 | seg_img = np.zeros((int(HEIGHT/2), int(WIDTH/2),3)) 72 | for c in range(NCLASSES): 73 | seg_img[:, :, 0] += ((pr[:,: ] == c) * class_colors[c][0]).astype('uint8') 74 | seg_img[:, :, 1] += ((pr[:,: ] == c) * class_colors[c][1]).astype('uint8') 75 | seg_img[:, :, 2] += ((pr[:,: ] == c) * class_colors[c][2]).astype('uint8') 76 | 77 | seg_img = Image.fromarray(np.uint8(seg_img)).resize((orininal_w,orininal_h)) 78 | #------------------------------------------------# 79 | # 将新图片和原图片混合 80 | #------------------------------------------------# 81 | image = Image.blend(old_img,seg_img,0.3) 82 | 83 | image.save("./img_out/"+jpg) 84 | 85 | 86 | -------------------------------------------------------------------------------- /SegNet_Conv/test.py: -------------------------------------------------------------------------------- 1 | #---------------------------------------------# 2 | # 该部分用于查看网络结构 3 | #---------------------------------------------# 4 | from nets.segnet import convnet_segnet 5 | 6 | if __name__ == "__main__": 7 | model = convnet_segnet(2, input_height=416, input_width=416) 8 | model.summary() 9 | -------------------------------------------------------------------------------- /SegNet_Conv/train.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import numpy as np 3 | from keras import backend as K 4 | from keras.callbacks import (EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, 5 | TensorBoard) 6 | from keras.optimizers import Adam 7 | from keras.utils.data_utils import get_file 8 | from PIL import Image 9 | 10 | from nets.segnet import convnet_segnet 11 | 12 | #-------------------------------------------------------------# 13 | # 定义了一个生成器,用于读取datasets2文件夹里面的图片与标签 14 | #-------------------------------------------------------------# 15 | def generate_arrays_from_file(lines,batch_size): 16 | n = len(lines) 17 | i = 0 18 | while 1: 19 | X_train = [] 20 | Y_train = [] 21 | for _ in range(batch_size): 22 | if i==0: 23 | np.random.shuffle(lines) 24 | #-------------------------------------# 25 | # 读取输入图片并进行归一化和resize 26 | #-------------------------------------# 27 | name = lines[i].split(';')[0] 28 | img = Image.open("./dataset2/jpg/" + name) 29 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 30 | img = np.array(img)/255 31 | X_train.append(img) 32 | 33 | #-------------------------------------# 34 | # 读取标签图片并进行归一化和resize 35 | #-------------------------------------# 36 | name = lines[i].split(';')[1].split()[0] 37 | label = Image.open("./dataset2/png/" + name) 38 | label = label.resize((int(WIDTH/2),int(HEIGHT/2)), Image.NEAREST) 39 | if len(np.shape(label)) == 3: 40 | label = np.array(label)[:,:,0] 41 | label = np.reshape(np.array(label), [-1]) 42 | one_hot_label = np.eye(NCLASSES)[np.array(label, np.int32)] 43 | Y_train.append(one_hot_label) 44 | 45 | i = (i+1) % n 46 | yield (np.array(X_train), np.array(Y_train)) 47 | 48 | if __name__ == "__main__": 49 | #---------------------------------------------# 50 | # 定义输入图片的高和宽,以及种类数量 51 | #---------------------------------------------# 52 | HEIGHT = 416 53 | WIDTH = 416 54 | #---------------------------------------------# 55 | # 背景 + 斑马线 = 2 56 | #---------------------------------------------# 57 | NCLASSES = 2 58 | 59 | log_dir = "logs/" 60 | model = convnet_segnet(n_classes=NCLASSES, input_height=HEIGHT, input_width=WIDTH) 61 | #---------------------------------------------------------------------# 62 | # 这一步是获得主干特征提取网络的权重、使用的是迁移学习的思想 63 | # 如果下载过慢,可以复制连接到迅雷进行下载。 64 | # 之后将权值复制到目录下,根据路径进行载入。 65 | # 如: 66 | # weights_path = "xxxxx.h5" 67 | # model.load_weights(weights_path,by_name=True,skip_mismatch=True) 68 | #---------------------------------------------------------------------# 69 | WEIGHTS_PATH_NO_TOP = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.1/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5' 70 | weights_path = get_file('vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5', WEIGHTS_PATH_NO_TOP, cache_subdir='models') 71 | model.load_weights(weights_path,by_name=True) 72 | 73 | # 打开数据集的txt 74 | with open("./dataset2/train.txt","r") as f: 75 | lines = f.readlines() 76 | 77 | #---------------------------------------------# 78 | # 打乱的数据更有利于训练 79 | # 90%用于训练,10%用于估计。 80 | #---------------------------------------------# 81 | np.random.seed(10101) 82 | np.random.shuffle(lines) 83 | np.random.seed(None) 84 | num_val = int(len(lines)*0.1) 85 | num_train = len(lines) - num_val 86 | 87 | #-------------------------------------------------------------------------------# 88 | # 训练参数的设置 89 | # checkpoint用于设置权值保存的细节,period用于修改多少epoch保存一次 90 | # reduce_lr用于设置学习率下降的方式 91 | # early_stopping用于设定早停,val_loss多次不下降自动结束训练,表示模型基本收敛 92 | #-------------------------------------------------------------------------------# 93 | checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5', 94 | monitor='val_loss', save_weights_only=True, save_best_only=False, period=2) 95 | reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1) 96 | early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1) 97 | 98 | #-------------------------------------------------------------------------------# 99 | # 这里使用的是迁移学习的思想,主干部分提取出来的特征是通用的 100 | # 所以我们可以不训练主干部分先,因此训练部分分为两步,分别是冻结训练和解冻训练 101 | # 冻结训练是不训练主干的,解冻训练是训练主干的。 102 | # 由于训练的特征层变多,解冻后所需显存变大 103 | #-------------------------------------------------------------------------------# 104 | trainable_layer = 15 105 | for i in range(trainable_layer): 106 | model.layers[i].trainable = False 107 | print('freeze the first {} layers of total {} layers.'.format(trainable_layer, len(model.layers))) 108 | 109 | if True: 110 | lr = 1e-3 111 | batch_size = 4 112 | model.compile(loss = 'categorical_crossentropy', 113 | optimizer = Adam(lr=lr), 114 | metrics = ['accuracy']) 115 | 116 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 117 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 118 | steps_per_epoch=max(1, num_train//batch_size), 119 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 120 | validation_steps=max(1, num_val//batch_size), 121 | epochs=50, 122 | initial_epoch=0, 123 | callbacks=[checkpoint, reduce_lr,early_stopping]) 124 | 125 | 126 | for i in range(len(model.layers)): 127 | model.layers[i].trainable = True 128 | 129 | if True: 130 | lr = 1e-4 131 | batch_size = 4 132 | model.compile(loss = 'categorical_crossentropy', 133 | optimizer = Adam(lr=lr), 134 | metrics = ['accuracy']) 135 | 136 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 137 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 138 | steps_per_epoch=max(1, num_train//batch_size), 139 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 140 | validation_steps=max(1, num_val//batch_size), 141 | epochs=100, 142 | initial_epoch=50, 143 | callbacks=[checkpoint, reduce_lr,early_stopping]) 144 | -------------------------------------------------------------------------------- /SegNet_Mobile/dataset2/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放dataset内的训练数据。 2 | This part is used to store the training data in the dataset. -------------------------------------------------------------------------------- /SegNet_Mobile/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img/timg.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/img/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img/timg2.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/img/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img/timg3.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/img/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img/timg4.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/img/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img/timg5.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/img_out/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img_out/timg.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/img_out/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img_out/timg2.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/img_out/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img_out/timg3.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/img_out/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img_out/timg4.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/img_out/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/img_out/timg5.jpg -------------------------------------------------------------------------------- /SegNet_Mobile/logs/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放训练后的文件。 2 | This part is used to store post training documents. -------------------------------------------------------------------------------- /SegNet_Mobile/nets/__pycache__/mobilenet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/nets/__pycache__/mobilenet.cpython-36.pyc -------------------------------------------------------------------------------- /SegNet_Mobile/nets/__pycache__/resnet50.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/nets/__pycache__/resnet50.cpython-36.pyc -------------------------------------------------------------------------------- /SegNet_Mobile/nets/__pycache__/segnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_Mobile/nets/__pycache__/segnet.cpython-36.pyc -------------------------------------------------------------------------------- /SegNet_Mobile/nets/mobilenet.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import keras.backend as K 3 | from keras.layers import * 4 | from keras.models import * 5 | 6 | def relu6(x): 7 | return K.relu(x, max_value=6) 8 | 9 | def _conv_block(inputs, filters, alpha, kernel=(3, 3), strides=(1, 1)): 10 | filters = int(filters * alpha) 11 | x = ZeroPadding2D(padding=(1, 1), name='conv1_pad')(inputs) 12 | x = Conv2D(filters, kernel, padding='valid', 13 | use_bias=False, 14 | strides=strides, 15 | name='conv1')(x) 16 | x = BatchNormalization(name='conv1_bn')(x) 17 | return Activation(relu6, name='conv1_relu')(x) 18 | 19 | def _depthwise_conv_block(inputs, pointwise_conv_filters, alpha, depth_multiplier=1, strides=(1, 1), block_id=1): 20 | pointwise_conv_filters = int(pointwise_conv_filters * alpha) 21 | 22 | x = ZeroPadding2D((1, 1), name='conv_pad_%d' % block_id)(inputs) 23 | x = DepthwiseConv2D((3, 3), padding='valid', 24 | depth_multiplier=depth_multiplier, 25 | strides=strides, 26 | use_bias=False, 27 | name='conv_dw_%d' % block_id)(x) 28 | x = BatchNormalization(name='conv_dw_%d_bn' % block_id)(x) 29 | x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x) 30 | 31 | x = Conv2D(pointwise_conv_filters, (1, 1), 32 | padding='same', 33 | use_bias=False, 34 | strides=(1, 1), 35 | name='conv_pw_%d' % block_id)(x) 36 | x = BatchNormalization(name='conv_pw_%d_bn' % block_id)(x) 37 | return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x) 38 | 39 | def get_mobilenet_encoder(input_height=416, input_width=416): 40 | alpha=1.0 41 | depth_multiplier=1 42 | 43 | img_input = Input(shape=(input_height, input_width, 3)) 44 | 45 | # 416,416,3 -> 208,208,32 -> 208,208,64 46 | x = _conv_block(img_input, 32, alpha, strides=(2, 2)) 47 | x = _depthwise_conv_block(x, 64, alpha, depth_multiplier, block_id=1) 48 | f1 = x 49 | 50 | # 208,208,64 -> 104,104,128 51 | x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, strides=(2, 2), block_id=2) 52 | x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, block_id=3) 53 | f2 = x 54 | 55 | # 104,104,128 -> 52,52,256 56 | x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, strides=(2, 2), block_id=4) 57 | x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, block_id=5) 58 | f3 = x 59 | 60 | # 52,52,256 -> 26,26,512 61 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, strides=(2, 2), block_id=6) 62 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=7) 63 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=8) 64 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=9) 65 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=10) 66 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=11) 67 | f4 = x 68 | 69 | # 26,26,512 -> 13,13,1024 70 | x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, strides=(2, 2), block_id=12) 71 | x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, block_id=13) 72 | f5 = x 73 | 74 | return img_input, [f1 , f2 , f3 , f4 , f5] 75 | -------------------------------------------------------------------------------- /SegNet_Mobile/nets/segnet.py: -------------------------------------------------------------------------------- 1 | from keras.layers import * 2 | from keras.models import * 3 | 4 | from nets.mobilenet import get_mobilenet_encoder 5 | 6 | def segnet_decoder(f, n_classes, n_up=3): 7 | assert n_up >= 2 8 | 9 | o = f 10 | # 26,26,512 -> 26,26,512 11 | o = ZeroPadding2D((1,1))(o) 12 | o = Conv2D(512, (3, 3), padding='valid')(o) 13 | o = BatchNormalization()(o) 14 | 15 | # 进行一次UpSampling2D,此时hw变为原来的1/8 16 | # 26,26,512 -> 52,52,256 17 | o = UpSampling2D((2,2))(o) 18 | o = ZeroPadding2D((1,1))(o) 19 | o = Conv2D(256, (3, 3), padding='valid')(o) 20 | o = BatchNormalization()(o) 21 | 22 | # 进行一次UpSampling2D,此时hw变为原来的1/4 23 | # 52,52,256 -> 104,104,128 24 | for _ in range(n_up-2): 25 | o = UpSampling2D((2,2))(o) 26 | o = ZeroPadding2D((1,1))(o) 27 | o = Conv2D(128, (3, 3), padding='valid')(o) 28 | o = BatchNormalization()(o) 29 | 30 | # 进行一次UpSampling2D,此时hw变为原来的1/2 31 | # 104,104,128 -> 208,208,64 32 | o = UpSampling2D((2,2))(o) 33 | o = ZeroPadding2D((1,1))(o) 34 | o = Conv2D(64, (3, 3), padding='valid')(o) 35 | o = BatchNormalization()(o) 36 | 37 | # 此时输出为h_input/2,w_input/2,nclasses 38 | # 208,208,2 39 | o = Conv2D(n_classes, (3, 3), padding='same')(o) 40 | 41 | return o 42 | 43 | def _segnet(n_classes, encoder, input_height=416, input_width=416, encoder_level=3): 44 | # encoder通过主干网络 45 | img_input , levels = encoder(input_height=input_height, input_width=input_width ) 46 | 47 | # 获取hw压缩四次后的结果 48 | feat = levels[encoder_level] 49 | 50 | # 将特征传入segnet网络 51 | o = segnet_decoder(feat, n_classes, n_up=3 ) 52 | 53 | # 将结果进行reshape 54 | o = Reshape((int(input_height/2)*int(input_width/2), -1))(o) 55 | o = Softmax()(o) 56 | model = Model(img_input,o) 57 | 58 | return model 59 | 60 | def mobilenet_segnet(n_classes, input_height=224, input_width=224, encoder_level=3): 61 | model = _segnet(n_classes, get_mobilenet_encoder, input_height=input_height, input_width=input_width, encoder_level=encoder_level) 62 | model.model_name = "convnet_segnet" 63 | return model 64 | -------------------------------------------------------------------------------- /SegNet_Mobile/predict.py: -------------------------------------------------------------------------------- 1 | from nets.segnet import mobilenet_segnet 2 | import copy 3 | import os 4 | import random 5 | 6 | import numpy as np 7 | from PIL import Image 8 | 9 | if __name__ == "__main__": 10 | #---------------------------------------------------# 11 | # 定义了输入图片的颜色,当我们想要去区分两类的时候 12 | # 我们定义了两个颜色,分别用于背景和斑马线 13 | # [0,0,0], [0,255,0]代表了颜色的RGB色彩 14 | #---------------------------------------------------# 15 | class_colors = [[0,0,0],[0,255,0]] 16 | #---------------------------------------------# 17 | # 定义输入图片的高和宽,以及种类数量 18 | #---------------------------------------------# 19 | HEIGHT = 416 20 | WIDTH = 416 21 | #---------------------------------------------# 22 | # 背景 + 斑马线 = 2 23 | #---------------------------------------------# 24 | NCLASSES = 2 25 | 26 | #---------------------------------------------# 27 | # 载入模型 28 | #---------------------------------------------# 29 | model = mobilenet_segnet(n_classes=NCLASSES,input_height=HEIGHT, input_width=WIDTH) 30 | #--------------------------------------------------# 31 | # 载入权重,训练好的权重会保存在logs文件夹里面 32 | # 我们需要将对应的权重载入 33 | # 修改model_path,将其对应我们训练好的权重即可 34 | # 下面只是一个示例 35 | #--------------------------------------------------# 36 | model.load_weights("logs/ep030-loss0.007-val_loss0.024.h5") 37 | 38 | #--------------------------------------------------# 39 | # 对imgs文件夹进行一个遍历 40 | #--------------------------------------------------# 41 | imgs = os.listdir("./img/") 42 | for jpg in imgs: 43 | #--------------------------------------------------# 44 | # 打开imgs文件夹里面的每一个图片 45 | #--------------------------------------------------# 46 | img = Image.open("./img/"+jpg) 47 | 48 | old_img = copy.deepcopy(img) 49 | orininal_h = np.array(img).shape[0] 50 | orininal_w = np.array(img).shape[1] 51 | 52 | #--------------------------------------------------# 53 | # 对输入进来的每一个图片进行Resize 54 | # resize成[HEIGHT, WIDTH, 3] 55 | #--------------------------------------------------# 56 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 57 | img = np.array(img) / 255 58 | img = img.reshape(-1, HEIGHT, WIDTH, 3) 59 | 60 | #--------------------------------------------------# 61 | # 将图像输入到网络当中进行预测 62 | #--------------------------------------------------# 63 | pr = model.predict(img)[0] 64 | pr = pr.reshape((int(HEIGHT/2), int(WIDTH/2), NCLASSES)).argmax(axis=-1) 65 | 66 | #------------------------------------------------# 67 | # 创建一副新图,并根据每个像素点的种类赋予颜色 68 | #------------------------------------------------# 69 | seg_img = np.zeros((int(HEIGHT/2), int(WIDTH/2),3)) 70 | for c in range(NCLASSES): 71 | seg_img[:, :, 0] += ((pr[:,: ] == c) * class_colors[c][0]).astype('uint8') 72 | seg_img[:, :, 1] += ((pr[:,: ] == c) * class_colors[c][1]).astype('uint8') 73 | seg_img[:, :, 2] += ((pr[:,: ] == c) * class_colors[c][2]).astype('uint8') 74 | 75 | seg_img = Image.fromarray(np.uint8(seg_img)).resize((orininal_w,orininal_h)) 76 | #------------------------------------------------# 77 | # 将新图片和原图片混合 78 | #------------------------------------------------# 79 | image = Image.blend(old_img,seg_img,0.3) 80 | 81 | image.save("./img_out/"+jpg) 82 | 83 | 84 | -------------------------------------------------------------------------------- /SegNet_Mobile/test.py: -------------------------------------------------------------------------------- 1 | #---------------------------------------------# 2 | # 该部分用于查看网络结构 3 | #---------------------------------------------# 4 | from nets.segnet import mobilenet_segnet 5 | 6 | if __name__ == "__main__": 7 | model = mobilenet_segnet(2,input_height=416,input_width=416) 8 | model.summary() 9 | -------------------------------------------------------------------------------- /SegNet_Mobile/train.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import numpy as np 3 | from keras import backend as K 4 | from keras.callbacks import (EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, 5 | TensorBoard) 6 | from keras.optimizers import Adam 7 | from PIL import Image 8 | 9 | from nets.segnet import mobilenet_segnet 10 | 11 | #-------------------------------------------------------------# 12 | # 定义了一个生成器,用于读取datasets2文件夹里面的图片与标签 13 | #-------------------------------------------------------------# 14 | def generate_arrays_from_file(lines,batch_size): 15 | n = len(lines) 16 | i = 0 17 | while 1: 18 | X_train = [] 19 | Y_train = [] 20 | for _ in range(batch_size): 21 | if i==0: 22 | np.random.shuffle(lines) 23 | #-------------------------------------# 24 | # 读取输入图片并进行归一化和resize 25 | #-------------------------------------# 26 | name = lines[i].split(';')[0] 27 | img = Image.open("./dataset2/jpg/" + name) 28 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 29 | img = np.array(img)/255 30 | X_train.append(img) 31 | 32 | #-------------------------------------# 33 | # 读取标签图片并进行归一化和resize 34 | #-------------------------------------# 35 | name = lines[i].split(';')[1].split()[0] 36 | label = Image.open("./dataset2/png/" + name) 37 | label = label.resize((int(WIDTH/2),int(HEIGHT/2)), Image.NEAREST) 38 | if len(np.shape(label)) == 3: 39 | label = np.array(label)[:,:,0] 40 | label = np.reshape(np.array(label), [-1]) 41 | one_hot_label = np.eye(NCLASSES)[np.array(label, np.int32)] 42 | Y_train.append(one_hot_label) 43 | 44 | i = (i+1) % n 45 | yield (np.array(X_train), np.array(Y_train)) 46 | 47 | if __name__ == "__main__": 48 | #---------------------------------------------# 49 | # 定义输入图片的高和宽,以及种类数量 50 | #---------------------------------------------# 51 | HEIGHT = 416 52 | WIDTH = 416 53 | #---------------------------------------------# 54 | # 背景 + 斑马线 = 2 55 | #---------------------------------------------# 56 | NCLASSES = 2 57 | 58 | log_dir = "logs/" 59 | model = mobilenet_segnet(n_classes=NCLASSES,input_height=HEIGHT, input_width=WIDTH) 60 | #---------------------------------------------------------------------# 61 | # 这一步是获得主干特征提取网络的权重、使用的是迁移学习的思想 62 | # 如果下载过慢,可以复制连接到迅雷进行下载。 63 | # 之后将权值复制到目录下,根据路径进行载入。 64 | # 如: 65 | # weights_path = "xxxxx.h5" 66 | # model.load_weights(weights_path,by_name=True,skip_mismatch=True) 67 | #---------------------------------------------------------------------# 68 | BASE_WEIGHT_PATH = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.6/' 69 | model_name = 'mobilenet_%s_%d_tf_no_top.h5' % ('1_0', 224) 70 | weight_path = BASE_WEIGHT_PATH + model_name 71 | weights_path = keras.utils.get_file(model_name, weight_path) 72 | model.load_weights(weights_path, by_name=True, skip_mismatch=True) 73 | 74 | # 打开数据集的txt 75 | with open("./dataset2/train.txt","r") as f: 76 | lines = f.readlines() 77 | 78 | #---------------------------------------------# 79 | # 打乱的数据更有利于训练 80 | # 90%用于训练,10%用于估计。 81 | #---------------------------------------------# 82 | np.random.seed(10101) 83 | np.random.shuffle(lines) 84 | np.random.seed(None) 85 | num_val = int(len(lines)*0.1) 86 | num_train = len(lines) - num_val 87 | 88 | #-------------------------------------------------------------------------------# 89 | # 训练参数的设置 90 | # checkpoint用于设置权值保存的细节,period用于修改多少epoch保存一次 91 | # reduce_lr用于设置学习率下降的方式 92 | # early_stopping用于设定早停,val_loss多次不下降自动结束训练,表示模型基本收敛 93 | #-------------------------------------------------------------------------------# 94 | checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5', 95 | monitor='val_loss', save_weights_only=True, save_best_only=False, period=2) 96 | reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1) 97 | early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1) 98 | 99 | #-------------------------------------------------------------------------------# 100 | # 这里使用的是迁移学习的思想,主干部分提取出来的特征是通用的 101 | # 所以我们可以不训练主干部分先,因此训练部分分为两步,分别是冻结训练和解冻训练 102 | # 冻结训练是不训练主干的,解冻训练是训练主干的。 103 | # 由于训练的特征层变多,解冻后所需显存变大 104 | #-------------------------------------------------------------------------------# 105 | trainable_layer = 60 106 | for i in range(trainable_layer): 107 | model.layers[i].trainable = False 108 | print('freeze the first {} layers of total {} layers.'.format(trainable_layer, len(model.layers))) 109 | 110 | if True: 111 | lr = 1e-3 112 | batch_size = 4 113 | model.compile(loss = 'categorical_crossentropy', 114 | optimizer = Adam(lr=lr), 115 | metrics = ['accuracy']) 116 | 117 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 118 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 119 | steps_per_epoch=max(1, num_train//batch_size), 120 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 121 | validation_steps=max(1, num_val//batch_size), 122 | epochs=50, 123 | initial_epoch=0, 124 | callbacks=[checkpoint, reduce_lr,early_stopping]) 125 | 126 | for i in range(len(model.layers)): 127 | model.layers[i].trainable = True 128 | 129 | if True: 130 | lr = 1e-4 131 | batch_size = 4 132 | model.compile(loss = 'categorical_crossentropy', 133 | optimizer = Adam(lr=lr), 134 | metrics = ['accuracy']) 135 | 136 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 137 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 138 | steps_per_epoch=max(1, num_train//batch_size), 139 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 140 | validation_steps=max(1, num_val//batch_size), 141 | epochs=100, 142 | initial_epoch=50, 143 | callbacks=[checkpoint, reduce_lr,early_stopping]) 144 | -------------------------------------------------------------------------------- /SegNet_ResNet/dataset2/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放dataset内的训练数据。 2 | This part is used to store the training data in the dataset. -------------------------------------------------------------------------------- /SegNet_ResNet/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img/timg.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/img/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img/timg2.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/img/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img/timg3.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/img/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img/timg4.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/img/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img/timg5.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/img_out/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img_out/timg.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/img_out/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img_out/timg2.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/img_out/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img_out/timg3.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/img_out/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img_out/timg4.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/img_out/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/img_out/timg5.jpg -------------------------------------------------------------------------------- /SegNet_ResNet/logs/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放训练后的文件。 2 | This part is used to store post training documents. -------------------------------------------------------------------------------- /SegNet_ResNet/nets/__pycache__/resnet50.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/nets/__pycache__/resnet50.cpython-36.pyc -------------------------------------------------------------------------------- /SegNet_ResNet/nets/__pycache__/segnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/SegNet_ResNet/nets/__pycache__/segnet.cpython-36.pyc -------------------------------------------------------------------------------- /SegNet_ResNet/nets/resnet50.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import keras.backend as K 3 | from keras import layers 4 | from keras.layers import * 5 | from keras.models import * 6 | 7 | def identity_block(input_tensor, kernel_size, filters, stage, block): 8 | 9 | filters1, filters2, filters3 = filters 10 | 11 | conv_name_base = 'res' + str(stage) + block + '_branch' 12 | bn_name_base = 'bn' + str(stage) + block + '_branch' 13 | 14 | x = Conv2D(filters1, (1, 1), name=conv_name_base + '2a')(input_tensor) 15 | x = BatchNormalization(name=bn_name_base + '2a')(x) 16 | x = Activation('relu')(x) 17 | 18 | x = Conv2D(filters2, kernel_size,padding='same', name=conv_name_base + '2b')(x) 19 | x = BatchNormalization(name=bn_name_base + '2b')(x) 20 | x = Activation('relu')(x) 21 | 22 | x = Conv2D(filters3, (1, 1), name=conv_name_base + '2c')(x) 23 | x = BatchNormalization(name=bn_name_base + '2c')(x) 24 | 25 | x = layers.add([x, input_tensor]) 26 | x = Activation('relu')(x) 27 | return x 28 | 29 | 30 | def conv_block(input_tensor, kernel_size, filters, stage, block, strides=(2, 2)): 31 | 32 | filters1, filters2, filters3 = filters 33 | 34 | conv_name_base = 'res' + str(stage) + block + '_branch' 35 | bn_name_base = 'bn' + str(stage) + block + '_branch' 36 | 37 | x = Conv2D(filters1, (1, 1), strides=strides, 38 | name=conv_name_base + '2a')(input_tensor) 39 | x = BatchNormalization(name=bn_name_base + '2a')(x) 40 | x = Activation('relu')(x) 41 | 42 | x = Conv2D(filters2, kernel_size, padding='same', 43 | name=conv_name_base + '2b')(x) 44 | x = BatchNormalization(name=bn_name_base + '2b')(x) 45 | x = Activation('relu')(x) 46 | 47 | x = Conv2D(filters3, (1, 1), name=conv_name_base + '2c')(x) 48 | x = BatchNormalization(name=bn_name_base + '2c')(x) 49 | 50 | shortcut = Conv2D(filters3, (1, 1), strides=strides, 51 | name=conv_name_base + '1')(input_tensor) 52 | shortcut = BatchNormalization(name=bn_name_base + '1')(shortcut) 53 | 54 | x = layers.add([x, shortcut]) 55 | x = Activation('relu')(x) 56 | return x 57 | 58 | 59 | def get_resnet50_encoder(input_height=224, input_width=224): 60 | img_input = Input([input_height, input_width, 3]) 61 | 62 | # 416,416,3 -> 208,208,64 63 | x = ZeroPadding2D((3, 3))(img_input) 64 | x = Conv2D(64, (7, 7), strides=(2, 2), name='conv1')(x) 65 | x = BatchNormalization(name='bn_conv1')(x) 66 | x = Activation('relu')(x) 67 | f1 = x 68 | 69 | # 208,208,64 -> 104,104,64 -> 104,104,256 70 | x = MaxPooling2D((3, 3), strides=(2, 2), padding="same")(x) 71 | 72 | x = conv_block(x, 3, [64, 64, 256], stage=2, block='a', strides=(1, 1)) 73 | x = identity_block(x, 3, [64, 64, 256], stage=2, block='b') 74 | x = identity_block(x, 3, [64, 64, 256], stage=2, block='c') 75 | # f2是hw方向压缩两次的结果 76 | f2 = x 77 | 78 | # 104,104,256 -> 52,52,512 79 | x = conv_block(x, 3, [128, 128, 512], stage=3, block='a') 80 | x = identity_block(x, 3, [128, 128, 512], stage=3, block='b') 81 | x = identity_block(x, 3, [128, 128, 512], stage=3, block='c') 82 | x = identity_block(x, 3, [128, 128, 512], stage=3, block='d') 83 | # f3是hw方向压缩三次的结果 84 | f3 = x 85 | 86 | # 52,52,512 -> 26,26,1024 87 | x = conv_block(x, 3, [256, 256, 1024], stage=4, block='a') 88 | x = identity_block(x, 3, [256, 256, 1024], stage=4, block='b') 89 | x = identity_block(x, 3, [256, 256, 1024], stage=4, block='c') 90 | x = identity_block(x, 3, [256, 256, 1024], stage=4, block='d') 91 | x = identity_block(x, 3, [256, 256, 1024], stage=4, block='e') 92 | x = identity_block(x, 3, [256, 256, 1024], stage=4, block='f') 93 | # f4是hw方向压缩四次的结果 94 | f4 = x 95 | 96 | # 26,26,1024 -> 13,13,2048 97 | x = conv_block(x, 3, [512, 512, 2048], stage=5, block='a') 98 | x = identity_block(x, 3, [512, 512, 2048], stage=5, block='b') 99 | x = identity_block(x, 3, [512, 512, 2048], stage=5, block='c') 100 | # f5是hw方向压缩五次的结果 101 | f5 = x 102 | 103 | return img_input, [f1 , f2 , f3 , f4 , f5] 104 | -------------------------------------------------------------------------------- /SegNet_ResNet/nets/segnet.py: -------------------------------------------------------------------------------- 1 | from keras.layers import * 2 | from keras.models import * 3 | 4 | from nets.resnet50 import get_resnet50_encoder 5 | 6 | def segnet_decoder(f, n_classes, n_up=3): 7 | assert n_up >= 2 8 | 9 | o = f 10 | # 26,26,512 -> 26,26,512 11 | o = ZeroPadding2D((1,1))(o) 12 | o = Conv2D(512, (3, 3), padding='valid')(o) 13 | o = BatchNormalization()(o) 14 | 15 | # 进行一次UpSampling2D,此时hw变为原来的1/8 16 | # 26,26,512 -> 52,52,256 17 | o = UpSampling2D((2,2))(o) 18 | o = ZeroPadding2D((1,1))(o) 19 | o = Conv2D(256, (3, 3), padding='valid')(o) 20 | o = BatchNormalization()(o) 21 | 22 | # 进行一次UpSampling2D,此时hw变为原来的1/4 23 | # 52,52,256 -> 104,104,128 24 | for _ in range(n_up-2): 25 | o = UpSampling2D((2,2))(o) 26 | o = ZeroPadding2D((1,1))(o) 27 | o = Conv2D(128, (3, 3), padding='valid')(o) 28 | o = BatchNormalization()(o) 29 | 30 | # 进行一次UpSampling2D,此时hw变为原来的1/2 31 | # 104,104,128 -> 208,208,64 32 | o = UpSampling2D((2,2))(o) 33 | o = ZeroPadding2D((1,1))(o) 34 | o = Conv2D(64, (3, 3), padding='valid')(o) 35 | o = BatchNormalization()(o) 36 | 37 | # 此时输出为h_input/2,w_input/2,nclasses 38 | # 208,208,2 39 | o = Conv2D(n_classes, (3, 3), padding='same')(o) 40 | 41 | return o 42 | 43 | def _segnet(n_classes, encoder, input_height=416, input_width=416, encoder_level=3): 44 | # encoder通过主干网络 45 | img_input , levels = encoder(input_height=input_height, input_width=input_width ) 46 | 47 | # 获取hw压缩四次后的结果 48 | feat = levels[encoder_level] 49 | 50 | # 将特征传入segnet网络 51 | o = segnet_decoder(feat, n_classes, n_up=3 ) 52 | 53 | # 将结果进行reshape 54 | o = Reshape((int(input_height/2)*int(input_width/2), -1))(o) 55 | o = Softmax()(o) 56 | model = Model(img_input,o) 57 | 58 | return model 59 | 60 | def resnet50_segnet(n_classes, input_height=224, input_width=224, encoder_level=3): 61 | model = _segnet(n_classes, get_resnet50_encoder, input_height=input_height, input_width=input_width, encoder_level=encoder_level) 62 | model.model_name = "resnet50_segnet" 63 | return model 64 | 65 | -------------------------------------------------------------------------------- /SegNet_ResNet/predict.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | import random 4 | 5 | import numpy as np 6 | from PIL import Image 7 | 8 | from nets.segnet import resnet50_segnet 9 | 10 | if __name__ == "__main__": 11 | #---------------------------------------------------# 12 | # 定义了输入图片的颜色,当我们想要去区分两类的时候 13 | # 我们定义了两个颜色,分别用于背景和斑马线 14 | # [0,0,0], [0,255,0]代表了颜色的RGB色彩 15 | #---------------------------------------------------# 16 | class_colors = [[0,0,0],[0,255,0]] 17 | #---------------------------------------------# 18 | # 定义输入图片的高和宽,以及种类数量 19 | #---------------------------------------------# 20 | HEIGHT = 416 21 | WIDTH = 416 22 | #---------------------------------------------# 23 | # 背景 + 斑马线 = 2 24 | #---------------------------------------------# 25 | NCLASSES = 2 26 | 27 | #---------------------------------------------# 28 | # 载入模型 29 | #---------------------------------------------# 30 | model = resnet50_segnet(n_classes=NCLASSES,input_height=HEIGHT, input_width=WIDTH) 31 | #--------------------------------------------------# 32 | # 载入权重,训练好的权重会保存在logs文件夹里面 33 | # 我们需要将对应的权重载入 34 | # 修改model_path,将其对应我们训练好的权重即可 35 | # 下面只是一个示例 36 | #--------------------------------------------------# 37 | model.load_weights("logs/ep053-loss0.021-val_loss0.028.h5") 38 | 39 | #--------------------------------------------------# 40 | # 对imgs文件夹进行一个遍历 41 | #--------------------------------------------------# 42 | imgs = os.listdir("./img") 43 | for jpg in imgs: 44 | #--------------------------------------------------# 45 | # 打开imgs文件夹里面的每一个图片 46 | #--------------------------------------------------# 47 | img = Image.open("./img/"+jpg) 48 | 49 | old_img = copy.deepcopy(img) 50 | orininal_h = np.array(img).shape[0] 51 | orininal_w = np.array(img).shape[1] 52 | 53 | #--------------------------------------------------# 54 | # 对输入进来的每一个图片进行Resize 55 | # resize成[HEIGHT, WIDTH, 3] 56 | #--------------------------------------------------# 57 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 58 | img = np.array(img) / 255 59 | img = img.reshape(-1, HEIGHT, WIDTH, 3) 60 | 61 | #--------------------------------------------------# 62 | # 将图像输入到网络当中进行预测 63 | #--------------------------------------------------# 64 | pr = model.predict(img)[0] 65 | pr = pr.reshape((int(HEIGHT/2), int(WIDTH/2), NCLASSES)).argmax(axis=-1) 66 | 67 | #------------------------------------------------# 68 | # 创建一副新图,并根据每个像素点的种类赋予颜色 69 | #------------------------------------------------# 70 | seg_img = np.zeros((int(HEIGHT/2), int(WIDTH/2),3)) 71 | for c in range(NCLASSES): 72 | seg_img[:, :, 0] += ((pr[:,: ] == c) * class_colors[c][0]).astype('uint8') 73 | seg_img[:, :, 1] += ((pr[:,: ] == c) * class_colors[c][1]).astype('uint8') 74 | seg_img[:, :, 2] += ((pr[:,: ] == c) * class_colors[c][2]).astype('uint8') 75 | 76 | seg_img = Image.fromarray(np.uint8(seg_img)).resize((orininal_w,orininal_h)) 77 | #------------------------------------------------# 78 | # 将新图片和原图片混合 79 | #------------------------------------------------# 80 | image = Image.blend(old_img,seg_img,0.3) 81 | 82 | image.save("./img_out/"+jpg) 83 | 84 | 85 | -------------------------------------------------------------------------------- /SegNet_ResNet/test.py: -------------------------------------------------------------------------------- 1 | #---------------------------------------------# 2 | # 该部分用于查看网络结构 3 | #---------------------------------------------# 4 | from nets.segnet import resnet50_segnet 5 | 6 | if __name__ == "__main__": 7 | model = resnet50_segnet(n_classes=2, input_height=416, input_width=416) 8 | model.summary() 9 | -------------------------------------------------------------------------------- /SegNet_ResNet/train.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import numpy as np 3 | from keras import backend as K 4 | from keras.callbacks import (EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, 5 | TensorBoard) 6 | from keras.optimizers import Adam 7 | from PIL import Image 8 | 9 | from nets.segnet import resnet50_segnet 10 | 11 | 12 | #-------------------------------------------------------------# 13 | # 定义了一个生成器,用于读取datasets2文件夹里面的图片与标签 14 | #-------------------------------------------------------------# 15 | def generate_arrays_from_file(lines,batch_size): 16 | n = len(lines) 17 | i = 0 18 | while 1: 19 | X_train = [] 20 | Y_train = [] 21 | for _ in range(batch_size): 22 | if i==0: 23 | np.random.shuffle(lines) 24 | #-------------------------------------# 25 | # 读取输入图片并进行归一化和resize 26 | #-------------------------------------# 27 | name = lines[i].split(';')[0] 28 | img = Image.open("./dataset2/jpg/" + name) 29 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 30 | img = np.array(img)/255 31 | X_train.append(img) 32 | 33 | #-------------------------------------# 34 | # 读取标签图片并进行归一化和resize 35 | #-------------------------------------# 36 | name = lines[i].split(';')[1].split()[0] 37 | label = Image.open("./dataset2/png/" + name) 38 | label = label.resize((int(WIDTH/2),int(HEIGHT/2)), Image.NEAREST) 39 | if len(np.shape(label)) == 3: 40 | label = np.array(label)[:,:,0] 41 | label = np.reshape(np.array(label), [-1]) 42 | one_hot_label = np.eye(NCLASSES)[np.array(label, np.int32)] 43 | Y_train.append(one_hot_label) 44 | 45 | i = (i+1) % n 46 | yield (np.array(X_train), np.array(Y_train)) 47 | 48 | if __name__ == "__main__": 49 | #---------------------------------------------# 50 | # 定义输入图片的高和宽,以及种类数量 51 | #---------------------------------------------# 52 | HEIGHT = 416 53 | WIDTH = 416 54 | #---------------------------------------------# 55 | # 背景 + 斑马线 = 2 56 | #---------------------------------------------# 57 | NCLASSES = 2 58 | 59 | log_dir = "logs/" 60 | model = resnet50_segnet(n_classes=NCLASSES,input_height=HEIGHT, input_width=WIDTH) 61 | #---------------------------------------------------------------------# 62 | # 这一步是获得主干特征提取网络的权重、使用的是迁移学习的思想 63 | # 如果下载过慢,可以复制连接到迅雷进行下载。 64 | # 之后将权值复制到目录下,根据路径进行载入。 65 | # 如: 66 | # weights_path = "xxxxx.h5" 67 | # model.load_weights(weights_path,by_name=True,skip_mismatch=True) 68 | #---------------------------------------------------------------------# 69 | pretrained_url = "https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels.h5" 70 | weights_path = keras.utils.get_file(pretrained_url.split("/")[-1], pretrained_url) 71 | model.load_weights(weights_path,by_name=True) 72 | 73 | # 打开数据集的txt 74 | with open(r".\dataset2\train.txt","r") as f: 75 | lines = f.readlines() 76 | 77 | #---------------------------------------------# 78 | # 打乱的数据更有利于训练 79 | # 90%用于训练,10%用于估计。 80 | #---------------------------------------------# 81 | np.random.seed(10101) 82 | np.random.shuffle(lines) 83 | np.random.seed(None) 84 | num_val = int(len(lines)*0.1) 85 | num_train = len(lines) - num_val 86 | 87 | #-------------------------------------------------------------------------------# 88 | # 训练参数的设置 89 | # checkpoint用于设置权值保存的细节,period用于修改多少epoch保存一次 90 | # reduce_lr用于设置学习率下降的方式 91 | # early_stopping用于设定早停,val_loss多次不下降自动结束训练,表示模型基本收敛 92 | #-------------------------------------------------------------------------------# 93 | checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5', 94 | monitor='val_loss', save_weights_only=True, save_best_only=False, period=2) 95 | reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1) 96 | early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1) 97 | 98 | trainable_layer = 142 99 | for i in range(trainable_layer): 100 | model.layers[i].trainable = False 101 | print('freeze the first {} layers of total {} layers.'.format(trainable_layer, len(model.layers))) 102 | 103 | if True: 104 | lr = 1e-3 105 | batch_size = 4 106 | model.compile(loss = 'categorical_crossentropy', 107 | optimizer = Adam(lr=lr), 108 | metrics = ['accuracy']) 109 | 110 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 111 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 112 | steps_per_epoch=max(1, num_train//batch_size), 113 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 114 | validation_steps=max(1, num_val//batch_size), 115 | epochs=50, 116 | initial_epoch=0, 117 | callbacks=[checkpoint, reduce_lr,early_stopping]) 118 | 119 | for i in range(len(model.layers)): 120 | model.layers[i].trainable = True 121 | 122 | if True: 123 | lr = 1e-4 124 | batch_size = 4 125 | model.compile(loss = 'categorical_crossentropy', 126 | optimizer = Adam(lr=lr), 127 | metrics = ['accuracy']) 128 | 129 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 130 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 131 | steps_per_epoch=max(1, num_train//batch_size), 132 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 133 | validation_steps=max(1, num_val//batch_size), 134 | epochs=100, 135 | initial_epoch=50, 136 | callbacks=[checkpoint, reduce_lr,early_stopping]) 137 | model.save_weights(log_dir+'last1.h5') 138 | -------------------------------------------------------------------------------- /Unet_Mobile/dataset2/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放dataset内的训练数据。 2 | This part is used to store the training data in the dataset. -------------------------------------------------------------------------------- /Unet_Mobile/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img/timg.jpg -------------------------------------------------------------------------------- /Unet_Mobile/img/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img/timg2.jpg -------------------------------------------------------------------------------- /Unet_Mobile/img/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img/timg3.jpg -------------------------------------------------------------------------------- /Unet_Mobile/img/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img/timg4.jpg -------------------------------------------------------------------------------- /Unet_Mobile/img/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img/timg5.jpg -------------------------------------------------------------------------------- /Unet_Mobile/img_out/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img_out/timg.jpg -------------------------------------------------------------------------------- /Unet_Mobile/img_out/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img_out/timg2.jpg -------------------------------------------------------------------------------- /Unet_Mobile/img_out/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img_out/timg3.jpg -------------------------------------------------------------------------------- /Unet_Mobile/img_out/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img_out/timg4.jpg -------------------------------------------------------------------------------- /Unet_Mobile/img_out/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/img_out/timg5.jpg -------------------------------------------------------------------------------- /Unet_Mobile/logs/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放训练后的文件。 2 | This part is used to store post training documents. -------------------------------------------------------------------------------- /Unet_Mobile/nets/__pycache__/mobilenet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/nets/__pycache__/mobilenet.cpython-36.pyc -------------------------------------------------------------------------------- /Unet_Mobile/nets/__pycache__/resnet50.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/nets/__pycache__/resnet50.cpython-36.pyc -------------------------------------------------------------------------------- /Unet_Mobile/nets/__pycache__/segnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/nets/__pycache__/segnet.cpython-36.pyc -------------------------------------------------------------------------------- /Unet_Mobile/nets/__pycache__/unet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/Unet_Mobile/nets/__pycache__/unet.cpython-36.pyc -------------------------------------------------------------------------------- /Unet_Mobile/nets/mobilenet.py: -------------------------------------------------------------------------------- 1 | 2 | import keras 3 | import keras.backend as K 4 | from keras.layers import * 5 | from keras.models import * 6 | 7 | def relu6(x): 8 | return K.relu(x, max_value=6) 9 | 10 | def _conv_block(inputs, filters, alpha, kernel=(3, 3), strides=(1, 1)): 11 | filters = int(filters * alpha) 12 | x = ZeroPadding2D(padding=(1, 1), name='conv1_pad')(inputs) 13 | x = Conv2D(filters, kernel, padding='valid', 14 | use_bias=False, 15 | strides=strides, 16 | name='conv1')(x) 17 | x = BatchNormalization(name='conv1_bn')(x) 18 | return Activation(relu6, name='conv1_relu')(x) 19 | 20 | def _depthwise_conv_block(inputs, pointwise_conv_filters, alpha, depth_multiplier=1, strides=(1, 1), block_id=1): 21 | pointwise_conv_filters = int(pointwise_conv_filters * alpha) 22 | 23 | x = ZeroPadding2D((1, 1), name='conv_pad_%d' % block_id)(inputs) 24 | x = DepthwiseConv2D((3, 3), padding='valid', 25 | depth_multiplier=depth_multiplier, 26 | strides=strides, 27 | use_bias=False, 28 | name='conv_dw_%d' % block_id)(x) 29 | x = BatchNormalization(name='conv_dw_%d_bn' % block_id)(x) 30 | x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x) 31 | 32 | x = Conv2D(pointwise_conv_filters, (1, 1), 33 | padding='same', 34 | use_bias=False, 35 | strides=(1, 1), 36 | name='conv_pw_%d' % block_id)(x) 37 | x = BatchNormalization(name='conv_pw_%d_bn' % block_id)(x) 38 | return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x) 39 | 40 | def get_mobilenet_encoder(input_height=224, input_width=224): 41 | alpha=1.0 42 | depth_multiplier=1 43 | 44 | img_input = Input(shape=(input_height, input_width, 3)) 45 | 46 | # 416,416,3 -> 208,208,32 -> 208,208,64 47 | x = _conv_block(img_input, 32, alpha, strides=(2, 2)) 48 | x = _depthwise_conv_block(x, 64, alpha, depth_multiplier, block_id=1) 49 | f1 = x 50 | 51 | # 208,208,64 -> 104,104,128 52 | x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, strides=(2, 2), block_id=2) 53 | x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, block_id=3) 54 | f2 = x 55 | 56 | # 104,104,128 -> 52,52,256 57 | x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, strides=(2, 2), block_id=4) 58 | x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, block_id=5) 59 | f3 = x 60 | 61 | # 52,52,256 -> 26,26,512 62 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, strides=(2, 2), block_id=6) 63 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=7) 64 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=8) 65 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=9) 66 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=10) 67 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=11) 68 | f4 = x 69 | 70 | # 26,26,512 -> 13,13,1024 71 | x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, strides=(2, 2), block_id=12) 72 | x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, block_id=13) 73 | f5 = x 74 | 75 | return img_input, [f1 , f2 , f3 , f4 , f5] 76 | -------------------------------------------------------------------------------- /Unet_Mobile/nets/unet.py: -------------------------------------------------------------------------------- 1 | from keras.layers import * 2 | from keras.models import * 3 | 4 | from nets.mobilenet import get_mobilenet_encoder 5 | 6 | def _unet(n_classes, encoder, input_height=416, input_width=608): 7 | 8 | img_input , levels = encoder(input_height=input_height, input_width=input_width) 9 | [f1 , f2 , f3 , f4 , f5] = levels 10 | 11 | o = f4 12 | # 26,26,512 -> 26,26,512 13 | o = ZeroPadding2D((1,1))(o) 14 | o = Conv2D(512, (3, 3), padding='valid')(o) 15 | o = BatchNormalization()(o) 16 | 17 | # 26,26,512 -> 52,52,512 18 | o = UpSampling2D((2,2))(o) 19 | # 52,52,512 + 52,52,256 -> 52,52,768 20 | o = concatenate([o, f3]) 21 | o = ZeroPadding2D((1,1))(o) 22 | # 52,52,768 -> 52,52,256 23 | o = Conv2D(256, (3, 3), padding='valid')(o) 24 | o = BatchNormalization()(o) 25 | 26 | # 52,52,256 -> 104,104,256 27 | o = UpSampling2D((2,2))(o) 28 | # 104,104,256 + 104,104,128-> 104,104,384 29 | o = concatenate([o,f2]) 30 | o = ZeroPadding2D((1,1) )(o) 31 | # 104,104,384 -> 104,104,128 32 | o = Conv2D(128, (3, 3), padding='valid')(o) 33 | o = BatchNormalization()(o) 34 | 35 | # 104,104,128 -> 208,208,128 36 | o = UpSampling2D((2,2))(o) 37 | # 208,208,128 + 208,208,64 -> 208,208,192 38 | o = concatenate([o,f1]) 39 | 40 | # 208,208,192 -> 208,208,64 41 | o = ZeroPadding2D((1,1))(o) 42 | o = Conv2D(64, (3, 3), padding='valid')(o) 43 | o = BatchNormalization()(o) 44 | 45 | # 208,208,64 -> 208,208,n_classes 46 | o = Conv2D(n_classes, (3, 3), padding='same')(o) 47 | 48 | # 将结果进行reshape 49 | o = Reshape((int(input_height/2)*int(input_width/2), -1))(o) 50 | o = Softmax()(o) 51 | model = Model(img_input,o) 52 | 53 | return model 54 | 55 | def mobilenet_unet(n_classes, input_height=224, input_width=224): 56 | model = _unet(n_classes, get_mobilenet_encoder, input_height=input_height, input_width=input_width) 57 | model.model_name = "mobilenet_unet" 58 | return model 59 | -------------------------------------------------------------------------------- /Unet_Mobile/predict.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | import random 4 | 5 | import numpy as np 6 | from PIL import Image 7 | 8 | from nets.unet import mobilenet_unet 9 | 10 | if __name__ == "__main__": 11 | #---------------------------------------------------# 12 | # 定义了输入图片的颜色,当我们想要去区分两类的时候 13 | # 我们定义了两个颜色,分别用于背景和斑马线 14 | # [0,0,0], [0,255,0]代表了颜色的RGB色彩 15 | #---------------------------------------------------# 16 | class_colors = [[0,0,0],[0,255,0]] 17 | #---------------------------------------------# 18 | # 定义输入图片的高和宽,以及种类数量 19 | #---------------------------------------------# 20 | HEIGHT = 416 21 | WIDTH = 416 22 | #---------------------------------------------# 23 | # 背景 + 斑马线 = 2 24 | #---------------------------------------------# 25 | NCLASSES = 2 26 | 27 | #---------------------------------------------# 28 | # 载入模型 29 | #---------------------------------------------# 30 | model = mobilenet_unet(n_classes=NCLASSES,input_height=HEIGHT, input_width=WIDTH) 31 | #--------------------------------------------------# 32 | # 载入权重,训练好的权重会保存在logs文件夹里面 33 | # 我们需要将对应的权重载入 34 | # 修改model_path,将其对应我们训练好的权重即可 35 | # 下面只是一个示例 36 | #--------------------------------------------------# 37 | model.load_weights("logs/ep059-loss0.005-val_loss0.029.h5") 38 | 39 | #--------------------------------------------------# 40 | # 对imgs文件夹进行一个遍历 41 | #--------------------------------------------------# 42 | imgs = os.listdir("./img/") 43 | for jpg in imgs: 44 | #--------------------------------------------------# 45 | # 打开imgs文件夹里面的每一个图片 46 | #--------------------------------------------------# 47 | img = Image.open("./img/"+jpg) 48 | 49 | old_img = copy.deepcopy(img) 50 | orininal_h = np.array(img).shape[0] 51 | orininal_w = np.array(img).shape[1] 52 | 53 | #--------------------------------------------------# 54 | # 对输入进来的每一个图片进行Resize 55 | # resize成[HEIGHT, WIDTH, 3] 56 | #--------------------------------------------------# 57 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 58 | img = np.array(img) / 255 59 | img = img.reshape(-1, HEIGHT, WIDTH, 3) 60 | 61 | #--------------------------------------------------# 62 | # 将图像输入到网络当中进行预测 63 | #--------------------------------------------------# 64 | pr = model.predict(img)[0] 65 | pr = pr.reshape((int(HEIGHT/2), int(WIDTH/2), NCLASSES)).argmax(axis=-1) 66 | 67 | #------------------------------------------------# 68 | # 创建一副新图,并根据每个像素点的种类赋予颜色 69 | #------------------------------------------------# 70 | seg_img = np.zeros((int(HEIGHT/2), int(WIDTH/2),3)) 71 | for c in range(NCLASSES): 72 | seg_img[:, :, 0] += ((pr[:,: ] == c) * class_colors[c][0]).astype('uint8') 73 | seg_img[:, :, 1] += ((pr[:,: ] == c) * class_colors[c][1]).astype('uint8') 74 | seg_img[:, :, 2] += ((pr[:,: ] == c) * class_colors[c][2]).astype('uint8') 75 | 76 | seg_img = Image.fromarray(np.uint8(seg_img)).resize((orininal_w,orininal_h)) 77 | #------------------------------------------------# 78 | # 将新图片和原图片混合 79 | #------------------------------------------------# 80 | image = Image.blend(old_img,seg_img,0.3) 81 | 82 | image.save("./img_out/"+jpg) 83 | 84 | 85 | -------------------------------------------------------------------------------- /Unet_Mobile/test.py: -------------------------------------------------------------------------------- 1 | #---------------------------------------------# 2 | # 该部分用于查看网络结构 3 | #---------------------------------------------# 4 | from nets.unet import mobilenet_unet 5 | 6 | if __name__ == "__main__": 7 | model = mobilenet_unet(2, 416, 416) 8 | model.summary() -------------------------------------------------------------------------------- /Unet_Mobile/train.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import numpy as np 3 | from keras import backend as K 4 | from keras.callbacks import (EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, 5 | TensorBoard) 6 | from keras.optimizers import Adam 7 | from PIL import Image 8 | 9 | from nets.unet import mobilenet_unet 10 | 11 | 12 | #-------------------------------------------------------------# 13 | # 定义了一个生成器,用于读取datasets2文件夹里面的图片与标签 14 | #-------------------------------------------------------------# 15 | def generate_arrays_from_file(lines,batch_size): 16 | n = len(lines) 17 | i = 0 18 | while 1: 19 | X_train = [] 20 | Y_train = [] 21 | for _ in range(batch_size): 22 | if i==0: 23 | np.random.shuffle(lines) 24 | #-------------------------------------# 25 | # 读取输入图片并进行归一化和resize 26 | #-------------------------------------# 27 | name = lines[i].split(';')[0] 28 | img = Image.open("./dataset2/jpg/" + name) 29 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 30 | img = np.array(img)/255 31 | X_train.append(img) 32 | 33 | #-------------------------------------# 34 | # 读取标签图片并进行归一化和resize 35 | #-------------------------------------# 36 | name = lines[i].split(';')[1].split()[0] 37 | label = Image.open("./dataset2/png/" + name) 38 | label = label.resize((int(WIDTH/2),int(HEIGHT/2)), Image.NEAREST) 39 | if len(np.shape(label)) == 3: 40 | label = np.array(label)[:,:,0] 41 | label = np.reshape(np.array(label), [-1]) 42 | one_hot_label = np.eye(NCLASSES)[np.array(label, np.int32)] 43 | Y_train.append(one_hot_label) 44 | 45 | i = (i+1) % n 46 | yield (np.array(X_train), np.array(Y_train)) 47 | 48 | if __name__ == "__main__": 49 | #---------------------------------------------# 50 | # 定义输入图片的高和宽,以及种类数量 51 | #---------------------------------------------# 52 | HEIGHT = 416 53 | WIDTH = 416 54 | #---------------------------------------------# 55 | # 背景 + 斑马线 = 2 56 | #---------------------------------------------# 57 | NCLASSES = 2 58 | 59 | log_dir = "logs/" 60 | model = mobilenet_unet(n_classes=NCLASSES,input_height=HEIGHT, input_width=WIDTH) 61 | #---------------------------------------------------------------------# 62 | # 这一步是获得主干特征提取网络的权重、使用的是迁移学习的思想 63 | # 如果下载过慢,可以复制连接到迅雷进行下载。 64 | # 之后将权值复制到目录下,根据路径进行载入。 65 | # 如: 66 | # weights_path = "xxxxx.h5" 67 | # model.load_weights(weights_path,by_name=True,skip_mismatch=True) 68 | #---------------------------------------------------------------------# 69 | BASE_WEIGHT_PATH = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.6/' 70 | model_name = 'mobilenet_%s_%d_tf_no_top.h5' % ('1_0', 224) 71 | weight_path = BASE_WEIGHT_PATH + model_name 72 | weights_path = keras.utils.get_file(model_name, weight_path) 73 | model.load_weights(weights_path, by_name=True, skip_mismatch=True) 74 | 75 | # 打开数据集的txt 76 | with open("./dataset2/train.txt","r") as f: 77 | lines = f.readlines() 78 | 79 | #---------------------------------------------# 80 | # 打乱的数据更有利于训练 81 | # 90%用于训练,10%用于估计。 82 | #---------------------------------------------# 83 | np.random.seed(10101) 84 | np.random.shuffle(lines) 85 | np.random.seed(None) 86 | num_val = int(len(lines)*0.1) 87 | num_train = len(lines) - num_val 88 | 89 | #-------------------------------------------------------------------------------# 90 | # 训练参数的设置 91 | # checkpoint用于设置权值保存的细节,period用于修改多少epoch保存一次 92 | # reduce_lr用于设置学习率下降的方式 93 | # early_stopping用于设定早停,val_loss多次不下降自动结束训练,表示模型基本收敛 94 | #-------------------------------------------------------------------------------# 95 | checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5', 96 | monitor='val_loss', save_weights_only=True, save_best_only=False, period=2) 97 | reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1) 98 | early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1) 99 | 100 | #-------------------------------------------------------------------------------# 101 | # 这里使用的是迁移学习的思想,主干部分提取出来的特征是通用的 102 | # 所以我们可以不训练主干部分先,因此训练部分分为两步,分别是冻结训练和解冻训练 103 | # 冻结训练是不训练主干的,解冻训练是训练主干的。 104 | # 由于训练的特征层变多,解冻后所需显存变大 105 | #-------------------------------------------------------------------------------# 106 | trainable_layer = 60 107 | for i in range(trainable_layer): 108 | model.layers[i].trainable = False 109 | print('freeze the first {} layers of total {} layers.'.format(trainable_layer, len(model.layers))) 110 | 111 | if True: 112 | lr = 1e-3 113 | batch_size = 4 114 | model.compile(loss = 'categorical_crossentropy', 115 | optimizer = Adam(lr=lr), 116 | metrics = ['accuracy']) 117 | 118 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 119 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 120 | steps_per_epoch=max(1, num_train//batch_size), 121 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 122 | validation_steps=max(1, num_val//batch_size), 123 | epochs=50, 124 | initial_epoch=0, 125 | callbacks=[checkpoint, reduce_lr,early_stopping]) 126 | 127 | for i in range(len(model.layers)): 128 | model.layers[i].trainable = True 129 | 130 | if True: 131 | lr = 1e-4 132 | batch_size = 4 133 | model.compile(loss = 'categorical_crossentropy', 134 | optimizer = Adam(lr=lr), 135 | metrics = ['accuracy']) 136 | 137 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 138 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 139 | steps_per_epoch=max(1, num_train//batch_size), 140 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 141 | validation_steps=max(1, num_val//batch_size), 142 | epochs=100, 143 | initial_epoch=50, 144 | callbacks=[checkpoint, reduce_lr,early_stopping]) -------------------------------------------------------------------------------- /deeplab_Mobile/dataset2/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放dataset内的训练数据。 2 | This part is used to store the training data in the dataset. -------------------------------------------------------------------------------- /deeplab_Mobile/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img/timg.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/img/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img/timg2.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/img/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img/timg3.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/img/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img/timg4.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/img/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img/timg5.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/img_out/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img_out/timg.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/img_out/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img_out/timg2.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/img_out/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img_out/timg3.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/img_out/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img_out/timg4.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/img_out/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/img_out/timg5.jpg -------------------------------------------------------------------------------- /deeplab_Mobile/logs/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放训练后的文件。 2 | This part is used to store post training documents. -------------------------------------------------------------------------------- /deeplab_Mobile/nets/__pycache__/Xception.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/nets/__pycache__/Xception.cpython-36.pyc -------------------------------------------------------------------------------- /deeplab_Mobile/nets/__pycache__/deeplab.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/nets/__pycache__/deeplab.cpython-36.pyc -------------------------------------------------------------------------------- /deeplab_Mobile/nets/__pycache__/mobilenetV2.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Mobile/nets/__pycache__/mobilenetV2.cpython-36.pyc -------------------------------------------------------------------------------- /deeplab_Mobile/nets/deeplab.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from keras import backend as K 3 | from keras import layers 4 | from keras.activations import relu 5 | from keras.applications.imagenet_utils import preprocess_input 6 | from keras.layers import (Activation, Add, BatchNormalization, Concatenate, 7 | Conv2D, DepthwiseConv2D, Dropout, 8 | GlobalAveragePooling2D, Input, Lambda, Reshape, 9 | Softmax, ZeroPadding2D) 10 | from keras.models import Model 11 | from keras.utils.data_utils import get_file 12 | 13 | from nets.mobilenetV2 import mobilenetV2 14 | 15 | 16 | def SepConv_BN(x, filters, prefix, stride=1, kernel_size=3, rate=1, depth_activation=False, epsilon=1e-3): 17 | if stride == 1: 18 | depth_padding = 'same' 19 | else: 20 | kernel_size_effective = kernel_size + (kernel_size - 1) * (rate - 1) 21 | pad_total = kernel_size_effective - 1 22 | pad_beg = pad_total // 2 23 | pad_end = pad_total - pad_beg 24 | x = ZeroPadding2D((pad_beg, pad_end))(x) 25 | depth_padding = 'valid' 26 | 27 | if not depth_activation: 28 | x = Activation('relu')(x) 29 | 30 | # 首先使用3x3的深度可分离卷积 31 | x = DepthwiseConv2D((kernel_size, kernel_size), strides=(stride, stride), dilation_rate=(rate, rate), 32 | padding=depth_padding, use_bias=False, name=prefix + '_depthwise')(x) 33 | x = BatchNormalization(name=prefix + '_depthwise_BN', epsilon=epsilon)(x) 34 | if depth_activation: 35 | x = Activation('relu')(x) 36 | 37 | # 利用1x1卷积进行通道数调整 38 | x = Conv2D(filters, (1, 1), padding='same', use_bias=False, name=prefix + '_pointwise')(x) 39 | x = BatchNormalization(name=prefix + '_pointwise_BN', epsilon=epsilon)(x) 40 | if depth_activation: 41 | x = Activation('relu')(x) 42 | 43 | return x 44 | 45 | def Deeplabv3(input_shape=(416, 416, 3), classes=21, alpha=1.): 46 | img_input = Input(shape=input_shape) 47 | 48 | # x 52, 52, 320 49 | # skip1 104, 104, 24 50 | x, skip1 = mobilenetV2(img_input, alpha) 51 | size_before = tf.keras.backend.int_shape(x) 52 | 53 | #---------------------------------------------------------------# 54 | # 全部求平均后,再利用expand_dims扩充维度 55 | # 52,52,320 -> 1,1,320 -> 1,1,320 56 | #---------------------------------------------------------------# 57 | b4 = GlobalAveragePooling2D()(x) 58 | b4 = Lambda(lambda x: K.expand_dims(x, 1))(b4) 59 | b4 = Lambda(lambda x: K.expand_dims(x, 1))(b4) 60 | b4 = Conv2D(256, (1, 1), padding='same', use_bias=False, name='image_pooling')(b4) 61 | b4 = BatchNormalization(name='image_pooling_BN', epsilon=1e-5)(b4) 62 | b4 = Activation('relu')(b4) 63 | # 1,1,256 -> 52,52,256 64 | b4 = Lambda(lambda x: tf.image.resize_images(x, size_before[1:3]))(b4) 65 | 66 | #---------------------------------------------------------------# 67 | # 调整通道 68 | #---------------------------------------------------------------# 69 | b0 = Conv2D(256, (1, 1), padding='same', use_bias=False, name='aspp0')(x) 70 | b0 = BatchNormalization(name='aspp0_BN', epsilon=1e-5)(b0) 71 | b0 = Activation('relu', name='aspp0_activation')(b0) 72 | 73 | #---------------------------------------------------------------# 74 | # rate值与OS相关,SepConv_BN为先3x3膨胀卷积,再1x1卷积,进行压缩 75 | # 其膨胀率就是rate值 76 | #---------------------------------------------------------------# 77 | b1 = SepConv_BN(x, 256, 'aspp1', rate=6, depth_activation=True, epsilon=1e-5) 78 | b2 = SepConv_BN(x, 256, 'aspp2', rate=12, depth_activation=True, epsilon=1e-5) 79 | b3 = SepConv_BN(x, 256, 'aspp3', rate=18, depth_activation=True, epsilon=1e-5) 80 | 81 | # 52, 52, 256 + 52, 52, 256 + 52, 52, 256 + 52, 52, 256 + 52, 52, 256 -> 52, 52, 1280 82 | x = Concatenate()([b4, b0, b1, b2, b3]) 83 | 84 | # 利用1x1卷积调整通道数 85 | # 52, 52, 1280 -> 52,52,256 86 | x = Conv2D(256, (1, 1), padding='same', use_bias=False, name='concat_projection')(x) 87 | x = BatchNormalization(name='concat_projection_BN', epsilon=1e-5)(x) 88 | x = Activation('relu')(x) 89 | x = Dropout(0.1)(x) 90 | 91 | # skip1.shape[1:3] 为 104,104 92 | # 52,52,256 -> 104,104,256 93 | x = Lambda(lambda xx: tf.image.resize_images(x, skip1.shape[1:3]))(x) 94 | 95 | # 104,104,24 -> 104,104,48 96 | dec_skip1 = Conv2D(48, (1, 1), padding='same', use_bias=False, name='feature_projection0')(skip1) 97 | dec_skip1 = BatchNormalization(name='feature_projection0_BN', epsilon=1e-5)(dec_skip1) 98 | dec_skip1 = Activation('relu')(dec_skip1) 99 | 100 | # 104,104,256 + 104,104,48 -> 104,104,304 101 | x = Concatenate()([x, dec_skip1]) 102 | # 104,104,304 -> 104,104,256 -> 104,104,256 103 | x = SepConv_BN(x, 256, 'decoder_conv0', depth_activation=True, epsilon=1e-5) 104 | x = SepConv_BN(x, 256, 'decoder_conv1', depth_activation=True, epsilon=1e-5) 105 | 106 | # 104,104,256 -> 104,104,2 -> 416,416,2 107 | size_before3 = tf.keras.backend.int_shape(img_input) 108 | x = Conv2D(classes, (1, 1), padding='same')(x) 109 | x = Lambda(lambda xx:tf.image.resize_images(xx, size_before3[1:3]))(x) 110 | 111 | x = Reshape((-1,classes))(x) 112 | x = Softmax()(x) 113 | 114 | model = Model(img_input, x, name='deeplabv3plus') 115 | return model 116 | 117 | -------------------------------------------------------------------------------- /deeplab_Mobile/nets/mobilenetV2.py: -------------------------------------------------------------------------------- 1 | from keras import layers 2 | from keras.activations import relu 3 | from keras.layers import (Activation, Add, BatchNormalization, Concatenate, 4 | Conv2D, DepthwiseConv2D, Dropout, 5 | GlobalAveragePooling2D, Input, Lambda, ZeroPadding2D) 6 | from keras.models import Model 7 | 8 | 9 | def _make_divisible(v, divisor, min_value=None): 10 | if min_value is None: 11 | min_value = divisor 12 | new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) 13 | if new_v < 0.9 * v: 14 | new_v += divisor 15 | return new_v 16 | 17 | def relu6(x): 18 | return relu(x, max_value=6) 19 | 20 | def _inverted_res_block(inputs, expansion, stride, alpha, filters, block_id, skip_connection, rate=1): 21 | in_channels = inputs.shape[-1].value # inputs._keras_shape[-1] 22 | pointwise_filters = _make_divisible(int(filters * alpha), 8) 23 | prefix = 'expanded_conv_{}_'.format(block_id) 24 | 25 | x = inputs 26 | # 利用1x1卷积进行通道数的扩张 27 | if block_id: 28 | x = Conv2D(expansion * in_channels, kernel_size=1, padding='same', 29 | use_bias=False, activation=None, 30 | name=prefix + 'expand')(x) 31 | x = BatchNormalization(epsilon=1e-3, momentum=0.999, 32 | name=prefix + 'expand_BN')(x) 33 | x = Activation(relu6, name=prefix + 'expand_relu')(x) 34 | else: 35 | prefix = 'expanded_conv_' 36 | 37 | # 利用3x3深度可分离卷积提取特征 38 | x = DepthwiseConv2D(kernel_size=3, strides=stride, activation=None, 39 | use_bias=False, padding='same', dilation_rate=(rate, rate), 40 | name=prefix + 'depthwise')(x) 41 | x = BatchNormalization(epsilon=1e-3, momentum=0.999, 42 | name=prefix + 'depthwise_BN')(x) 43 | 44 | x = Activation(relu6, name=prefix + 'depthwise_relu')(x) 45 | 46 | # 利用1x1卷积进行通道数的下降 47 | x = Conv2D(pointwise_filters, 48 | kernel_size=1, padding='same', use_bias=False, activation=None, 49 | name=prefix + 'project')(x) 50 | x = BatchNormalization(epsilon=1e-3, momentum=0.999, 51 | name=prefix + 'project_BN')(x) 52 | 53 | if skip_connection: 54 | return Add(name=prefix + 'add')([inputs, x]) 55 | 56 | return x 57 | 58 | def mobilenetV2(inputs,alpha=1): 59 | first_block_filters = _make_divisible(32 * alpha, 8) 60 | # 416,416,3 -> 208,208,32 61 | x = Conv2D(first_block_filters, 62 | kernel_size=3, 63 | strides=(2, 2), padding='same', 64 | use_bias=False, name='Conv')(inputs) 65 | x = BatchNormalization(epsilon=1e-3, momentum=0.999, name='Conv_BN')(x) 66 | x = Activation(relu6, name='Conv_Relu6')(x) 67 | 68 | 69 | # 208,208,32 -> 208,208,16 70 | x = _inverted_res_block(x, filters=16, alpha=alpha, stride=1, 71 | expansion=1, block_id=0, skip_connection=False) 72 | 73 | # 208,208,16 -> 104,104,24 74 | x = _inverted_res_block(x, filters=24, alpha=alpha, stride=2, 75 | expansion=6, block_id=1, skip_connection=False) 76 | x = _inverted_res_block(x, filters=24, alpha=alpha, stride=1, 77 | expansion=6, block_id=2, skip_connection=True) 78 | skip1 = x 79 | 80 | # 104,104,24 -> 52,52,32 81 | x = _inverted_res_block(x, filters=32, alpha=alpha, stride=2, 82 | expansion=6, block_id=3, skip_connection=False) 83 | x = _inverted_res_block(x, filters=32, alpha=alpha, stride=1, 84 | expansion=6, block_id=4, skip_connection=True) 85 | x = _inverted_res_block(x, filters=32, alpha=alpha, stride=1, 86 | expansion=6, block_id=5, skip_connection=True) 87 | 88 | #---------------------------------------------------------------# 89 | # 52,52,32 -> 52,52,64 90 | x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1, 91 | expansion=6, block_id=6, skip_connection=False) 92 | x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1, rate=2, 93 | expansion=6, block_id=7, skip_connection=True) 94 | x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1, rate=2, 95 | expansion=6, block_id=8, skip_connection=True) 96 | x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1, rate=2, 97 | expansion=6, block_id=9, skip_connection=True) 98 | 99 | # 52,52,64 -> 52,52,96 100 | x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1, rate=2, 101 | expansion=6, block_id=10, skip_connection=False) 102 | x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1, rate=2, 103 | expansion=6, block_id=11, skip_connection=True) 104 | x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1, rate=2, 105 | expansion=6, block_id=12, skip_connection=True) 106 | 107 | # 52,52,96 -> 52,52,160 108 | x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1, rate=2, # 1! 109 | expansion=6, block_id=13, skip_connection=False) 110 | x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1, rate=4, 111 | expansion=6, block_id=14, skip_connection=True) 112 | x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1, rate=4, 113 | expansion=6, block_id=15, skip_connection=True) 114 | 115 | # 52,52,160 -> 52,52,320 116 | x = _inverted_res_block(x, filters=320, alpha=alpha, stride=1, rate=4, 117 | expansion=6, block_id=16, skip_connection=False) 118 | return x,skip1 119 | -------------------------------------------------------------------------------- /deeplab_Mobile/predict.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | import random 4 | 5 | import numpy as np 6 | from PIL import Image 7 | 8 | from nets.deeplab import Deeplabv3 9 | 10 | if __name__ == "__main__": 11 | #---------------------------------------------------# 12 | # 定义了输入图片的颜色,当我们想要去区分两类的时候 13 | # 我们定义了两个颜色,分别用于背景和斑马线 14 | # [0,0,0], [0,255,0]代表了颜色的RGB色彩 15 | #---------------------------------------------------# 16 | class_colors = [[0,0,0],[0,255,0]] 17 | #---------------------------------------------# 18 | # 定义输入图片的高和宽,以及种类数量 19 | #---------------------------------------------# 20 | HEIGHT = 416 21 | WIDTH = 416 22 | #---------------------------------------------# 23 | # 背景 + 斑马线 = 2 24 | #---------------------------------------------# 25 | NCLASSES = 2 26 | 27 | #---------------------------------------------# 28 | # 载入模型 29 | #---------------------------------------------# 30 | model = Deeplabv3(classes=NCLASSES,input_shape=(HEIGHT,WIDTH,3)) 31 | #--------------------------------------------------# 32 | # 载入权重,训练好的权重会保存在logs文件夹里面 33 | # 我们需要将对应的权重载入 34 | # 修改model_path,将其对应我们训练好的权重即可 35 | # 下面只是一个示例 36 | #--------------------------------------------------# 37 | model.load_weights("logs/ep026-loss0.006-val_loss0.028.h5") 38 | 39 | #--------------------------------------------------# 40 | # 对imgs文件夹进行一个遍历 41 | #--------------------------------------------------# 42 | imgs = os.listdir("./img/") 43 | for jpg in imgs: 44 | #--------------------------------------------------# 45 | # 打开imgs文件夹里面的每一个图片 46 | #--------------------------------------------------# 47 | img = Image.open("./img/"+jpg) 48 | 49 | old_img = copy.deepcopy(img) 50 | orininal_h = np.array(img).shape[0] 51 | orininal_w = np.array(img).shape[1] 52 | 53 | #--------------------------------------------------# 54 | # 对输入进来的每一个图片进行Resize 55 | # resize成[HEIGHT, WIDTH, 3] 56 | #--------------------------------------------------# 57 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 58 | img = np.array(img) / 255 59 | img = img.reshape(-1, HEIGHT, WIDTH, 3) 60 | 61 | #--------------------------------------------------# 62 | # 将图像输入到网络当中进行预测 63 | #--------------------------------------------------# 64 | pr = model.predict(img)[0] 65 | pr = pr.reshape((int(HEIGHT), int(WIDTH), NCLASSES)).argmax(axis=-1) 66 | 67 | #------------------------------------------------# 68 | # 创建一副新图,并根据每个像素点的种类赋予颜色 69 | #------------------------------------------------# 70 | seg_img = np.zeros((int(HEIGHT), int(WIDTH),3)) 71 | for c in range(NCLASSES): 72 | seg_img[:, :, 0] += ((pr[:,: ] == c) * class_colors[c][0]).astype('uint8') 73 | seg_img[:, :, 1] += ((pr[:,: ] == c) * class_colors[c][1]).astype('uint8') 74 | seg_img[:, :, 2] += ((pr[:,: ] == c) * class_colors[c][2]).astype('uint8') 75 | 76 | seg_img = Image.fromarray(np.uint8(seg_img)).resize((orininal_w,orininal_h)) 77 | #------------------------------------------------# 78 | # 将新图片和原图片混合 79 | #------------------------------------------------# 80 | image = Image.blend(old_img,seg_img,0.3) 81 | 82 | image.save("./img_out/"+jpg) 83 | 84 | 85 | -------------------------------------------------------------------------------- /deeplab_Mobile/test.py: -------------------------------------------------------------------------------- 1 | #---------------------------------------------# 2 | # 该部分用于查看网络结构 3 | #---------------------------------------------# 4 | from nets.deeplab import Deeplabv3 5 | 6 | if __name__ == "__main__": 7 | deeplab_model = Deeplabv3() 8 | deeplab_model.summary() 9 | 10 | for i in range(len(deeplab_model.layers)): 11 | print(i,deeplab_model.layers[i].name) 12 | -------------------------------------------------------------------------------- /deeplab_Mobile/train.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import keras 4 | import numpy as np 5 | from keras import backend as K 6 | from keras.callbacks import (EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, 7 | TensorBoard) 8 | from keras.optimizers import Adam 9 | from keras.utils.data_utils import get_file 10 | from PIL import Image 11 | 12 | from nets.deeplab import Deeplabv3 13 | 14 | 15 | #-------------------------------------------------------------# 16 | # 定义了一个生成器,用于读取datasets2文件夹里面的图片与标签 17 | #-------------------------------------------------------------# 18 | def generate_arrays_from_file(lines,batch_size): 19 | n = len(lines) 20 | i = 0 21 | while 1: 22 | X_train = [] 23 | Y_train = [] 24 | for _ in range(batch_size): 25 | if i==0: 26 | np.random.shuffle(lines) 27 | #-------------------------------------# 28 | # 读取输入图片并进行归一化和resize 29 | #-------------------------------------# 30 | name = lines[i].split(';')[0] 31 | img = Image.open("./dataset2/jpg/" + name) 32 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 33 | img = np.array(img)/255 34 | X_train.append(img) 35 | 36 | #-------------------------------------# 37 | # 读取标签图片并进行resize 38 | #-------------------------------------# 39 | name = lines[i].split(';')[1].split()[0] 40 | label = Image.open("./dataset2/png/" + name) 41 | label = label.resize((int(WIDTH),int(HEIGHT)), Image.NEAREST) 42 | if len(np.shape(label)) == 3: 43 | label = np.array(label)[:,:,0] 44 | label = np.reshape(np.array(label), [-1]) 45 | one_hot_label = np.eye(NCLASSES)[np.array(label, np.int32)] 46 | Y_train.append(one_hot_label) 47 | 48 | i = (i+1) % n 49 | yield (np.array(X_train), np.array(Y_train)) 50 | 51 | if __name__ == "__main__": 52 | #---------------------------------------------# 53 | # 定义输入图片的高和宽,以及种类数量 54 | #---------------------------------------------# 55 | HEIGHT = 416 56 | WIDTH = 416 57 | #---------------------------------------------# 58 | # 背景 + 斑马线 = 2 59 | #---------------------------------------------# 60 | NCLASSES = 2 61 | 62 | log_dir = "logs/" 63 | model = Deeplabv3(classes=NCLASSES,input_shape=(HEIGHT,WIDTH,3)) 64 | #---------------------------------------------------------------------# 65 | # 这一步是获得主干特征提取网络的权重、使用的是迁移学习的思想 66 | # 如果下载过慢,可以复制连接到迅雷进行下载。 67 | # 之后将权值复制到目录下,根据路径进行载入。 68 | # 如: 69 | # weights_path = "xxxxx.h5" 70 | # model.load_weights(weights_path,by_name=True,skip_mismatch=True) 71 | #---------------------------------------------------------------------# 72 | WEIGHTS_PATH_MOBILE = "https://github.com/bonlime/keras-deeplab-v3-plus/releases/download/1.1/deeplabv3_mobilenetv2_tf_dim_ordering_tf_kernels.h5" 73 | weights_path = get_file('deeplabv3_mobilenetv2_tf_dim_ordering_tf_kernels.h5', WEIGHTS_PATH_MOBILE, cache_subdir='models') 74 | model.load_weights(weights_path,by_name=True,skip_mismatch=True) 75 | 76 | # 打开数据集的txt 77 | with open("./dataset2/train.txt","r") as f: 78 | lines = f.readlines() 79 | 80 | #---------------------------------------------# 81 | # 打乱的数据更有利于训练 82 | # 90%用于训练,10%用于估计。 83 | #---------------------------------------------# 84 | np.random.seed(10101) 85 | np.random.shuffle(lines) 86 | np.random.seed(None) 87 | num_val = int(len(lines)*0.1) 88 | num_train = len(lines) - num_val 89 | 90 | #-------------------------------------------------------------------------------# 91 | # 训练参数的设置 92 | # checkpoint用于设置权值保存的细节,period用于修改多少epoch保存一次 93 | # reduce_lr用于设置学习率下降的方式 94 | # early_stopping用于设定早停,val_loss多次不下降自动结束训练,表示模型基本收敛 95 | #-------------------------------------------------------------------------------# 96 | checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5', 97 | monitor='val_loss', save_weights_only=True, save_best_only=False, period=2) 98 | reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1) 99 | early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1) 100 | 101 | #-------------------------------------------------------------------------------# 102 | # 这里使用的是迁移学习的思想,主干部分提取出来的特征是通用的 103 | # 所以我们可以不训练主干部分先,因此训练部分分为两步,分别是冻结训练和解冻训练 104 | # 冻结训练是不训练主干的,解冻训练是训练主干的。 105 | # 由于训练的特征层变多,解冻后所需显存变大 106 | #-------------------------------------------------------------------------------# 107 | trainable_layer = 130 108 | for i in range(trainable_layer): 109 | model.layers[i].trainable = False 110 | print('freeze the first {} layers of total {} layers.'.format(trainable_layer, len(model.layers))) 111 | 112 | if True: 113 | lr = 1e-3 114 | batch_size = 4 115 | model.compile(loss = 'categorical_crossentropy', 116 | optimizer = Adam(lr=lr), 117 | metrics = ['accuracy']) 118 | 119 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 120 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 121 | steps_per_epoch=max(1, num_train//batch_size), 122 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 123 | validation_steps=max(1, num_val//batch_size), 124 | epochs=50, 125 | initial_epoch=0, 126 | callbacks=[checkpoint, reduce_lr,early_stopping]) 127 | model.save_weights(log_dir+'last1.h5') 128 | 129 | for i in range(len(model.layers)): 130 | model.layers[i].trainable = True 131 | 132 | if True: 133 | lr = 1e-4 134 | batch_size = 4 135 | model.compile(loss = 'categorical_crossentropy', 136 | optimizer = Adam(lr=lr), 137 | metrics = ['accuracy']) 138 | 139 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 140 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 141 | steps_per_epoch=max(1, num_train//batch_size), 142 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 143 | validation_steps=max(1, num_val//batch_size), 144 | epochs=100, 145 | initial_epoch=50, 146 | callbacks=[checkpoint, reduce_lr,early_stopping]) 147 | model.save_weights(log_dir+'last1.h5') 148 | -------------------------------------------------------------------------------- /deeplab_Xception/dataset2/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放dataset内的训练数据。 2 | This part is used to store the training data in the dataset. -------------------------------------------------------------------------------- /deeplab_Xception/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img/timg.jpg -------------------------------------------------------------------------------- /deeplab_Xception/img/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img/timg2.jpg -------------------------------------------------------------------------------- /deeplab_Xception/img/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img/timg3.jpg -------------------------------------------------------------------------------- /deeplab_Xception/img/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img/timg4.jpg -------------------------------------------------------------------------------- /deeplab_Xception/img/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img/timg5.jpg -------------------------------------------------------------------------------- /deeplab_Xception/img_out/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img_out/timg.jpg -------------------------------------------------------------------------------- /deeplab_Xception/img_out/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img_out/timg2.jpg -------------------------------------------------------------------------------- /deeplab_Xception/img_out/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img_out/timg3.jpg -------------------------------------------------------------------------------- /deeplab_Xception/img_out/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img_out/timg4.jpg -------------------------------------------------------------------------------- /deeplab_Xception/img_out/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/img_out/timg5.jpg -------------------------------------------------------------------------------- /deeplab_Xception/logs/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放训练后的文件。 2 | This part is used to store post training documents. -------------------------------------------------------------------------------- /deeplab_Xception/nets/Xception.py: -------------------------------------------------------------------------------- 1 | from keras import layers 2 | from keras.layers import (Activation, Add, BatchNormalization, Concatenate, 3 | Conv2D, DepthwiseConv2D, Dropout, 4 | GlobalAveragePooling2D, Input, Lambda, ZeroPadding2D) 5 | from keras.models import Model 6 | 7 | 8 | def _conv2d_same(x, filters, prefix, stride=1, kernel_size=3, rate=1): 9 | # 计算padding的数量,hw是否需要收缩 10 | if stride == 1: 11 | return Conv2D(filters, 12 | (kernel_size, kernel_size), 13 | strides=(stride, stride), 14 | padding='same', use_bias=False, 15 | dilation_rate=(rate, rate), 16 | name=prefix)(x) 17 | else: 18 | kernel_size_effective = kernel_size + (kernel_size - 1) * (rate - 1) 19 | pad_total = kernel_size_effective - 1 20 | pad_beg = pad_total // 2 21 | pad_end = pad_total - pad_beg 22 | x = ZeroPadding2D((pad_beg, pad_end))(x) 23 | return Conv2D(filters, 24 | (kernel_size, kernel_size), 25 | strides=(stride, stride), 26 | padding='valid', use_bias=False, 27 | dilation_rate=(rate, rate), 28 | name=prefix)(x) 29 | 30 | def SepConv_BN(x, filters, prefix, stride=1, kernel_size=3, rate=1, depth_activation=False, epsilon=1e-3): 31 | # 计算padding的数量,hw是否需要收缩 32 | if stride == 1: 33 | depth_padding = 'same' 34 | else: 35 | kernel_size_effective = kernel_size + (kernel_size - 1) * (rate - 1) 36 | pad_total = kernel_size_effective - 1 37 | pad_beg = pad_total // 2 38 | pad_end = pad_total - pad_beg 39 | x = ZeroPadding2D((pad_beg, pad_end))(x) 40 | depth_padding = 'valid' 41 | 42 | # 如果需要激活函数 43 | if not depth_activation: 44 | x = Activation('relu')(x) 45 | 46 | # 分离卷积,首先3x3分离卷积,再1x1卷积 47 | # 3x3采用膨胀卷积 48 | x = DepthwiseConv2D((kernel_size, kernel_size), strides=(stride, stride), dilation_rate=(rate, rate), 49 | padding=depth_padding, use_bias=False, name=prefix + '_depthwise')(x) 50 | x = BatchNormalization(name=prefix + '_depthwise_BN', epsilon=epsilon)(x) 51 | if depth_activation: 52 | x = Activation('relu')(x) 53 | 54 | # 1x1卷积,进行压缩 55 | x = Conv2D(filters, (1, 1), padding='same', 56 | use_bias=False, name=prefix + '_pointwise')(x) 57 | x = BatchNormalization(name=prefix + '_pointwise_BN', epsilon=epsilon)(x) 58 | if depth_activation: 59 | x = Activation('relu')(x) 60 | 61 | return x 62 | 63 | def _xception_block(inputs, depth_list, prefix, skip_connection_type, stride, 64 | rate=1, depth_activation=False, return_skip=False): 65 | 66 | residual = inputs 67 | for i in range(3): 68 | residual = SepConv_BN(residual, 69 | depth_list[i], 70 | prefix + '_separable_conv{}'.format(i + 1), 71 | stride=stride if i == 2 else 1, 72 | rate=rate, 73 | depth_activation=depth_activation) 74 | if i == 1: 75 | skip = residual 76 | if skip_connection_type == 'conv': 77 | shortcut = _conv2d_same(inputs, depth_list[-1], prefix + '_shortcut', 78 | kernel_size=1, 79 | stride=stride) 80 | shortcut = BatchNormalization(name=prefix + '_shortcut_BN')(shortcut) 81 | outputs = layers.add([residual, shortcut]) 82 | elif skip_connection_type == 'sum': 83 | outputs = layers.add([residual, inputs]) 84 | elif skip_connection_type == 'none': 85 | outputs = residual 86 | if return_skip: 87 | return outputs, skip 88 | else: 89 | return outputs 90 | 91 | def Xception(inputs,alpha=1,OS=16): 92 | if OS == 8: 93 | entry_block3_stride = 1 94 | middle_block_rate = 2 # ! Not mentioned in paper, but required 95 | exit_block_rates = (2, 4) 96 | atrous_rates = (12, 24, 36) 97 | else: 98 | entry_block3_stride = 2 99 | middle_block_rate = 1 100 | exit_block_rates = (1, 2) 101 | atrous_rates = (6, 12, 18) 102 | 103 | # 512,512,3 -> 256,256,32 104 | x = Conv2D(32, (3, 3), strides=(2, 2), 105 | name='entry_flow_conv1_1', use_bias=False, padding='same')(inputs) 106 | x = BatchNormalization(name='entry_flow_conv1_1_BN')(x) 107 | x = Activation('relu')(x) 108 | 109 | # 256,256,32 -> 256,256,64 110 | x = _conv2d_same(x, 64, 'entry_flow_conv1_2', kernel_size=3, stride=1) 111 | x = BatchNormalization(name='entry_flow_conv1_2_BN')(x) 112 | x = Activation('relu')(x) 113 | 114 | # 256,256,64 -> 256,256,128 -> 256,256,128 -> 128,128,128 115 | x = _xception_block(x, [128, 128, 128], 'entry_flow_block1', 116 | skip_connection_type='conv', stride=2, 117 | depth_activation=False) 118 | 119 | # 128,128,128 -> 128,128,256 -> 128,128,256 -> 64,64,256 120 | # skip = 128,128,256 121 | x, skip1 = _xception_block(x, [256, 256, 256], 'entry_flow_block2', 122 | skip_connection_type='conv', stride=2, 123 | depth_activation=False, return_skip=True) 124 | 125 | x = _xception_block(x, [728, 728, 728], 'entry_flow_block3', 126 | skip_connection_type='conv', stride=entry_block3_stride, 127 | depth_activation=False) 128 | for i in range(16): 129 | x = _xception_block(x, [728, 728, 728], 'middle_flow_unit_{}'.format(i + 1), 130 | skip_connection_type='sum', stride=1, rate=middle_block_rate, 131 | depth_activation=False) 132 | 133 | x = _xception_block(x, [728, 1024, 1024], 'exit_flow_block1', 134 | skip_connection_type='conv', stride=1, rate=exit_block_rates[0], 135 | depth_activation=False) 136 | x = _xception_block(x, [1536, 1536, 2048], 'exit_flow_block2', 137 | skip_connection_type='none', stride=1, rate=exit_block_rates[1], 138 | depth_activation=True) 139 | return x,atrous_rates,skip1 -------------------------------------------------------------------------------- /deeplab_Xception/nets/__pycache__/Xception.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/nets/__pycache__/Xception.cpython-36.pyc -------------------------------------------------------------------------------- /deeplab_Xception/nets/__pycache__/deeplab.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/nets/__pycache__/deeplab.cpython-36.pyc -------------------------------------------------------------------------------- /deeplab_Xception/nets/__pycache__/mobilenetV2.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/deeplab_Xception/nets/__pycache__/mobilenetV2.cpython-36.pyc -------------------------------------------------------------------------------- /deeplab_Xception/nets/deeplab.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from keras import backend as K 3 | from keras import layers 4 | from keras.activations import relu 5 | from keras.applications.imagenet_utils import preprocess_input 6 | from keras.layers import (Activation, Add, BatchNormalization, Concatenate, 7 | Conv2D, DepthwiseConv2D, Dropout, 8 | GlobalAveragePooling2D, Input, Lambda, Reshape, 9 | Softmax, ZeroPadding2D) 10 | from keras.models import Model 11 | from keras.utils.data_utils import get_file 12 | 13 | from nets.Xception import Xception 14 | 15 | 16 | def SepConv_BN(x, filters, prefix, stride=1, kernel_size=3, rate=1, depth_activation=False, epsilon=1e-3): 17 | if stride == 1: 18 | depth_padding = 'same' 19 | else: 20 | kernel_size_effective = kernel_size + (kernel_size - 1) * (rate - 1) 21 | pad_total = kernel_size_effective - 1 22 | pad_beg = pad_total // 2 23 | pad_end = pad_total - pad_beg 24 | x = ZeroPadding2D((pad_beg, pad_end))(x) 25 | depth_padding = 'valid' 26 | 27 | if not depth_activation: 28 | x = Activation('relu')(x) 29 | 30 | # 首先使用3x3的深度可分离卷积 31 | x = DepthwiseConv2D((kernel_size, kernel_size), strides=(stride, stride), dilation_rate=(rate, rate), 32 | padding=depth_padding, use_bias=False, name=prefix + '_depthwise')(x) 33 | x = BatchNormalization(name=prefix + '_depthwise_BN', epsilon=epsilon)(x) 34 | if depth_activation: 35 | x = Activation('relu')(x) 36 | 37 | # 利用1x1卷积进行通道数调整 38 | x = Conv2D(filters, (1, 1), padding='same', use_bias=False, name=prefix + '_pointwise')(x) 39 | x = BatchNormalization(name=prefix + '_pointwise_BN', epsilon=epsilon)(x) 40 | if depth_activation: 41 | x = Activation('relu')(x) 42 | 43 | return x 44 | 45 | def Deeplabv3(input_shape=(512, 512, 3), classes=21, alpha=1.,OS=16): 46 | 47 | img_input = Input(shape=input_shape) 48 | 49 | # x 64, 64, 2048 50 | # skip1 128, 128, 256 51 | x, atrous_rates, skip1 = Xception(img_input,alpha,OS=OS) 52 | size_before = tf.keras.backend.int_shape(x) 53 | 54 | #---------------------------------------------------------------# 55 | # 全部求平均后,再利用expand_dims扩充维度 56 | # 64,64,2048 -> 1,1,2048 -> 1,1,2048 57 | #---------------------------------------------------------------# 58 | b4 = GlobalAveragePooling2D()(x) 59 | b4 = Lambda(lambda x: K.expand_dims(x, 1))(b4) 60 | b4 = Lambda(lambda x: K.expand_dims(x, 1))(b4) 61 | b4 = Conv2D(256, (1, 1), padding='same', use_bias=False, name='image_pooling')(b4) 62 | b4 = BatchNormalization(name='image_pooling_BN', epsilon=1e-5)(b4) 63 | b4 = Activation('relu')(b4) 64 | # 1,1,256 -> 64,64,256 65 | b4 = Lambda(lambda x: tf.image.resize_images(x, size_before[1:3]))(b4) 66 | 67 | #---------------------------------------------------------------# 68 | # 调整通道 69 | #---------------------------------------------------------------# 70 | b0 = Conv2D(256, (1, 1), padding='same', use_bias=False, name='aspp0')(x) 71 | b0 = BatchNormalization(name='aspp0_BN', epsilon=1e-5)(b0) 72 | b0 = Activation('relu', name='aspp0_activation')(b0) 73 | 74 | #---------------------------------------------------------------# 75 | # rate值与OS相关,SepConv_BN为先3x3膨胀卷积,再1x1卷积,进行压缩 76 | # 其膨胀率就是rate值 77 | #---------------------------------------------------------------# 78 | b1 = SepConv_BN(x, 256, 'aspp1', rate=atrous_rates[0], depth_activation=True, epsilon=1e-5) 79 | b2 = SepConv_BN(x, 256, 'aspp2', rate=atrous_rates[1], depth_activation=True, epsilon=1e-5) 80 | b3 = SepConv_BN(x, 256, 'aspp3', rate=atrous_rates[2], depth_activation=True, epsilon=1e-5) 81 | 82 | # 64, 64, 256 + 64, 64, 256 + 64, 64, 256 + 64, 64, 256 + 64, 64, 256 -> 64, 64, 1280 83 | x = Concatenate()([b4, b0, b1, b2, b3]) 84 | 85 | # 利用1x1卷积调整通道数 86 | # 64, 64, 1280 -> 64,64,256 87 | x = Conv2D(256, (1, 1), padding='same', 88 | use_bias=False, name='concat_projection')(x) 89 | x = BatchNormalization(name='concat_projection_BN', epsilon=1e-5)(x) 90 | x = Activation('relu')(x) 91 | x = Dropout(0.1)(x) 92 | 93 | # skip1.shape[1:3] 为 128,128 94 | # 64,64,256 -> 128,128,256 95 | x = Lambda(lambda xx: tf.image.resize_images(x, skip1.shape[1:3]))(x) 96 | 97 | # 128,128,24 -> 128,128,48 98 | dec_skip1 = Conv2D(48, (1, 1), padding='same',use_bias=False, name='feature_projection0')(skip1) 99 | dec_skip1 = BatchNormalization(name='feature_projection0_BN', epsilon=1e-5)(dec_skip1) 100 | dec_skip1 = Activation('relu')(dec_skip1) 101 | 102 | # 128,128,256 + 128,128,48 -> 128,128,304 103 | x = Concatenate()([x, dec_skip1]) 104 | # 128,128,304 -> 128,128,256 -> 128,128,256 105 | x = SepConv_BN(x, 256, 'decoder_conv0', 106 | depth_activation=True, epsilon=1e-5) 107 | x = SepConv_BN(x, 256, 'decoder_conv1', 108 | depth_activation=True, epsilon=1e-5) 109 | 110 | # 128,128,256 -> 128,128,2 -> 512,512,2 111 | x = Conv2D(classes, (1, 1), padding='same')(x) 112 | 113 | size_before3 = tf.keras.backend.int_shape(img_input) 114 | x = Lambda(lambda xx:tf.image.resize_images(xx,size_before3[1:3]))(x) 115 | 116 | x = Reshape((-1,classes))(x) 117 | x = Softmax()(x) 118 | 119 | inputs = img_input 120 | model = Model(inputs, x, name='deeplabv3plus') 121 | 122 | return model 123 | 124 | -------------------------------------------------------------------------------- /deeplab_Xception/predict.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | import random 4 | 5 | import numpy as np 6 | from PIL import Image 7 | 8 | from nets.deeplab import Deeplabv3 9 | 10 | if __name__ == "__main__": 11 | #---------------------------------------------------# 12 | # 定义了输入图片的颜色,当我们想要去区分两类的时候 13 | # 我们定义了两个颜色,分别用于背景和斑马线 14 | # [0,0,0], [0,255,0]代表了颜色的RGB色彩 15 | #---------------------------------------------------# 16 | class_colors = [[0,0,0],[0,255,0]] 17 | #---------------------------------------------# 18 | # 定义输入图片的高和宽,以及种类数量 19 | #---------------------------------------------# 20 | HEIGHT = 416 21 | WIDTH = 416 22 | #---------------------------------------------# 23 | # 背景 + 斑马线 = 2 24 | #---------------------------------------------# 25 | NCLASSES = 2 26 | 27 | #---------------------------------------------# 28 | # 载入模型 29 | #---------------------------------------------# 30 | model = Deeplabv3(classes=NCLASSES,input_shape=(HEIGHT,WIDTH,3)) 31 | #--------------------------------------------------# 32 | # 载入权重,训练好的权重会保存在logs文件夹里面 33 | # 我们需要将对应的权重载入 34 | # 修改model_path,将其对应我们训练好的权重即可 35 | # 下面只是一个示例 36 | #--------------------------------------------------# 37 | model.load_weights("logs/ep018-loss0.010-val_loss0.044.h5") 38 | 39 | #--------------------------------------------------# 40 | # 对imgs文件夹进行一个遍历 41 | #--------------------------------------------------# 42 | imgs = os.listdir("./img/") 43 | for jpg in imgs: 44 | #--------------------------------------------------# 45 | # 打开imgs文件夹里面的每一个图片 46 | #--------------------------------------------------# 47 | img = Image.open("./img/"+jpg) 48 | 49 | old_img = copy.deepcopy(img) 50 | orininal_h = np.array(img).shape[0] 51 | orininal_w = np.array(img).shape[1] 52 | 53 | #--------------------------------------------------# 54 | # 对输入进来的每一个图片进行Resize 55 | # resize成[HEIGHT, WIDTH, 3] 56 | #--------------------------------------------------# 57 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 58 | img = np.array(img) / 255 59 | img = img.reshape(-1, HEIGHT, WIDTH, 3) 60 | 61 | #--------------------------------------------------# 62 | # 将图像输入到网络当中进行预测 63 | #--------------------------------------------------# 64 | pr = model.predict(img)[0] 65 | pr = pr.reshape((int(HEIGHT), int(WIDTH), NCLASSES)).argmax(axis=-1) 66 | 67 | #------------------------------------------------# 68 | # 创建一副新图,并根据每个像素点的种类赋予颜色 69 | #------------------------------------------------# 70 | seg_img = np.zeros((int(HEIGHT), int(WIDTH),3)) 71 | for c in range(NCLASSES): 72 | seg_img[:, :, 0] += ((pr[:,: ] == c) * class_colors[c][0]).astype('uint8') 73 | seg_img[:, :, 1] += ((pr[:,: ] == c) * class_colors[c][1]).astype('uint8') 74 | seg_img[:, :, 2] += ((pr[:,: ] == c) * class_colors[c][2]).astype('uint8') 75 | 76 | seg_img = Image.fromarray(np.uint8(seg_img)).resize((orininal_w,orininal_h)) 77 | #------------------------------------------------# 78 | # 将新图片和原图片混合 79 | #------------------------------------------------# 80 | image = Image.blend(old_img,seg_img,0.3) 81 | 82 | image.save("./img_out/"+jpg) 83 | 84 | 85 | -------------------------------------------------------------------------------- /deeplab_Xception/test.py: -------------------------------------------------------------------------------- 1 | #---------------------------------------------# 2 | # 该部分用于查看网络结构 3 | #---------------------------------------------# 4 | from nets.deeplab import Deeplabv3 5 | 6 | if __name__ == "__main__": 7 | model = Deeplabv3(classes=2,OS=16) 8 | model.summary() 9 | for i in range(len(model.layers)): 10 | print(i,model.layers[i].name) 11 | -------------------------------------------------------------------------------- /deeplab_Xception/train.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import numpy as np 3 | from keras import backend as K 4 | from keras.callbacks import (EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, 5 | TensorBoard) 6 | from keras.optimizers import Adam 7 | from keras.utils.data_utils import get_file 8 | from PIL import Image 9 | 10 | from nets.deeplab import Deeplabv3 11 | 12 | #-------------------------------------------------------------# 13 | # 定义了一个生成器,用于读取datasets2文件夹里面的图片与标签 14 | #-------------------------------------------------------------# 15 | def generate_arrays_from_file(lines,batch_size): 16 | n = len(lines) 17 | i = 0 18 | while 1: 19 | X_train = [] 20 | Y_train = [] 21 | for _ in range(batch_size): 22 | if i==0: 23 | np.random.shuffle(lines) 24 | #-------------------------------------# 25 | # 读取输入图片并进行归一化和resize 26 | #-------------------------------------# 27 | name = lines[i].split(';')[0] 28 | img = Image.open("./dataset2/jpg/" + name) 29 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 30 | img = np.array(img)/255 31 | X_train.append(img) 32 | 33 | #-------------------------------------# 34 | # 读取标签图片并进行归一化和resize 35 | #-------------------------------------# 36 | name = lines[i].split(';')[1].split()[0] 37 | label = Image.open("./dataset2/png/" + name) 38 | label = label.resize((int(WIDTH),int(HEIGHT)), Image.NEAREST) 39 | if len(np.shape(label)) == 3: 40 | label = np.array(label)[:,:,0] 41 | label = np.reshape(np.array(label), [-1]) 42 | one_hot_label = np.eye(NCLASSES)[np.array(label, np.int32)] 43 | Y_train.append(one_hot_label) 44 | 45 | i = (i+1) % n 46 | yield (np.array(X_train), np.array(Y_train)) 47 | 48 | if __name__ == "__main__": 49 | #---------------------------------------------# 50 | # 定义输入图片的高和宽,以及种类数量 51 | #---------------------------------------------# 52 | HEIGHT = 512 53 | WIDTH = 512 54 | #---------------------------------------------# 55 | # 背景 + 斑马线 = 2 56 | #---------------------------------------------# 57 | NCLASSES = 2 58 | 59 | log_dir = "logs/" 60 | model = Deeplabv3(classes=NCLASSES,input_shape=(HEIGHT,WIDTH,3)) 61 | #---------------------------------------------------------------------# 62 | # 这一步是获得主干特征提取网络的权重、使用的是迁移学习的思想 63 | # 如果下载过慢,可以复制连接到迅雷进行下载。 64 | # 之后将权值复制到目录下,根据路径进行载入。 65 | # 如: 66 | # weights_path = "xxxxx.h5" 67 | # model.load_weights(weights_path,by_name=True,skip_mismatch=True) 68 | #---------------------------------------------------------------------# 69 | WEIGHTS_PATH_X = "https://github.com/bonlime/keras-deeplab-v3-plus/releases/download/1.1/deeplabv3_xception_tf_dim_ordering_tf_kernels.h5" 70 | weights_path = get_file('deeplabv3_xception_tf_dim_ordering_tf_kernels.h5', WEIGHTS_PATH_X, cache_subdir='models') 71 | model.load_weights(weights_path,by_name=True) 72 | 73 | # 打开数据集的txt 74 | with open("./dataset2/train.txt","r") as f: 75 | lines = f.readlines() 76 | 77 | #---------------------------------------------# 78 | # 打乱的数据更有利于训练 79 | # 90%用于训练,10%用于估计。 80 | #---------------------------------------------# 81 | np.random.seed(10101) 82 | np.random.shuffle(lines) 83 | np.random.seed(None) 84 | num_val = int(len(lines)*0.1) 85 | num_train = len(lines) - num_val 86 | 87 | #-------------------------------------------------------------------------------# 88 | # 训练参数的设置 89 | # checkpoint用于设置权值保存的细节,period用于修改多少epoch保存一次 90 | # reduce_lr用于设置学习率下降的方式 91 | # early_stopping用于设定早停,val_loss多次不下降自动结束训练,表示模型基本收敛 92 | #-------------------------------------------------------------------------------# 93 | checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5', 94 | monitor='val_loss', save_weights_only=True, save_best_only=False, period=2) 95 | reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1) 96 | early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1) 97 | 98 | #-------------------------------------------------------------------------------# 99 | # 这里使用的是迁移学习的思想,主干部分提取出来的特征是通用的 100 | # 所以我们可以不训练主干部分先,因此训练部分分为两步,分别是冻结训练和解冻训练 101 | # 冻结训练是不训练主干的,解冻训练是训练主干的。 102 | # 由于训练的特征层变多,解冻后所需显存变大 103 | #-------------------------------------------------------------------------------# 104 | trainable_layer = 358 105 | for i in range(trainable_layer): 106 | model.layers[i].trainable = False 107 | print('freeze the first {} layers of total {} layers.'.format(trainable_layer, len(model.layers))) 108 | 109 | if True: 110 | lr = 1e-3 111 | batch_size = 4 112 | model.compile(loss = 'categorical_crossentropy', 113 | optimizer = Adam(lr=lr), 114 | metrics = ['accuracy']) 115 | 116 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 117 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 118 | steps_per_epoch=max(1, num_train//batch_size), 119 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 120 | validation_steps=max(1, num_val//batch_size), 121 | epochs=50, 122 | initial_epoch=0, 123 | callbacks=[checkpoint, reduce_lr,early_stopping]) 124 | model.save_weights(log_dir+'last1.h5') 125 | 126 | for i in range(len(model.layers)): 127 | model.layers[i].trainable = True 128 | 129 | if True: 130 | lr = 1e-4 131 | batch_size = 2 132 | model.compile(loss = 'categorical_crossentropy', 133 | optimizer = Adam(lr=lr), 134 | metrics = ['accuracy']) 135 | 136 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 137 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 138 | steps_per_epoch=max(1, num_train//batch_size), 139 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 140 | validation_steps=max(1, num_val//batch_size), 141 | epochs=100, 142 | initial_epoch=50, 143 | callbacks=[checkpoint, reduce_lr,early_stopping]) 144 | model.save_weights(log_dir+'last1.h5') 145 | -------------------------------------------------------------------------------- /make_dataset/before/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/before/1.jpg -------------------------------------------------------------------------------- /make_dataset/before/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/before/2.jpg -------------------------------------------------------------------------------- /make_dataset/before/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/before/3.jpg -------------------------------------------------------------------------------- /make_dataset/before/class_name.txt: -------------------------------------------------------------------------------- 1 | _background_ 2 | cat 3 | dog -------------------------------------------------------------------------------- /make_dataset/get_jpg_and_png.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import numpy as np 4 | from PIL import Image 5 | 6 | 7 | def main(): 8 | # 读取原文件夹 9 | count = os.listdir("./before/") 10 | for i in range(0, len(count)): 11 | # 如果里的文件以jpg结尾 12 | # 则寻找它对应的png 13 | if count[i].endswith("jpg"): 14 | path = os.path.join("./before", count[i]) 15 | img = Image.open(path) 16 | img.save(os.path.join("./jpg", count[i])) 17 | 18 | # 找到对应的png 19 | path = "./output/" + count[i].split(".")[0] + "_json/label.png" 20 | img = Image.open(path) 21 | 22 | # 找到全局的类 23 | class_txt = open("./before/class_name.txt","r") 24 | class_name = class_txt.read().splitlines() 25 | # ["bk","cat","dog"] 26 | # 打开json文件里面存在的类,称其为局部类 27 | with open("./output/" + count[i].split(".")[0] + "_json/label_names.txt","r") as f: 28 | names = f.read().splitlines() 29 | # ["bk","dog"] 30 | new = Image.new("RGB",[np.shape(img)[1],np.shape(img)[0]]) 31 | for name in names: 32 | # index_json是json文件里存在的类,局部类 33 | index_json = names.index(name) 34 | # index_all是全局的类 35 | index_all = class_name.index(name) 36 | # 将局部类转换成为全局类 37 | 38 | new = new + np.expand_dims(index_all*(np.array(img) == index_json),-1) 39 | 40 | new = Image.fromarray(np.uint8(new)) 41 | new.save(os.path.join("./png", count[i].replace("jpg","png"))) 42 | print(np.max(new),np.min(new)) 43 | 44 | if __name__ == '__main__': 45 | main() 46 | -------------------------------------------------------------------------------- /make_dataset/get_train_txt.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | jpgs = os.listdir("./jpg") 4 | pngs = os.listdir("./png") 5 | 6 | with open("train.txt","w") as f: 7 | for jpg in jpgs: 8 | png = jpg.replace("jpg","png") 9 | # 判断jpg是否存在对应的png 10 | if png in pngs: 11 | f.write(jpg+";"+png+"\n") 12 | -------------------------------------------------------------------------------- /make_dataset/jpg/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/jpg/1.jpg -------------------------------------------------------------------------------- /make_dataset/jpg/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/jpg/2.jpg -------------------------------------------------------------------------------- /make_dataset/jpg/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/jpg/3.jpg -------------------------------------------------------------------------------- /make_dataset/json_to_dataset.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import os.path as osp 5 | import warnings 6 | 7 | import PIL.Image 8 | import yaml 9 | 10 | from labelme import utils 11 | import base64 12 | 13 | def main(): 14 | count = os.listdir("./before/") 15 | for i in range(0, len(count)): 16 | path = os.path.join("./before", count[i]) 17 | 18 | if os.path.isfile(path) and path.endswith('json'): 19 | data = json.load(open(path)) 20 | 21 | if data['imageData']: 22 | imageData = data['imageData'] 23 | else: 24 | imagePath = os.path.join(os.path.dirname(path), data['imagePath']) 25 | with open(imagePath, 'rb') as f: 26 | imageData = f.read() 27 | imageData = base64.b64encode(imageData).decode('utf-8') 28 | img = utils.img_b64_to_arr(imageData) 29 | label_name_to_value = {'_background_': 0} 30 | for shape in data['shapes']: 31 | label_name = shape['label'] 32 | if label_name in label_name_to_value: 33 | label_value = label_name_to_value[label_name] 34 | else: 35 | label_value = len(label_name_to_value) 36 | label_name_to_value[label_name] = label_value 37 | 38 | # label_values must be dense 39 | label_values, label_names = [], [] 40 | for ln, lv in sorted(label_name_to_value.items(), key=lambda x: x[1]): 41 | label_values.append(lv) 42 | label_names.append(ln) 43 | assert label_values == list(range(len(label_values))) 44 | 45 | lbl = utils.shapes_to_label(img.shape, data['shapes'], label_name_to_value) 46 | 47 | captions = ['{}: {}'.format(lv, ln) 48 | for ln, lv in label_name_to_value.items()] 49 | lbl_viz = utils.draw_label(lbl, img, captions) 50 | out_dir = osp.basename(count[i]).replace('.', '_') 51 | out_dir = osp.join(osp.dirname(count[i]), out_dir) 52 | out_dir = osp.join("output",out_dir) 53 | 54 | if not osp.exists(out_dir): 55 | os.mkdir(out_dir) 56 | 57 | PIL.Image.fromarray(img).save(osp.join(out_dir, 'img.png')) 58 | 59 | utils.lblsave(osp.join(out_dir, 'label.png'), lbl) 60 | PIL.Image.fromarray(lbl_viz).save(osp.join(out_dir, 'label_viz.png')) 61 | 62 | with open(osp.join(out_dir, 'label_names.txt'), 'w') as f: 63 | for lbl_name in label_names: 64 | f.write(lbl_name + '\n') 65 | 66 | warnings.warn('info.yaml is being replaced by label_names.txt') 67 | info = dict(label_names=label_names) 68 | with open(osp.join(out_dir, 'info.yaml'), 'w') as f: 69 | yaml.safe_dump(info, f, default_flow_style=False) 70 | 71 | print('Saved to: %s' % out_dir) 72 | if __name__ == '__main__': 73 | main() -------------------------------------------------------------------------------- /make_dataset/output/1_json/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/output/1_json/img.png -------------------------------------------------------------------------------- /make_dataset/output/1_json/info.yaml: -------------------------------------------------------------------------------- 1 | label_names: 2 | - _background_ 3 | - cat 4 | -------------------------------------------------------------------------------- /make_dataset/output/1_json/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/output/1_json/label.png -------------------------------------------------------------------------------- /make_dataset/output/1_json/label_names.txt: -------------------------------------------------------------------------------- 1 | _background_ 2 | cat 3 | -------------------------------------------------------------------------------- /make_dataset/output/1_json/label_viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/output/1_json/label_viz.png -------------------------------------------------------------------------------- /make_dataset/output/2_json/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/output/2_json/img.png -------------------------------------------------------------------------------- /make_dataset/output/2_json/info.yaml: -------------------------------------------------------------------------------- 1 | label_names: 2 | - _background_ 3 | - cat 4 | -------------------------------------------------------------------------------- /make_dataset/output/2_json/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/output/2_json/label.png -------------------------------------------------------------------------------- /make_dataset/output/2_json/label_names.txt: -------------------------------------------------------------------------------- 1 | _background_ 2 | cat 3 | -------------------------------------------------------------------------------- /make_dataset/output/2_json/label_viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/output/2_json/label_viz.png -------------------------------------------------------------------------------- /make_dataset/output/3_json/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/output/3_json/img.png -------------------------------------------------------------------------------- /make_dataset/output/3_json/info.yaml: -------------------------------------------------------------------------------- 1 | label_names: 2 | - _background_ 3 | - dog 4 | -------------------------------------------------------------------------------- /make_dataset/output/3_json/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/output/3_json/label.png -------------------------------------------------------------------------------- /make_dataset/output/3_json/label_names.txt: -------------------------------------------------------------------------------- 1 | _background_ 2 | dog 3 | -------------------------------------------------------------------------------- /make_dataset/output/3_json/label_viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/output/3_json/label_viz.png -------------------------------------------------------------------------------- /make_dataset/png/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/png/1.png -------------------------------------------------------------------------------- /make_dataset/png/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/png/2.png -------------------------------------------------------------------------------- /make_dataset/png/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/make_dataset/png/3.png -------------------------------------------------------------------------------- /make_dataset/train.txt: -------------------------------------------------------------------------------- 1 | 1.jpg;1.png 2 | 2.jpg;2.png 3 | 3.jpg;3.png 4 | -------------------------------------------------------------------------------- /pspnet_Mobile/dataset2/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放dataset内的训练数据。 2 | This part is used to store the training data in the dataset. -------------------------------------------------------------------------------- /pspnet_Mobile/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img/timg.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/img/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img/timg2.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/img/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img/timg3.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/img/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img/timg4.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/img/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img/timg5.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/img_out/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img_out/timg.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/img_out/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img_out/timg2.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/img_out/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img_out/timg3.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/img_out/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img_out/timg4.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/img_out/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/img_out/timg5.jpg -------------------------------------------------------------------------------- /pspnet_Mobile/logs/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放训练后的文件。 2 | This part is used to store post training documents. -------------------------------------------------------------------------------- /pspnet_Mobile/nets/__pycache__/mobilenet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/nets/__pycache__/mobilenet.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Mobile/nets/__pycache__/pspnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/nets/__pycache__/pspnet.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Mobile/nets/__pycache__/resnet50.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/nets/__pycache__/resnet50.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Mobile/nets/__pycache__/segnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/nets/__pycache__/segnet.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Mobile/nets/__pycache__/unet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Mobile/nets/__pycache__/unet.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Mobile/nets/mobilenet.py: -------------------------------------------------------------------------------- 1 | 2 | import keras 3 | import keras.backend as K 4 | from keras.layers import * 5 | from keras.models import * 6 | 7 | def relu6(x): 8 | return K.relu(x, max_value=6) 9 | 10 | def _conv_block(inputs, filters, alpha, kernel=(3, 3), strides=(1, 1)): 11 | filters = int(filters * alpha) 12 | x = ZeroPadding2D(padding=(1, 1), name='conv1_pad')(inputs) 13 | x = Conv2D(filters, kernel, padding='valid', 14 | use_bias=False, 15 | strides=strides, 16 | name='conv1')(x) 17 | x = BatchNormalization(name='conv1_bn')(x) 18 | return Activation(relu6, name='conv1_relu')(x) 19 | 20 | def _depthwise_conv_block(inputs, pointwise_conv_filters, alpha, depth_multiplier=1, strides=(1, 1), block_id=1): 21 | pointwise_conv_filters = int(pointwise_conv_filters * alpha) 22 | 23 | x = ZeroPadding2D((1, 1), name='conv_pad_%d' % block_id)(inputs) 24 | x = DepthwiseConv2D((3, 3), padding='valid', 25 | depth_multiplier=depth_multiplier, 26 | strides=strides, 27 | use_bias=False, 28 | name='conv_dw_%d' % block_id)(x) 29 | x = BatchNormalization(name='conv_dw_%d_bn' % block_id)(x) 30 | x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x) 31 | 32 | x = Conv2D(pointwise_conv_filters, (1, 1), 33 | padding='same', 34 | use_bias=False, 35 | strides=(1, 1), 36 | name='conv_pw_%d' % block_id)(x) 37 | x = BatchNormalization(name='conv_pw_%d_bn' % block_id)(x) 38 | return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x) 39 | 40 | def get_mobilenet_encoder(input_height=224, input_width=224): 41 | alpha=1.0 42 | depth_multiplier=1 43 | 44 | img_input = Input(shape=(input_height, input_width, 3)) 45 | 46 | # 576,576,3 -> 288,288,32 -> 288,288,64 47 | x = _conv_block(img_input, 32, alpha, strides=(2, 2)) 48 | x = _depthwise_conv_block(x, 64, alpha, depth_multiplier, block_id=1) 49 | f1 = x 50 | 51 | # 288,288,64 -> 144,144,128 52 | x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, strides=(2, 2), block_id=2) 53 | x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, block_id=3) 54 | f2 = x 55 | 56 | # 144,144,128 -> 72,72,256 57 | x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, strides=(2, 2), block_id=4) 58 | x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, block_id=5) 59 | f3 = x 60 | 61 | # 72,72,256 -> 36,36,512 62 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, strides=(2, 2), block_id=6) 63 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=7) 64 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=8) 65 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=9) 66 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=10) 67 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=11) 68 | f4 = x 69 | 70 | # 36,36,512 -> 18,18,1024 71 | x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, strides=(2, 2), block_id=12) 72 | x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, block_id=13) 73 | f5 = x 74 | 75 | return img_input, [f1 , f2 , f3 , f4 , f5] 76 | -------------------------------------------------------------------------------- /pspnet_Mobile/nets/pspnet.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from keras.layers import * 3 | from keras.models import * 4 | 5 | from nets.mobilenet import get_mobilenet_encoder 6 | 7 | def resize_image(inp, s): 8 | return Lambda(lambda x: tf.image.resize_images(x, (K.int_shape(x)[1]*s[0], K.int_shape(x)[2]*s[1])))( inp ) 9 | 10 | def pool_block( feats , pool_factor ): 11 | h = K.int_shape( feats )[1] 12 | w = K.int_shape( feats )[2] 13 | 14 | #-----------------------------------------------------# 15 | # strides = [18, 18],[9, 9],[6, 6],[3, 3] 16 | # 进行不同程度的平均 17 | #-----------------------------------------------------# 18 | pool_size = strides = [int(np.round(float(h)/pool_factor)),int(np.round(float(w)/pool_factor))] 19 | x = AveragePooling2D(pool_size, strides=strides, padding='same')(feats) 20 | 21 | #-----------------------------------------------------# 22 | # 利用1x1卷积进行通道数的调整 23 | #-----------------------------------------------------# 24 | x = Conv2D(512, (1, 1), padding='same', use_bias=False)(x) 25 | x = BatchNormalization()(x) 26 | x = Activation('relu')(x) 27 | 28 | #-----------------------------------------------------# 29 | # 利用resize扩大特征层面积 30 | #-----------------------------------------------------# 31 | x = resize_image(x, strides) 32 | return x 33 | 34 | def _pspnet(n_classes, encoder, input_height=576, input_width=576): 35 | assert input_height%192 == 0 36 | assert input_width%192 == 0 37 | 38 | img_input , levels = encoder( input_height=input_height,input_width=input_width) 39 | [f1 , f2 , f3 , f4 , f5] = levels 40 | 41 | o = f5 42 | #--------------------------------------------------------------# 43 | # PSP模块,分区域进行池化 44 | # 分别分割成1x1的区域,2x2的区域,3x3的区域,6x6的区域 45 | #--------------------------------------------------------------# 46 | pool_factors = [1,2,3,6] 47 | pool_outs = [o] 48 | for p in pool_factors: 49 | pooled = pool_block(o, p) 50 | pool_outs.append(pooled) 51 | 52 | #-----------------------------------------------------------------------------------------# 53 | # 利用获取到的特征层进行堆叠 54 | # 18, 18, 1024 + 18, 18, 512 + 18, 18, 512 + 18, 18, 512 + 18, 18, 512 = 18, 18, 3072 55 | #-----------------------------------------------------------------------------------------# 56 | o = Concatenate()(pool_outs) 57 | 58 | # 18, 18, 3072 -> 18, 18, 512 59 | o = Conv2D(512, (1,1), use_bias=False )(o) 60 | o = BatchNormalization()(o) 61 | o = Activation('relu')(o) 62 | 63 | # 18, 18, 512 -> 144, 144, nclasses 64 | o = Conv2D(n_classes, (3,3), padding='same')(o) 65 | o = resize_image(o, (8,8)) 66 | 67 | o = Reshape((-1,n_classes))(o) 68 | o = Softmax()(o) 69 | model = Model(img_input,o) 70 | return model 71 | 72 | 73 | 74 | def mobilenet_pspnet(n_classes, input_height=224, input_width=224): 75 | model = _pspnet(n_classes, get_mobilenet_encoder, input_height=input_height, input_width=input_width) 76 | model.model_name = "mobilenet_pspnet" 77 | return model 78 | -------------------------------------------------------------------------------- /pspnet_Mobile/predict.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | import random 4 | 5 | import numpy as np 6 | from PIL import Image 7 | 8 | from nets.pspnet import mobilenet_pspnet 9 | 10 | if __name__ == "__main__": 11 | #---------------------------------------------------# 12 | # 定义了输入图片的颜色,当我们想要去区分两类的时候 13 | # 我们定义了两个颜色,分别用于背景和斑马线 14 | # [0,0,0], [0,255,0]代表了颜色的RGB色彩 15 | #---------------------------------------------------# 16 | class_colors = [[0,0,0],[0,255,0]] 17 | #---------------------------------------------# 18 | # 定义输入图片的高和宽,以及种类数量 19 | #---------------------------------------------# 20 | HEIGHT = 576 21 | WIDTH = 576 22 | #---------------------------------------------# 23 | # 背景 + 斑马线 = 2 24 | #---------------------------------------------# 25 | NCLASSES = 2 26 | 27 | #---------------------------------------------# 28 | # 载入模型 29 | #---------------------------------------------# 30 | model = mobilenet_pspnet(n_classes=NCLASSES,input_height=HEIGHT, input_width=WIDTH) 31 | #--------------------------------------------------# 32 | # 载入权重,训练好的权重会保存在logs文件夹里面 33 | # 我们需要将对应的权重载入 34 | # 修改model_path,将其对应我们训练好的权重即可 35 | # 下面只是一个示例 36 | #--------------------------------------------------# 37 | model.load_weights("logs/ep054-loss0.014-val_loss0.030.h5") 38 | 39 | #--------------------------------------------------# 40 | # 对imgs文件夹进行一个遍历 41 | #--------------------------------------------------# 42 | imgs = os.listdir("./img/") 43 | for jpg in imgs: 44 | #--------------------------------------------------# 45 | # 打开imgs文件夹里面的每一个图片 46 | #--------------------------------------------------# 47 | img = Image.open("./img/"+jpg) 48 | 49 | old_img = copy.deepcopy(img) 50 | orininal_h = np.array(img).shape[0] 51 | orininal_w = np.array(img).shape[1] 52 | 53 | #--------------------------------------------------# 54 | # 对输入进来的每一个图片进行Resize 55 | # resize成[HEIGHT, WIDTH, 3] 56 | #--------------------------------------------------# 57 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 58 | img = np.array(img) / 255 59 | img = img.reshape(-1, HEIGHT, WIDTH, 3) 60 | 61 | #--------------------------------------------------# 62 | # 将图像输入到网络当中进行预测 63 | #--------------------------------------------------# 64 | pr = model.predict(img)[0] 65 | pr = pr.reshape((int(HEIGHT/4), int(WIDTH/4), NCLASSES)).argmax(axis=-1) 66 | 67 | #------------------------------------------------# 68 | # 创建一副新图,并根据每个像素点的种类赋予颜色 69 | #------------------------------------------------# 70 | seg_img = np.zeros((int(HEIGHT/4), int(WIDTH/4),3)) 71 | for c in range(NCLASSES): 72 | seg_img[:, :, 0] += ((pr[:,: ] == c) * class_colors[c][0]).astype('uint8') 73 | seg_img[:, :, 1] += ((pr[:,: ] == c) * class_colors[c][1]).astype('uint8') 74 | seg_img[:, :, 2] += ((pr[:,: ] == c) * class_colors[c][2]).astype('uint8') 75 | 76 | seg_img = Image.fromarray(np.uint8(seg_img)).resize((orininal_w,orininal_h)) 77 | #------------------------------------------------# 78 | # 将新图片和原图片混合 79 | #------------------------------------------------# 80 | image = Image.blend(old_img,seg_img,0.3) 81 | 82 | image.save("./img_out/"+jpg) 83 | 84 | 85 | -------------------------------------------------------------------------------- /pspnet_Mobile/test.py: -------------------------------------------------------------------------------- 1 | #---------------------------------------------# 2 | # 该部分用于查看网络结构 3 | #---------------------------------------------# 4 | from nets.pspnet import mobilenet_pspnet 5 | 6 | if __name__ == "__main__": 7 | model = mobilenet_pspnet(2,576,576) 8 | model.summary() -------------------------------------------------------------------------------- /pspnet_Mobile/train.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import numpy as np 3 | from keras import backend as K 4 | from keras.callbacks import (EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, 5 | TensorBoard) 6 | from keras.optimizers import Adam 7 | from PIL import Image 8 | 9 | from nets.pspnet import mobilenet_pspnet 10 | 11 | 12 | #-------------------------------------------------------------# 13 | # 定义了一个生成器,用于读取datasets2文件夹里面的图片与标签 14 | #-------------------------------------------------------------# 15 | def generate_arrays_from_file(lines,batch_size): 16 | n = len(lines) 17 | i = 0 18 | while 1: 19 | X_train = [] 20 | Y_train = [] 21 | for _ in range(batch_size): 22 | if i==0: 23 | np.random.shuffle(lines) 24 | #-------------------------------------# 25 | # 读取输入图片并进行归一化和resize 26 | #-------------------------------------# 27 | name = lines[i].split(';')[0] 28 | img = Image.open("./dataset2/jpg/" + name) 29 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 30 | img = np.array(img)/255 31 | X_train.append(img) 32 | 33 | #-------------------------------------# 34 | # 读取标签图片并进行归一化和resize 35 | #-------------------------------------# 36 | name = lines[i].split(';')[1].split()[0] 37 | label = Image.open("./dataset2/png/" + name) 38 | label = label.resize((int(WIDTH/4),int(HEIGHT/4)), Image.NEAREST) 39 | if len(np.shape(label)) == 3: 40 | label = np.array(label)[:,:,0] 41 | label = np.reshape(np.array(label), [-1]) 42 | one_hot_label = np.eye(NCLASSES)[np.array(label, np.int32)] 43 | Y_train.append(one_hot_label) 44 | 45 | i = (i+1) % n 46 | yield (np.array(X_train), np.array(Y_train)) 47 | 48 | if __name__ == "__main__": 49 | #---------------------------------------------# 50 | # 定义输入图片的高和宽,以及种类数量 51 | #---------------------------------------------# 52 | HEIGHT = 576 53 | WIDTH = 576 54 | #---------------------------------------------# 55 | # 背景 + 斑马线 = 2 56 | #---------------------------------------------# 57 | NCLASSES = 2 58 | 59 | log_dir = "logs/" 60 | model = mobilenet_pspnet(n_classes=NCLASSES, input_height=HEIGHT, input_width=WIDTH) 61 | #---------------------------------------------------------------------# 62 | # 这一步是获得主干特征提取网络的权重、使用的是迁移学习的思想 63 | # 如果下载过慢,可以复制连接到迅雷进行下载。 64 | # 之后将权值复制到目录下,根据路径进行载入。 65 | # 如: 66 | # weights_path = "xxxxx.h5" 67 | # model.load_weights(weights_path,by_name=True,skip_mismatch=True) 68 | #---------------------------------------------------------------------# 69 | BASE_WEIGHT_PATH = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.6/' 70 | model_name = 'mobilenet_%s_%d_tf_no_top.h5' % ('1_0', 224) 71 | weight_path = BASE_WEIGHT_PATH + model_name 72 | weights_path = keras.utils.get_file(model_name, weight_path) 73 | model.load_weights(weights_path, by_name=True, skip_mismatch=True) 74 | 75 | # 打开数据集的txt 76 | with open("./dataset2/train.txt","r") as f: 77 | lines = f.readlines() 78 | 79 | #---------------------------------------------# 80 | # 打乱的数据更有利于训练 81 | # 90%用于训练,10%用于估计。 82 | #---------------------------------------------# 83 | np.random.seed(10101) 84 | np.random.shuffle(lines) 85 | np.random.seed(None) 86 | num_val = int(len(lines)*0.1) 87 | num_train = len(lines) - num_val 88 | 89 | #-------------------------------------------------------------------------------# 90 | # 训练参数的设置 91 | # checkpoint用于设置权值保存的细节,period用于修改多少epoch保存一次 92 | # reduce_lr用于设置学习率下降的方式 93 | # early_stopping用于设定早停,val_loss多次不下降自动结束训练,表示模型基本收敛 94 | #-------------------------------------------------------------------------------# 95 | checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5', 96 | monitor='val_loss', save_weights_only=True, save_best_only=False, period=2) 97 | reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1) 98 | early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1) 99 | 100 | #-------------------------------------------------------------------------------# 101 | # 这里使用的是迁移学习的思想,主干部分提取出来的特征是通用的 102 | # 所以我们可以不训练主干部分先,因此训练部分分为两步,分别是冻结训练和解冻训练 103 | # 冻结训练是不训练主干的,解冻训练是训练主干的。 104 | # 由于训练的特征层变多,解冻后所需显存变大 105 | #-------------------------------------------------------------------------------# 106 | trainable_layer = 60 107 | for i in range(trainable_layer): 108 | model.layers[i].trainable = False 109 | print('freeze the first {} layers of total {} layers.'.format(trainable_layer, len(model.layers))) 110 | 111 | if True: 112 | lr = 1e-3 113 | batch_size = 4 114 | model.compile(loss = 'categorical_crossentropy', 115 | optimizer = Adam(lr=lr), 116 | metrics = ['accuracy']) 117 | 118 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 119 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 120 | steps_per_epoch=max(1, num_train//batch_size), 121 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 122 | validation_steps=max(1, num_val//batch_size), 123 | epochs=50, 124 | initial_epoch=0, 125 | callbacks=[checkpoint, reduce_lr,early_stopping]) 126 | 127 | for i in range(len(model.layers)): 128 | model.layers[i].trainable = True 129 | 130 | if True: 131 | lr = 1e-4 132 | batch_size = 4 133 | model.compile(loss = 'categorical_crossentropy', 134 | optimizer = Adam(lr=lr), 135 | metrics = ['accuracy']) 136 | 137 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 138 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 139 | steps_per_epoch=max(1, num_train//batch_size), 140 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 141 | validation_steps=max(1, num_val//batch_size), 142 | epochs=100, 143 | initial_epoch=50, 144 | callbacks=[checkpoint, reduce_lr,early_stopping]) -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/dataset2/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放dataset内的训练数据。 2 | This part is used to store the training data in the dataset. -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img/timg.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img/timg2.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img/timg3.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img/timg4.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img/timg5.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img_out/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img_out/timg.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img_out/timg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img_out/timg2.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img_out/timg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img_out/timg3.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img_out/timg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img_out/timg4.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/img_out/timg5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/img_out/timg5.jpg -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/logs/README.MD: -------------------------------------------------------------------------------- 1 | 这一部分用来存放训练后的文件。 2 | This part is used to store post training documents. -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/nets/__pycache__/mobilenet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/nets/__pycache__/mobilenet.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/nets/__pycache__/pspnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/nets/__pycache__/pspnet.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/nets/__pycache__/resnet50.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/nets/__pycache__/resnet50.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/nets/__pycache__/segnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/nets/__pycache__/segnet.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/nets/__pycache__/unet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bubbliiiing/Semantic-Segmentation/4cc89a22ffc9018d2b44e69e85672c7bdd1ab706/pspnet_Multi_Mobile/nets/__pycache__/unet.cpython-36.pyc -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/nets/mobilenet.py: -------------------------------------------------------------------------------- 1 | 2 | import keras 3 | import keras.backend as K 4 | from keras.layers import * 5 | from keras.models import * 6 | 7 | def relu6(x): 8 | return K.relu(x, max_value=6) 9 | 10 | def _conv_block(inputs, filters, alpha, kernel=(3, 3), strides=(1, 1)): 11 | filters = int(filters * alpha) 12 | x = ZeroPadding2D(padding=(1, 1), name='conv1_pad')(inputs) 13 | x = Conv2D(filters, kernel, padding='valid', 14 | use_bias=False, 15 | strides=strides, 16 | name='conv1')(x) 17 | x = BatchNormalization(name='conv1_bn')(x) 18 | return Activation(relu6, name='conv1_relu')(x) 19 | 20 | def _depthwise_conv_block(inputs, pointwise_conv_filters, alpha, depth_multiplier=1, strides=(1, 1), block_id=1): 21 | pointwise_conv_filters = int(pointwise_conv_filters * alpha) 22 | 23 | x = ZeroPadding2D((1, 1), name='conv_pad_%d' % block_id)(inputs) 24 | x = DepthwiseConv2D((3, 3), padding='valid', 25 | depth_multiplier=depth_multiplier, 26 | strides=strides, 27 | use_bias=False, 28 | name='conv_dw_%d' % block_id)(x) 29 | x = BatchNormalization(name='conv_dw_%d_bn' % block_id)(x) 30 | x = Activation(relu6, name='conv_dw_%d_relu' % block_id)(x) 31 | 32 | x = Conv2D(pointwise_conv_filters, (1, 1), 33 | padding='same', 34 | use_bias=False, 35 | strides=(1, 1), 36 | name='conv_pw_%d' % block_id)(x) 37 | x = BatchNormalization(name='conv_pw_%d_bn' % block_id)(x) 38 | return Activation(relu6, name='conv_pw_%d_relu' % block_id)(x) 39 | 40 | def get_mobilenet_encoder(input_height=224, input_width=224): 41 | alpha=1.0 42 | depth_multiplier=1 43 | 44 | img_input = Input(shape=(input_height, input_width, 3)) 45 | 46 | # 576,576,3 -> 288,288,32 -> 288,288,64 47 | x = _conv_block(img_input, 32, alpha, strides=(2, 2)) 48 | x = _depthwise_conv_block(x, 64, alpha, depth_multiplier, block_id=1) 49 | f1 = x 50 | 51 | # 288,288,64 -> 144,144,128 52 | x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, strides=(2, 2), block_id=2) 53 | x = _depthwise_conv_block(x, 128, alpha, depth_multiplier, block_id=3) 54 | f2 = x 55 | 56 | # 144,144,128 -> 72,72,256 57 | x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, strides=(2, 2), block_id=4) 58 | x = _depthwise_conv_block(x, 256, alpha, depth_multiplier, block_id=5) 59 | f3 = x 60 | 61 | # 72,72,256 -> 36,36,512 62 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, strides=(2, 2), block_id=6) 63 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=7) 64 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=8) 65 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=9) 66 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=10) 67 | x = _depthwise_conv_block(x, 512, alpha, depth_multiplier, block_id=11) 68 | f4 = x 69 | 70 | # 36,36,512 -> 18,18,1024 71 | x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, strides=(2, 2), block_id=12) 72 | x = _depthwise_conv_block(x, 1024, alpha, depth_multiplier, block_id=13) 73 | f5 = x 74 | 75 | return img_input, [f1 , f2 , f3 , f4 , f5] 76 | -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/nets/pspnet.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from keras.layers import * 3 | from keras.models import * 4 | 5 | from nets.mobilenet import get_mobilenet_encoder 6 | 7 | def resize_image(inp, s): 8 | return Lambda(lambda x: tf.image.resize_images(x, (K.int_shape(x)[1]*s[0], K.int_shape(x)[2]*s[1])))( inp ) 9 | 10 | def pool_block( feats , pool_factor ): 11 | h = K.int_shape( feats )[1] 12 | w = K.int_shape( feats )[2] 13 | 14 | #-----------------------------------------------------# 15 | # strides = [18, 18],[9, 9],[6, 6],[3, 3] 16 | # 进行不同程度的平均 17 | #-----------------------------------------------------# 18 | pool_size = strides = [int(np.round(float(h)/pool_factor)),int(np.round(float(w)/pool_factor))] 19 | x = AveragePooling2D(pool_size, strides=strides, padding='same')(feats) 20 | 21 | #-----------------------------------------------------# 22 | # 利用1x1卷积进行通道数的调整 23 | #-----------------------------------------------------# 24 | x = Conv2D(512, (1, 1), padding='same', use_bias=False)(x) 25 | x = BatchNormalization()(x) 26 | x = Activation('relu')(x) 27 | 28 | #-----------------------------------------------------# 29 | # 利用resize扩大特征层面积 30 | #-----------------------------------------------------# 31 | x = resize_image(x, strides) 32 | return x 33 | 34 | def _pspnet(n_classes, encoder, input_height=576, input_width=576): 35 | assert input_height%192 == 0 36 | assert input_width%192 == 0 37 | 38 | img_input , levels = encoder( input_height=input_height,input_width=input_width) 39 | [f1 , f2 , f3 , f4 , f5] = levels 40 | 41 | o = f5 42 | #--------------------------------------------------------------# 43 | # PSP模块,分区域进行池化 44 | # 分别分割成1x1的区域,2x2的区域,3x3的区域,6x6的区域 45 | #--------------------------------------------------------------# 46 | pool_factors = [1,2,3,6] 47 | pool_outs = [o] 48 | for p in pool_factors: 49 | pooled = pool_block(o, p) 50 | pool_outs.append(pooled) 51 | #-----------------------------------------------------------------------------------------# 52 | # 利用获取到的特征层进行堆叠 53 | # 18, 18, 1024 + 18, 18, 512 + 18, 18, 512 + 18, 18, 512 + 18, 18, 512 = 18, 18, 3072 54 | #-----------------------------------------------------------------------------------------# 55 | o = Concatenate()(pool_outs ) 56 | 57 | # 18, 18, 3072 -> 36, 36, 512 58 | o = Conv2D(512, (1, 1), padding='valid')(o) 59 | o = BatchNormalization()(o) 60 | o = resize_image(o, (2,2)) 61 | # 36, 36, 512 + 36, 36, 512 -> 36, 36, 1024 62 | o = Concatenate()([o,f4]) 63 | 64 | # 36, 36, 1024 -> 36, 36, 512 65 | o = Conv2D(512, (1, 1), padding='valid')(o) 66 | o = BatchNormalization()(o) 67 | o = Activation('relu' )(o) 68 | 69 | #--------------------------------------------------------------# 70 | # PSP模块,分区域进行池化 71 | # 分别分割成1x1的区域,2x2的区域,3x3的区域,6x6的区域 72 | #--------------------------------------------------------------# 73 | pool_factors = [1,2,3,6] 74 | pool_outs = [o] 75 | for p in pool_factors: 76 | pooled = pool_block(o, p) 77 | pool_outs.append(pooled) 78 | #-----------------------------------------------------------------------------------------# 79 | # 利用获取到的特征层进行堆叠 80 | # 36, 36, 512 + 36, 36, 512 + 36, 36, 512 + 36, 36, 512 + 36, 36, 512 = 36, 36, 2560 81 | #-----------------------------------------------------------------------------------------# 82 | o = Concatenate()(pool_outs) 83 | 84 | # 36, 36, 2560 -> 72, 72, 512 85 | o = Conv2D(512, (1, 1), padding='valid')(o) 86 | o = BatchNormalization()(o) 87 | o = resize_image(o, (2,2)) 88 | # 72, 72, 512 + 72, 72, 256 -> 72, 72, 768 89 | o = Concatenate()([o,f3]) 90 | 91 | # 72, 72, 768 -> 72, 72, 512 92 | o = Conv2D(512, (1, 1), padding='valid')(o) 93 | o = BatchNormalization()(o) 94 | o = Activation('relu')(o) 95 | 96 | #--------------------------------------------------------------# 97 | # PSP模块,分区域进行池化 98 | # 分别分割成1x1的区域,2x2的区域,3x3的区域,6x6的区域 99 | #--------------------------------------------------------------# 100 | pool_factors = [1,2,3,6] 101 | pool_outs = [o] 102 | for p in pool_factors: 103 | pooled = pool_block(o, p) 104 | pool_outs.append(pooled) 105 | #-----------------------------------------------------------------------------------------# 106 | # 利用获取到的特征层进行堆叠 107 | # 72, 72, 512 + 72, 72, 512 + 72, 72, 512 + 72, 72, 512 + 72, 72, 512 = 72, 72, 2560 108 | #-----------------------------------------------------------------------------------------# 109 | o = Concatenate()(pool_outs) 110 | 111 | # 72, 72, 2560 -> 72, 72, 512 112 | o = Conv2D(512, (1,1), use_bias=False )(o) 113 | o = BatchNormalization()(o) 114 | o = Activation('relu' )(o) 115 | 116 | # 72, 72, 512 -> 144,144,nclasses 117 | o = Conv2D(n_classes, (3,3), padding='same')(o) 118 | o = resize_image(o, (2,2)) 119 | o = Reshape((-1, n_classes))(o) 120 | o = Softmax()(o) 121 | model = Model(img_input,o) 122 | return model 123 | 124 | def mobilenet_pspnet(n_classes, input_height=224, input_width=224 ): 125 | model = _pspnet(n_classes, get_mobilenet_encoder, input_height=input_height, input_width=input_width) 126 | model.model_name = "mobilenet_pspnet" 127 | return model 128 | -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/predict.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | import random 4 | 5 | import numpy as np 6 | from PIL import Image 7 | 8 | from nets.pspnet import mobilenet_pspnet 9 | 10 | if __name__ == "__main__": 11 | #---------------------------------------------------# 12 | # 定义了输入图片的颜色,当我们想要去区分两类的时候 13 | # 我们定义了两个颜色,分别用于背景和斑马线 14 | # [0,0,0], [0,255,0]代表了颜色的RGB色彩 15 | #---------------------------------------------------# 16 | class_colors = [[0,0,0],[0,255,0]] 17 | #---------------------------------------------# 18 | # 定义输入图片的高和宽,以及种类数量 19 | #---------------------------------------------# 20 | HEIGHT = 576 21 | WIDTH = 576 22 | #---------------------------------------------# 23 | # 背景 + 斑马线 = 2 24 | #---------------------------------------------# 25 | NCLASSES = 2 26 | 27 | #---------------------------------------------# 28 | # 载入模型 29 | #---------------------------------------------# 30 | model = mobilenet_pspnet(n_classes=NCLASSES,input_height=HEIGHT, input_width=WIDTH) 31 | #--------------------------------------------------# 32 | # 载入权重,训练好的权重会保存在logs文件夹里面 33 | # 我们需要将对应的权重载入 34 | # 修改model_path,将其对应我们训练好的权重即可 35 | # 下面只是一个示例 36 | #--------------------------------------------------# 37 | model.load_weights("logs/ep052-loss0.015-val_loss0.023.h5") 38 | 39 | #--------------------------------------------------# 40 | # 对imgs文件夹进行一个遍历 41 | #--------------------------------------------------# 42 | imgs = os.listdir("./img/") 43 | for jpg in imgs: 44 | #--------------------------------------------------# 45 | # 打开imgs文件夹里面的每一个图片 46 | #--------------------------------------------------# 47 | img = Image.open("./img/"+jpg) 48 | 49 | old_img = copy.deepcopy(img) 50 | orininal_h = np.array(img).shape[0] 51 | orininal_w = np.array(img).shape[1] 52 | 53 | #--------------------------------------------------# 54 | # 对输入进来的每一个图片进行Resize 55 | # resize成[HEIGHT, WIDTH, 3] 56 | #--------------------------------------------------# 57 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 58 | img = np.array(img) / 255 59 | img = img.reshape(-1, HEIGHT, WIDTH, 3) 60 | 61 | #--------------------------------------------------# 62 | # 将图像输入到网络当中进行预测 63 | #--------------------------------------------------# 64 | pr = model.predict(img)[0] 65 | pr = pr.reshape((int(HEIGHT/4), int(WIDTH/4), NCLASSES)).argmax(axis=-1) 66 | 67 | #------------------------------------------------# 68 | # 创建一副新图,并根据每个像素点的种类赋予颜色 69 | #------------------------------------------------# 70 | seg_img = np.zeros((int(HEIGHT/4), int(WIDTH/4),3)) 71 | for c in range(NCLASSES): 72 | seg_img[:, :, 0] += ((pr[:,: ] == c) * class_colors[c][0]).astype('uint8') 73 | seg_img[:, :, 1] += ((pr[:,: ] == c) * class_colors[c][1]).astype('uint8') 74 | seg_img[:, :, 2] += ((pr[:,: ] == c) * class_colors[c][2]).astype('uint8') 75 | 76 | seg_img = Image.fromarray(np.uint8(seg_img)).resize((orininal_w,orininal_h)) 77 | #------------------------------------------------# 78 | # 将新图片和原图片混合 79 | #------------------------------------------------# 80 | image = Image.blend(old_img,seg_img,0.3) 81 | 82 | image.save("./img_out/"+jpg) 83 | 84 | 85 | -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/test.py: -------------------------------------------------------------------------------- 1 | #---------------------------------------------# 2 | # 该部分用于查看网络结构 3 | #---------------------------------------------# 4 | from nets.pspnet import mobilenet_pspnet 5 | 6 | if __name__ == "__main__": 7 | model = mobilenet_pspnet(2,576,576) 8 | model.summary() -------------------------------------------------------------------------------- /pspnet_Multi_Mobile/train.py: -------------------------------------------------------------------------------- 1 | import keras 2 | import numpy as np 3 | from keras import backend as K 4 | from keras.callbacks import (EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, 5 | TensorBoard) 6 | from keras.optimizers import Adam 7 | from PIL import Image 8 | 9 | from nets.pspnet import mobilenet_pspnet 10 | 11 | 12 | #-------------------------------------------------------------# 13 | # 定义了一个生成器,用于读取datasets2文件夹里面的图片与标签 14 | #-------------------------------------------------------------# 15 | def generate_arrays_from_file(lines,batch_size): 16 | n = len(lines) 17 | i = 0 18 | while 1: 19 | X_train = [] 20 | Y_train = [] 21 | for _ in range(batch_size): 22 | if i==0: 23 | np.random.shuffle(lines) 24 | #-------------------------------------# 25 | # 读取输入图片并进行归一化和resize 26 | #-------------------------------------# 27 | name = lines[i].split(';')[0] 28 | img = Image.open("./dataset2/jpg/" + name) 29 | img = img.resize((WIDTH,HEIGHT), Image.BICUBIC) 30 | img = np.array(img)/255 31 | X_train.append(img) 32 | 33 | #-------------------------------------# 34 | # 读取标签图片并进行归一化和resize 35 | #-------------------------------------# 36 | name = lines[i].split(';')[1].split()[0] 37 | label = Image.open("./dataset2/png/" + name) 38 | label = label.resize((int(WIDTH/4),int(HEIGHT/4)), Image.NEAREST) 39 | if len(np.shape(label)) == 3: 40 | label = np.array(label)[:,:,0] 41 | label = np.reshape(np.array(label), [-1]) 42 | one_hot_label = np.eye(NCLASSES)[np.array(label, np.int32)] 43 | Y_train.append(one_hot_label) 44 | 45 | i = (i+1) % n 46 | yield (np.array(X_train), np.array(Y_train)) 47 | 48 | if __name__ == "__main__": 49 | #---------------------------------------------# 50 | # 定义输入图片的高和宽,以及种类数量 51 | #---------------------------------------------# 52 | HEIGHT = 576 53 | WIDTH = 576 54 | #---------------------------------------------# 55 | # 背景 + 斑马线 = 2 56 | #---------------------------------------------# 57 | NCLASSES = 2 58 | 59 | log_dir = "logs/" 60 | model = mobilenet_pspnet(n_classes=NCLASSES, input_height=HEIGHT, input_width=WIDTH) 61 | #---------------------------------------------------------------------# 62 | # 这一步是获得主干特征提取网络的权重、使用的是迁移学习的思想 63 | # 如果下载过慢,可以复制连接到迅雷进行下载。 64 | # 之后将权值复制到目录下,根据路径进行载入。 65 | # 如: 66 | # weights_path = "xxxxx.h5" 67 | # model.load_weights(weights_path,by_name=True,skip_mismatch=True) 68 | #---------------------------------------------------------------------# 69 | BASE_WEIGHT_PATH = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.6/' 70 | model_name = 'mobilenet_%s_%d_tf_no_top.h5' % ('1_0', 224) 71 | weight_path = BASE_WEIGHT_PATH + model_name 72 | weights_path = keras.utils.get_file(model_name, weight_path) 73 | model.load_weights(weights_path, by_name=True, skip_mismatch=True) 74 | 75 | # 打开数据集的txt 76 | with open("./dataset2/train.txt","r") as f: 77 | lines = f.readlines() 78 | 79 | #---------------------------------------------# 80 | # 打乱的数据更有利于训练 81 | # 90%用于训练,10%用于估计。 82 | #---------------------------------------------# 83 | np.random.seed(10101) 84 | np.random.shuffle(lines) 85 | np.random.seed(None) 86 | num_val = int(len(lines)*0.1) 87 | num_train = len(lines) - num_val 88 | 89 | #-------------------------------------------------------------------------------# 90 | # 训练参数的设置 91 | # checkpoint用于设置权值保存的细节,period用于修改多少epoch保存一次 92 | # reduce_lr用于设置学习率下降的方式 93 | # early_stopping用于设定早停,val_loss多次不下降自动结束训练,表示模型基本收敛 94 | #-------------------------------------------------------------------------------# 95 | checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5', 96 | monitor='val_loss', save_weights_only=True, save_best_only=False, period=2) 97 | reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1) 98 | early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1) 99 | 100 | #-------------------------------------------------------------------------------# 101 | # 这里使用的是迁移学习的思想,主干部分提取出来的特征是通用的 102 | # 所以我们可以不训练主干部分先,因此训练部分分为两步,分别是冻结训练和解冻训练 103 | # 冻结训练是不训练主干的,解冻训练是训练主干的。 104 | # 由于训练的特征层变多,解冻后所需显存变大 105 | #-------------------------------------------------------------------------------# 106 | trainable_layer = 60 107 | for i in range(trainable_layer): 108 | model.layers[i].trainable = False 109 | print('freeze the first {} layers of total {} layers.'.format(trainable_layer, len(model.layers))) 110 | 111 | if True: 112 | lr = 1e-3 113 | batch_size = 4 114 | model.compile(loss = 'categorical_crossentropy', 115 | optimizer = Adam(lr=lr), 116 | metrics = ['accuracy']) 117 | 118 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 119 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 120 | steps_per_epoch=max(1, num_train//batch_size), 121 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 122 | validation_steps=max(1, num_val//batch_size), 123 | epochs=50, 124 | initial_epoch=0, 125 | callbacks=[checkpoint, reduce_lr,early_stopping]) 126 | 127 | for i in range(len(model.layers)): 128 | model.layers[i].trainable = True 129 | 130 | if True: 131 | lr = 1e-4 132 | batch_size = 4 133 | model.compile(loss = 'categorical_crossentropy', 134 | optimizer = Adam(lr=lr), 135 | metrics = ['accuracy']) 136 | 137 | print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) 138 | model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size), 139 | steps_per_epoch=max(1, num_train//batch_size), 140 | validation_data=generate_arrays_from_file(lines[num_train:], batch_size), 141 | validation_steps=max(1, num_val//batch_size), 142 | epochs=100, 143 | initial_epoch=50, 144 | callbacks=[checkpoint, reduce_lr,early_stopping]) 145 | --------------------------------------------------------------------------------