text: import os.path as osp
  
  import mmcv
  import numpy as np
  import pycocotools.mask as maskUtils
  
  import random
  import cv2
  import copy
  import math
  from skimage import img_as_ubyte
  
  from mmdet.core import BitmapMasks, PolygonMasks
  from ..builder import PIPELINES
  random.seed(42)
  
  
  @PIPELINES.register_module()
  class LoadImageFromFile(object):
      """Load an image from file.
  
      Required keys are "img_prefix" and "img_info" (a dict that must contain the
      key "filename"). Added or updated keys are "filename", "img", "img_shape",
      "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`),
      "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1).
  
      Args:
          to_float32 (bool): Whether to convert the loaded image to a float32
              numpy array. If set to False, the loaded image is an uint8 array.
              Defaults to False.
          color_type (str): The flag argument for :func:`mmcv.imfrombytes`.
              Defaults to 'color'.
          file_client_args (dict): Arguments to instantiate a FileClient.
              See :class:`mmcv.fileio.FileClient` for details.
              Defaults to ``dict(backend='disk')``.
      """
  
      def __init__(self,
                   to_float32=False,
                   color_type='color',
                   file_client_args=dict(backend='disk')):
          self.to_float32 = to_float32
          self.color_type = color_type
          self.file_client_args = file_client_args.copy()
          self.file_client = None
  
      def __call__(self, results):
          """Call functions to load image and get image meta information.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded image and meta information.
          """
  
          if self.file_client is None:
              self.file_client = mmcv.FileClient(**self.file_client_args)
  
          if results['img_prefix'] is not None:
              filename = osp.join(results['img_prefix'],
                                  results['img_info']['filename'])
          else:
              filename = results['img_info']['filename']
  
          img_bytes = self.file_client.get(filename)
          img = mmcv.imfrombytes(img_bytes, flag=self.color_type)
          if self.to_float32:
              img = img.astype(np.float32)
  
          results['filename'] = filename
          results['ori_filename'] = results['img_info']['filename']
          results['img'] = img
          results['img_shape'] = img.shape
          results['ori_shape'] = img.shape
          results['img_fields'] = ['img']
          return results
  
      def __repr__(self):
          repr_str = (f'{self.__class__.__name__}('
                      f'to_float32={self.to_float32}, '
                      f"color_type='{self.color_type}', "
                      f'file_client_args={self.file_client_args})')
          return repr_str
  
  
  @PIPELINES.register_module()
  class LoadMultiChannelImageFromFiles(object):
      """Load multi-channel images from a list of separate channel files.
  
      Required keys are "img_prefix" and "img_info" (a dict that must contain the
      key "filename", which is expected to be a list of filenames).
      Added or updated keys are "filename", "img", "img_shape",
      "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`),
      "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1).
  
      Args:
          to_float32 (bool): Whether to convert the loaded image to a float32
              numpy array. If set to False, the loaded image is an uint8 array.
              Defaults to False.
          color_type (str): The flag argument for :func:`mmcv.imfrombytes`.
              Defaults to 'color'.
          file_client_args (dict): Arguments to instantiate a FileClient.
              See :class:`mmcv.fileio.FileClient` for details.
              Defaults to ``dict(backend='disk')``.
      """
  
      def __init__(self,
                   to_float32=False,
                   color_type='unchanged',
                   file_client_args=dict(backend='disk')):
          self.to_float32 = to_float32
          self.color_type = color_type
          self.file_client_args = file_client_args.copy()
          self.file_client = None
  
      def __call__(self, results):
          """Call functions to load multiple images and get images meta
          information.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded images and meta information.
          """
  
          if self.file_client is None:
              self.file_client = mmcv.FileClient(**self.file_client_args)
  
          if results['img_prefix'] is not None:
              filename = [
                  osp.join(results['img_prefix'], fname)
                  for fname in results['img_info']['filename']
              ]
          else:
              filename = results['img_info']['filename']
  
          img = []
          for name in filename:
              img_bytes = self.file_client.get(name)
              img.append(mmcv.imfrombytes(img_bytes, flag=self.color_type))
          img = np.stack(img, axis=-1)
          if self.to_float32:
              img = img.astype(np.float32)
  
          results['filename'] = filename
          results['ori_filename'] = results['img_info']['filename']
          results['img'] = img
          results['img_shape'] = img.shape
          results['ori_shape'] = img.shape
          # Set initial values for default meta_keys
          results['pad_shape'] = img.shape
          results['scale_factor'] = 1.0
          num_channels = 1 if len(img.shape) < 3 else img.shape[2]
          results['img_norm_cfg'] = dict(
              mean=np.zeros(num_channels, dtype=np.float32),
              std=np.ones(num_channels, dtype=np.float32),
              to_rgb=False)
          return results
  
      def __repr__(self):
          repr_str = (f'{self.__class__.__name__}('
                      f'to_float32={self.to_float32}, '
                      f"color_type='{self.color_type}', "
                      f'file_client_args={self.file_client_args})')
          return repr_str
  
  
  @PIPELINES.register_module()
  class LoadAnnotations(object):
      """Load mutiple types of annotations.
  
      Args:
          with_bbox (bool): Whether to parse and load the bbox annotation.
               Default: True.
          with_label (bool): Whether to parse and load the label annotation.
              Default: True.
          with_mask (bool): Whether to parse and load the mask annotation.
               Default: False.
          with_seg (bool): Whether to parse and load the semantic segmentation
              annotation. Default: False.
          poly2mask (bool): Whether to convert the instance masks from polygons
              to bitmaps. Default: True.
          file_client_args (dict): Arguments to instantiate a FileClient.
              See :class:`mmcv.fileio.FileClient` for details.
              Defaults to ``dict(backend='disk')``.
      """
  
      def __init__(self,
                   with_bbox=True,
                   with_label=True,
                   with_mask=False,
                   with_seg=False,
                   poly2mask=True,
                   file_client_args=dict(backend='disk')):
          self.with_bbox = with_bbox
          self.with_label = with_label
          self.with_mask = with_mask
          self.with_seg = with_seg
          self.poly2mask = poly2mask
          self.file_client_args = file_client_args.copy()
          self.file_client = None
  
      def _load_bboxes(self, results):
          """Private function to load bounding box annotations.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded bounding box annotations.
          """
  
          ann_info = results['ann_info']
          results['gt_bboxes'] = ann_info['bboxes'].copy()
  
          gt_bboxes_ignore = ann_info.get('bboxes_ignore', None)
          if gt_bboxes_ignore is not None:
              results['gt_bboxes_ignore'] = gt_bboxes_ignore.copy()
              results['bbox_fields'].append('gt_bboxes_ignore')
          results['bbox_fields'].append('gt_bboxes')
          return results
  
      def _load_labels(self, results):
          """Private function to load label annotations.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded label annotations.
          """
  
          results['gt_labels'] = results['ann_info']['labels'].copy()
          return results
  
      def _poly2mask(self, mask_ann, img_h, img_w):
          """Private function to convert masks represented with polygon to
          bitmaps.
  
          Args:
              mask_ann (list | dict): Polygon mask annotation input.
              img_h (int): The height of output mask.
              img_w (int): The width of output mask.
  
          Returns:
              numpy.ndarray: The decode bitmap mask of shape (img_h, img_w).
          """
  
          if isinstance(mask_ann, list):
              # polygon -- a single object might consist of multiple parts
              # we merge all parts into one mask rle code
              rles = maskUtils.frPyObjects(mask_ann, img_h, img_w)
              rle = maskUtils.merge(rles)
          elif isinstance(mask_ann['counts'], list):
              # uncompressed RLE
              rle = maskUtils.frPyObjects(mask_ann, img_h, img_w)
          else:
              # rle
              rle = mask_ann
          mask = maskUtils.decode(rle)
          return mask
  
      def process_polygons(self, polygons):
          """Convert polygons to list of ndarray and filter invalid polygons.
  
          Args:
              polygons (list[list]): Polygons of one instance.
  
          Returns:
              list[numpy.ndarray]: Processed polygons.
          """
  
          polygons = [np.array(p) for p in polygons]
          valid_polygons = []
          for polygon in polygons:
              if len(polygon) % 2 == 0 and len(polygon) >= 6:
                  valid_polygons.append(polygon)
          return valid_polygons
  
      def _load_masks(self, results):
          """Private function to load mask annotations.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded mask annotations.
                  If ``self.poly2mask`` is set ``True``, `gt_mask` will contain
                  :obj:`PolygonMasks`. Otherwise, :obj:`BitmapMasks` is used.
          """
  
          h, w = results['img_info']['height'], results['img_info']['width']
          gt_masks = results['ann_info']['masks']
          if self.poly2mask:
              gt_masks = BitmapMasks(
                  [self._poly2mask(mask, h, w) for mask in gt_masks], h, w)
          else:
              gt_masks = PolygonMasks(
                  [self.process_polygons(polygons) for polygons in gt_masks], h,
                  w)
          results['gt_masks'] = gt_masks
          results['mask_fields'].append('gt_masks')
          return results
  
      def _load_semantic_seg(self, results):
          """Private function to load semantic segmentation annotations.
  
          Args:
              results (dict): Result dict from :obj:`dataset`.
  
          Returns:
              dict: The dict contains loaded semantic segmentation annotations.
          """
  
          if self.file_client is None:
              self.file_client = mmcv.FileClient(**self.file_client_args)
  
          filename = osp.join(results['seg_prefix'],
                              results['ann_info']['seg_map'])
          img_bytes = self.file_client.get(filename)
          results['gt_semantic_seg'] = mmcv.imfrombytes(
              img_bytes, flag='unchanged').squeeze()
          results['seg_fields'].append('gt_semantic_seg')
          return results
  
      def __call__(self, results):
          """Call function to load multiple types annotations.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded bounding box, label, mask and
                  semantic segmentation annotations.
          """
  
          if self.with_bbox:
              results = self._load_bboxes(results)
              if results is None:
                  return None
          if self.with_label:
              results = self._load_labels(results)
          if self.with_mask:
              results = self._load_masks(results)
          if self.with_seg:
              results = self._load_semantic_seg(results)
          return results
  
      def __repr__(self):
          repr_str = self.__class__.__name__
          repr_str += f'(with_bbox={self.with_bbox}, '
          repr_str += f'with_label={self.with_label}, '
          repr_str += f'with_mask={self.with_mask}, '
          repr_str += f'with_seg={self.with_seg})'
          repr_str += f'poly2mask={self.poly2mask})'
          repr_str += f'poly2mask={self.file_client_args})'
          return repr_str
  
  
  @PIPELINES.register_module()
  class LoadProposals(object):
      """Load proposal pipeline.
  
      Required key is "proposals". Updated keys are "proposals", "bbox_fields".
  
      Args:
          num_max_proposals (int, optional): Maximum number of proposals to load.
              If not specified, all proposals will be loaded.
      """
  
      def __init__(self, num_max_proposals=None):
          self.num_max_proposals = num_max_proposals
  
      def __call__(self, results):
          """Call function to load proposals from file.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded proposal annotations.
          """
  
          proposals = results['proposals']
          if proposals.shape[1] not in (4, 5):
              raise AssertionError(
                  'proposals should have shapes (n, 4) or (n, 5), '
                  f'but found {proposals.shape}')
          proposals = proposals[:, :4]
  
          if self.num_max_proposals is not None:
              proposals = proposals[:self.num_max_proposals]
  
          if len(proposals) == 0:
              proposals = np.array([[0, 0, 0, 0]], dtype=np.float32)
          results['proposals'] = proposals
          results['bbox_fields'].append('proposals')
          return results
  
      def __repr__(self):
          return self.__class__.__name__ + \
              f'(num_max_proposals={self.num_max_proposals})'
  
  @PIPELINES.register_module()
  class LoadMosaicImageAndAnnotations(object):
      def __init__(self, to_float32=False,
                   color_type='color',
                   file_client_args=dict(backend='disk'),
                   with_bbox=True,
                   with_label=True,
                   with_mask=False,
                   with_seg=False,
                   poly2mask=True,
                   hsv_aug=True,
                   h_gain=0.014,
                   s_gain=0.68,
                   v_gain=0.36,
                   skip_box_w=0,
                   skip_box_h=0,
                   image_shape=[1024, 1024]
                   ):
          self.to_float32 = to_float32
          self.color_type = color_type
          self.file_client_args = file_client_args.copy()
          self.file_client = None
          self.with_bbox = with_bbox
          self.with_label = with_label
          self.with_mask = with_mask
          self.with_seg = with_seg
          self.poly2mask = poly2mask
          self.h_gain = h_gain
          self.s_gain = s_gain
          self.v_gain = v_gain
          self.hsv_aug = hsv_aug
          self.skip_box_w = skip_box_w
          self.skip_box_h = skip_box_h
          self.image_shape = image_shape
  
      def __call__(self, results):
          if len(results) == 1:
              results, labels = self._load_image_annotations(results, 0)
              # print('no mosaic:', results['ann_info'])
          if len(results) == 4:
              results = self._load_mosaic_image_and_annotations(results)
              # print('mosaic', results['ann_info'])
          return results
  
      def _load_mosaic_image_and_annotations(self, results):
          indexes = [0, 1, 2, 3]
          #result_boxes = []
          results_c = copy.deepcopy(results)
          imsize = self.image_shape[0]
          #print(imsize)
          w, h = self.image_shape[0], self.image_shape[1]
          s = imsize
          yc, xc = [int(random.uniform(-x, 2 * s + x)) for x in [-s//2, -s//2]]
          labels4 = []
          for i, index in enumerate(indexes):
              result, labels = self._load_image_annotations(results_c, index)
              labels = np.array(labels)
              #print(labels[0])
              #print(result.keys())
              img = result['img'].copy()
              #boxes = result['gt_bboxes'].copy()
              # place img in img4
              if i == 0:  # top left
                  img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tiles
                  x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc  # xmin, ymin, xmax, ymax (large image)
                  x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h  # xmin, ymin, xmax, ymax (small image)
              elif i == 1:  # top right
                  x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc
                  x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h
              elif i == 2:  # bottom left
                  x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)
                  x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, max(xc, w), min(y2a - y1a, h)
              elif i == 3:  # bottom right
                  x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)
                  x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)
  
              img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b]  # img4[ymin:ymax, xmin:xmax]
              padw = x1a - x1b
              padh = y1a - y1b
  
              x = labels.copy()
              if len(x) > 0:  # Normalized xywh to pixel xyxy format
                  labels[:, 1] = w * (x[:, 1] - x[:, 3] / 2) + padw
                  labels[:, 2] = h * (x[:, 2] - x[:, 4] / 2) + padh
                  labels[:, 3] = w * (x[:, 1] + x[:, 3] / 2) + padw
                  labels[:, 4] = h * (x[:, 2] + x[:, 4] / 2) + padh
              labels4.append(labels)
          # print(len(labels4))
  
          if len(labels4):
              labels4 = np.concatenate(labels4, 0)
              # np.clip(labels4[:, 1:] - s / 2, 0, s, out=labels4[:, 1:])  # use with center crop
              np.clip(labels4[:, 1:], 0, 2 * s, out=labels4[:, 1:])  # use with random_affine
  
          img4, labels4 = random_affine(img4, labels4,
                                        degrees=0.,
                                        translate=0.,
                                        scale=0.5,
                                        shear=0.,
                                        border=[-s//2, -s//2],
                                        skip_box_w=self.skip_box_w,
                                        skip_box_h=self.skip_box_h)  # border to remove
          # print(len(labels4))
          if self.hsv_aug:
              augment_hsv(img=img4, hgain=self.h_gain, sgain=self.s_gain, vgain=self.v_gain)
          if random.random() < 0.5:
              labels4 = cutout(img4, labels4)
  
          results_0 = results[0].copy()
          results_0['ann_info']['bboxes'] = labels4[:, 1:5].astype(np.float32)
          result_labels = labels4[:, 0].astype(np.int64)
          results_0['ann_info']['labels'] = result_labels
  
          masks = []
          for box in labels4[:, 1:5]:
              min_x = box[0]
              min_y = box[1]
              max_x = box[2]
              max_y = box[3]
              mask_h = max_y - min_y
              mask_w = max_x - min_x
              masks.append([[min_x, min_y, min_x, min_y + 0.5 * mask_h, min_x, max_y, min_x + 0.5 * mask_w, max_y, max_x,
                             max_y, max_x,
                             max_y - 0.5 * mask_h, max_x, min_y, max_x - 0.5 * mask_w, min_y]])
  
          results_0['ann_info']['masks'] = masks
  
          if results_0['img_prefix'] is not None:
              filename = osp.join(results_0['img_prefix'],
                                  results_0['img_info']['filename'])
          else:
              filename = results_0['img_info']['filename']
  
          results_0['filename'] = filename
          results_0['ori_filename'] = results_0['img_info']['filename']
          #result_image = img_as_ubyte(result_image)
          img4 = np.ascontiguousarray(img4)
          results_0['img'] = img4
          results_0['img_shape'] = img4.shape
          results_0['ori_shape'] = img4.shape
          # Set initial values for default meta_keys
          results_0['pad_shape'] = img4.shape
          # results['scale_factor'] = 1.0
          # num_channels = 1 if len(result_image.shape) < 3 else result_image.shape[2]
          # results['img_norm_cfg'] = dict(
          #     mean=np.zeros(num_channels, dtype=np.float32),
          #     std=np.ones(num_channels, dtype=np.float32),
          #     to_rgb=False)
          results_0['img_fields'] = ['img']
  
          if self.with_bbox:
              results_0 = self._load_bboxes(results_0)
              if results_0 is None:
                  return None
          if self.with_label:
              results_0 = self._load_labels(results_0)
          if self.with_mask:
              results_0 = self._load_masks(results_0)
          if self.with_seg:
              results_0 = self._load_semantic_seg(results_0)
          return results_0
  
      def _load_image_annotations(self, results, k):
          results_k = results[k].copy()
          if self.file_client is None:
              self.file_client = mmcv.FileClient(**self.file_client_args)
          if results_k['img_prefix'] is not None:
              filename = osp.join(results_k['img_prefix'],
                                  results_k['img_info']['filename'])
          else:
              filename = results_k['img_info']['filename']
  
          img_bytes = self.file_client.get(filename)
          img = mmcv.imfrombytes(img_bytes, flag=self.color_type)
  
          if self.to_float32:
              img = img.astype(np.float32)
  
          results_k['filename'] = filename
          results_k['ori_filename'] = results_k['img_info']['filename']
          results_k['img'] = img
          results_k['img_shape'] = img.shape
          results_k['ori_shape'] = img.shape
          # Set initial values for default meta_keys
          results_k['pad_shape'] = img.shape
          # results['scale_factor'] = 1.0
          # num_channels = 1 if len(img.shape) < 3 else img.shape[2]
          # results['img_norm_cfg'] = dict(
          #     mean=np.zeros(num_channels, dtype=np.float32),
          #     std=np.ones(num_channels, dtype=np.float32),
          #     to_rgb=False)
          results_k['img_fields'] = ['img']
  
          if self.with_bbox:
              results_k = self._load_bboxes(results_k)
              if results_k is None:
                  return None
          if self.with_label:
              results_k = self._load_labels(results_k)
          if self.with_mask:
              results_k = self._load_masks(results_k)
          if self.with_seg:
              results_k = self._load_semantic_seg(results_k)
          labels = xyxy2normxcycwh(results_k['gt_bboxes'], results_k['gt_labels'],
                                   results_k['img_shape'][0],
                                   results_k['img_shape'][1])
  
          return results_k, labels
  
      def _load_bboxes(self, results):
          """Private function to load bounding box annotations.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded bounding box annotations.
          """
  
          ann_info = results['ann_info']
          results['gt_bboxes'] = ann_info['bboxes'].copy()
  
          gt_bboxes_ignore = ann_info.get('bboxes_ignore', None)
          if gt_bboxes_ignore is not None:
              results['gt_bboxes_ignore'] = gt_bboxes_ignore.copy()
              results['bbox_fields'].append('gt_bboxes_ignore')
          results['bbox_fields'].append('gt_bboxes')
          return results
  
      def _load_labels(self, results):
          """Private function to load label annotations.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded label annotations.
          """
  
          results['gt_labels'] = results['ann_info']['labels'].copy()
          return results
  
      def _poly2mask(self, mask_ann, img_h, img_w):
          """Private function to convert masks represented with polygon to
          bitmaps.
  
          Args:
              mask_ann (list | dict): Polygon mask annotation input.
              img_h (int): The height of output mask.
              img_w (int): The width of output mask.
  
          Returns:
              numpy.ndarray: The decode bitmap mask of shape (img_h, img_w).
          """
  
          if isinstance(mask_ann, list):
              # polygon -- a single object might consist of multiple parts
              # we merge all parts into one mask rle code
              rles = maskUtils.frPyObjects(mask_ann, img_h, img_w)
              rle = maskUtils.merge(rles)
          elif isinstance(mask_ann['counts'], list):
              # uncompressed RLE
              rle = maskUtils.frPyObjects(mask_ann, img_h, img_w)
          else:
              # rle
              rle = mask_ann
          mask = maskUtils.decode(rle)
          return mask
  
      def process_polygons(self, polygons):
          """Convert polygons to list of ndarray and filter invalid polygons.
  
          Args:
              polygons (list[list]): Polygons of one instance.
  
          Returns:
              list[numpy.ndarray]: Processed polygons.
          """
  
          polygons = [np.array(p) for p in polygons]
          valid_polygons = []
          for polygon in polygons:
              if len(polygon) % 2 == 0 and len(polygon) >= 6:
                  valid_polygons.append(polygon)
          return valid_polygons
  
      def _load_masks(self, results):
          """Private function to load mask annotations.
  
          Args:
              results (dict): Result dict from :obj:`mmdet.CustomDataset`.
  
          Returns:
              dict: The dict contains loaded mask annotations.
                  If ``self.poly2mask`` is set ``True``, `gt_mask` will contain
                  :obj:`PolygonMasks`. Otherwise, :obj:`BitmapMasks` is used.
          """
  
          h, w = results['img_info']['height'], results['img_info']['width']
          gt_masks = results['ann_info']['masks']
          if self.poly2mask:
              gt_masks = BitmapMasks(
                  [self._poly2mask(mask, h, w) for mask in gt_masks], h, w)
          else:
              gt_masks = PolygonMasks(
                  [self.process_polygons(polygons) for polygons in gt_masks], h,
                  w)
          results['gt_masks'] = gt_masks
          results['mask_fields'].append('gt_masks')
          return results
  
      def _load_semantic_seg(self, results):
          """Private function to load semantic segmentation annotations.
  
          Args:
              results (dict): Result dict from :obj:`dataset`.
  
          Returns:
              dict: The dict contains loaded semantic segmentation annotations.
          """
  
          if self.file_client is None:
              self.file_client = mmcv.FileClient(**self.file_client_args)
  
          filename = osp.join(results['seg_prefix'],
                              results['ann_info']['seg_map'])
          img_bytes = self.file_client.get(filename)
          results['gt_semantic_seg'] = mmcv.imfrombytes(
              img_bytes, flag='unchanged').squeeze()
          results['seg_fields'].append('gt_semantic_seg')
          return results
  
      def __repr__(self):
          repr_str = self.__class__.__name__
          repr_str += f'(with_bbox={self.with_bbox}, '
          repr_str += f'with_label={self.with_label}, '
          repr_str += f'with_mask={self.with_mask}, '
          repr_str += f'image_shape={self.image_shape}, '
          repr_str += f'hsv_aug={self.hsv_aug}, '
          repr_str += f'h_gain={self.h_gain}, '
          repr_str += f's_gain={self.s_gain}, '
          repr_str += f'v_gain={self.v_gain}, '
          repr_str += f'skip_box_w={self.skip_box_w}, '
          repr_str += f'skip_box_h={self.skip_box_h}, '
          repr_str += f'to_float32={self.to_float32}, '
          repr_str += f"color_type='{self.color_type}', "
          repr_str += f'with_seg={self.with_seg})'
          repr_str += f'poly2mask={self.poly2mask})'
          repr_str += f'poly2mask={self.file_client_args})'
          repr_str += f'file_client_args={self.file_client_args})'
          return repr_str
  
  
  def xyxy2normxcycwh(boxes, label, im_w, im_h):
      labels = []
      for i in range(len(boxes)):
          x1, y1, x2, y2 = boxes[i]
          xc, yc, w, h = 0.5 * x1 / im_w + 0.5 * x2 / im_w, 0.5 * y1 / im_h + 0.5 * y2 / im_h, abs(
              x2 / im_w - x1 / im_w), abs(y2 / im_h - y1 / im_h)
          labels.append([label[i], xc, yc, w, h])
      return labels
  
  
  def augment_hsv(img, hgain=0.5, sgain=0.5, vgain=0.5):
      r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1  # random gains
      hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))
      dtype = img.dtype  # uint8
  
      x = np.arange(0, 256, dtype=np.int16)
      lut_hue = ((x * r[0]) % 180).astype(dtype)
      lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
      lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
  
      img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))).astype(dtype)
      cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR, dst=img)
  
  
  def cutout(image, labels):
      # https://arxiv.org/abs/1708.04552
      # https://github.com/hysts/pytorch_cutout/blob/master/dataloader.py
      # https://towardsdatascience.com/when-conventional-wisdom-fails-revisiting-data-augmentation-for-self-driving-cars-4831998c5509
      h, w = image.shape[:2]
  
      def bbox_ioa(box1, box2):
          # Returns the intersection over box2 area given box1, box2. box1 is 4, box2 is nx4. boxes are x1y1x2y2
          box2 = box2.transpose()
  
          # Get the coordinates of bounding boxes
          b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3]
          b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3]
  
          # Intersection area
          inter_area = (np.minimum(b1_x2, b2_x2) - np.maximum(b1_x1, b2_x1)).clip(0) * \
                       (np.minimum(b1_y2, b2_y2) - np.maximum(b1_y1, b2_y1)).clip(0)
  
          # box2 area
          box2_area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1) + 1e-16
  
          # Intersection over box2 area
  
          return inter_area / box2_area
  
      # create random masks
      scales = [0.5] * 1 + [0.25] * 2 + [0.125] * 4 + [0.0625] * 8 + [0.03125] * 16  # image size fraction
      for s in scales:
          mask_h = random.randint(1, int(h * s))
          mask_w = random.randint(1, int(w * s))
  
          # box
          xmin = max(0, random.randint(0, w) - mask_w // 2)
          ymin = max(0, random.randint(0, h) - mask_h // 2)
          xmax = min(w, xmin + mask_w)
          ymax = min(h, ymin + mask_h)
  
          # apply random color mask
          image[ymin:ymax, xmin:xmax] = [random.randint(64, 191) for _ in range(3)]
  
          # return unobscured labels
          if len(labels) and s > 0.03:
              box = np.array([xmin, ymin, xmax, ymax], dtype=np.float32)
              ioa = bbox_ioa(box, labels[:, 1:5])  # intersection over area
              labels = labels[ioa < 0.60]  # remove >60% obscured labels
  
      return labels
  
  
  def random_affine(img, targets=(), degrees=10, translate=.1, scale=.1, shear=10, border=(0, 0), skip_box_w=5, skip_box_h=5):
      # torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(.1, .1), scale=(.9, 1.1), shear=(-10, 10))
      # https://medium.com/uruvideo/dataset-augmentation-with-random-homographies-a8f4b44830d4
      # targets = [cls, xyxy]
  
      height = img.shape[0] + border[0] * 2  # shape(h,w,c)
      width = img.shape[1] + border[1] * 2
  
      # Rotation and Scale
      R = np.eye(3)
      a = random.uniform(-degrees, degrees)
      # a += random.choice([-180, -90, 0, 90])  # add 90deg rotations to small rotations
      s = random.uniform(1 - scale, 1 + scale)
      # s = 2 ** random.uniform(-scale, scale)
      R[:2] = cv2.getRotationMatrix2D(angle=a, center=(img.shape[1] / 2, img.shape[0] / 2), scale=s)
  
      # Translation
      T = np.eye(3)
      T[0, 2] = random.uniform(-translate, translate) * img.shape[1] + border[1]  # x translation (pixels)
      T[1, 2] = random.uniform(-translate, translate) * img.shape[0] + border[0]  # y translation (pixels)
  
      # Shear
      S = np.eye(3)
      S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # x shear (deg)
      S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # y shear (deg)
  
      # Combined rotation matrix
      M = S @ T @ R  # ORDER IS IMPORTANT HERE!!
      if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any():  # image changed
          img = cv2.warpAffine(img, M[:2], dsize=(width, height), flags=cv2.INTER_LINEAR, borderValue=(114, 114, 114))
  
      # Transform label coordinates
      n = len(targets)
      if n:
          # warp points
          xy = np.ones((n * 4, 3))
          xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2)  # x1y1, x2y2, x1y2, x2y1
          xy = (xy @ M.T)[:, :2].reshape(n, 8)
  
          # create new boxes
          x = xy[:, [0, 2, 4, 6]]
          y = xy[:, [1, 3, 5, 7]]
          xy = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T
  
          # # apply angle-based reduction of bounding boxes
          # radians = a * math.pi / 180
          # reduction = max(abs(math.sin(radians)), abs(math.cos(radians))) ** 0.5
          # x = (xy[:, 2] + xy[:, 0]) / 2
          # y = (xy[:, 3] + xy[:, 1]) / 2
          # w = (xy[:, 2] - xy[:, 0]) * reduction
          # h = (xy[:, 3] - xy[:, 1]) * reduction
          # xy = np.concatenate((x - w / 2, y - h / 2, x + w / 2, y + h / 2)).reshape(4, n).T
  
          # reject warped points outside of image
          xy[:, [0, 2]] = xy[:, [0, 2]].clip(0, width)
          xy[:, [1, 3]] = xy[:, [1, 3]].clip(0, height)
          w = xy[:, 2] - xy[:, 0]
          h = xy[:, 3] - xy[:, 1]
          area = w * h
          area0 = (targets[:, 3] - targets[:, 1]) * (targets[:, 4] - targets[:, 2])
          ar = np.maximum(w / (h + 1e-16), h / (w + 1e-16))  # aspect ratio
          i = (w > skip_box_w) & (h > skip_box_h) & (area / (area0 * s + 1e-16) > 0.2) & (ar < 7.5)
  
          targets = targets[i]
          targets[:, 1:5] = xy[i]
  
      return img, targets