glenn-jocher
commited on
Commit
•
bcd452c
1
Parent(s):
bb87276
replace random_affine() with random_perspective()
Browse filesSigned-off-by: Glenn Jocher <[email protected]>
- train.py +1 -1
- utils/datasets.py +47 -25
train.py
CHANGED
@@ -32,7 +32,7 @@ hyp = {'optimizer': 'SGD', # ['adam', 'SGD', None] if none, default is SGD
|
|
32 |
'hsv_s': 0.7, # image HSV-Saturation augmentation (fraction)
|
33 |
'hsv_v': 0.4, # image HSV-Value augmentation (fraction)
|
34 |
'degrees': 0.0, # image rotation (+/- deg)
|
35 |
-
'translate': 0.
|
36 |
'scale': 0.5, # image scale (+/- gain)
|
37 |
'shear': 0.0} # image shear (+/- deg)
|
38 |
|
|
|
32 |
'hsv_s': 0.7, # image HSV-Saturation augmentation (fraction)
|
33 |
'hsv_v': 0.4, # image HSV-Value augmentation (fraction)
|
34 |
'degrees': 0.0, # image rotation (+/- deg)
|
35 |
+
'translate': 0.5, # image translation (+/- fraction)
|
36 |
'scale': 0.5, # image scale (+/- gain)
|
37 |
'shear': 0.0} # image shear (+/- deg)
|
38 |
|
utils/datasets.py
CHANGED
@@ -485,9 +485,9 @@ class LoadImagesAndLabels(Dataset): # for training/testing
|
|
485 |
|
486 |
# MixUp https://arxiv.org/pdf/1710.09412.pdf
|
487 |
# if random.random() < 0.5:
|
488 |
-
#
|
489 |
# r = np.random.beta(0.3, 0.3) # mixup ratio, alpha=beta=0.3
|
490 |
-
#
|
491 |
# labels = np.concatenate((labels, labels2), 0)
|
492 |
|
493 |
else:
|
@@ -513,11 +513,11 @@ class LoadImagesAndLabels(Dataset): # for training/testing
|
|
513 |
if self.augment:
|
514 |
# Augment imagespace
|
515 |
if not self.mosaic:
|
516 |
-
img, labels =
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
|
522 |
# Augment colorspace
|
523 |
augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v'])
|
@@ -610,7 +610,7 @@ def load_mosaic(self, index):
|
|
610 |
|
611 |
labels4 = []
|
612 |
s = self.img_size
|
613 |
-
yc, xc =
|
614 |
indices = [index] + [random.randint(0, len(self.labels) - 1) for _ in range(3)] # 3 additional image indices
|
615 |
for i, index in enumerate(indices):
|
616 |
# Load image
|
@@ -656,12 +656,12 @@ def load_mosaic(self, index):
|
|
656 |
|
657 |
# Augment
|
658 |
# img4 = img4[s // 2: int(s * 1.5), s // 2:int(s * 1.5)] # center crop (WARNING, requires box pruning)
|
659 |
-
img4, labels4 =
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
|
666 |
return img4, labels4
|
667 |
|
@@ -716,36 +716,54 @@ def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scale
|
|
716 |
return img, ratio, (dw, dh)
|
717 |
|
718 |
|
719 |
-
def
|
720 |
# torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(.1, .1), scale=(.9, 1.1), shear=(-10, 10))
|
721 |
-
# https://medium.com/uruvideo/dataset-augmentation-with-random-homographies-a8f4b44830d4
|
722 |
# targets = [cls, xyxy]
|
723 |
|
724 |
height = img.shape[0] + border[0] * 2 # shape(h,w,c)
|
725 |
width = img.shape[1] + border[1] * 2
|
726 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
727 |
# Rotation and Scale
|
728 |
R = np.eye(3)
|
729 |
a = random.uniform(-degrees, degrees)
|
730 |
# a += random.choice([-180, -90, 0, 90]) # add 90deg rotations to small rotations
|
731 |
s = random.uniform(1 - scale, 1 + scale)
|
732 |
# s = 2 ** random.uniform(-scale, scale)
|
733 |
-
R[:2] = cv2.getRotationMatrix2D(angle=a, center=(
|
734 |
-
|
735 |
-
# Translation
|
736 |
-
T = np.eye(3)
|
737 |
-
T[0, 2] = random.uniform(-translate, translate) * img.shape[1] + border[1] # x translation (pixels)
|
738 |
-
T[1, 2] = random.uniform(-translate, translate) * img.shape[0] + border[0] # y translation (pixels)
|
739 |
|
740 |
# Shear
|
741 |
S = np.eye(3)
|
742 |
S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # x shear (deg)
|
743 |
S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # y shear (deg)
|
744 |
|
|
|
|
|
|
|
|
|
|
|
745 |
# Combined rotation matrix
|
746 |
-
M = S @
|
747 |
if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any(): # image changed
|
748 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
749 |
|
750 |
# Transform label coordinates
|
751 |
n = len(targets)
|
@@ -753,7 +771,11 @@ def random_affine(img, targets=(), degrees=10, translate=.1, scale=.1, shear=10,
|
|
753 |
# warp points
|
754 |
xy = np.ones((n * 4, 3))
|
755 |
xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2) # x1y1, x2y2, x1y2, x2y1
|
756 |
-
xy =
|
|
|
|
|
|
|
|
|
757 |
|
758 |
# create new boxes
|
759 |
x = xy[:, [0, 2, 4, 6]]
|
|
|
485 |
|
486 |
# MixUp https://arxiv.org/pdf/1710.09412.pdf
|
487 |
# if random.random() < 0.5:
|
488 |
+
# img2, labels2 = load_mosaic(self, random.randint(0, len(self.labels) - 1))
|
489 |
# r = np.random.beta(0.3, 0.3) # mixup ratio, alpha=beta=0.3
|
490 |
+
# img = (img * r + img2 * (1 - r)).astype(np.uint8)
|
491 |
# labels = np.concatenate((labels, labels2), 0)
|
492 |
|
493 |
else:
|
|
|
513 |
if self.augment:
|
514 |
# Augment imagespace
|
515 |
if not self.mosaic:
|
516 |
+
img, labels = random_perspective(img, labels,
|
517 |
+
degrees=hyp['degrees'],
|
518 |
+
translate=hyp['translate'],
|
519 |
+
scale=hyp['scale'],
|
520 |
+
shear=hyp['shear'])
|
521 |
|
522 |
# Augment colorspace
|
523 |
augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v'])
|
|
|
610 |
|
611 |
labels4 = []
|
612 |
s = self.img_size
|
613 |
+
yc, xc = s, s # mosaic center x, y
|
614 |
indices = [index] + [random.randint(0, len(self.labels) - 1) for _ in range(3)] # 3 additional image indices
|
615 |
for i, index in enumerate(indices):
|
616 |
# Load image
|
|
|
656 |
|
657 |
# Augment
|
658 |
# img4 = img4[s // 2: int(s * 1.5), s // 2:int(s * 1.5)] # center crop (WARNING, requires box pruning)
|
659 |
+
img4, labels4 = random_perspective(img4, labels4,
|
660 |
+
degrees=self.hyp['degrees'],
|
661 |
+
translate=self.hyp['translate'],
|
662 |
+
scale=self.hyp['scale'],
|
663 |
+
shear=self.hyp['shear'],
|
664 |
+
border=self.mosaic_border) # border to remove
|
665 |
|
666 |
return img4, labels4
|
667 |
|
|
|
716 |
return img, ratio, (dw, dh)
|
717 |
|
718 |
|
719 |
+
def random_perspective(img, targets=(), degrees=10, translate=.1, scale=.1, shear=10, perspective=0.0, border=(0, 0)):
|
720 |
# torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(.1, .1), scale=(.9, 1.1), shear=(-10, 10))
|
|
|
721 |
# targets = [cls, xyxy]
|
722 |
|
723 |
height = img.shape[0] + border[0] * 2 # shape(h,w,c)
|
724 |
width = img.shape[1] + border[1] * 2
|
725 |
|
726 |
+
# Center
|
727 |
+
C = np.eye(3)
|
728 |
+
C[0, 2] = -img.shape[1] / 2 # x translation (pixels)
|
729 |
+
C[1, 2] = -img.shape[0] / 2 # y translation (pixels)
|
730 |
+
|
731 |
+
# Perspective
|
732 |
+
P = np.eye(3)
|
733 |
+
P[2, 0] = random.uniform(-perspective, perspective) # x perspective (about y)
|
734 |
+
P[2, 1] = random.uniform(-perspective, perspective) # y perspective (about x)
|
735 |
+
|
736 |
# Rotation and Scale
|
737 |
R = np.eye(3)
|
738 |
a = random.uniform(-degrees, degrees)
|
739 |
# a += random.choice([-180, -90, 0, 90]) # add 90deg rotations to small rotations
|
740 |
s = random.uniform(1 - scale, 1 + scale)
|
741 |
# s = 2 ** random.uniform(-scale, scale)
|
742 |
+
R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s)
|
|
|
|
|
|
|
|
|
|
|
743 |
|
744 |
# Shear
|
745 |
S = np.eye(3)
|
746 |
S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # x shear (deg)
|
747 |
S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # y shear (deg)
|
748 |
|
749 |
+
# Translation
|
750 |
+
T = np.eye(3)
|
751 |
+
T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * width # x translation (pixels)
|
752 |
+
T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * height # y translation (pixels)
|
753 |
+
|
754 |
# Combined rotation matrix
|
755 |
+
M = T @ S @ R @ P @ C # order of operations (right to left) is IMPORTANT
|
756 |
if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any(): # image changed
|
757 |
+
if perspective:
|
758 |
+
img = cv2.warpPerspective(img, M, dsize=(width, height), borderValue=(114, 114, 114))
|
759 |
+
else: # affine
|
760 |
+
img = cv2.warpAffine(img, M[:2], dsize=(width, height), borderValue=(114, 114, 114))
|
761 |
+
|
762 |
+
# Visualize
|
763 |
+
# import matplotlib.pyplot as plt
|
764 |
+
# ax = plt.subplots(1, 2, figsize=(12, 6))[1].ravel()
|
765 |
+
# ax[0].imshow(img[:, :, ::-1]) # base
|
766 |
+
# ax[1].imshow(img2[:, :, ::-1]) # warped
|
767 |
|
768 |
# Transform label coordinates
|
769 |
n = len(targets)
|
|
|
771 |
# warp points
|
772 |
xy = np.ones((n * 4, 3))
|
773 |
xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2) # x1y1, x2y2, x1y2, x2y1
|
774 |
+
xy = xy @ M.T # transform
|
775 |
+
if perspective:
|
776 |
+
xy = (xy[:, :2] / xy[:, 2:3]).reshape(n, 8) # rescale
|
777 |
+
else: # affine
|
778 |
+
xy = xy[:, :2].reshape(n, 8)
|
779 |
|
780 |
# create new boxes
|
781 |
x = xy[:, [0, 2, 4, 6]]
|