PKaushik commited on
Commit
f42fe08
1 Parent(s): 474a909
Files changed (1) hide show
  1. yolov6/data/data_augment.py +193 -0
yolov6/data/data_augment.py CHANGED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ # This code is based on
4
+ # https://github.com/ultralytics/yolov5/blob/master/utils/dataloaders.py
5
+
6
+ import math
7
+ import random
8
+
9
+ import cv2
10
+ import numpy as np
11
+
12
+
13
+ def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5):
14
+ # HSV color-space augmentation
15
+ if hgain or sgain or vgain:
16
+ r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1 # random gains
17
+ hue, sat, val = cv2.split(cv2.cvtColor(im, cv2.COLOR_BGR2HSV))
18
+ dtype = im.dtype # uint8
19
+
20
+ x = np.arange(0, 256, dtype=r.dtype)
21
+ lut_hue = ((x * r[0]) % 180).astype(dtype)
22
+ lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
23
+ lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
24
+
25
+ im_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val)))
26
+ cv2.cvtColor(im_hsv, cv2.COLOR_HSV2BGR, dst=im) # no return needed
27
+
28
+
29
+ def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
30
+ # Resize and pad image while meeting stride-multiple constraints
31
+ shape = im.shape[:2] # current shape [height, width]
32
+ if isinstance(new_shape, int):
33
+ new_shape = (new_shape, new_shape)
34
+
35
+ # Scale ratio (new / old)
36
+ r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
37
+ if not scaleup: # only scale down, do not scale up (for better val mAP)
38
+ r = min(r, 1.0)
39
+
40
+ # Compute padding
41
+ new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
42
+ dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
43
+
44
+ if auto: # minimum rectangle
45
+ dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
46
+
47
+ dw /= 2 # divide padding into 2 sides
48
+ dh /= 2
49
+
50
+ if shape[::-1] != new_unpad: # resize
51
+ im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
52
+ top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
53
+ left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
54
+ im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
55
+ return im, r, (dw, dh)
56
+
57
+
58
+ def mixup(im, labels, im2, labels2):
59
+ # Applies MixUp augmentation https://arxiv.org/pdf/1710.09412.pdf
60
+ r = np.random.beta(32.0, 32.0) # mixup ratio, alpha=beta=32.0
61
+ im = (im * r + im2 * (1 - r)).astype(np.uint8)
62
+ labels = np.concatenate((labels, labels2), 0)
63
+ return im, labels
64
+
65
+
66
+ def box_candidates(box1, box2, wh_thr=2, ar_thr=20, area_thr=0.1, eps=1e-16): # box1(4,n), box2(4,n)
67
+ # Compute candidate boxes: box1 before augment, box2 after augment, wh_thr (pixels), aspect_ratio_thr, area_ratio
68
+ w1, h1 = box1[2] - box1[0], box1[3] - box1[1]
69
+ w2, h2 = box2[2] - box2[0], box2[3] - box2[1]
70
+ ar = np.maximum(w2 / (h2 + eps), h2 / (w2 + eps)) # aspect ratio
71
+ return (w2 > wh_thr) & (h2 > wh_thr) & (w2 * h2 / (w1 * h1 + eps) > area_thr) & (ar < ar_thr) # candidates
72
+
73
+
74
+ def random_affine(img, labels=(), degrees=10, translate=.1, scale=.1, shear=10,
75
+ new_shape=(640, 640)):
76
+
77
+ n = len(labels)
78
+ height, width = new_shape
79
+
80
+ M, s = get_transform_matrix(img.shape[:2], (height, width), degrees, scale, shear, translate)
81
+ if (M != np.eye(3)).any(): # image changed
82
+ img = cv2.warpAffine(img, M[:2], dsize=(width, height), borderValue=(114, 114, 114))
83
+
84
+ # Transform label coordinates
85
+ if n:
86
+ new = np.zeros((n, 4))
87
+
88
+ xy = np.ones((n * 4, 3))
89
+ xy[:, :2] = labels[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2) # x1y1, x2y2, x1y2, x2y1
90
+ xy = xy @ M.T # transform
91
+ xy = xy[:, :2].reshape(n, 8) # perspective rescale or affine
92
+
93
+ # create new boxes
94
+ x = xy[:, [0, 2, 4, 6]]
95
+ y = xy[:, [1, 3, 5, 7]]
96
+ new = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T
97
+
98
+ # clip
99
+ new[:, [0, 2]] = new[:, [0, 2]].clip(0, width)
100
+ new[:, [1, 3]] = new[:, [1, 3]].clip(0, height)
101
+
102
+ # filter candidates
103
+ i = box_candidates(box1=labels[:, 1:5].T * s, box2=new.T, area_thr=0.1)
104
+ labels = labels[i]
105
+ labels[:, 1:5] = new[i]
106
+
107
+ return img, labels
108
+
109
+
110
+ def get_transform_matrix(img_shape, new_shape, degrees, scale, shear, translate):
111
+ new_height, new_width = new_shape
112
+ # Center
113
+ C = np.eye(3)
114
+ C[0, 2] = -img_shape[1] / 2 # x translation (pixels)
115
+ C[1, 2] = -img_shape[0] / 2 # y translation (pixels)
116
+
117
+ # Rotation and Scale
118
+ R = np.eye(3)
119
+ a = random.uniform(-degrees, degrees)
120
+ # a += random.choice([-180, -90, 0, 90]) # add 90deg rotations to small rotations
121
+ s = random.uniform(1 - scale, 1 + scale)
122
+ # s = 2 ** random.uniform(-scale, scale)
123
+ R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s)
124
+
125
+ # Shear
126
+ S = np.eye(3)
127
+ S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # x shear (deg)
128
+ S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # y shear (deg)
129
+
130
+ # Translation
131
+ T = np.eye(3)
132
+ T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * new_width # x translation (pixels)
133
+ T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * new_height # y transla ion (pixels)
134
+
135
+ # Combined rotation matrix
136
+ M = T @ S @ R @ C # order of operations (right to left) is IMPORTANT
137
+ return M, s
138
+
139
+
140
+ def mosaic_augmentation(img_size, imgs, hs, ws, labels, hyp):
141
+
142
+ assert len(imgs) == 4, "Mosaic augmentation of current version only supports 4 images."
143
+
144
+ labels4 = []
145
+ s = img_size
146
+ yc, xc = (int(random.uniform(s//2, 3*s//2)) for _ in range(2)) # mosaic center x, y
147
+ for i in range(len(imgs)):
148
+ # Load image
149
+ img, h, w = imgs[i], hs[i], ws[i]
150
+ # place img in img4
151
+ if i == 0: # top left
152
+ img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles
153
+ x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc # xmin, ymin, xmax, ymax (large image)
154
+ x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h # xmin, ymin, xmax, ymax (small image)
155
+ elif i == 1: # top right
156
+ x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc
157
+ x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h
158
+ elif i == 2: # bottom left
159
+ x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)
160
+ x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, w, min(y2a - y1a, h)
161
+ elif i == 3: # bottom right
162
+ x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)
163
+ x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)
164
+
165
+ img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b] # img4[ymin:ymax, xmin:xmax]
166
+ padw = x1a - x1b
167
+ padh = y1a - y1b
168
+
169
+ # Labels
170
+ labels_per_img = labels[i].copy()
171
+ if labels_per_img.size:
172
+ boxes = np.copy(labels_per_img[:, 1:])
173
+ boxes[:, 0] = w * (labels_per_img[:, 1] - labels_per_img[:, 3] / 2) + padw # top left x
174
+ boxes[:, 1] = h * (labels_per_img[:, 2] - labels_per_img[:, 4] / 2) + padh # top left y
175
+ boxes[:, 2] = w * (labels_per_img[:, 1] + labels_per_img[:, 3] / 2) + padw # bottom right x
176
+ boxes[:, 3] = h * (labels_per_img[:, 2] + labels_per_img[:, 4] / 2) + padh # bottom right y
177
+ labels_per_img[:, 1:] = boxes
178
+
179
+ labels4.append(labels_per_img)
180
+
181
+ # Concat/clip labels
182
+ labels4 = np.concatenate(labels4, 0)
183
+ for x in (labels4[:, 1:]):
184
+ np.clip(x, 0, 2 * s, out=x)
185
+
186
+ # Augment
187
+ img4, labels4 = random_affine(img4, labels4,
188
+ degrees=hyp['degrees'],
189
+ translate=hyp['translate'],
190
+ scale=hyp['scale'],
191
+ shear=hyp['shear'])
192
+
193
+ return img4, labels4