YOLOv5 + Albumentations integration (#3882)
* Albumentations integration * ToGray p=0.01 * print confirmation * create instance in dataloader init method * improved version handling * transform not defined fix * assert string update * create check_version() * add spaces * update class comment
This commit is contained in:
1 parent
6a3ee7cf03
commit
33202b7f0b
4 files changed
+61
-27
No files matched your search
@@ -27,4 +27,5 @@ pandas
|
|||||||
# extras --------------------------------------
|
# extras --------------------------------------
|
||||||
# Cython # for pycocotools https://github.com/cocodataset/cocoapi/issues/172
|
# Cython # for pycocotools https://github.com/cocodataset/cocoapi/issues/172
|
||||||
# pycocotools>=2.0 # COCO mAP
|
# pycocotools>=2.0 # COCO mAP
|
||||||
|
# albumentations>=1.0.0
|
||||||
thop # FLOPs computation
|
thop # FLOPs computation
|
||||||
+29
-1
@@ -1,15 +1,43 @@
|
|||||||
# YOLOv5 image augmentation functions
|
# YOLOv5 image augmentation functions
|
||||||
|
|
||||||
|
import logging
|
||||||
import random
|
import random
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import math
|
import math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from utils.general import segment2box, resample_segments
|
from utils.general import colorstr, segment2box, resample_segments, check_version
|
||||||
from utils.metrics import bbox_ioa
|
from utils.metrics import bbox_ioa
|
||||||
|
|
||||||
|
|
||||||
|
class Albumentations:
|
||||||
|
# YOLOv5 Albumentations class (optional, only used if package is installed)
|
||||||
|
def __init__(self):
|
||||||
|
self.transform = None
|
||||||
|
try:
|
||||||
|
import albumentations as A
|
||||||
|
check_version(A.__version__, '1.0.0') # version requirement
|
||||||
|
|
||||||
|
self.transform = A.Compose([
|
||||||
|
A.Blur(p=0.1),
|
||||||
|
A.MedianBlur(p=0.1),
|
||||||
|
A.ToGray(p=0.01)],
|
||||||
|
bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels']))
|
||||||
|
|
||||||
|
logging.info(colorstr('albumentations: ') + ', '.join(f'{x}' for x in self.transform.transforms))
|
||||||
|
except ImportError: # package not installed, skip
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
logging.info(colorstr('albumentations: ') + f'{e}')
|
||||||
|
|
||||||
|
def __call__(self, im, labels, p=1.0):
|
||||||
|
if self.transform and random.random() < p:
|
||||||
|
new = self.transform(image=im, bboxes=labels[:, 1:], class_labels=labels[:, 0]) # transformed
|
||||||
|
im, labels = new['image'], np.array([[c, *b] for c, b in zip(new['class_labels'], new['bboxes'])])
|
||||||
|
return im, labels
|
||||||
|
|
||||||
|
|
||||||
def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5):
|
def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5):
|
||||||
# HSV color-space augmentation
|
# HSV color-space augmentation
|
||||||
if hgain or sgain or vgain:
|
if hgain or sgain or vgain:
|
||||||
|
|||||||
+21
-19
@@ -22,7 +22,7 @@ from PIL import Image, ExifTags
|
|||||||
from torch.utils.data import Dataset
|
from torch.utils.data import Dataset
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from utils.augmentations import augment_hsv, copy_paste, letterbox, mixup, random_perspective
|
from utils.augmentations import Albumentations, augment_hsv, copy_paste, letterbox, mixup, random_perspective
|
||||||
from utils.general import check_requirements, check_file, check_dataset, xywh2xyxy, xywhn2xyxy, xyxy2xywhn, \
|
from utils.general import check_requirements, check_file, check_dataset, xywh2xyxy, xywhn2xyxy, xyxy2xywhn, \
|
||||||
xyn2xy, segments2boxes, clean_str
|
xyn2xy, segments2boxes, clean_str
|
||||||
from utils.torch_utils import torch_distributed_zero_first
|
from utils.torch_utils import torch_distributed_zero_first
|
||||||
@@ -372,6 +372,7 @@ class LoadImagesAndLabels(Dataset): # for training/testing
|
|||||||
self.mosaic_border = [-img_size // 2, -img_size // 2]
|
self.mosaic_border = [-img_size // 2, -img_size // 2]
|
||||||
self.stride = stride
|
self.stride = stride
|
||||||
self.path = path
|
self.path = path
|
||||||
|
self.albumentations = Albumentations() if augment else None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
f = [] # image files
|
f = [] # image files
|
||||||
@@ -539,9 +540,7 @@ class LoadImagesAndLabels(Dataset): # for training/testing
|
|||||||
if labels.size: # normalized xywh to pixel xyxy format
|
if labels.size: # normalized xywh to pixel xyxy format
|
||||||
labels[:, 1:] = xywhn2xyxy(labels[:, 1:], ratio[0] * w, ratio[1] * h, padw=pad[0], padh=pad[1])
|
labels[:, 1:] = xywhn2xyxy(labels[:, 1:], ratio[0] * w, ratio[1] * h, padw=pad[0], padh=pad[1])
|
||||||
|
|
||||||
if self.augment:
|
if self.augment:
|
||||||
# Augment imagespace
|
|
||||||
if not mosaic:
|
|
||||||
img, labels = random_perspective(img, labels,
|
img, labels = random_perspective(img, labels,
|
||||||
degrees=hyp['degrees'],
|
degrees=hyp['degrees'],
|
||||||
translate=hyp['translate'],
|
translate=hyp['translate'],
|
||||||
@@ -549,32 +548,35 @@ class LoadImagesAndLabels(Dataset): # for training/testing
|
|||||||
shear=hyp['shear'],
|
shear=hyp['shear'],
|
||||||
perspective=hyp['perspective'])
|
perspective=hyp['perspective'])
|
||||||
|
|
||||||
# Augment colorspace
|
nl = len(labels) # number of labels
|
||||||
augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v'])
|
if nl:
|
||||||
|
|
||||||
# Apply cutouts
|
|
||||||
# if random.random() < 0.9:
|
|
||||||
# labels = cutout(img, labels)
|
|
||||||
|
|
||||||
nL = len(labels) # number of labels
|
|
||||||
if nL:
|
|
||||||
labels[:, 1:5] = xyxy2xywhn(labels[:, 1:5], w=img.shape[1], h=img.shape[0]) # xyxy to xywh normalized
|
labels[:, 1:5] = xyxy2xywhn(labels[:, 1:5], w=img.shape[1], h=img.shape[0]) # xyxy to xywh normalized
|
||||||
|
|
||||||
if self.augment:
|
if self.augment:
|
||||||
# flip up-down
|
# Albumentations
|
||||||
|
img, labels = self.albumentations(img, labels)
|
||||||
|
|
||||||
|
# HSV color-space
|
||||||
|
augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v'])
|
||||||
|
|
||||||
|
# Flip up-down
|
||||||
if random.random() < hyp['flipud']:
|
if random.random() < hyp['flipud']:
|
||||||
img = np.flipud(img)
|
img = np.flipud(img)
|
||||||
if nL:
|
if nl:
|
||||||
labels[:, 2] = 1 - labels[:, 2]
|
labels[:, 2] = 1 - labels[:, 2]
|
||||||
|
|
||||||
# flip left-right
|
# Flip left-right
|
||||||
if random.random() < hyp['fliplr']:
|
if random.random() < hyp['fliplr']:
|
||||||
img = np.fliplr(img)
|
img = np.fliplr(img)
|
||||||
if nL:
|
if nl:
|
||||||
labels[:, 1] = 1 - labels[:, 1]
|
labels[:, 1] = 1 - labels[:, 1]
|
||||||
|
|
||||||
labels_out = torch.zeros((nL, 6))
|
# Cutouts
|
||||||
if nL:
|
# if random.random() < 0.9:
|
||||||
|
# labels = cutout(img, labels)
|
||||||
|
|
||||||
|
labels_out = torch.zeros((nl, 6))
|
||||||
|
if nl:
|
||||||
labels_out[:, 1:] = torch.from_numpy(labels)
|
labels_out[:, 1:] = torch.from_numpy(labels)
|
||||||
|
|
||||||
# Convert
|
# Convert
|
||||||
|
|||||||
+10
-7
@@ -3,7 +3,6 @@
|
|||||||
import contextlib
|
import contextlib
|
||||||
import glob
|
import glob
|
||||||
import logging
|
import logging
|
||||||
import math
|
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import random
|
import random
|
||||||
@@ -17,6 +16,7 @@ from pathlib import Path
|
|||||||
from subprocess import check_output
|
from subprocess import check_output
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
|
import math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import pkg_resources as pkg
|
import pkg_resources as pkg
|
||||||
@@ -136,13 +136,16 @@ def check_git_status(err_msg=', for updates see https://github.com/ultralytics/y
|
|||||||
print(f'{e}{err_msg}')
|
print(f'{e}{err_msg}')
|
||||||
|
|
||||||
|
|
||||||
def check_python(minimum='3.6.2', required=True):
|
def check_python(minimum='3.6.2'):
|
||||||
# Check current python version vs. required python version
|
# Check current python version vs. required python version
|
||||||
current = platform.python_version()
|
check_version(platform.python_version(), minimum, name='Python ')
|
||||||
result = pkg.parse_version(current) >= pkg.parse_version(minimum)
|
|
||||||
if required:
|
|
||||||
assert result, f'Python {minimum} required by YOLOv5, but Python {current} is currently installed'
|
def check_version(current='0.0.0', minimum='0.0.0', name='version ', pinned=False):
|
||||||
return result
|
# Check version vs. required version
|
||||||
|
current, minimum = (pkg.parse_version(x) for x in (current, minimum))
|
||||||
|
result = (current == minimum) if pinned else (current >= minimum)
|
||||||
|
assert result, f'{name}{minimum} required by YOLOv5, but {name}{current} is currently installed'
|
||||||
|
|
||||||
|
|
||||||
def check_requirements(requirements='requirements.txt', exclude=()):
|
def check_requirements(requirements='requirements.txt', exclude=()):
|
||||||
|
|||||||
Reference in new issue
Block a user