text: '''
  Code from my DenseNet repository : https://github.com/titu1994/DenseNet
  '''
  #Original file by Titu1994, changed for this project
  
  
  from tensorflow.keras.models import Model
  from tensorflow.keras.layers import Dense, Dropout, Activation
  from tensorflow.keras.layers import Convolution2D
  from tensorflow.keras.layers import AveragePooling2D
  from tensorflow.keras.layers import GlobalAveragePooling2D
  from tensorflow.keras.layers import Input, concatenate
  from tensorflow.keras.layers import BatchNormalization
  from tensorflow.keras.regularizers import l2
  import keras.backend as K
  
  def conv_block(ip, nb_filter, dropout_rate=None, weight_decay=1E-4):
      ''' Apply BatchNorm, Relu 3x3, Conv2D, optional dropout
  
      Args:
          ip: Input keras tensor
          nb_filter: number of filters
          dropout_rate: dropout rate
          weight_decay: weight decay factor
  
      Returns: keras tensor with batch_norm, relu and convolution2d added
  
      '''
  
      concat_axis = 1 if K.image_data_format() == "th" else -1
  
      x = BatchNormalization(axis=concat_axis, gamma_regularizer=l2(weight_decay), #mode=0 was deleted from original implementation
                             beta_regularizer=l2(weight_decay))(ip)
      x = Activation('relu')(x)
      x = Convolution2D(nb_filter, (3, 3), kernel_initializer="he_uniform", padding="same", use_bias=False,
                        kernel_regularizer                                                                                                                                                                                                                                                                                                                                                        =l2(weight_decay))(x)
      if dropout_rate:
          x = Dropout(dropout_rate)(x)
  
      return x
  
  
  def transition_block(ip, nb_filter, dropout_rate=None, weight_decay=1E-4):
      ''' Apply BatchNorm, Relu 1x1, Conv2D, optional dropout and Maxpooling2D
  
      Args:
          ip: keras tensor
          nb_filter: number of filters
          dropout_rate: dropout rate
          weight_decay: weight decay factor
  
      Returns: keras tensor, after applying batch_norm, relu-conv, dropout, maxpool
  
      '''
  
      concat_axis = 1 if K.image_data_format() == "th" else -1
  
      x = BatchNormalization(axis=concat_axis, gamma_regularizer=l2(weight_decay),
                             beta_regularizer=l2(weight_decay))(ip)
      x = Activation('relu')(x)
      x = Convolution2D(nb_filter, 1, 1, kernel_initializer="he_uniform", padding="same", use_bias=False,
                        kernel_regularizer=l2(weight_decay))(x)
      if dropout_rate:
          x = Dropout(dropout_rate)(x)
      x = AveragePooling2D((2, 2), strides=(2, 2))(x)
  
      return x
  
  
  def dense_block(x, nb_layers, nb_filter, growth_rate, dropout_rate=None, weight_decay=1E-4):
      ''' Build a dense_block where the output of each conv_block is fed to subsequent ones
  
      Args:
          x: keras tensor
          nb_layers: the number of layers of conv_block to append to the model.
          nb_filter: number of filters
          growth_rate: growth rate
          dropout_rate: dropout rate
          weight_decay: weight decay factor
  
      Returns: keras tensor with nb_layers of conv_block appended
  
      '''
  
      concat_axis = 1 if K.image_data_format() == "th" else -1
  
      feature_list = [x]
  
      for i in range(nb_layers):
          x = conv_block(x, growth_rate, dropout_rate, weight_decay)
          feature_list.append(x)
          x = concatenate(feature_list, axis=concat_axis) #mode="concat" removed
          nb_filter += growth_rate
  
      return x, nb_filter
  
  
  def create_dense_net(nb_classes, img_dim, depth=40, nb_dense_block=1, growth_rate=12, nb_filter=16, dropout_rate=None,
                       weight_decay=1E-4, verbose=True):
      ''' Build the create_dense_net model
  
      Args:
          nb_classes: number of classes
          img_dim: tuple of shape (channels, rows, columns) or (rows, columns, channels)
          depth: number or layers
          nb_dense_block: number of dense blocks to add to end
          growth_rate: number of filters to add
          nb_filter: number of filters
          dropout_rate: dropout rate
          weight_decay: weight decay
  
      Returns: keras tensor with nb_layers of conv_block appended
  
      '''
  
      model_input = Input(shape=img_dim)
  
      concat_axis = 1 if K.image_data_format() == "th" else -1
  
      assert (depth - 4) % 3 == 0, "Depth must be 3 N + 4"
  
      # layers in each dense block
      nb_layers = int((depth - 4) / 3)
  
      # Initial convolution
      x = Convolution2D(nb_filter, (3, 3), kernel_initializer="he_uniform", padding="same", name="initial_conv2D",
                        kernel_regularizer=l2(weight_decay), use_bias=False)(model_input)
  
      # Add dense blocks
      for block_idx in range(nb_dense_block - 1):
          x, nb_filter = dense_block(x, nb_layers, nb_filter, growth_rate, dropout_rate=dropout_rate,
                                     weight_decay=weight_decay)
          # add transition_block
          x = transition_block(x, nb_filter, dropout_rate=dropout_rate, weight_decay=weight_decay)
  
      # The last dense_block does not have a transition_block
      x, nb_filter = dense_block(x, nb_layers, nb_filter, growth_rate, dropout_rate=dropout_rate,
                                 weight_decay=weight_decay)
  
      x = BatchNormalization(axis=concat_axis, gamma_regularizer=l2(weight_decay),
                             beta_regularizer=l2(weight_decay))(x)
      x = Activation('relu')(x)
      x = GlobalAveragePooling2D()(x)
      x = Dense(nb_classes, activation='softmax', kernel_regularizer=l2(weight_decay), bias_regularizer=l2(weight_decay))(x)
  
      densenet = Model(inputs=model_input, outputs=x, name="create_dense_net")
  
      if verbose: print("DenseNet-%d-%d created." % (depth, growth_rate))
  
      return densenet