glenn-jocher commited on
Commit
b57abb1
1 Parent(s): dc7e093

Move trainloader functions to class methods (#6559)

Browse files

* Move trainloader functions to class methods

* results = ThreadPool(NUM_THREADS).imap(self.load_image, range(n))

* Cleanup

Files changed (1) hide show
  1. utils/datasets.py +157 -159
utils/datasets.py CHANGED
@@ -484,7 +484,7 @@ class LoadImagesAndLabels(Dataset):
484
 
485
  self.batch_shapes = np.ceil(np.array(shapes) * img_size / stride + pad).astype(np.int) * stride
486
 
487
- # Cache images into memory for faster training (WARNING: large datasets may exceed system RAM)
488
  self.imgs, self.img_npy = [None] * n, [None] * n
489
  if cache_images:
490
  if cache_images == 'disk':
@@ -493,14 +493,14 @@ class LoadImagesAndLabels(Dataset):
493
  self.im_cache_dir.mkdir(parents=True, exist_ok=True)
494
  gb = 0 # Gigabytes of cached images
495
  self.img_hw0, self.img_hw = [None] * n, [None] * n
496
- results = ThreadPool(NUM_THREADS).imap(lambda x: load_image(*x), zip(repeat(self), range(n)))
497
  pbar = tqdm(enumerate(results), total=n)
498
  for i, x in pbar:
499
  if cache_images == 'disk':
500
  if not self.img_npy[i].exists():
501
  np.save(self.img_npy[i].as_posix(), x[0])
502
  gb += self.img_npy[i].stat().st_size
503
- else:
504
  self.imgs[i], self.img_hw0[i], self.img_hw[i] = x # im, hw_orig, hw_resized = load_image(self, i)
505
  gb += self.imgs[i].nbytes
506
  pbar.desc = f'{prefix}Caching images ({gb / 1E9:.1f}GB {cache_images})'
@@ -558,16 +558,16 @@ class LoadImagesAndLabels(Dataset):
558
  mosaic = self.mosaic and random.random() < hyp['mosaic']
559
  if mosaic:
560
  # Load mosaic
561
- img, labels = load_mosaic(self, index)
562
  shapes = None
563
 
564
  # MixUp augmentation
565
  if random.random() < hyp['mixup']:
566
- img, labels = mixup(img, labels, *load_mosaic(self, random.randint(0, self.n - 1)))
567
 
568
  else:
569
  # Load image
570
- img, (h0, w0), (h, w) = load_image(self, index)
571
 
572
  # Letterbox
573
  shape = self.batch_shapes[self.batch[index]] if self.rect else self.img_size # final letterboxed shape
@@ -624,6 +624,157 @@ class LoadImagesAndLabels(Dataset):
624
 
625
  return torch.from_numpy(img), labels_out, self.img_files[index], shapes
626
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
627
  @staticmethod
628
  def collate_fn(batch):
629
  img, label, path, shapes = zip(*batch) # transposed
@@ -659,159 +810,6 @@ class LoadImagesAndLabels(Dataset):
659
 
660
 
661
  # Ancillary functions --------------------------------------------------------------------------------------------------
662
- def load_image(self, i):
663
- # loads 1 image from dataset index 'i', returns im, original hw, resized hw
664
- im = self.imgs[i]
665
- if im is None: # not cached in ram
666
- npy = self.img_npy[i]
667
- if npy and npy.exists(): # load npy
668
- im = np.load(npy)
669
- else: # read image
670
- path = self.img_files[i]
671
- im = cv2.imread(path) # BGR
672
- assert im is not None, f'Image Not Found {path}'
673
- h0, w0 = im.shape[:2] # orig hw
674
- r = self.img_size / max(h0, w0) # ratio
675
- if r != 1: # if sizes are not equal
676
- im = cv2.resize(im, (int(w0 * r), int(h0 * r)),
677
- interpolation=cv2.INTER_AREA if r < 1 and not self.augment else cv2.INTER_LINEAR)
678
- return im, (h0, w0), im.shape[:2] # im, hw_original, hw_resized
679
- else:
680
- return self.imgs[i], self.img_hw0[i], self.img_hw[i] # im, hw_original, hw_resized
681
-
682
-
683
- def load_mosaic(self, index):
684
- # YOLOv5 4-mosaic loader. Loads 1 image + 3 random images into a 4-image mosaic
685
- labels4, segments4 = [], []
686
- s = self.img_size
687
- yc, xc = (int(random.uniform(-x, 2 * s + x)) for x in self.mosaic_border) # mosaic center x, y
688
- indices = [index] + random.choices(self.indices, k=3) # 3 additional image indices
689
- random.shuffle(indices)
690
- for i, index in enumerate(indices):
691
- # Load image
692
- img, _, (h, w) = load_image(self, index)
693
-
694
- # place img in img4
695
- if i == 0: # top left
696
- img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles
697
- x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc # xmin, ymin, xmax, ymax (large image)
698
- x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h # xmin, ymin, xmax, ymax (small image)
699
- elif i == 1: # top right
700
- x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc
701
- x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h
702
- elif i == 2: # bottom left
703
- x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)
704
- x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, w, min(y2a - y1a, h)
705
- elif i == 3: # bottom right
706
- x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)
707
- x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)
708
-
709
- img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b] # img4[ymin:ymax, xmin:xmax]
710
- padw = x1a - x1b
711
- padh = y1a - y1b
712
-
713
- # Labels
714
- labels, segments = self.labels[index].copy(), self.segments[index].copy()
715
- if labels.size:
716
- labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padw, padh) # normalized xywh to pixel xyxy format
717
- segments = [xyn2xy(x, w, h, padw, padh) for x in segments]
718
- labels4.append(labels)
719
- segments4.extend(segments)
720
-
721
- # Concat/clip labels
722
- labels4 = np.concatenate(labels4, 0)
723
- for x in (labels4[:, 1:], *segments4):
724
- np.clip(x, 0, 2 * s, out=x) # clip when using random_perspective()
725
- # img4, labels4 = replicate(img4, labels4) # replicate
726
-
727
- # Augment
728
- img4, labels4, segments4 = copy_paste(img4, labels4, segments4, p=self.hyp['copy_paste'])
729
- img4, labels4 = random_perspective(img4, labels4, segments4,
730
- degrees=self.hyp['degrees'],
731
- translate=self.hyp['translate'],
732
- scale=self.hyp['scale'],
733
- shear=self.hyp['shear'],
734
- perspective=self.hyp['perspective'],
735
- border=self.mosaic_border) # border to remove
736
-
737
- return img4, labels4
738
-
739
-
740
- def load_mosaic9(self, index):
741
- # YOLOv5 9-mosaic loader. Loads 1 image + 8 random images into a 9-image mosaic
742
- labels9, segments9 = [], []
743
- s = self.img_size
744
- indices = [index] + random.choices(self.indices, k=8) # 8 additional image indices
745
- random.shuffle(indices)
746
- hp, wp = -1, -1 # height, width previous
747
- for i, index in enumerate(indices):
748
- # Load image
749
- img, _, (h, w) = load_image(self, index)
750
-
751
- # place img in img9
752
- if i == 0: # center
753
- img9 = np.full((s * 3, s * 3, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles
754
- h0, w0 = h, w
755
- c = s, s, s + w, s + h # xmin, ymin, xmax, ymax (base) coordinates
756
- elif i == 1: # top
757
- c = s, s - h, s + w, s
758
- elif i == 2: # top right
759
- c = s + wp, s - h, s + wp + w, s
760
- elif i == 3: # right
761
- c = s + w0, s, s + w0 + w, s + h
762
- elif i == 4: # bottom right
763
- c = s + w0, s + hp, s + w0 + w, s + hp + h
764
- elif i == 5: # bottom
765
- c = s + w0 - w, s + h0, s + w0, s + h0 + h
766
- elif i == 6: # bottom left
767
- c = s + w0 - wp - w, s + h0, s + w0 - wp, s + h0 + h
768
- elif i == 7: # left
769
- c = s - w, s + h0 - h, s, s + h0
770
- elif i == 8: # top left
771
- c = s - w, s + h0 - hp - h, s, s + h0 - hp
772
-
773
- padx, pady = c[:2]
774
- x1, y1, x2, y2 = (max(x, 0) for x in c) # allocate coords
775
-
776
- # Labels
777
- labels, segments = self.labels[index].copy(), self.segments[index].copy()
778
- if labels.size:
779
- labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padx, pady) # normalized xywh to pixel xyxy format
780
- segments = [xyn2xy(x, w, h, padx, pady) for x in segments]
781
- labels9.append(labels)
782
- segments9.extend(segments)
783
-
784
- # Image
785
- img9[y1:y2, x1:x2] = img[y1 - pady:, x1 - padx:] # img9[ymin:ymax, xmin:xmax]
786
- hp, wp = h, w # height, width previous
787
-
788
- # Offset
789
- yc, xc = (int(random.uniform(0, s)) for _ in self.mosaic_border) # mosaic center x, y
790
- img9 = img9[yc:yc + 2 * s, xc:xc + 2 * s]
791
-
792
- # Concat/clip labels
793
- labels9 = np.concatenate(labels9, 0)
794
- labels9[:, [1, 3]] -= xc
795
- labels9[:, [2, 4]] -= yc
796
- c = np.array([xc, yc]) # centers
797
- segments9 = [x - c for x in segments9]
798
-
799
- for x in (labels9[:, 1:], *segments9):
800
- np.clip(x, 0, 2 * s, out=x) # clip when using random_perspective()
801
- # img9, labels9 = replicate(img9, labels9) # replicate
802
-
803
- # Augment
804
- img9, labels9 = random_perspective(img9, labels9, segments9,
805
- degrees=self.hyp['degrees'],
806
- translate=self.hyp['translate'],
807
- scale=self.hyp['scale'],
808
- shear=self.hyp['shear'],
809
- perspective=self.hyp['perspective'],
810
- border=self.mosaic_border) # border to remove
811
-
812
- return img9, labels9
813
-
814
-
815
  def create_folder(path='./new'):
816
  # Create folder
817
  if os.path.exists(path):
 
484
 
485
  self.batch_shapes = np.ceil(np.array(shapes) * img_size / stride + pad).astype(np.int) * stride
486
 
487
+ # Cache images into RAM/disk for faster training (WARNING: large datasets may exceed system resources)
488
  self.imgs, self.img_npy = [None] * n, [None] * n
489
  if cache_images:
490
  if cache_images == 'disk':
 
493
  self.im_cache_dir.mkdir(parents=True, exist_ok=True)
494
  gb = 0 # Gigabytes of cached images
495
  self.img_hw0, self.img_hw = [None] * n, [None] * n
496
+ results = ThreadPool(NUM_THREADS).imap(self.load_image, range(n))
497
  pbar = tqdm(enumerate(results), total=n)
498
  for i, x in pbar:
499
  if cache_images == 'disk':
500
  if not self.img_npy[i].exists():
501
  np.save(self.img_npy[i].as_posix(), x[0])
502
  gb += self.img_npy[i].stat().st_size
503
+ else: # 'ram'
504
  self.imgs[i], self.img_hw0[i], self.img_hw[i] = x # im, hw_orig, hw_resized = load_image(self, i)
505
  gb += self.imgs[i].nbytes
506
  pbar.desc = f'{prefix}Caching images ({gb / 1E9:.1f}GB {cache_images})'
 
558
  mosaic = self.mosaic and random.random() < hyp['mosaic']
559
  if mosaic:
560
  # Load mosaic
561
+ img, labels = self.load_mosaic(index)
562
  shapes = None
563
 
564
  # MixUp augmentation
565
  if random.random() < hyp['mixup']:
566
+ img, labels = mixup(img, labels, *self.load_mosaic(random.randint(0, self.n - 1)))
567
 
568
  else:
569
  # Load image
570
+ img, (h0, w0), (h, w) = self.load_image(index)
571
 
572
  # Letterbox
573
  shape = self.batch_shapes[self.batch[index]] if self.rect else self.img_size # final letterboxed shape
 
624
 
625
  return torch.from_numpy(img), labels_out, self.img_files[index], shapes
626
 
627
+ def load_image(self, i):
628
+ # loads 1 image from dataset index 'i', returns (im, original hw, resized hw)
629
+ im = self.imgs[i]
630
+ if im is None: # not cached in RAM
631
+ npy = self.img_npy[i]
632
+ if npy and npy.exists(): # load npy
633
+ im = np.load(npy)
634
+ else: # read image
635
+ f = self.img_files[i]
636
+ im = cv2.imread(f) # BGR
637
+ assert im is not None, f'Image Not Found {f}'
638
+ h0, w0 = im.shape[:2] # orig hw
639
+ r = self.img_size / max(h0, w0) # ratio
640
+ if r != 1: # if sizes are not equal
641
+ im = cv2.resize(im,
642
+ (int(w0 * r), int(h0 * r)),
643
+ interpolation=cv2.INTER_LINEAR if (self.augment or r > 1) else cv2.INTER_AREA)
644
+ return im, (h0, w0), im.shape[:2] # im, hw_original, hw_resized
645
+ else:
646
+ return self.imgs[i], self.img_hw0[i], self.img_hw[i] # im, hw_original, hw_resized
647
+
648
+ def load_mosaic(self, index):
649
+ # YOLOv5 4-mosaic loader. Loads 1 image + 3 random images into a 4-image mosaic
650
+ labels4, segments4 = [], []
651
+ s = self.img_size
652
+ yc, xc = (int(random.uniform(-x, 2 * s + x)) for x in self.mosaic_border) # mosaic center x, y
653
+ indices = [index] + random.choices(self.indices, k=3) # 3 additional image indices
654
+ random.shuffle(indices)
655
+ for i, index in enumerate(indices):
656
+ # Load image
657
+ img, _, (h, w) = self.load_image(index)
658
+
659
+ # place img in img4
660
+ if i == 0: # top left
661
+ img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles
662
+ x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc # xmin, ymin, xmax, ymax (large image)
663
+ x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h # xmin, ymin, xmax, ymax (small image)
664
+ elif i == 1: # top right
665
+ x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc
666
+ x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h
667
+ elif i == 2: # bottom left
668
+ x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)
669
+ x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, w, min(y2a - y1a, h)
670
+ elif i == 3: # bottom right
671
+ x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)
672
+ x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)
673
+
674
+ img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b] # img4[ymin:ymax, xmin:xmax]
675
+ padw = x1a - x1b
676
+ padh = y1a - y1b
677
+
678
+ # Labels
679
+ labels, segments = self.labels[index].copy(), self.segments[index].copy()
680
+ if labels.size:
681
+ labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padw, padh) # normalized xywh to pixel xyxy format
682
+ segments = [xyn2xy(x, w, h, padw, padh) for x in segments]
683
+ labels4.append(labels)
684
+ segments4.extend(segments)
685
+
686
+ # Concat/clip labels
687
+ labels4 = np.concatenate(labels4, 0)
688
+ for x in (labels4[:, 1:], *segments4):
689
+ np.clip(x, 0, 2 * s, out=x) # clip when using random_perspective()
690
+ # img4, labels4 = replicate(img4, labels4) # replicate
691
+
692
+ # Augment
693
+ img4, labels4, segments4 = copy_paste(img4, labels4, segments4, p=self.hyp['copy_paste'])
694
+ img4, labels4 = random_perspective(img4, labels4, segments4,
695
+ degrees=self.hyp['degrees'],
696
+ translate=self.hyp['translate'],
697
+ scale=self.hyp['scale'],
698
+ shear=self.hyp['shear'],
699
+ perspective=self.hyp['perspective'],
700
+ border=self.mosaic_border) # border to remove
701
+
702
+ return img4, labels4
703
+
704
+ def load_mosaic9(self, index):
705
+ # YOLOv5 9-mosaic loader. Loads 1 image + 8 random images into a 9-image mosaic
706
+ labels9, segments9 = [], []
707
+ s = self.img_size
708
+ indices = [index] + random.choices(self.indices, k=8) # 8 additional image indices
709
+ random.shuffle(indices)
710
+ hp, wp = -1, -1 # height, width previous
711
+ for i, index in enumerate(indices):
712
+ # Load image
713
+ img, _, (h, w) = self.load_image(index)
714
+
715
+ # place img in img9
716
+ if i == 0: # center
717
+ img9 = np.full((s * 3, s * 3, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles
718
+ h0, w0 = h, w
719
+ c = s, s, s + w, s + h # xmin, ymin, xmax, ymax (base) coordinates
720
+ elif i == 1: # top
721
+ c = s, s - h, s + w, s
722
+ elif i == 2: # top right
723
+ c = s + wp, s - h, s + wp + w, s
724
+ elif i == 3: # right
725
+ c = s + w0, s, s + w0 + w, s + h
726
+ elif i == 4: # bottom right
727
+ c = s + w0, s + hp, s + w0 + w, s + hp + h
728
+ elif i == 5: # bottom
729
+ c = s + w0 - w, s + h0, s + w0, s + h0 + h
730
+ elif i == 6: # bottom left
731
+ c = s + w0 - wp - w, s + h0, s + w0 - wp, s + h0 + h
732
+ elif i == 7: # left
733
+ c = s - w, s + h0 - h, s, s + h0
734
+ elif i == 8: # top left
735
+ c = s - w, s + h0 - hp - h, s, s + h0 - hp
736
+
737
+ padx, pady = c[:2]
738
+ x1, y1, x2, y2 = (max(x, 0) for x in c) # allocate coords
739
+
740
+ # Labels
741
+ labels, segments = self.labels[index].copy(), self.segments[index].copy()
742
+ if labels.size:
743
+ labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padx, pady) # normalized xywh to pixel xyxy format
744
+ segments = [xyn2xy(x, w, h, padx, pady) for x in segments]
745
+ labels9.append(labels)
746
+ segments9.extend(segments)
747
+
748
+ # Image
749
+ img9[y1:y2, x1:x2] = img[y1 - pady:, x1 - padx:] # img9[ymin:ymax, xmin:xmax]
750
+ hp, wp = h, w # height, width previous
751
+
752
+ # Offset
753
+ yc, xc = (int(random.uniform(0, s)) for _ in self.mosaic_border) # mosaic center x, y
754
+ img9 = img9[yc:yc + 2 * s, xc:xc + 2 * s]
755
+
756
+ # Concat/clip labels
757
+ labels9 = np.concatenate(labels9, 0)
758
+ labels9[:, [1, 3]] -= xc
759
+ labels9[:, [2, 4]] -= yc
760
+ c = np.array([xc, yc]) # centers
761
+ segments9 = [x - c for x in segments9]
762
+
763
+ for x in (labels9[:, 1:], *segments9):
764
+ np.clip(x, 0, 2 * s, out=x) # clip when using random_perspective()
765
+ # img9, labels9 = replicate(img9, labels9) # replicate
766
+
767
+ # Augment
768
+ img9, labels9 = random_perspective(img9, labels9, segments9,
769
+ degrees=self.hyp['degrees'],
770
+ translate=self.hyp['translate'],
771
+ scale=self.hyp['scale'],
772
+ shear=self.hyp['shear'],
773
+ perspective=self.hyp['perspective'],
774
+ border=self.mosaic_border) # border to remove
775
+
776
+ return img9, labels9
777
+
778
  @staticmethod
779
  def collate_fn(batch):
780
  img, label, path, shapes = zip(*batch) # transposed
 
810
 
811
 
812
  # Ancillary functions --------------------------------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
813
  def create_folder(path='./new'):
814
  # Create folder
815
  if os.path.exists(path):