//  [python]
def resnet_fpn_backbone(image, num_blocks,resolution_requirement,
                        use_deformable=False, use_dilations=False,
                        tf_pad_reverse=False, finer_resolution=False,
                        freeze=0, use_gn=False, use_basic_block=False,
                        use_se=False, use_resnext=False):
  assert len(num_blocks) == 4
  shape2d = tf.shape(image)[2:]

  # this gets the nearest H, W that is a multiplier of 32
  # 720 -> 736, 1080 -> 1088
  mult = resolution_requirement * 1.0
  new_shape2d = tf.to_int32(tf.ceil(tf.to_float(shape2d) / mult) * mult)
  pad_shape2d = new_shape2d - shape2d

  channel = image.shape[1]  # [B, C, H, W]

  if use_gn:
    NormReLU = GNReLU
  else:
    NormReLU = BNReLU

  block_func = resnet_bottleneck
  if use_basic_block:
    block_func = resnet_basicblock
  if use_resnext:
    block_func = resnext_32x4d_bottleneck

  # tf_pad_reverse= True
  pad_base = maybe_reverse_pad(2, 3, tf_pad_reverse)

  l = tf.pad(
      image, [
          [0, 0], [0, 0],
          [pad_base[0], pad_base[1] + pad_shape2d[0]],
          [pad_base[0], pad_base[1] + pad_shape2d[1]]])
  l.set_shape([None, channel, None, None])

  # 720, 1280 -> 736, 1280 / 1080, 1920 -> 1088, 1920, a multiplier of 32
  # actually 741/1093 due to the pad_base of [2, 3]
  # pad_base is for first conv and max pool
  #l = tf.Print(l, data=[tf.shape(l)], summarize=10)

  # rest is the same as c4 backbone
  l = conv2d(
      l, 64, 7, stride=2, activation=NormReLU, padding="VALID",
      scope="conv0", use_bias=False, data_format="NCHW")
  c1 = l
  l = tf.pad(
      l, [[0, 0], [0, 0], maybe_reverse_pad(0, 1, tf_pad_reverse),
          maybe_reverse_pad(0, 1, tf_pad_reverse)])  # H+1,W+1

  l = MaxPooling(
      l, shape=3, stride=2, padding="VALID",
      scope="pool0", data_format="NCHW")
  # here 4x down already, so smallest anchor box can on use these
  # 4X down from 1088 and 1920
  #l = tf.Print(l, data=[tf.shape(l)], summarize=10) # [1 64 272 480]

  # resnet_group output channel is 64*4
  c2 = resnet_group(
      l, "group0", block_func, 64, num_blocks[0], stride=1,
      tf_pad_reverse=tf_pad_reverse, use_gn=use_gn, use_se=use_se)
  if freeze >= 0:  # tensorpack setting
    c2 = tf.stop_gradient(c2)

  #c2 = tf.Print(c2, data=[tf.shape(c2)], summarize=10) # [1 256 272 480] #

  # for dilated conv and deformable conv, we will add to the last 3
  # block in each group
  mbn = 3 # modify the last 3 conv block
  # [4]
  c3 = resnet_group(
      c2, "group1", block_func, 128, num_blocks[1], dilations=1,
      modified_block_num=mbn, stride=2, use_deformable=use_deformable,
      tf_pad_reverse=tf_pad_reverse, use_gn=use_gn, use_se=use_se)
  if freeze >= 1:
    c3 = tf.stop_gradient(c3)

  # [23]
  c4 = resnet_group(
      c3, "group2", block_func, 256, num_blocks[2], dilations=1,
      modified_block_num=mbn, stride=2, use_deformable=use_deformable,
      tf_pad_reverse=tf_pad_reverse, use_gn=use_gn, use_se=use_se)
  if freeze >= 2:
    c4 = tf.stop_gradient(c4)
  #c4 = tf.Print(c4, data=[tf.shape(c4)], summarize=10) # [1 1024 68 120]

  # [3]
  # people change the last stride to 1 and with dilations 2?
  c5 = resnet_group(
      c4, "group3", block_func, 512, num_blocks[3],
      dilations=2 if use_dilations else 1,
      use_deformable=use_deformable, modified_block_num=mbn,
      stride=2, tf_pad_reverse=tf_pad_reverse, use_gn=use_gn, use_se=use_se)
  # [1 2048 34 60] same for dilation or not
  #c5 = tf.Print(c5, data=[tf.shape(c5)], summarize=10)

  if freeze >= 3:
    c5 = tf.stop_gradient(c5)
  ## 32x downsampling up to now
  # size of c5: ceil(input/32)
  return c2, c3, c4, c5