CoolFace
Apppublic

k20hcmus/FishEye8K

sourceHugging Faceapache-2.0updated 2y agoView on Hugging Face
3likes
augmentations.py396 linesDownload Raw Back to utils
1import math2import random3 4import cv25import numpy as np6import torch7import torchvision.transforms as T8import torchvision.transforms.functional as TF9 10from utils.general import LOGGER, check_version, colorstr, resample_segments, segment2box, xywhn2xyxy11from utils.metrics import bbox_ioa12 13IMAGENET_MEAN = 0.485, 0.456, 0.406  # RGB mean14IMAGENET_STD = 0.229, 0.224, 0.225  # RGB standard deviation15 16 17class Albumentations:18    # YOLOv5 Albumentations class (optional, only used if package is installed)19    def __init__(self, size=640):20        self.transform = None21        prefix = colorstr('albumentations: ')22        try:23            import albumentations as A24            check_version(A.__version__, '1.0.3', hard=True)  # version requirement25 26            T = [27                A.RandomResizedCrop(height=size, width=size, scale=(0.8, 1.0), ratio=(0.9, 1.11), p=0.0),28                A.Blur(p=0.01),29                A.MedianBlur(p=0.01),30                A.ToGray(p=0.01),31                A.CLAHE(p=0.01),32                A.RandomBrightnessContrast(p=0.0),33                A.RandomGamma(p=0.0),34                A.ImageCompression(quality_lower=75, p=0.0)]  # transforms35            self.transform = A.Compose(T, bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels']))36 37            LOGGER.info(prefix + ', '.join(f'{x}'.replace('always_apply=False, ', '') for x in T if x.p))38        except ImportError:  # package not installed, skip39            pass40        except Exception as e:41            LOGGER.info(f'{prefix}{e}')42 43    def __call__(self, im, labels, p=1.0):44        if self.transform and random.random() < p:45            new = self.transform(image=im, bboxes=labels[:, 1:], class_labels=labels[:, 0])  # transformed46            im, labels = new['image'], np.array([[c, *b] for c, b in zip(new['class_labels'], new['bboxes'])])47        return im, labels48 49 50def normalize(x, mean=IMAGENET_MEAN, std=IMAGENET_STD, inplace=False):51    # Denormalize RGB images x per ImageNet stats in BCHW format, i.e. = (x - mean) / std52    return TF.normalize(x, mean, std, inplace=inplace)53 54 55def denormalize(x, mean=IMAGENET_MEAN, std=IMAGENET_STD):56    # Denormalize RGB images x per ImageNet stats in BCHW format, i.e. = x * std + mean57    for i in range(3):58        x[:, i] = x[:, i] * std[i] + mean[i]59    return x60 61 62def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5):63    # HSV color-space augmentation64    if hgain or sgain or vgain:65        r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1  # random gains66        hue, sat, val = cv2.split(cv2.cvtColor(im, cv2.COLOR_BGR2HSV))67        dtype = im.dtype  # uint868 69        x = np.arange(0, 256, dtype=r.dtype)70        lut_hue = ((x * r[0]) % 180).astype(dtype)71        lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)72        lut_val = np.clip(x * r[2], 0, 255).astype(dtype)73 74        im_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val)))75        cv2.cvtColor(im_hsv, cv2.COLOR_HSV2BGR, dst=im)  # no return needed76 77 78def hist_equalize(im, clahe=True, bgr=False):79    # Equalize histogram on BGR image 'im' with im.shape(n,m,3) and range 0-25580    yuv = cv2.cvtColor(im, cv2.COLOR_BGR2YUV if bgr else cv2.COLOR_RGB2YUV)81    if clahe:82        c = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))83        yuv[:, :, 0] = c.apply(yuv[:, :, 0])84    else:85        yuv[:, :, 0] = cv2.equalizeHist(yuv[:, :, 0])  # equalize Y channel histogram86    return cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR if bgr else cv2.COLOR_YUV2RGB)  # convert YUV image to RGB87 88 89def replicate(im, labels):90    # Replicate labels91    h, w = im.shape[:2]92    boxes = labels[:, 1:].astype(int)93    x1, y1, x2, y2 = boxes.T94    s = ((x2 - x1) + (y2 - y1)) / 2  # side length (pixels)95    for i in s.argsort()[:round(s.size * 0.5)]:  # smallest indices96        x1b, y1b, x2b, y2b = boxes[i]97        bh, bw = y2b - y1b, x2b - x1b98        yc, xc = int(random.uniform(0, h - bh)), int(random.uniform(0, w - bw))  # offset x, y99        x1a, y1a, x2a, y2a = [xc, yc, xc + bw, yc + bh]100        im[y1a:y2a, x1a:x2a] = im[y1b:y2b, x1b:x2b]  # im4[ymin:ymax, xmin:xmax]101        labels = np.append(labels, [[labels[i, 0], x1a, y1a, x2a, y2a]], axis=0)102 103    return im, labels104 105 106def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):107    # Resize and pad image while meeting stride-multiple constraints108    shape = im.shape[:2]  # current shape [height, width]109    if isinstance(new_shape, int):110        new_shape = (new_shape, new_shape)111 112    # Scale ratio (new / old)113    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])114    if not scaleup:  # only scale down, do not scale up (for better val mAP)115        r = min(r, 1.0)116 117    # Compute padding118    ratio = r, r  # width, height ratios119    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))120    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding121    if auto:  # minimum rectangle122        dw, dh = np.mod(dw, stride), np.mod(dh, stride)  # wh padding123    elif scaleFill:  # stretch124        dw, dh = 0.0, 0.0125        new_unpad = (new_shape[1], new_shape[0])126        ratio = new_shape[1] / shape[1], new_shape[0] / shape[0]  # width, height ratios127 128    dw /= 2  # divide padding into 2 sides129    dh /= 2130 131    if shape[::-1] != new_unpad:  # resize132        im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)133    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))134    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))135    im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border136    return im, ratio, (dw, dh)137 138 139def random_perspective(im,140                       targets=(),141                       segments=(),142                       degrees=10,143                       translate=.1,144                       scale=.1,145                       shear=10,146                       perspective=0.0,147                       border=(0, 0)):148    # torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(0.1, 0.1), scale=(0.9, 1.1), shear=(-10, 10))149    # targets = [cls, xyxy]150 151    height = im.shape[0] + border[0] * 2  # shape(h,w,c)152    width = im.shape[1] + border[1] * 2153 154    # Center155    C = np.eye(3)156    C[0, 2] = -im.shape[1] / 2  # x translation (pixels)157    C[1, 2] = -im.shape[0] / 2  # y translation (pixels)158 159    # Perspective160    P = np.eye(3)161    P[2, 0] = random.uniform(-perspective, perspective)  # x perspective (about y)162    P[2, 1] = random.uniform(-perspective, perspective)  # y perspective (about x)163 164    # Rotation and Scale165    R = np.eye(3)166    a = random.uniform(-degrees, degrees)167    # a += random.choice([-180, -90, 0, 90])  # add 90deg rotations to small rotations168    s = random.uniform(1 - scale, 1 + scale)169    # s = 2 ** random.uniform(-scale, scale)170    R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s)171 172    # Shear173    S = np.eye(3)174    S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # x shear (deg)175    S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # y shear (deg)176 177    # Translation178    T = np.eye(3)179    T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * width  # x translation (pixels)180    T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * height  # y translation (pixels)181 182    # Combined rotation matrix183    M = T @ S @ R @ P @ C  # order of operations (right to left) is IMPORTANT184    if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any():  # image changed185        if perspective:186            im = cv2.warpPerspective(im, M, dsize=(width, height), borderValue=(114, 114, 114))187        else:  # affine188            im = cv2.warpAffine(im, M[:2], dsize=(width, height), borderValue=(114, 114, 114))189 190    # Visualize191    # import matplotlib.pyplot as plt192    # ax = plt.subplots(1, 2, figsize=(12, 6))[1].ravel()193    # ax[0].imshow(im[:, :, ::-1])  # base194    # ax[1].imshow(im2[:, :, ::-1])  # warped195 196    # Transform label coordinates197    n = len(targets)198    if n:199        use_segments = any(x.any() for x in segments)200        new = np.zeros((n, 4))201        if use_segments:  # warp segments202            segments = resample_segments(segments)  # upsample203            for i, segment in enumerate(segments):204                xy = np.ones((len(segment), 3))205                xy[:, :2] = segment206                xy = xy @ M.T  # transform207                xy = xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2]  # perspective rescale or affine208 209                # clip210                new[i] = segment2box(xy, width, height)211 212        else:  # warp boxes213            xy = np.ones((n * 4, 3))214            xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2)  # x1y1, x2y2, x1y2, x2y1215            xy = xy @ M.T  # transform216            xy = (xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2]).reshape(n, 8)  # perspective rescale or affine217 218            # create new boxes219            x = xy[:, [0, 2, 4, 6]]220            y = xy[:, [1, 3, 5, 7]]221            new = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T222 223            # clip224            new[:, [0, 2]] = new[:, [0, 2]].clip(0, width)225            new[:, [1, 3]] = new[:, [1, 3]].clip(0, height)226 227        # filter candidates228        i = box_candidates(box1=targets[:, 1:5].T * s, box2=new.T, area_thr=0.01 if use_segments else 0.10)229        targets = targets[i]230        targets[:, 1:5] = new[i]231 232    return im, targets233 234 235def copy_paste(im, labels, segments, p=0.5):236    # Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy)237    n = len(segments)238    if p and n:239        h, w, c = im.shape  # height, width, channels240        im_new = np.zeros(im.shape, np.uint8)241 242        # calculate ioa first then select indexes randomly243        boxes = np.stack([w - labels[:, 3], labels[:, 2], w - labels[:, 1], labels[:, 4]], axis=-1)  # (n, 4)244        ioa = bbox_ioa(boxes, labels[:, 1:5])  # intersection over area245        indexes = np.nonzero((ioa < 0.30).all(1))[0]  # (N, )246        n = len(indexes)247        for j in random.sample(list(indexes), k=round(p * n)):248            l, box, s = labels[j], boxes[j], segments[j]249            labels = np.concatenate((labels, [[l[0], *box]]), 0)250            segments.append(np.concatenate((w - s[:, 0:1], s[:, 1:2]), 1))251            cv2.drawContours(im_new, [segments[j].astype(np.int32)], -1, (1, 1, 1), cv2.FILLED)252 253        result = cv2.flip(im, 1)  # augment segments (flip left-right)254        i = cv2.flip(im_new, 1).astype(bool)255        im[i] = result[i]  # cv2.imwrite('debug.jpg', im)  # debug256 257    return im, labels, segments258 259 260def cutout(im, labels, p=0.5):261    # Applies image cutout augmentation https://arxiv.org/abs/1708.04552262    if random.random() < p:263        h, w = im.shape[:2]264        scales = [0.5] * 1 + [0.25] * 2 + [0.125] * 4 + [0.0625] * 8 + [0.03125] * 16  # image size fraction265        for s in scales:266            mask_h = random.randint(1, int(h * s))  # create random masks267            mask_w = random.randint(1, int(w * s))268 269            # box270            xmin = max(0, random.randint(0, w) - mask_w // 2)271            ymin = max(0, random.randint(0, h) - mask_h // 2)272            xmax = min(w, xmin + mask_w)273            ymax = min(h, ymin + mask_h)274 275            # apply random color mask276            im[ymin:ymax, xmin:xmax] = [random.randint(64, 191) for _ in range(3)]277 278            # return unobscured labels279            if len(labels) and s > 0.03:280                box = np.array([[xmin, ymin, xmax, ymax]], dtype=np.float32)281                ioa = bbox_ioa(box, xywhn2xyxy(labels[:, 1:5], w, h))[0]  # intersection over area282                labels = labels[ioa < 0.60]  # remove >60% obscured labels283 284    return labels285 286 287def mixup(im, labels, im2, labels2):288    # Applies MixUp augmentation https://arxiv.org/pdf/1710.09412.pdf289    r = np.random.beta(32.0, 32.0)  # mixup ratio, alpha=beta=32.0290    im = (im * r + im2 * (1 - r)).astype(np.uint8)291    labels = np.concatenate((labels, labels2), 0)292    return im, labels293 294 295def box_candidates(box1, box2, wh_thr=2, ar_thr=100, area_thr=0.1, eps=1e-16):  # box1(4,n), box2(4,n)296    # Compute candidate boxes: box1 before augment, box2 after augment, wh_thr (pixels), aspect_ratio_thr, area_ratio297    w1, h1 = box1[2] - box1[0], box1[3] - box1[1]298    w2, h2 = box2[2] - box2[0], box2[3] - box2[1]299    ar = np.maximum(w2 / (h2 + eps), h2 / (w2 + eps))  # aspect ratio300    return (w2 > wh_thr) & (h2 > wh_thr) & (w2 * h2 / (w1 * h1 + eps) > area_thr) & (ar < ar_thr)  # candidates301 302 303def classify_albumentations(304        augment=True,305        size=224,306        scale=(0.08, 1.0),307        ratio=(0.75, 1.0 / 0.75),  # 0.75, 1.33308        hflip=0.5,309        vflip=0.0,310        jitter=0.4,311        mean=IMAGENET_MEAN,312        std=IMAGENET_STD,313        auto_aug=False):314    # YOLOv5 classification Albumentations (optional, only used if package is installed)315    prefix = colorstr('albumentations: ')316    try:317        import albumentations as A318        from albumentations.pytorch import ToTensorV2319        check_version(A.__version__, '1.0.3', hard=True)  # version requirement320        if augment:  # Resize and crop321            T = [A.RandomResizedCrop(height=size, width=size, scale=scale, ratio=ratio)]322            if auto_aug:323                # TODO: implement AugMix, AutoAug & RandAug in albumentation324                LOGGER.info(f'{prefix}auto augmentations are currently not supported')325            else:326                if hflip > 0:327                    T += [A.HorizontalFlip(p=hflip)]328                if vflip > 0:329                    T += [A.VerticalFlip(p=vflip)]330                if jitter > 0:331                    color_jitter = (float(jitter),) * 3  # repeat value for brightness, contrast, satuaration, 0 hue332                    T += [A.ColorJitter(*color_jitter, 0)]333        else:  # Use fixed crop for eval set (reproducibility)334            T = [A.SmallestMaxSize(max_size=size), A.CenterCrop(height=size, width=size)]335        T += [A.Normalize(mean=mean, std=std), ToTensorV2()]  # Normalize and convert to Tensor336        LOGGER.info(prefix + ', '.join(f'{x}'.replace('always_apply=False, ', '') for x in T if x.p))337        return A.Compose(T)338 339    except ImportError:  # package not installed, skip340        LOGGER.warning(f'{prefix}⚠️ not found, install with `pip install albumentations` (recommended)')341    except Exception as e:342        LOGGER.info(f'{prefix}{e}')343 344 345def classify_transforms(size=224):346    # Transforms to apply if albumentations not installed347    assert isinstance(size, int), f'ERROR: classify_transforms size {size} must be integer, not (list, tuple)'348    # T.Compose([T.ToTensor(), T.Resize(size), T.CenterCrop(size), T.Normalize(IMAGENET_MEAN, IMAGENET_STD)])349    return T.Compose([CenterCrop(size), ToTensor(), T.Normalize(IMAGENET_MEAN, IMAGENET_STD)])350 351 352class LetterBox:353    # YOLOv5 LetterBox class for image preprocessing, i.e. T.Compose([LetterBox(size), ToTensor()])354    def __init__(self, size=(640, 640), auto=False, stride=32):355        super().__init__()356        self.h, self.w = (size, size) if isinstance(size, int) else size357        self.auto = auto  # pass max size integer, automatically solve for short side using stride358        self.stride = stride  # used with auto359 360    def __call__(self, im):  # im = np.array HWC361        imh, imw = im.shape[:2]362        r = min(self.h / imh, self.w / imw)  # ratio of new/old363        h, w = round(imh * r), round(imw * r)  # resized image364        hs, ws = (math.ceil(x / self.stride) * self.stride for x in (h, w)) if self.auto else self.h, self.w365        top, left = round((hs - h) / 2 - 0.1), round((ws - w) / 2 - 0.1)366        im_out = np.full((self.h, self.w, 3), 114, dtype=im.dtype)367        im_out[top:top + h, left:left + w] = cv2.resize(im, (w, h), interpolation=cv2.INTER_LINEAR)368        return im_out369 370 371class CenterCrop:372    # YOLOv5 CenterCrop class for image preprocessing, i.e. T.Compose([CenterCrop(size), ToTensor()])373    def __init__(self, size=640):374        super().__init__()375        self.h, self.w = (size, size) if isinstance(size, int) else size376 377    def __call__(self, im):  # im = np.array HWC378        imh, imw = im.shape[:2]379        m = min(imh, imw)  # min dimension380        top, left = (imh - m) // 2, (imw - m) // 2381        return cv2.resize(im[top:top + m, left:left + m], (self.w, self.h), interpolation=cv2.INTER_LINEAR)382 383 384class ToTensor:385    # YOLOv5 ToTensor class for image preprocessing, i.e. T.Compose([LetterBox(size), ToTensor()])386    def __init__(self, half=False):387        super().__init__()388        self.half = half389 390    def __call__(self, im):  # im = np.array HWC in BGR order391        im = np.ascontiguousarray(im.transpose((2, 0, 1))[::-1])  # HWC to CHW -> BGR to RGB -> contiguous392        im = torch.from_numpy(im)  # to torch393        im = im.half() if self.half else im.float()  # uint8 to fp16/32394        im /= 255.0  # 0-255 to 0.0-1.0395        return im396