text: # --------------------------------------------------------
  # Tensorflow Faster R-CNN
  # Licensed under The MIT License [see LICENSE for details]
  # Written by Zheqi He and Xinlei Chen
  # --------------------------------------------------------
  from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  
  import tensorflow as tf
  import tensorflow.contrib.slim as slim
  from tensorflow.contrib.slim import losses
  from tensorflow.contrib.slim import arg_scope
  from tensorflow.contrib.slim.python.slim.nets import resnet_utils
  from tensorflow.contrib.slim.python.slim.nets import resnet_v1
  from tensorflow.contrib.slim.python.slim.nets.resnet_v1 import resnet_v1_block
  import numpy as np
  
  from nets.network import Network
  from model.config import cfg
  
  def resnet_arg_scope(is_training=True,
                       weight_decay=cfg.TRAIN.WEIGHT_DECAY,
                       batch_norm_decay=0.997,
                       batch_norm_epsilon=1e-5,
                       batch_norm_scale=True):
    batch_norm_params = {
      'is_training': cfg.TRAIN.BN_TRAIN and is_training,
      'decay': batch_norm_decay,
      'epsilon': batch_norm_epsilon,
      'scale': batch_norm_scale,
      'trainable': cfg.TRAIN.BN_TRAIN,
      'updates_collections': tf.GraphKeys.UPDATE_OPS
    }
  
    with arg_scope(
        [slim.conv2d],
        weights_regularizer=slim.l2_regularizer(weight_decay),
        weights_initializer=slim.variance_scaling_initializer(),
        trainable=is_training,
        activation_fn=tf.nn.relu,
        normalizer_fn=slim.batch_norm,
        normalizer_params=batch_norm_params):
      with arg_scope([slim.batch_norm], **batch_norm_params) as arg_sc:
        return arg_sc
  
  class resnetv1_dense(Network):
    def __init__(self, batch_size=1, num_layers=50):
      Network.__init__(self, batch_size=batch_size)
      self._num_layers = num_layers
      self._resnet_scope = 'resnet_v1_%d' % num_layers
  
    def _crop_pool_layer(self, bottom, rois, name):
      with tf.variable_scope(name) as scope:
        batch_ids = tf.squeeze(tf.slice(rois, [0, 0], [-1, 1], name="batch_id"), [1])
        # Get the normalized coordinates of bboxes
        bottom_shape = tf.shape(bottom)
        height = (tf.to_float(bottom_shape[1]) - 1.) * np.float32(self._feat_stride[0])
        width = (tf.to_float(bottom_shape[2]) - 1.) * np.float32(self._feat_stride[0])
        x1 = tf.slice(rois, [0, 1], [-1, 1], name="x1") / width
        y1 = tf.slice(rois, [0, 2], [-1, 1], name="y1") / height
        x2 = tf.slice(rois, [0, 3], [-1, 1], name="x2") / width
        y2 = tf.slice(rois, [0, 4], [-1, 1], name="y2") / height
        # Won't be back-propagated to rois anyway, but to save time
        bboxes = tf.stop_gradient(tf.concat([y1, x1, y2, x2], 1))
        if cfg.RESNET.MAX_POOL:
          pre_pool_size = cfg.POOLING_SIZE * 2
          crops = tf.image.crop_and_resize(bottom, bboxes, tf.to_int32(batch_ids), [pre_pool_size, pre_pool_size],
                                           name="crops")
          crops = slim.max_pool2d(crops, [2, 2], padding='SAME')
        else:
          crops = tf.image.crop_and_resize(bottom, bboxes, tf.to_int32(batch_ids), [cfg.POOLING_SIZE, cfg.POOLING_SIZE],
                                           name="crops")
      return crops
  
    # Do the first few layers manually, because 'SAME' padding can behave inconsistently
    # for images of different sizes: sometimes 0, sometimes 1
    def _build_base(self):
      with tf.variable_scope(self._resnet_scope, self._resnet_scope):
        net = resnet_utils.conv2d_same(self._image, 64, 7, stride=2, scope='conv1')
        net = tf.pad(net, [[0, 0], [1, 1], [1, 1], [0, 0]])
        net = slim.max_pool2d(net, [3, 3], stride=2, padding='VALID', scope='pool1')
  
      return net
  
    def _build_network(self, sess, is_training=True):
      # select initializers
      if cfg.TRAIN.TRUNCATED:
        initializer = tf.truncated_normal_initializer(mean=0.0, stddev=0.01)
        initializer_bbox = tf.truncated_normal_initializer(mean=0.0, stddev=0.001)
      else:
        initializer = tf.random_normal_initializer(mean=0.0, stddev=0.01)
        initializer_bbox = tf.random_normal_initializer(mean=0.0, stddev=0.001)
  
      # choose different blocks for different number of layers
      if self._num_layers == 50:
        blocks = [resnet_v1_block('block1', base_depth=64, num_units=3, stride=2),
                  resnet_v1_block('block2', base_depth=128, num_units=4, stride=2),
                  # use stride 1 for the last conv4 layer
                  resnet_v1_block('block3', base_depth=256, num_units=6, stride=1),
                  resnet_v1_block('block4', base_depth=512, num_units=3, stride=1)]
  
      elif self._num_layers == 101:
        blocks = [resnet_v1_block('block1', base_depth=64, num_units=3, stride=2),
                  resnet_v1_block('block2', base_depth=128, num_units=4, stride=2),
                  # use stride 1 for the last conv4 layer
                  resnet_v1_block('block3', base_depth=256, num_units=23, stride=1),
                  resnet_v1_block('block4', base_depth=512, num_units=3, stride=1)]
  
      elif self._num_layers == 152:
        blocks = [resnet_v1_block('block1', base_depth=64, num_units=3, stride=2),
                  resnet_v1_block('block2', base_depth=128, num_units=8, stride=2),
                  # use stride 1 for the last conv4 layer
                  resnet_v1_block('block3', base_depth=256, num_units=36, stride=1),
                  resnet_v1_block('block4', base_depth=512, num_units=3, stride=1)]
  
      else:
        # other numbers are not supported
        raise NotImplementedError
  
      assert (0 <= cfg.RESNET.FIXED_BLOCKS <= 3)
      # Now the base is always fixed during training
      with slim.arg_scope(resnet_arg_scope(is_training=False)):
        net_conv = self._build_base()
        base_output = net_conv
      if cfg.RESNET.FIXED_BLOCKS > 0:
        with slim.arg_scope(resnet_arg_scope(is_training=False)):
          net_conv, end_points_initial = resnet_v1.resnet_v1(net_conv,
                                       blocks[0:cfg.RESNET.FIXED_BLOCKS],
                                       global_pool=False,
                                       include_root_block=False,
                                       scope=self._resnet_scope)
      if cfg.RESNET.FIXED_BLOCKS < 3:
        with slim.arg_scope(resnet_arg_scope(is_training=is_training)):
          net_conv, end_points_initial = resnet_v1.resnet_v1(net_conv,
                                             blocks[cfg.RESNET.FIXED_BLOCKS:-1],
                                             global_pool=False,
                                             include_root_block=False,
                                             scope=self._resnet_scope)
  
      self._act_summaries.append(net_conv)
      self._layers['head'] = net_conv
      with tf.variable_scope(self._resnet_scope, self._resnet_scope):
        # build the anchors for the image
        self._anchor_component()
        # region proposal network
        rois = self._region_proposal(net_conv, is_training, initializer)
        # region of interest pooling
        if cfg.POOLING_MODE == 'crop':
          pool5 = self._crop_pool_layer(net_conv, rois, "pool5")
        else:
          raise NotImplementedError
  
      with slim.arg_scope(resnet_arg_scope(is_training=is_training)):
        fc7, end_points_last = resnet_v1.resnet_v1(pool5,
                                     blocks[-1:],
                                     global_pool=False,
                                     include_root_block=False,
                                     scope=self._resnet_scope)
  
        print ("Layer dimensions")
        print (base_output.get_shape())
        print (end_points_initial['resnet_v1_152/block2'].get_shape())
        print (end_points_initial['resnet_v1_152/block3'].get_shape())
        concatenated_features_lower = tf.concat(values=[tf.reduce_mean(base_output, axis=[1, 2]), \
                                                        tf.reduce_mean(end_points_initial['resnet_v1_152/block2'], axis=[1, 2]), \
                                                        tf.reduce_mean(end_points_initial['resnet_v1_152/block3'], axis=[1, 2])], axis=1)
        print ("Concatenated lower features dimensions")
        feature_size = concatenated_features_lower.get_shape().dims[1].value
        print (feature_size)
        # concatenated_features = tf.reshape(tf.tile(concatenated_features_lower, [1, 256]), [-1, tf.shape(concatenated_features_lower)[1]])
        concatenated_features = tf.reshape(tf.tile(concatenated_features_lower, [1, 256]), [256, feature_size])
        print (concatenated_features.get_shape())
        concatenated_features = tf.concat(values=[concatenated_features, tf.reduce_mean(end_points_last['resnet_v1_152/block4'], axis=[1, 2])], axis=1)
        print (concatenated_features.get_shape())
  
      with tf.variable_scope(self._resnet_scope, self._resnet_scope):
        # average pooling done by reduce_mean
        fc7 = tf.reduce_mean(fc7, axis=[1, 2])
        print (fc7.get_shape())
        final_feature_vec = tf.concat(values=[concatenated_features, fc7], axis=1)
        print ("Final feature vector dimensions")
        print (final_feature_vec.get_shape())
        # region classification
        cls_prob, bbox_pred = self._region_classification(fc7, is_training, 
                                                          initializer, initializer_bbox)
  
      self._score_summaries.update(self._predictions)
  
      return rois, cls_prob, bbox_pred
  
    def get_variables_to_restore(self, variables, var_keep_dic):
      variables_to_restore = []
  
      for v in variables:
        # exclude the first conv layer to swap RGB to BGR
        if v.name == (self._resnet_scope + '/conv1/weights:0'):
          self._variables_to_fix[v.name] = v
          continue
        if v.name.split(':')[0] in var_keep_dic:
          print('Variables restored: %s' % v.name)
          variables_to_restore.append(v)
  
      return variables_to_restore
  
    def fix_variables(self, sess, pretrained_model):
      print('Fix Resnet V1 layers..')
      with tf.variable_scope('Fix_Resnet_V1') as scope:
        with tf.device("/cpu:0"):
          # fix RGB to BGR
          conv1_rgb = tf.get_variable("conv1_rgb", [7, 7, 3, 64], trainable=False)
          restorer_fc = tf.train.Saver({self._resnet_scope + "/conv1/weights": conv1_rgb})
          restorer_fc.restore(sess, pretrained_model)
  
          sess.run(tf.assign(self._variables_to_fix[self._resnet_scope + '/conv1/weights:0'], 
                             tf.reverse(conv1_rgb, [2])))
  