Spaces:
Runtime error
Runtime error
# coding=utf-8 | |
# Copyright 2021 The Deeplab2 Authors. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
"""This file contains code to build an ASPP layer. | |
Reference: | |
- [Rethinking Atrous Convolution for Semantic Image Segmentation]( | |
https://arxiv.org/pdf/1706.05587.pdf) | |
- [ParseNet: Looking Wider to See Better]( | |
https://arxiv.org/pdf/1506.04579.pdf). | |
""" | |
from absl import logging | |
import tensorflow as tf | |
from deeplab2.model import utils | |
from deeplab2.model.layers import convolutions | |
layers = tf.keras.layers | |
backend = tf.keras.backend | |
class ASPPConv(tf.keras.layers.Layer): | |
"""An atrous convolution for ASPP.""" | |
def __init__(self, | |
output_channels, | |
atrous_rate, | |
name, | |
bn_layer=tf.keras.layers.BatchNormalization): | |
"""Creates a atrous convolution layer for the ASPP. | |
This layer consists of an atrous convolution followed by a BatchNorm layer | |
and a ReLU activation. | |
Args: | |
output_channels: An integer specifying the number of output channels of | |
the convolution. | |
atrous_rate: An integer specifying the atrous/dilation rate of the | |
convolution. | |
name: A string specifying the name of this layer. | |
bn_layer: An optional tf.keras.layers.Layer that computes the | |
normalization (default: tf.keras.layers.BatchNormalization). | |
""" | |
super(ASPPConv, self).__init__(name=name) | |
self._conv_bn_act = convolutions.Conv2DSame( | |
output_channels, | |
kernel_size=3, | |
name='conv_bn_act', | |
atrous_rate=atrous_rate, | |
use_bias=False, | |
use_bn=True, | |
bn_layer=bn_layer, | |
activation='relu') | |
def call(self, input_tensor, training=False): | |
"""Performs a forward pass. | |
Args: | |
input_tensor: An input tensor of type tf.Tensor with shape [batch, height, | |
width, channels]. | |
training: A boolean flag indicating whether training behavior should be | |
used (default: False). | |
Returns: | |
The output tensor. | |
""" | |
return self._conv_bn_act(input_tensor, training=training) | |
class ASPPPool(tf.keras.layers.Layer): | |
"""A pooling layer for ASPP.""" | |
def __init__(self, | |
output_channels, | |
name, | |
bn_layer=tf.keras.layers.BatchNormalization): | |
"""Creates a pooling layer for the ASPP. | |
This layer consists of a global average pooling, followed by a convolution, | |
and by a BatchNorm layer and a ReLU activation. | |
Args: | |
output_channels: An integer specifying the number of output channels of | |
the convolution. | |
name: A string specifying the name of this layer. | |
bn_layer: An optional tf.keras.layers.Layer that computes the | |
normalization (default: tf.keras.layers.BatchNormalization). | |
""" | |
super(ASPPPool, self).__init__(name=name) | |
self._pool_size = (None, None) | |
self._conv_bn_act = convolutions.Conv2DSame( | |
output_channels, | |
kernel_size=1, | |
name='conv_bn_act', | |
use_bias=False, | |
use_bn=True, | |
bn_layer=bn_layer, | |
activation='relu') | |
def set_pool_size(self, pool_size): | |
"""Sets the pooling size of the pooling layer. | |
The default behavior of the pooling layer is global average pooling. A | |
custom pooling size can be set here. | |
Args: | |
pool_size: A tuple specifying the pooling size of the pooling layer. | |
Raises: | |
An error occurs if exactly one pooling dimension is set to 'None'. | |
""" | |
# If exactly one pooling dimension is 'None' raise an error. | |
if None in pool_size and pool_size != (None, None): | |
raise ValueError('The ASPP pooling layer requires that the pooling size ' | |
'is set explicitly for both dimensions. In case, global ' | |
'average pooling should be used, call ' | |
'reset_pooling_layer() or set both to None.') | |
self._pool_size = pool_size | |
logging.info('Global average pooling in the ASPP pooling layer was replaced' | |
' with tiled average pooling using the provided pool_size. ' | |
'Please make sure this behavior is intended.') | |
def get_pool_size(self): | |
return self._pool_size | |
def reset_pooling_layer(self): | |
"""Resets the pooling layer to global average pooling.""" | |
self._pool_size = (None, None) | |
def call(self, input_tensor, training=False): | |
"""Performs a forward pass. | |
Args: | |
input_tensor: An input tensor of type tf.Tensor with shape [batch, height, | |
width, channels]. | |
training: A boolean flag indicating whether training behavior should be | |
used (default: False). | |
Returns: | |
The output tensor. | |
""" | |
if tuple(self._pool_size) == (None, None): | |
# Global image pooling | |
pool_size = input_tensor.shape[1:3] | |
else: | |
# Tiled image pooling | |
pool_size = self._pool_size | |
x = backend.pool2d(input_tensor, pool_size, padding='valid', | |
pool_mode='avg') | |
x = self._conv_bn_act(x, training=training) | |
target_h = tf.shape(input_tensor)[1] | |
target_w = tf.shape(input_tensor)[2] | |
x = utils.resize_align_corners(x, [target_h, target_w]) | |
return x | |
class ASPP(tf.keras.layers.Layer): | |
"""An atrous spatial pyramid pooling layer.""" | |
def __init__(self, | |
output_channels, | |
atrous_rates, | |
aspp_use_only_1x1_proj_conv=False, | |
name='ASPP', | |
bn_layer=tf.keras.layers.BatchNormalization): | |
"""Creates an ASPP layer. | |
Args: | |
output_channels: An integer specifying the number of output channels of | |
each ASPP convolution layer. | |
atrous_rates: A list of three integers specifying the atrous/dilation rate | |
of each ASPP convolution layer. | |
aspp_use_only_1x1_proj_conv: Boolean, specifying if the ASPP five branches | |
are turned off or not. If True, the ASPP module is degenerated to one | |
1x1 convolution, projecting the input channels to `output_channels`. | |
name: A string specifying the name of this layer (default: 'ASPP'). | |
bn_layer: An optional tf.keras.layers.Layer that computes the | |
normalization (default: tf.keras.layers.BatchNormalization). | |
Raises: | |
ValueError: An error occurs when both atrous_rates does not contain 3 | |
elements and `aspp_use_only_1x1_proj_conv` is False. | |
""" | |
super(ASPP, self).__init__(name=name) | |
if not aspp_use_only_1x1_proj_conv and len(atrous_rates) != 3: | |
raise ValueError( | |
'The ASPP layers need exactly 3 atrous rates, but %d were given' % | |
len(atrous_rates)) | |
self._aspp_use_only_1x1_proj_conv = aspp_use_only_1x1_proj_conv | |
# Projection convolution is always used. | |
self._proj_conv_bn_act = convolutions.Conv2DSame( | |
output_channels, | |
kernel_size=1, | |
name='proj_conv_bn_act', | |
use_bias=False, | |
use_bn=True, | |
bn_layer=bn_layer, | |
activation='relu') | |
if not aspp_use_only_1x1_proj_conv: | |
self._conv_bn_act = convolutions.Conv2DSame( | |
output_channels, | |
kernel_size=1, | |
name='conv_bn_act', | |
use_bias=False, | |
use_bn=True, | |
bn_layer=bn_layer, | |
activation='relu') | |
rate1, rate2, rate3 = atrous_rates | |
self._aspp_conv1 = ASPPConv(output_channels, rate1, name='aspp_conv1', | |
bn_layer=bn_layer) | |
self._aspp_conv2 = ASPPConv(output_channels, rate2, name='aspp_conv2', | |
bn_layer=bn_layer) | |
self._aspp_conv3 = ASPPConv(output_channels, rate3, name='aspp_conv3', | |
bn_layer=bn_layer) | |
self._aspp_pool = ASPPPool(output_channels, name='aspp_pool', | |
bn_layer=bn_layer) | |
# Dropout is needed only when ASPP five branches are used. | |
self._proj_drop = layers.Dropout(rate=0.1) | |
def set_pool_size(self, pool_size): | |
"""Sets the pooling size of the ASPP pooling layer. | |
The default behavior of the pooling layer is global average pooling. A | |
custom pooling size can be set here. | |
Args: | |
pool_size: A tuple specifying the pooling size of the ASPP pooling layer. | |
""" | |
if not self._aspp_use_only_1x1_proj_conv: | |
self._aspp_pool.set_pool_size(pool_size) | |
def get_pool_size(self): | |
if not self._aspp_use_only_1x1_proj_conv: | |
return self._aspp_pool.get_pool_size() | |
else: | |
return (None, None) | |
def reset_pooling_layer(self): | |
"""Resets the pooling layer to global average pooling.""" | |
self._aspp_pool.reset_pooling_layer() | |
def call(self, input_tensor, training=False): | |
"""Performs a forward pass. | |
Args: | |
input_tensor: An input tensor of type tf.Tensor with shape [batch, height, | |
width, channels]. | |
training: A boolean flag indicating whether training behavior should be | |
used (default: False). | |
Returns: | |
The output tensor. | |
""" | |
if self._aspp_use_only_1x1_proj_conv: | |
x = self._proj_conv_bn_act(input_tensor, training=training) | |
else: | |
# Apply the ASPP module. | |
results = [] | |
results.append(self._conv_bn_act(input_tensor, training=training)) | |
results.append(self._aspp_conv1(input_tensor, training=training)) | |
results.append(self._aspp_conv2(input_tensor, training=training)) | |
results.append(self._aspp_conv3(input_tensor, training=training)) | |
results.append(self._aspp_pool(input_tensor, training=training)) | |
x = tf.concat(results, 3) | |
x = self._proj_conv_bn_act(x, training=training) | |
x = self._proj_drop(x, training=training) | |
return x | |