Retina Blood Vessel Segmentation using VGG16-UNET

Saif Gazali
3 min readAug 14, 2021

This is a continuation from the previous post.

The UNET model uses a Convolution-BatchNormalization-ReLU blocks of layers to create deep convolutional neural networks. The configuration of a specific layer can be seen in the appendix of the paper. The U-Net model architecture is used for the segmentation process rather than the traditional encoder-decoder model which involves taking image as input and down-sampling it for a few layers until a layer where in the image is up-sampled for a few layers and a final image is outputted. The UNET architecture also down-samples the image and up-samples it again but would have skip-connections between layers of same size in encoder and decoder which would allow the information to be shared between input and output.

The encoder layers in our UNET model will be using Pretrained layers from VGG-16 model whereas the other layers would be trained during the model training. Defining a method to return a covolutional block(Convolution-BatchNormalization-ReLU).

def conv_block(inputs,num_filters):
x = Conv2D(num_filters,3,padding='same')(inputs)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(num_filters,3,padding='same')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
return x

Defining our decoder block.

def define_decoder(inputs,skip_layer,num_filters):
init = RandomNormal(stddev=0.02)
x = Conv2DTranspose(num_filters,(2,2),strides=(2,2),padding='same',kernel_initializer=init)(inputs)
g = Concatenate()([x,skip_layer])
g = conv_block(g,num_filters)
return g

VGG16-UNET Model

Importing the VGG-16 model from Keras applications library and printing its summary.

vgg16 = VGG16(include_top=False,weights='imagenet')
vgg16.summary()

WE would select the layers according to our output shape required for each layer in our traditional encoder block of the UNET model. We require layers with output shape 512,256,128 and 64. Hence selecting the layers from the VGG16 model which satisfy the requirements.

def vgg16_unet(input_shape):
inputs = Input(shape=input_shape)
vgg16 = VGG16(include_top=False,weights='imagenet',input_tensor=inputs) # We will extract encoder layers based on their output shape from vgg16 model s1 = vgg16.get_layer('block1_conv2').output
s2 = vgg16.get_layer('block2_conv2').output
s3 = vgg16.get_layer('block3_conv3').output
s4 = vgg16.get_layer('block4_conv3').output
# bottleneck/bridege layer from vgg16
b1 = vgg16.get_layer('block5_conv3').output #32

# Decoder Block
d1 = define_decoder(b1,s4,512)
d2 = define_decoder(d1,s3,256)
d3 = define_decoder(d2,s2,128)
d4 = define_decoder(d3,s1,64)
#output layer
outputs = Conv2D(1,1,padding='same',activation='sigmoid')(d4)
model = Model(inputs,outputs)

return model

Defining our VGG16-UNET model and compiling.

model = vgg16_unet((256,256,3))model.compile(loss=dice_loss,optimizer=Adam(lr),metrics=[dice_coef,iou,Recall(),Precision()])

Training our model for 500 epochs.

model.fit(
train_dataset,
epochs=500,
validation_data=valid_dataset,
steps_per_epoch=train_steps,
validation_steps=test_steps)

Our Intersection over Union ratio has improved to around 78%.

Using our model to to plot segmented images for the test dataset and plotting it. The image plot has the retina image, its annotated image and our predicted image.

--

--