build.py 7.4 KB

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