text: from absl import logging
  import numpy as np
  import tensorflow as tf
  import cv2
  
  YOLOV3_LAYER_LIST = [
      'yolo_darknet',
      'yolo_conv_0',
      'yolo_output_0',
      'yolo_conv_1',
      'yolo_output_1',
      'yolo_conv_2',
      'yolo_output_2',
  ]
  
  
  def load_darknet_weights(model, weights_file):
      wf = open(weights_file, 'rb')
      major, minor, revision, seen, _ = np.fromfile(wf, dtype=np.int32, count=5)
  
      for layer_name in YOLOV3_LAYER_LIST:
          sub_model = model.get_layer(layer_name)
          for i, layer in enumerate(sub_model.layers):
              if not layer.name.startswith('conv2d'):
                  continue
              batch_norm = None
              if i + 1 < len(sub_model.layers) and \
                      sub_model.layers[i + 1].name.startswith('batch_norm'):
                  batch_norm = sub_model.layers[i + 1]
  
              logging.info("{}/{} {}".format(
                  sub_model.name, layer.name, 'bn' if batch_norm else 'bias'))
  
              filters = layer.filters
              size = layer.kernel_size[0]
              in_dim = layer.input_shape[-1]
  
              if batch_norm is None:
                  conv_bias = np.fromfile(wf, dtype=np.float32, count=filters)
              else:
                  # darknet [beta, gamma, mean, variance]
                  bn_weights = np.fromfile(
                      wf, dtype=np.float32, count=4 * filters)
                  # tf [gamma, beta, mean, variance]
                  bn_weights = bn_weights.reshape((4, filters))[[1, 0, 2, 3]]
  
              # darknet shape (out_dim, in_dim, height, width)
              conv_shape = (filters, in_dim, size, size)
              conv_weights = np.fromfile(
                  wf, dtype=np.float32, count=np.product(conv_shape))
              # tf shape (height, width, in_dim, out_dim)
              conv_weights = conv_weights.reshape(
                  conv_shape).transpose([2, 3, 1, 0])
  
              if batch_norm is None:
                  layer.set_weights([conv_weights, conv_bias])
              else:
                  layer.set_weights([conv_weights])
                  batch_norm.set_weights(bn_weights)
  
      assert len(wf.read()) == 0, 'failed to read all data'
      wf.close()
  
  
  def broadcast_iou(box_1, box_2):
      # box_1: (..., (x1, y1, x2, y2))
      # box_2: (N, (x1, y1, x2, y2))
  
      # broadcast boxes
      box_1 = tf.expand_dims(box_1, -2)
      box_2 = tf.expand_dims(box_2, 0)
      # new_shape: (..., N, (x1, y1, x2, y2))
      new_shape = tf.broadcast_dynamic_shape(tf.shape(box_1), tf.shape(box_2))
      box_1 = tf.broadcast_to(box_1, new_shape)
      box_2 = tf.broadcast_to(box_2, new_shape)
  
      int_w = tf.maximum(tf.minimum(box_1[..., 2], box_2[..., 2]) -
                         tf.maximum(box_1[..., 0], box_2[..., 0]), 0)
      int_h = tf.maximum(tf.minimum(box_1[..., 3], box_2[..., 3]) -
                         tf.maximum(box_1[..., 1], box_2[..., 1]), 0)
      int_area = int_w * int_h
      box_1_area = (box_1[..., 2] - box_1[..., 0]) * \
          (box_1[..., 3] - box_1[..., 1])
      box_2_area = (box_2[..., 2] - box_2[..., 0]) * \
          (box_2[..., 3] - box_2[..., 1])
      return int_area / (box_1_area + box_2_area - int_area)
  
  
  def draw_outputs(img, outputs, class_names):
      boxes, objectness, classes, nums = outputs
      boxes, objectness, classes, nums = boxes[0], objectness[0], classes[0], nums[0]
      wh = np.flip(img.shape[0:2])
      for i in range(nums):
          x1y1 = tuple((np.array(boxes[i][0:2]) * wh).astype(np.int32))
          x2y2 = tuple((np.array(boxes[i][2:4]) * wh).astype(np.int32))
          img = cv2.rectangle(img, x1y1, x2y2, (255, 0, 0), 2)
          img = cv2.putText(img, '{} {:.4f}'.format(
              class_names[int(classes[i])], objectness[i]),
              x1y1, cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 2)
      return img
  
  
  def draw_labels(x, y, class_names):
      img = x.numpy()
      boxes, classes = tf.split(y, (4, 1), axis=-1)
      classes = classes[..., 0]
      wh = np.flip(img.shape[0:2])
      for i in range(len(boxes)):
          x1y1 = tuple((np.array(boxes[i][0:2]) * wh).astype(np.int32))
          x2y2 = tuple((np.array(boxes[i][2:4]) * wh).astype(np.int32))
          img = cv2.rectangle(img, x1y1, x2y2, (255, 0, 0), 2)
          img = cv2.putText(img, class_names[classes[i]],
                            x1y1, cv2.FONT_HERSHEY_COMPLEX_SMALL,
                            1, (0, 0, 255), 2)
      return img
  
  
  def freeze_all(model, frozen=True):
      model.trainable = not frozen
      if isinstance(model, tf.keras.Model):
          for l in model.layers:
              freeze_all(l, frozen)
              
              
   ## Dataset utils ============
  @tf.function
  def transform_targets_for_output(y_true, grid_size, anchor_idxs, classes):
      # y_true: (N, boxes, (x1, y1, x2, y2, class, best_anchor))
      N = tf.shape(y_true)[0]
  
      # y_true_out: (N, grid, grid, anchors, [x, y, w, h, obj, class])
      y_true_out = tf.zeros(
          (N, grid_size, grid_size, tf.shape(anchor_idxs)[0], 6))
  
      anchor_idxs = tf.cast(anchor_idxs, tf.int32)
  
      indexes = tf.TensorArray(tf.int32, 1, dynamic_size=True)
      updates = tf.TensorArray(tf.float32, 1, dynamic_size=True)
      idx = 0
      for i in tf.range(N):
          for j in tf.range(tf.shape(y_true)[1]):
              if tf.equal(y_true[i][j][2], 0):
                  continue
              anchor_eq = tf.equal(
                  anchor_idxs, tf.cast(y_true[i][j][5], tf.int32))
  
              if tf.reduce_any(anchor_eq):
                  box = y_true[i][j][0:4]
                  box_xy = (y_true[i][j][0:2] + y_true[i][j][2:4]) / 2
  
                  anchor_idx = tf.cast(tf.where(anchor_eq), tf.int32)
                  grid_xy = tf.cast(box_xy // (1/grid_size), tf.int32)
  
                  # grid[y][x][anchor] = (tx, ty, bw, bh, obj, class)
                  indexes = indexes.write(
                      idx, [i, grid_xy[1], grid_xy[0], anchor_idx[0][0]])
                  updates = updates.write(
                      idx, [box[0], box[1], box[2], box[3], 1, y_true[i][j][4]])
                  idx += 1
  
      # tf.print(indexes.stack())
      # tf.print(updates.stack())
  
      return tf.tensor_scatter_nd_update(
          y_true_out, indexes.stack(), updates.stack())
  
  
  def transform_targets(y_train, anchors, anchor_masks, classes):
      y_outs = []
      grid_size = 13
  
      # calculate anchor index for true boxes
      anchors = tf.cast(anchors, tf.float32)
      anchor_area = anchors[..., 0] * anchors[..., 1]
      box_wh = y_train[..., 2:4] - y_train[..., 0:2]
      box_wh = tf.tile(tf.expand_dims(box_wh, -2),
                       (1, 1, tf.shape(anchors)[0], 1))
      box_area = box_wh[..., 0] * box_wh[..., 1]
      intersection = tf.minimum(box_wh[..., 0], anchors[..., 0]) * \
          tf.minimum(box_wh[..., 1], anchors[..., 1])
      iou = intersection / (box_area + anchor_area - intersection)
      anchor_idx = tf.cast(tf.argmax(iou, axis=-1), tf.float32)
      anchor_idx = tf.expand_dims(anchor_idx, axis=-1)
  
      y_train = tf.concat([y_train, anchor_idx], axis=-1)
  
      for anchor_idxs in anchor_masks:
          y_outs.append(transform_targets_for_output(
              y_train, grid_size, anchor_idxs, classes))
          grid_size *= 2
  
      return tuple(y_outs)
  
  
  def transform_images(x_train, size):
      x_train = tf.image.resize(x_train, (size, size))
      x_train = x_train / 255
      return x_train
  