| | |
| |
|
| | 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) |
| |
|