text: # Copyright 2018 The TensorFlow Authors All Rights Reserved.
  #
  # 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.
  # ==============================================================================
  
  """Extracts features for different models."""
  import functools
  
  import tensorflow as tf
  
  from backbones.vgg_16 import vgg_16, vgg_arg_scope
  from backbones.xception import xception_65, xception_arg_scope
  from tf_ops.wrap_ops import arg_scope
  
  # A map from network name to network function.
  networks_map = {
      'vgg_16': vgg_16,
      'xception_65': xception_65,
  }
  
  # A map from network name to network arg scope.
  arg_scopes_map = {
      'vgg_16': vgg_arg_scope,
      'xception_65': xception_arg_scope,
  }
  
  # Names for end point features.
  DECODER_END_POINTS = 'decoder_end_points'
  
  # A dictionary from network name to a map of end point features.
  networks_to_feature_maps = {
      'xception_65': {
          DECODER_END_POINTS: [
              'entry_flow/block2/unit_1/xception_module/separable_conv2/pointwise_conv/BatchNorm2d/batchnorm/add_1:0',
          ],
      }
  }
  
  # A map from feature extractor name to the network name scope used in the
  # ImageNet pretrained versions of these models.
  name_scope = {
      'vgg_16': 'vgg_16',
      'xception_65': 'xception_65',
  }
  
  # Mean pixel value.
  _MEAN_RGB = [123.15, 115.90, 103.06]
  
  
  def _preprocess_subtract_imagenet_mean(inputs):
      """Subtract Imagenet mean RGB value."""
      mean_rgb = tf.reshape(_MEAN_RGB, [1, 1, 1, 3])
      return inputs - mean_rgb
  
  
  def _preprocess_zero_mean_unit_range(inputs):
      """Map image values from [0, 255] to [-1, 1]."""
      return (2.0 / 255.0) * tf.to_float(inputs) - 1.0
  
  
  _PREPROCESS_FN = {
      'vgg_16': _preprocess_zero_mean_unit_range,
      'xception_65': _preprocess_zero_mean_unit_range,
  }
  
  
  def mean_pixel(model_variant=None):
      """Gets mean pixel value.
  
      This function returns different mean pixel value, depending on the input
      model_variant which adopts different preprocessing functions. We currently
      handle the following preprocessing functions:
      (1) _preprocess_subtract_imagenet_mean. We simply return mean pixel value.
      (2) _preprocess_zero_mean_unit_range. We return [127.5, 127.5, 127.5].
      The return values are used in a way that the padded regions after
      pre-processing will contain value 0.
  
      Args:
        model_variant: Model variant (string) for feature extraction. For
          backwards compatibility, model_variant=None returns _MEAN_RGB.
  
      Returns:
        Mean pixel value.
      """
      if model_variant is None:
          return _MEAN_RGB
      else:
          return [127.5, 127.5, 127.5]
  
  
  def extract_features(images,
                       output_stride=8,
                       multi_grid=None,
                       depth_multiplier=1.0,
                       final_endpoint=None,
                       model_variant=None,
                       weight_decay=0.0001,
                       reuse=tf.AUTO_REUSE,
                       is_training=False,
                       fine_tune_batch_norm=False,
                       regularize_depthwise=False,
                       preprocess_images=True,
                       num_classes=None,
                       global_pool=False):
      """Extracts features by the parituclar model_variant.
  
      Args:
        images: A tensor of size [batch, height, width, channels].
        output_stride: The ratio of input to output spatial resolution.
        multi_grid: Employ a hierarchy of different atrous rates within network.
        depth_multiplier: Float multiplier for the depth (number of channels)
          for all convolution ops used in MobileNet.
        final_endpoint: The MobileNet endpoint to construct the network up to.
        model_variant: Model variant for feature extraction.
        weight_decay: The weight decay for model variables.
        reuse: Reuse the model variables or not.
        is_training: Is training or not.
        fine_tune_batch_norm: Fine-tune the batch norm parameters or not.
        regularize_depthwise: Whether or not apply L2-norm regularization on the
          depthwise convolution weights.
        preprocess_images: Performs preprocessing on images or not. Defaults to
          True. Set to False if preprocessing will be done by other functions. We
          supprot two types of preprocessing: (1) Mean pixel substraction and (2)
          Pixel values normalization to be [-1, 1].
        num_classes: Number of classes for image classification task. Defaults
          to None for dense prediction tasks.
        global_pool: Global pooling for image classification task. Defaults to
          False, since dense prediction tasks do not use this.
  
      Returns:
        features: A tensor of size [batch, feature_height, feature_width,
          feature_channels], where feature_height/feature_width are determined
          by the images height/width and output_stride.
        end_points: A dictionary from components of the network to the corresponding
          activation.
  
      Raises:
        ValueError: Unrecognized model variant.
      """
      if 'xception' in model_variant:
          net_arg_scope = arg_scopes_map[model_variant](
              weight_decay=weight_decay,
              batch_norm_decay=0.9997,
              batch_norm_epsilon=1e-3,
              batch_norm_scale=True,
              regularize_depthwise=regularize_depthwise)
          features, end_points = get_network(
              model_variant, preprocess_images, net_arg_scope)(
              inputs=images,
              num_classes=num_classes,
              is_training=(is_training and fine_tune_batch_norm),
              global_pool=global_pool,
              output_stride=output_stride,
              multi_grid=multi_grid,
              reuse=reuse,
              scope=name_scope[model_variant])
      elif 'mobilenet' in model_variant:
          net_arg_scope = arg_scopes_map[model_variant](
              is_training=(is_training and fine_tune_batch_norm),
              weight_decay=weight_decay)
          features, end_points = get_network(
              model_variant, preprocess_images, net_arg_scope)(
              inputs=images,
              depth_multiplier=depth_multiplier,
              output_stride=output_stride,
              reuse=reuse,
              scope=name_scope[model_variant],
              final_endpoint=final_endpoint)
      else:
          raise ValueError('Unknown model variant %s.' % model_variant)
  
      return features, end_points
  
  
  def get_network(network_name, preprocess_images=False, net_arg_scope=None):
      """Gets the network.
  
      Args:
        network_name: Network name.
        preprocess_images: Preprocesses the images or not.
        net_arg_scope: Optional, arg_scope to build the network. If not provided the
          default arg_scope of the network would be used.
  
      Returns:
        A network function that is used to extract features.
  
      Raises:
        ValueError: network is not supported.
      """
      if network_name not in networks_map:
          raise ValueError('Unsupported network %s.' % network_name)
      net_arg_scope = net_arg_scope or arg_scopes_map[network_name]()
  
      def _identity_function(inputs):
          return inputs
  
      if preprocess_images:
          preprocess_function = _PREPROCESS_FN[network_name]
      else:
          preprocess_function = _identity_function
      # nake function
      func = networks_map[network_name]
  
      @functools.wraps(func)
      def network_fn(inputs, *args, **kwargs):
          with arg_scope(net_arg_scope):
              return func(preprocess_function(inputs), *args, **kwargs)
  
      return network_fn
  