text: import platform
  import random
  from functools import partial
  
  import numpy as np
  from mmcv.parallel import collate
  from mmcv.runner import get_dist_info
  from mmcv.utils import Registry, build_from_cfg
  from torch.utils.data import DataLoader
  import torch
  
  from .samplers import ClassSpecificDistributedSampler, DistributedSampler
  
  from collections.abc import Mapping, Sequence
  
  
  import torch.nn.functional as F
  from torch.utils.data.dataloader import default_collate
  
  from mmcv.parallel.data_container import DataContainer
  
  
  if platform.system() != 'Windows':
      # https://github.com/pytorch/pytorch/issues/973
      import resource
      rlimit = resource.getrlimit(resource.RLIMIT_NOFILE)
      hard_limit = rlimit[1]
      soft_limit = min(4096, hard_limit)
      resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit))
  
  DATASETS = Registry('dataset')
  PIPELINES = Registry('pipeline')
  BLENDINGS = Registry('blending')
  
  
  def repeat_collate(batch, repeat):
      r"""Puts each data field into a tensor with outer dimension batch size"""
  
      elem = batch[0]
      elem_type = type(elem)
      if isinstance(elem, torch.Tensor):
          out = None
          if torch.utils.data.get_worker_info() is not None:
              # If we're in a background process, concatenate directly into a
              # shared memory tensor to avoid an extra copy
              numel = sum([x.numel() for x in batch])
              storage = elem.storage()._new_shared(numel)
              out = elem.new(storage)
          ret = torch.stack(batch, 0, out=out)
          if len(ret.shape) > 2: # img
              bs,rep,c,f,h,w = ret.shape
              ret = ret.view(bs*rep, 1, c, f, h, w)
          else: #label
              ret = ret.view(-1, 1)
          return ret
  
  
  def mmcv_collate(batch, samples_per_gpu=1, repeat=1): 
      if not isinstance(batch, Sequence):
          raise TypeError(f'{batch.dtype} is not supported.')
      if isinstance(batch[0], DataContainer):
          stacked = []
          if batch[0].cpu_only:
              for i in range(0, len(batch), samples_per_gpu):
                  stacked.append(
                      [sample.data for sample in batch[i:i + samples_per_gpu]])
              return DataContainer(
                  stacked, batch[0].stack, batch[0].padding_value, cpu_only=True)
          elif batch[0].stack:
              for i in range(0, len(batch), samples_per_gpu):
                  assert isinstance(batch[i].data, torch.Tensor)
  
                  if batch[i].pad_dims is not None:
                      ndim = batch[i].dim()
                      assert ndim > batch[i].pad_dims
                      max_shape = [0 for _ in range(batch[i].pad_dims)]
                      for dim in range(1, batch[i].pad_dims + 1):
                          max_shape[dim - 1] = batch[i].size(-dim)
                      for sample in batch[i:i + samples_per_gpu]:
                          for dim in range(0, ndim - batch[i].pad_dims):
                              assert batch[i].size(dim) == sample.size(dim)
                          for dim in range(1, batch[i].pad_dims + 1):
                              max_shape[dim - 1] = max(max_shape[dim - 1],
                                                       sample.size(-dim))
                      padded_samples = []
                      for sample in batch[i:i + samples_per_gpu]:
                          pad = [0 for _ in range(batch[i].pad_dims * 2)]
                          for dim in range(1, batch[i].pad_dims + 1):
                              pad[2 * dim -
                                  1] = max_shape[dim - 1] - sample.size(-dim)
                          padded_samples.append(
                              F.pad(
                                  sample.data, pad, value=sample.padding_value))
                      stacked.append(default_collate(padded_samples))
                  elif batch[i].pad_dims is None:
                      stacked.append(
                          default_collate([
                              sample.data
                              for sample in batch[i:i + samples_per_gpu]
                          ]))
                  else:
                      raise ValueError(
                          'pad_dims should be either None or integers (1-3)')
  
          else:
              for i in range(0, len(batch), samples_per_gpu):
                  stacked.append(
                      [sample.data for sample in batch[i:i + samples_per_gpu]])
          return DataContainer(stacked, batch[0].stack, batch[0].padding_value)
      elif isinstance(batch[0], Sequence):
          transposed = zip(*batch)
          return [collate(samples, samples_per_gpu) for samples in transposed]
      elif isinstance(batch[0], Mapping):
          return {
              key: mmcv_collate([d[key] for d in batch], samples_per_gpu, repeat)
              for key in batch[0]
          }
      else:
          if repeat > 1:
              return repeat_collate(batch, repeat)
          else:
              return default_collate(batch)
  
  
  def build_dataset(cfg, default_args=None):
      """Build a dataset from config dict.
  
      Args:
          cfg (dict): Config dict. It should at least contain the key "type".
          default_args (dict | None, optional): Default initialization arguments.
              Default: None.
  
      Returns:
          Dataset: The constructed dataset.
      """
      if cfg['type'] == 'RepeatDataset':
          from .dataset_wrappers import RepeatDataset
          dataset = RepeatDataset(
              build_dataset(cfg['dataset'], default_args), cfg['times'])
      else:
          dataset = build_from_cfg(cfg, DATASETS, default_args)
      return dataset
  
  
  def build_dataloader(dataset,
                       videos_per_gpu,
                       workers_per_gpu,
                       num_gpus=1,
                       dist=True,
                       shuffle=True,
                       seed=None,
                       drop_last=False,
                       pin_memory=True,
                       repeat=1,
                       **kwargs):
      """Build PyTorch DataLoader.
  
      In distributed training, each GPU/process has a dataloader.
      In non-distributed training, there is only one dataloader for all GPUs.
  
      Args:
          dataset (:obj:`Dataset`): A PyTorch dataset.
          videos_per_gpu (int): Number of videos on each GPU, i.e.,
              batch size of each GPU.
          workers_per_gpu (int): How many subprocesses to use for data
              loading for each GPU.
          num_gpus (int): Number of GPUs. Only used in non-distributed
              training. Default: 1.
          dist (bool): Distributed training/test or not. Default: True.
          shuffle (bool): Whether to shuffle the data at every epoch.
              Default: True.
          seed (int | None): Seed to be used. Default: None.
          drop_last (bool): Whether to drop the last incomplete batch in epoch.
              Default: False
          pin_memory (bool): Whether to use pin_memory in DataLoader.
              Default: True
          kwargs (dict, optional): Any keyword argument to be used to initialize
              DataLoader.
  
      Returns:
          DataLoader: A PyTorch dataloader.
      """
      rank, world_size = get_dist_info()
      sample_by_class = getattr(dataset, 'sample_by_class', False)
  
      if dist:
          if sample_by_class:
              dynamic_length = getattr(dataset, 'dynamic_length', True)
              sampler = ClassSpecificDistributedSampler(
                  dataset,
                  world_size,
                  rank,
                  dynamic_length=dynamic_length,
                  shuffle=shuffle,
                  seed=seed)
          else:
              sampler = DistributedSampler(
                  dataset, world_size, rank, shuffle=shuffle, seed=seed)
          shuffle = False
          batch_size = videos_per_gpu
          num_workers = workers_per_gpu
      else:
          sampler = None
          batch_size = num_gpus * videos_per_gpu
          num_workers = num_gpus * workers_per_gpu
  
      init_fn = partial(
          worker_init_fn, num_workers=num_workers, rank=rank,
          seed=seed) if seed is not None else None
      
      data_loader = DataLoader(
          dataset,
          batch_size=batch_size,
          sampler=sampler,
          num_workers=num_workers,
          collate_fn=partial(mmcv_collate, samples_per_gpu=videos_per_gpu, repeat=repeat),
          pin_memory=pin_memory,
          shuffle=shuffle,
          worker_init_fn=init_fn,
          drop_last=drop_last,
          **kwargs)
  
      return data_loader
  
  
  def worker_init_fn(worker_id, num_workers, rank, seed):
      """Init the random seed for various workers."""
      # The seed of each worker equals to
      # num_worker * rank + worker_id + user_seed
      worker_seed = num_workers * rank + worker_id + seed
      np.random.seed(worker_seed)
      random.seed(worker_seed)
  