text: import os
  
  import paddle
  import paddle.nn as nn
  import paddle.nn.functional as F
  from paddle.nn.initializer import Normal, Constant
  from paddle.regularizer import L2Decay
  from paddlehub.module.cv_module import Yolov3Module
  import paddlehub.process.detect_transforms as T
  from paddlehub.module.module import moduleinfo
  
  
  class ConvBNLayer(nn.Layer):
      """Basic block for Darknet"""
  
      def __init__(self,
                   ch_in: int,
                   ch_out: int,
                   filter_size: int = 3,
                   stride: int = 1,
                   groups: int = 1,
                   padding: int = 0,
                   act: str = 'leakly',
                   is_test: bool = False):
          super(ConvBNLayer, self).__init__()
  
          self.conv = nn.Conv2d(
              ch_in,
              ch_out,
              filter_size,
              padding=padding,
              stride=stride,
              groups=groups,
              weight_attr=paddle.ParamAttr(initializer=Normal(0., 0.02)),
              bias_attr=False)
  
          self.batch_norm = nn.BatchNorm(
              num_channels=ch_out,
              is_test=is_test,
              param_attr=paddle.ParamAttr(initializer=Normal(0., 0.02), regularizer=L2Decay(0.)))
          self.act = act
  
      def forward(self, inputs: paddle.Tensor) -> paddle.Tensor:
          out = self.conv(inputs)
          out = self.batch_norm(out)
          if self.act == "leakly":
              out = F.leaky_relu(x=out, negative_slope=0.1)
          return out
  
  
  class DownSample(nn.Layer):
      """Downsample block for Darknet"""
  
      def __init__(self,
                   ch_in: int,
                   ch_out: int,
                   filter_size: int = 3,
                   stride: int = 2,
                   padding: int = 1,
                   is_test: bool = False):
          super(DownSample, self).__init__()
  
          self.conv_bn_layer = ConvBNLayer(
              ch_in=ch_in, ch_out=ch_out, filter_size=filter_size, stride=stride, padding=padding, is_test=is_test)
          self.ch_out = ch_out
  
      def forward(self, inputs: paddle.Tensor) -> paddle.Tensor:
          out = self.conv_bn_layer(inputs)
          return out
  
  
  class BasicBlock(nn.Layer):
      """Basic residual block for Darknet"""
  
      def __init__(self, ch_in: int, ch_out: int, is_test: bool = False):
          super(BasicBlock, self).__init__()
  
          self.conv1 = ConvBNLayer(ch_in=ch_in, ch_out=ch_out, filter_size=1, stride=1, padding=0, is_test=is_test)
          self.conv2 = ConvBNLayer(ch_in=ch_out, ch_out=ch_out * 2, filter_size=3, stride=1, padding=1, is_test=is_test)
  
      def forward(self, inputs: paddle.Tensor) -> paddle.Tensor:
          conv1 = self.conv1(inputs)
          conv2 = self.conv2(conv1)
          out = paddle.elementwise_add(x=inputs, y=conv2, act=None)
          return out
  
  
  class LayerWarp(nn.Layer):
      """Warp layer composed by basic residual blocks"""
  
      def __init__(self, ch_in: int, ch_out: int, count: int, is_test: bool = False):
          super(LayerWarp, self).__init__()
          self.basicblock0 = BasicBlock(ch_in, ch_out, is_test=is_test)
          self.res_out_list = []
          for i in range(1, count):
              res_out = self.add_sublayer("basic_block_%d" % (i), BasicBlock(ch_out * 2, ch_out, is_test=is_test))
              self.res_out_list.append(res_out)
          self.ch_out = ch_out
  
      def forward(self, inputs: paddle.Tensor) -> paddle.Tensor:
          y = self.basicblock0(inputs)
          for basic_block_i in self.res_out_list:
              y = basic_block_i(y)
          return y
  
  
  class DarkNet53_conv_body(nn.Layer):
      """Darknet53
      Args:
          ch_in(int): Input channels, default is 3.
          is_test (bool): Set the test mode, default is True.
      """
  
      def __init__(self, ch_in: int = 3, is_test: bool = False):
          super(DarkNet53_conv_body, self).__init__()
          self.stages = [1, 2, 8, 8, 4]
          self.stages = self.stages[0:5]
  
          self.conv0 = ConvBNLayer(ch_in=ch_in, ch_out=32, filter_size=3, stride=1, padding=1, is_test=is_test)
  
          self.downsample0 = DownSample(ch_in=32, ch_out=32 * 2, is_test=is_test)
          self.darknet53_conv_block_list = []
          self.downsample_list = []
          ch_in = [64, 128, 256, 512, 1024]
  
          for i, stage in enumerate(self.stages):
              conv_block = self.add_sublayer("stage_%d" % (i),
                                             LayerWarp(int(ch_in[i]), 32 * (2**i), stage, is_test=is_test))
              self.darknet53_conv_block_list.append(conv_block)
  
          for i in range(len(self.stages) - 1):
              downsample = self.add_sublayer(
                  "stage_%d_downsample" % i, DownSample(
                      ch_in=32 * (2**(i + 1)), ch_out=32 * (2**(i + 2)), is_test=is_test))
              self.downsample_list.append(downsample)
  
      def forward(self, inputs: paddle.Tensor) -> paddle.Tensor:
          out = self.conv0(inputs)
          out = self.downsample0(out)
          blocks = []
          for i, conv_block_i in enumerate(self.darknet53_conv_block_list):
              out = conv_block_i(out)
              blocks.append(out)
              if i < len(self.stages) - 1:
                  out = self.downsample_list[i](out)
          return blocks[-1:-4:-1]
  
  
  class YoloDetectionBlock(nn.Layer):
      """Basic block for Yolov3"""
  
      def __init__(self, ch_in: int, channel: int, is_test: bool = True):
          super(YoloDetectionBlock, self).__init__()
  
          assert channel % 2 == 0, \
              "channel {} cannot be divided by 2".format(channel)
  
          self.conv0 = ConvBNLayer(ch_in=ch_in, ch_out=channel, filter_size=1, stride=1, padding=0, is_test=is_test)
          self.conv1 = ConvBNLayer(ch_in=channel, ch_out=channel * 2, filter_size=3, stride=1, padding=1, is_test=is_test)
          self.conv2 = ConvBNLayer(ch_in=channel * 2, ch_out=channel, filter_size=1, stride=1, padding=0, is_test=is_test)
          self.conv3 = ConvBNLayer(ch_in=channel, ch_out=channel * 2, filter_size=3, stride=1, padding=1, is_test=is_test)
          self.route = ConvBNLayer(ch_in=channel * 2, ch_out=channel, filter_size=1, stride=1, padding=0, is_test=is_test)
          self.tip = ConvBNLayer(ch_in=channel, ch_out=channel * 2, filter_size=3, stride=1, padding=1, is_test=is_test)
  
      def forward(self, inputs):
          out = self.conv0(inputs)
          out = self.conv1(out)
          out = self.conv2(out)
          out = self.conv3(out)
          route = self.route(out)
          tip = self.tip(route)
          return route, tip
  
  
  class Upsample(nn.Layer):
      """Upsample block for Yolov3"""
  
      def __init__(self, scale: int = 2):
          super(Upsample, self).__init__()
          self.scale = scale
  
      def forward(self, inputs: paddle.Tensor):
          shape_nchw = paddle.to_tensor(inputs.shape)
          shape_hw = paddle.slice(shape_nchw, axes=[0], starts=[2], ends=[4])
          shape_hw.stop_gradient = True
          in_shape = paddle.cast(shape_hw, dtype='int32')
          out_shape = in_shape * self.scale
          out_shape.stop_gradient = True
          out = F.resize_nearest(input=inputs, scale=self.scale, actual_shape=out_shape)
          return out
  
  
  @moduleinfo(
      name="yolov3_darknet53_pascalvoc",
      type="CV/image_editing",
      author="paddlepaddle",
      author_email="",
      summary="Yolov3 is a detection model, this module is trained with VOC dataset.",
      version="1.0.0",
      meta=Yolov3Module)
  class YOLOv3(nn.Layer):
      """YOLOV3 for detection
  
      Args:
          ch_in(int): Input channels, default is 3.
          class_num(int): Categories for detection,if dataset is voc, class_num is 20.
          ignore_thresh(float): The ignore threshold to ignore confidence loss.
          valid_thresh(float): Threshold to filter out bounding boxes with low confidence score.
          nms_topk(int): Maximum number of detections to be kept according to the confidences after the filtering
                         detections based on score_threshold.
          nms_posk(int): Number of total bboxes to be kept per image after NMS step. -1 means keeping all bboxes after NMS
                         step.
          nms_thresh (float): The threshold to be used in NMS. Default: 0.3.
          is_train (bool): Set the train mode, default is True.
          load_checkpoint(str): Whether to load checkpoint.
      """
  
      def __init__(self,
                   ch_in: int = 3,
                   class_num: int = 20,
                   ignore_thresh: float = 0.7,
                   valid_thresh: float = 0.005,
                   nms_topk: int = 400,
                   nms_posk: int = 100,
                   nms_thresh: float = 0.45,
                   is_train: bool = True,
                   load_checkpoint: str = None):
          super(YOLOv3, self).__init__()
  
          self.is_train = is_train
          self.block = DarkNet53_conv_body(ch_in=ch_in, is_test=not self.is_train)
          self.block_outputs = []
          self.yolo_blocks = []
          self.route_blocks_2 = []
          self.anchor_masks = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
          self.anchors = [10, 13, 16, 30, 33, 23, 30, 61, 62, 45, 59, 119, 116, 90, 156, 198, 373, 326]
          self.class_num = class_num
          self.ignore_thresh = ignore_thresh
          self.valid_thresh = valid_thresh
          self.nms_topk = nms_topk
          self.nms_posk = nms_posk
          self.nms_thresh = nms_thresh
          ch_in_list = [1024, 768, 384]
  
          for i in range(3):
              yolo_block = self.add_sublayer(
                  "yolo_detecton_block_%d" % (i),
                  YoloDetectionBlock(ch_in_list[i], channel=512 // (2**i), is_test=not self.is_train))
              self.yolo_blocks.append(yolo_block)
  
              num_filters = len(self.anchor_masks[i]) * (self.class_num + 5)
              block_out = self.add_sublayer(
                  "block_out_%d" % (i),
                  nn.Conv2d(
                      1024 // (2**i),
                      num_filters,
                      1,
                      stride=1,
                      padding=0,
                      weight_attr=paddle.ParamAttr(initializer=Normal(0., 0.02)),
                      bias_attr=paddle.ParamAttr(initializer=Constant(0.0), regularizer=L2Decay(0.))))
              self.block_outputs.append(block_out)
  
              if i < 2:
                  route = self.add_sublayer(
                      "route2_%d" % i,
                      ConvBNLayer(
                          ch_in=512 // (2**i),
                          ch_out=256 // (2**i),
                          filter_size=1,
                          stride=1,
                          padding=0,
                          is_test=(not self.is_train)))
                  self.route_blocks_2.append(route)
              self.upsample = Upsample()
  
          if load_checkpoint is not None:
              model_dict = paddle.load(load_checkpoint)[0]
              self.set_dict(model_dict)
              print("load custom checkpoint success")
  
          else:
              checkpoint = os.path.join(self.directory, 'yolov3_darknet53_voc.pdparams')
              if not os.path.exists(checkpoint):
                  os.system(
                      'wget https://paddlehub.bj.bcebos.com/dygraph/detection/yolov3_darknet53_voc.pdparams -O ' \
                      + checkpoint)
              model_dict = paddle.load(checkpoint)[0]
              self.set_dict(model_dict)
              print("load pretrained checkpoint success")
  
      def transform(self, img):
          if self.is_train:
              transform = T.Compose([
                  T.RandomDistort(),
                  T.RandomExpand(fill=[0.485, 0.456, 0.406]),
                  T.RandomCrop(),
                  T.Resize(target_size=416),
                  T.RandomFlip(),
                  T.ShuffleBox(),
                  T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
              ])
          else:
              transform = T.Compose([
                  T.Resize(target_size=416, interp='CUBIC'),
                  T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
              ])
  
          return transform(img)
  
      def forward(self, inputs: paddle.Tensor):
          outputs = []
          blocks = self.block(inputs)
          route = None
          for i, block in enumerate(blocks):
              if i > 0:
                  block = paddle.concat([route, block], axis=1)
              route, tip = self.yolo_blocks[i](block)
              block_out = self.block_outputs[i](tip)
              outputs.append(block_out)
              if i < 2:
                  route = self.route_blocks_2[i](route)
                  route = self.upsample(route)
  
          return outputs
  