2
0

utils.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import collections
  2. import os
  3. import tarfile
  4. import urllib
  5. import zipfile
  6. from pathlib import Path
  7. import numpy as np
  8. import torch
  9. from taming.data.helper_types import Annotation
  10. from torch._six import string_classes
  11. from torch.utils.data._utils.collate import np_str_obj_array_pattern, default_collate_err_msg_format
  12. from tqdm import tqdm
  13. def unpack(path):
  14. if path.endswith("tar.gz"):
  15. with tarfile.open(path, "r:gz") as tar:
  16. tar.extractall(path=os.path.split(path)[0])
  17. elif path.endswith("tar"):
  18. with tarfile.open(path, "r:") as tar:
  19. tar.extractall(path=os.path.split(path)[0])
  20. elif path.endswith("zip"):
  21. with zipfile.ZipFile(path, "r") as f:
  22. f.extractall(path=os.path.split(path)[0])
  23. else:
  24. raise NotImplementedError(
  25. "Unknown file extension: {}".format(os.path.splitext(path)[1])
  26. )
  27. def reporthook(bar):
  28. """tqdm progress bar for downloads."""
  29. def hook(b=1, bsize=1, tsize=None):
  30. if tsize is not None:
  31. bar.total = tsize
  32. bar.update(b * bsize - bar.n)
  33. return hook
  34. def get_root(name):
  35. base = "data/"
  36. root = os.path.join(base, name)
  37. os.makedirs(root, exist_ok=True)
  38. return root
  39. def is_prepared(root):
  40. return Path(root).joinpath(".ready").exists()
  41. def mark_prepared(root):
  42. Path(root).joinpath(".ready").touch()
  43. def prompt_download(file_, source, target_dir, content_dir=None):
  44. targetpath = os.path.join(target_dir, file_)
  45. while not os.path.exists(targetpath):
  46. if content_dir is not None and os.path.exists(
  47. os.path.join(target_dir, content_dir)
  48. ):
  49. break
  50. print(
  51. "Please download '{}' from '{}' to '{}'.".format(file_, source, targetpath)
  52. )
  53. if content_dir is not None:
  54. print(
  55. "Or place its content into '{}'.".format(
  56. os.path.join(target_dir, content_dir)
  57. )
  58. )
  59. input("Press Enter when done...")
  60. return targetpath
  61. def download_url(file_, url, target_dir):
  62. targetpath = os.path.join(target_dir, file_)
  63. os.makedirs(target_dir, exist_ok=True)
  64. with tqdm(
  65. unit="B", unit_scale=True, unit_divisor=1024, miniters=1, desc=file_
  66. ) as bar:
  67. urllib.request.urlretrieve(url, targetpath, reporthook=reporthook(bar))
  68. return targetpath
  69. def download_urls(urls, target_dir):
  70. paths = dict()
  71. for fname, url in urls.items():
  72. outpath = download_url(fname, url, target_dir)
  73. paths[fname] = outpath
  74. return paths
  75. def quadratic_crop(x, bbox, alpha=1.0):
  76. """bbox is xmin, ymin, xmax, ymax"""
  77. im_h, im_w = x.shape[:2]
  78. bbox = np.array(bbox, dtype=np.float32)
  79. bbox = np.clip(bbox, 0, max(im_h, im_w))
  80. center = 0.5 * (bbox[0] + bbox[2]), 0.5 * (bbox[1] + bbox[3])
  81. w = bbox[2] - bbox[0]
  82. h = bbox[3] - bbox[1]
  83. l = int(alpha * max(w, h))
  84. l = max(l, 2)
  85. required_padding = -1 * min(
  86. center[0] - l, center[1] - l, im_w - (center[0] + l), im_h - (center[1] + l)
  87. )
  88. required_padding = int(np.ceil(required_padding))
  89. if required_padding > 0:
  90. padding = [
  91. [required_padding, required_padding],
  92. [required_padding, required_padding],
  93. ]
  94. padding += [[0, 0]] * (len(x.shape) - 2)
  95. x = np.pad(x, padding, "reflect")
  96. center = center[0] + required_padding, center[1] + required_padding
  97. xmin = int(center[0] - l / 2)
  98. ymin = int(center[1] - l / 2)
  99. return np.array(x[ymin : ymin + l, xmin : xmin + l, ...])
  100. def custom_collate(batch):
  101. r"""source: pytorch 1.9.0, only one modification to original code """
  102. elem = batch[0]
  103. elem_type = type(elem)
  104. if isinstance(elem, torch.Tensor):
  105. out = None
  106. if torch.utils.data.get_worker_info() is not None:
  107. # If we're in a background process, concatenate directly into a
  108. # shared memory tensor to avoid an extra copy
  109. numel = sum([x.numel() for x in batch])
  110. storage = elem.storage()._new_shared(numel)
  111. out = elem.new(storage)
  112. return torch.stack(batch, 0, out=out)
  113. elif elem_type.__module__ == 'numpy' and elem_type.__name__ != 'str_' \
  114. and elem_type.__name__ != 'string_':
  115. if elem_type.__name__ == 'ndarray' or elem_type.__name__ == 'memmap':
  116. # array of string classes and object
  117. if np_str_obj_array_pattern.search(elem.dtype.str) is not None:
  118. raise TypeError(default_collate_err_msg_format.format(elem.dtype))
  119. return custom_collate([torch.as_tensor(b) for b in batch])
  120. elif elem.shape == (): # scalars
  121. return torch.as_tensor(batch)
  122. elif isinstance(elem, float):
  123. return torch.tensor(batch, dtype=torch.float64)
  124. elif isinstance(elem, int):
  125. return torch.tensor(batch)
  126. elif isinstance(elem, string_classes):
  127. return batch
  128. elif isinstance(elem, collections.abc.Mapping):
  129. return {key: custom_collate([d[key] for d in batch]) for key in elem}
  130. elif isinstance(elem, tuple) and hasattr(elem, '_fields'): # namedtuple
  131. return elem_type(*(custom_collate(samples) for samples in zip(*batch)))
  132. if isinstance(elem, collections.abc.Sequence) and isinstance(elem[0], Annotation): # added
  133. return batch # added
  134. elif isinstance(elem, collections.abc.Sequence):
  135. # check to make sure that the elements in batch have consistent size
  136. it = iter(batch)
  137. elem_size = len(next(it))
  138. if not all(len(elem) == elem_size for elem in it):
  139. raise RuntimeError('each element in list of batch should be of equal size')
  140. transposed = zip(*batch)
  141. return [custom_collate(samples) for samples in transposed]
  142. raise TypeError(default_collate_err_msg_format.format(elem_type))