自从2015年,全卷积网络(FCN)诞生,图像分割在深度学习领域掀起旋风,同年稍晚Unet诞生,号称可用极少数据获取优质的结果,在数据可贵的医疗影像领域称王称霸。
2018年对Unet的改进Unet++诞生。
此外还有DeepLap、PSPNet等其他优质的图像分割网络,但它们的本质基本都是编码器-解码器(encode-decode)网络。编码器通常是一个预训练的分类网络,像 VGG、ResNet等,然后是一个解码器网络。这些架构不同的地方主要在于解码器网络。
Unet
- 网络结构
Unet网络结构如其名呈现一个U字形,即由卷积和池化单元构成,左半边为编码器即如传统的分类网络是“下采样阶段”,右半边为解码器是“上采样阶段”,中间的灰色箭头为跳跃连接,将浅层的特征与深层的特征拼接,因为浅层通常可以抓取图像的一些简单的特征,比如边界,颜色。深层经过的卷积操作多抓取到图像的一些说不清道不明的抽象特征,将浅深同时利用起来为上上策,同时允许解码器学习在编码器池化下采样中丢失的相关特征。纵观整个网络Unet其实有点带有残差(residual)结构的思想。结构如下两幅图:
- Keras实现unet
论文“上采样”采用的线性插值方式,keras的api即UpSampling2D,这里采用转置卷积的方式keras的api即Conv2DTranspose。论文中并没有采用padding,因此输入和输出大小并不一样,这里采用padding保持输入与输出大小一致。
def unet(input_size = (512,512,3),base_filter_num=64):
inputs = Input(input_size)
conv1 = Conv2D(base_filter_num, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs)
conv1 = Conv2D(base_filter_num, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1)
pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
conv2 = Conv2D(base_filter_num*2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1)
conv2 = Conv2D(base_filter_num*2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)
pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
conv3 = Conv2D(base_filter_num*4, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2)
conv3 = Conv2D(base_filter_num*4, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3)
pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)
conv4 = Conv2D(base_filter_num*8, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3)
conv4 = Conv2D(base_filter_num*8, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4)
pool4 = MaxPooling2D(pool_size=(2, 2))(conv4)
conv5 = Conv2D(base_filter_num*16, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4)
conv5 = Conv2D(base_filter_num*16, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5)
up6 = Conv2DTranspose(base_filter_num*8, (2, 2), strides=(2, 2), padding='same')(conv5)
# up6 = Conv2D(base_filter_num*8, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv5))
merge6 = concatenate([conv4,up6], axis = -1)
conv6 = Conv2D(base_filter_num*8, 3, activation = 'relu', padding = 'same', kernel_initializer =