| |
|
|
| import numpy as np |
| import cv2 |
| import random |
| import math |
|
|
|
|
| def augment_hsv(img, hgain=0.5, sgain=0.5, vgain=0.5): |
| """change color hue, saturation, value""" |
| r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1 |
| hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV)) |
| dtype = img.dtype |
|
|
| x = np.arange(0, 256, dtype=np.int16) |
| lut_hue = ((x * r[0]) % 180).astype(dtype) |
| lut_sat = np.clip(x * r[1], 0, 255).astype(dtype) |
| lut_val = np.clip(x * r[2], 0, 255).astype(dtype) |
|
|
| img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))).astype(dtype) |
| cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR, dst=img) |
|
|
| |
| |
| |
| |
|
|
|
|
| def random_perspective(combination, targets=(), degrees=10, translate=.1, scale=.1, shear=10, perspective=0.0, border=(0, 0)): |
| """combination of img transform""" |
| |
| |
| img, gray, line = combination |
| height = img.shape[0] + border[0] * 2 |
| width = img.shape[1] + border[1] * 2 |
|
|
| |
| C = np.eye(3) |
| C[0, 2] = -img.shape[1] / 2 |
| C[1, 2] = -img.shape[0] / 2 |
|
|
| |
| P = np.eye(3) |
| P[2, 0] = random.uniform(-perspective, perspective) |
| P[2, 1] = random.uniform(-perspective, perspective) |
|
|
| |
| R = np.eye(3) |
| a = random.uniform(-degrees, degrees) |
| |
| s = random.uniform(1 - scale, 1 + scale) |
| |
| R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s) |
|
|
| |
| S = np.eye(3) |
| S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180) |
| S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) |
|
|
| |
| T = np.eye(3) |
| T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * width |
| T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * height |
|
|
| |
| M = T @ S @ R @ P @ C |
| if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any(): |
| if perspective: |
| img = cv2.warpPerspective(img, M, dsize=(width, height), borderValue=(114, 114, 114)) |
| gray = cv2.warpPerspective(gray, M, dsize=(width, height), borderValue=0) |
| line = cv2.warpPerspective(line, M, dsize=(width, height), borderValue=0) |
| else: |
| img = cv2.warpAffine(img, M[:2], dsize=(width, height), borderValue=(114, 114, 114)) |
| gray = cv2.warpAffine(gray, M[:2], dsize=(width, height), borderValue=0) |
| line = cv2.warpAffine(line, M[:2], dsize=(width, height), borderValue=0) |
|
|
| |
| |
| |
| |
| |
|
|
| |
| n = len(targets) |
| if n: |
| |
| xy = np.ones((n * 4, 3)) |
| xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2) |
| xy = xy @ M.T |
| if perspective: |
| xy = (xy[:, :2] / xy[:, 2:3]).reshape(n, 8) |
| else: |
| xy = xy[:, :2].reshape(n, 8) |
|
|
| |
| x = xy[:, [0, 2, 4, 6]] |
| y = xy[:, [1, 3, 5, 7]] |
| xy = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| xy[:, [0, 2]] = xy[:, [0, 2]].clip(0, width) |
| xy[:, [1, 3]] = xy[:, [1, 3]].clip(0, height) |
|
|
| |
| i = _box_candidates(box1=targets[:, 1:5].T * s, box2=xy.T) |
| targets = targets[i] |
| targets[:, 1:5] = xy[i] |
|
|
| combination = (img, gray, line) |
| return combination, targets |
|
|
|
|
| def cutout(combination, labels): |
| |
| image, gray = combination |
| h, w = image.shape[:2] |
|
|
| def bbox_ioa(box1, box2): |
| |
| box2 = box2.transpose() |
|
|
| |
| b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3] |
| b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3] |
|
|
| |
| inter_area = (np.minimum(b1_x2, b2_x2) - np.maximum(b1_x1, b2_x1)).clip(0) * \ |
| (np.minimum(b1_y2, b2_y2) - np.maximum(b1_y1, b2_y1)).clip(0) |
|
|
| |
| box2_area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1) + 1e-16 |
|
|
| |
| return inter_area / box2_area |
|
|
| |
| scales = [0.5] * 1 + [0.25] * 2 + [0.125] * 4 + [0.0625] * 8 + [0.03125] * 16 |
| for s in scales: |
| mask_h = random.randint(1, int(h * s)) |
| mask_w = random.randint(1, int(w * s)) |
|
|
| |
| xmin = max(0, random.randint(0, w) - mask_w // 2) |
| ymin = max(0, random.randint(0, h) - mask_h // 2) |
| xmax = min(w, xmin + mask_w) |
| ymax = min(h, ymin + mask_h) |
| |
|
|
| |
| image[ymin:ymax, xmin:xmax] = [random.randint(64, 191) for _ in range(3)] |
| gray[ymin:ymax, xmin:xmax] = -1 |
|
|
| |
| if len(labels) and s > 0.03: |
| box = np.array([xmin, ymin, xmax, ymax], dtype=np.float32) |
| ioa = bbox_ioa(box, labels[:, 1:5]) |
| labels = labels[ioa < 0.60] |
|
|
| return image, gray, labels |
|
|
|
|
| def letterbox(combination, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True): |
| """Resize the input image and automatically padding to suitable shape :https://zhuanlan.zhihu.com/p/172121380""" |
| |
| img, gray, line = combination |
| shape = img.shape[:2] |
| if isinstance(new_shape, int): |
| new_shape = (new_shape, new_shape) |
|
|
| |
| r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) |
| if not scaleup: |
| r = min(r, 1.0) |
|
|
| |
| ratio = r, r |
| new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) |
| dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] |
| if auto: |
| dw, dh = np.mod(dw, 32), np.mod(dh, 32) |
| elif scaleFill: |
| dw, dh = 0.0, 0.0 |
| new_unpad = (new_shape[1], new_shape[0]) |
| ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] |
|
|
| dw /= 2 |
| dh /= 2 |
|
|
| if shape[::-1] != new_unpad: |
| img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) |
| gray = cv2.resize(gray, new_unpad, interpolation=cv2.INTER_LINEAR) |
| line = cv2.resize(line, new_unpad, interpolation=cv2.INTER_LINEAR) |
|
|
| top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) |
| left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) |
|
|
| img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) |
| gray = cv2.copyMakeBorder(gray, top, bottom, left, right, cv2.BORDER_CONSTANT, value=0) |
| line = cv2.copyMakeBorder(line, top, bottom, left, right, cv2.BORDER_CONSTANT, value=0) |
| |
| |
| combination = (img, gray, line) |
| return combination, ratio, (dw, dh) |
|
|
| def letterbox_for_img(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True): |
| |
| shape = img.shape[:2] |
| if isinstance(new_shape, int): |
| new_shape = (new_shape, new_shape) |
|
|
| |
|
|
| r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) |
| if not scaleup: |
| r = min(r, 1.0) |
|
|
| |
| ratio = r, r |
| new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) |
| dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] |
| if auto: |
| dw, dh = np.mod(dw, 32), np.mod(dh, 32) |
| elif scaleFill: |
| dw, dh = 0.0, 0.0 |
| new_unpad = (new_shape[1], new_shape[0]) |
| ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] |
|
|
| dw /= 2 |
| dh /= 2 |
| if shape[::-1] != new_unpad: |
| img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_AREA) |
| |
| top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) |
| left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) |
| img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) |
| return img, ratio, (dw, dh) |
|
|
|
|
| def _box_candidates(box1, box2, wh_thr=2, ar_thr=20, area_thr=0.1): |
| |
| w1, h1 = box1[2] - box1[0], box1[3] - box1[1] |
| w2, h2 = box2[2] - box2[0], box2[3] - box2[1] |
| ar = np.maximum(w2 / (h2 + 1e-16), h2 / (w2 + 1e-16)) |
| return (w2 > wh_thr) & (h2 > wh_thr) & (w2 * h2 / (w1 * h1 + 1e-16) > area_thr) & (ar < ar_thr) |
|
|