text: # coding: utf8
  # Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve.
  #
  # 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.
  
  from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import paddle
  import paddle.fluid as fluid
  from utils.config import cfg
  import contextlib
  
  bn_regularizer = fluid.regularizer.L2DecayRegularizer(regularization_coeff=0.0)
  name_scope = ""
  
  
  @contextlib.contextmanager
  def scope(name):
      global name_scope
      bk = name_scope
      name_scope = name_scope + name + '/'
      yield
      name_scope = bk
  
  
  def max_pool(input, kernel, stride, padding):
      data = fluid.layers.pool2d(
          input,
          pool_size=kernel,
          pool_type='max',
          pool_stride=stride,
          pool_padding=padding)
      return data
  
  
  def avg_pool(input, kernel, stride, padding=0):
      data = fluid.layers.pool2d(
          input,
          pool_size=kernel,
          pool_type='avg',
          pool_stride=stride,
          pool_padding=padding)
      return data
  
  
  def group_norm(input, G, eps=1e-5, param_attr=None, bias_attr=None):
      N, C, H, W = input.shape
      if C % G != 0:
          # print "group can not divide channle:", C, G
          for d in range(10):
              for t in [d, -d]:
                  if G + t <= 0: continue
                  if C % (G + t) == 0:
                      G = G + t
                      break
              if C % G == 0:
                  # print "use group size:", G
                  break
      assert C % G == 0
      x = fluid.layers.group_norm(
          input,
          groups=G,
          param_attr=param_attr,
          bias_attr=bias_attr,
          name=name_scope + 'group_norm')
      return x
  
  
  def bn(*args, **kargs):
      if cfg.MODEL.DEFAULT_NORM_TYPE == 'bn':
          with scope('BatchNorm'):
              return fluid.layers.batch_norm(
                  *args,
                  epsilon=cfg.MODEL.DEFAULT_EPSILON,
                  momentum=cfg.MODEL.BN_MOMENTUM,
                  param_attr=fluid.ParamAttr(
                      name=name_scope + 'gamma', regularizer=bn_regularizer),
                  bias_attr=fluid.ParamAttr(
                      name=name_scope + 'beta', regularizer=bn_regularizer),
                  moving_mean_name=name_scope + 'moving_mean',
                  moving_variance_name=name_scope + 'moving_variance',
                  **kargs)
      elif cfg.MODEL.DEFAULT_NORM_TYPE == 'gn':
          with scope('GroupNorm'):
              return group_norm(
                  args[0],
                  cfg.MODEL.DEFAULT_GROUP_NUMBER,
                  eps=cfg.MODEL.DEFAULT_EPSILON,
                  param_attr=fluid.ParamAttr(
                      name=name_scope + 'gamma', regularizer=bn_regularizer),
                  bias_attr=fluid.ParamAttr(
                      name=name_scope + 'beta', regularizer=bn_regularizer))
      else:
          raise Exception("Unsupport norm type:" + cfg.MODEL.DEFAULT_NORM_TYPE)
  
  
  def bn_relu(data):
      return fluid.layers.relu(bn(data))
  
  
  def relu(data):
      return fluid.layers.relu(data)
  
  
  def conv(*args, **kargs):
      kargs['param_attr'] = name_scope + 'weights'
      if 'bias_attr' in kargs and kargs['bias_attr']:
          kargs['bias_attr'] = fluid.ParamAttr(
              name=name_scope + 'biases',
              regularizer=None,
              initializer=fluid.initializer.ConstantInitializer(value=0.0))
      else:
          kargs['bias_attr'] = False
      return fluid.layers.conv2d(*args, **kargs)
  
  
  def deconv(*args, **kargs):
      kargs['param_attr'] = name_scope + 'weights'
      if 'bias_attr' in kargs and kargs['bias_attr']:
          kargs['bias_attr'] = name_scope + 'biases'
      else:
          kargs['bias_attr'] = False
      return fluid.layers.conv2d_transpose(*args, **kargs)
  
  
  def separate_conv(input, channel, stride, filter, dilation=1, act=None):
      param_attr = fluid.ParamAttr(
          name=name_scope + 'weights',
          regularizer=fluid.regularizer.L2DecayRegularizer(
              regularization_coeff=0.0),
          initializer=fluid.initializer.TruncatedNormal(loc=0.0, scale=0.33))
      with scope('depthwise'):
          input = conv(
              input,
              input.shape[1],
              filter,
              stride,
              groups=input.shape[1],
              padding=(filter // 2) * dilation,
              dilation=dilation,
              use_cudnn=False,
              param_attr=param_attr)
          input = bn(input)
          if act: input = act(input)
  
      param_attr = fluid.ParamAttr(
          name=name_scope + 'weights',
          regularizer=None,
          initializer=fluid.initializer.TruncatedNormal(loc=0.0, scale=0.06))
      with scope('pointwise'):
          input = conv(
              input, channel, 1, 1, groups=1, padding=0, param_attr=param_attr)
          input = bn(input)
          if act: input = act(input)
      return input
  
  
  def conv_bn_layer(input,
                    filter_size,
                    num_filters,
                    stride,
                    padding,
                    channels=None,
                    num_groups=1,
                    if_act=True,
                    name=None,
                    use_cudnn=True):
      conv = fluid.layers.conv2d(
          input=input,
          num_filters=num_filters,
          filter_size=filter_size,
          stride=stride,
          padding=padding,
          groups=num_groups,
          act=None,
          use_cudnn=use_cudnn,
          param_attr=fluid.ParamAttr(name=name + '_weights'),
          bias_attr=False)
      bn_name = name + '_bn'
      bn = fluid.layers.batch_norm(
          input=conv,
          param_attr=fluid.ParamAttr(name=bn_name + "_scale"),
          bias_attr=fluid.ParamAttr(name=bn_name + "_offset"),
          moving_mean_name=bn_name + '_mean',
          moving_variance_name=bn_name + '_variance')
      if if_act:
          return fluid.layers.relu6(bn)
      else:
          return bn
  
  
  def fuse(input,
           num_classes,
           filter_size1=3,
           filter_size2=3,
           num_filters1=512,
           num_filters2=256):
      param_attr = fluid.ParamAttr(
          name='logit_weights',
          regularizer=fluid.regularizer.L2DecayRegularizer(
              regularization_coeff=0.0),
          initializer=fluid.initializer.TruncatedNormal(loc=0.0, scale=0.01))
  
      fuse1 = conv_bn_layer(
          input,
          filter_size1,
          num_filters1,
          stride=1,
          padding=1,
          name="fuse1",
          if_act=False)
      fuse1 = fluid.layers.relu(fuse1)
  
      fuse2 = conv_bn_layer(
          fuse1,
          filter_size2,
          num_filters2,
          stride=1,
          padding=1,
          name="fuse2",
          if_act=False)
      fuse2 = fluid.layers.relu(fuse2)
  
      logit = conv(
          fuse2,
          num_classes,
          3,
          stride=1,
          padding=1,
          bias_attr=True,
          param_attr=param_attr)
  
      return logit
  