build.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. # Ultralytics YOLO 🚀, AGPL-3.0 license
  2. import os
  3. import random
  4. from pathlib import Path
  5. import numpy as np
  6. import torch
  7. from PIL import Image
  8. from torch.utils.data import dataloader, distributed
  9. from ultralytics.data.loaders import (LOADERS, LoadImages, LoadPilAndNumpy, LoadScreenshots, LoadStreams, LoadTensor,
  10. SourceTypes, autocast_list)
  11. from ultralytics.data.utils import IMG_FORMATS, VID_FORMATS
  12. from ultralytics.utils import RANK, colorstr
  13. from ultralytics.utils.checks import check_file
  14. from .dataset import YOLODataset
  15. from .utils import PIN_MEMORY
  16. class InfiniteDataLoader(dataloader.DataLoader):
  17. """Dataloader that reuses workers. Uses same syntax as vanilla DataLoader."""
  18. def __init__(self, *args, **kwargs):
  19. """Dataloader that infinitely recycles workers, inherits from DataLoader."""
  20. super().__init__(*args, **kwargs)
  21. object.__setattr__(self, 'batch_sampler', _RepeatSampler(self.batch_sampler))
  22. self.iterator = super().__iter__()
  23. def __len__(self):
  24. """Returns the length of the batch sampler's sampler."""
  25. return len(self.batch_sampler.sampler)
  26. def __iter__(self):
  27. """Creates a sampler that repeats indefinitely."""
  28. for _ in range(len(self)):
  29. yield next(self.iterator)
  30. def reset(self):
  31. """Reset iterator.
  32. This is useful when we want to modify settings of dataset while training.
  33. """
  34. self.iterator = self._get_iterator()
  35. class _RepeatSampler:
  36. """
  37. Sampler that repeats forever.
  38. Args:
  39. sampler (Dataset.sampler): The sampler to repeat.
  40. """
  41. def __init__(self, sampler):
  42. """Initializes an object that repeats a given sampler indefinitely."""
  43. self.sampler = sampler
  44. def __iter__(self):
  45. """Iterates over the 'sampler' and yields its contents."""
  46. while True:
  47. yield from iter(self.sampler)
  48. def seed_worker(worker_id): # noqa
  49. """Set dataloader worker seed https://pytorch.org/docs/stable/notes/randomness.html#dataloader."""
  50. worker_seed = torch.initial_seed() % 2 ** 32
  51. np.random.seed(worker_seed)
  52. random.seed(worker_seed)
  53. def build_yolo_dataset(cfg, img_path, batch, data, mode='train', rect=False, stride=32):
  54. """Build YOLO Dataset"""
  55. return YOLODataset(
  56. img_path=img_path,
  57. imgsz=cfg.imgsz,
  58. batch_size=batch,
  59. augment=mode == 'train', # augmentation
  60. hyp=cfg, # TODO: probably add a get_hyps_from_cfg function
  61. rect=cfg.rect or rect, # rectangular batches
  62. cache=cfg.cache or None,
  63. single_cls=cfg.single_cls or False,
  64. stride=int(stride),
  65. pad=0.0 if mode == 'train' else 0.5,
  66. prefix=colorstr(f'{mode}: '),
  67. use_segments=cfg.task == 'segment',
  68. use_keypoints=cfg.task == 'pose',
  69. classes=cfg.classes,
  70. data=data,
  71. fraction=cfg.fraction if mode == 'train' else 1.0)
  72. def build_dataloader(dataset, batch, workers, shuffle=True, rank=-1):
  73. """Return an InfiniteDataLoader or DataLoader for training or validation set."""
  74. batch = min(batch, len(dataset))
  75. nd = torch.cuda.device_count() # number of CUDA devices
  76. nw = min([os.cpu_count() // max(nd, 1), batch if batch > 1 else 0, workers]) # number of workers
  77. sampler = None if rank == -1 else distributed.DistributedSampler(dataset, shuffle=shuffle)
  78. generator = torch.Generator()
  79. generator.manual_seed(6148914691236517205 + RANK)
  80. return InfiniteDataLoader(dataset=dataset,
  81. batch_size=batch,
  82. shuffle=shuffle and sampler is None,
  83. num_workers=nw,
  84. sampler=sampler,
  85. pin_memory=PIN_MEMORY,
  86. collate_fn=getattr(dataset, 'collate_fn', None),
  87. worker_init_fn=seed_worker,
  88. generator=generator)
  89. def check_source(source):
  90. """Check source type and return corresponding flag values."""
  91. webcam, screenshot, from_img, in_memory, tensor = False, False, False, False, False
  92. if isinstance(source, (str, int, Path)): # int for local usb camera
  93. source = str(source)
  94. is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS)
  95. is_url = source.lower().startswith(('https://', 'http://', 'rtsp://', 'rtmp://'))
  96. webcam = source.isnumeric() or source.endswith('.streams') or (is_url and not is_file)
  97. screenshot = source.lower() == 'screen'
  98. if is_url and is_file:
  99. source = check_file(source) # download
  100. elif isinstance(source, LOADERS):
  101. in_memory = True
  102. elif isinstance(source, (list, tuple)):
  103. source = autocast_list(source) # convert all list elements to PIL or np arrays
  104. from_img = True
  105. elif isinstance(source, (Image.Image, np.ndarray)):
  106. from_img = True
  107. elif isinstance(source, torch.Tensor):
  108. tensor = True
  109. else:
  110. raise TypeError('Unsupported image type. For supported types see https://docs.ultralytics.com/modes/predict')
  111. return source, webcam, screenshot, from_img, in_memory, tensor
  112. def load_inference_source(source=None, imgsz=640, vid_stride=1):
  113. """
  114. Loads an inference source for object detection and applies necessary transformations.
  115. Args:
  116. source (str, Path, Tensor, PIL.Image, np.ndarray): The input source for inference.
  117. imgsz (int, optional): The size of the image for inference. Default is 640.
  118. vid_stride (int, optional): The frame interval for video sources. Default is 1.
  119. Returns:
  120. dataset (Dataset): A dataset object for the specified input source.
  121. """
  122. source, webcam, screenshot, from_img, in_memory, tensor = check_source(source)
  123. source_type = source.source_type if in_memory else SourceTypes(webcam, screenshot, from_img, tensor)
  124. # Dataloader
  125. if tensor:
  126. dataset = LoadTensor(source)
  127. elif in_memory:
  128. dataset = source
  129. elif webcam:
  130. dataset = LoadStreams(source, imgsz=imgsz, vid_stride=vid_stride)
  131. elif screenshot:
  132. dataset = LoadScreenshots(source, imgsz=imgsz)
  133. elif from_img:
  134. dataset = LoadPilAndNumpy(source, imgsz=imgsz)
  135. else:
  136. dataset = LoadImages(source, imgsz=imgsz, vid_stride=vid_stride)
  137. # Attach source types to the dataset
  138. setattr(dataset, 'source_type', source_type)
  139. return dataset