|
|
@ -44,20 +44,8 @@ class Compose:
|
|
|
|
self.transforms = transforms
|
|
|
|
self.transforms = transforms
|
|
|
|
|
|
|
|
|
|
|
|
def __call__(self, data):
|
|
|
|
def __call__(self, data):
|
|
|
|
mosaic_p = None
|
|
|
|
|
|
|
|
mosaic_imgsz = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for t in self.transforms:
|
|
|
|
for t in self.transforms:
|
|
|
|
if isinstance(t, Mosaic):
|
|
|
|
data = t(data)
|
|
|
|
temp = t(data)
|
|
|
|
|
|
|
|
mosaic_p = False if temp == data else True
|
|
|
|
|
|
|
|
mosaic_imgsz = t.imgsz
|
|
|
|
|
|
|
|
data = temp
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
if isinstance(t, RandomPerspective):
|
|
|
|
|
|
|
|
t.border = [-mosaic_imgsz // 2, -mosaic_imgsz // 2] if mosaic_p else [0, 0]
|
|
|
|
|
|
|
|
data = t(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return data
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
|
|
def append(self, transform):
|
|
|
|
def append(self, transform):
|
|
|
@ -140,7 +128,7 @@ class Mosaic(BaseMixTransform):
|
|
|
|
labels_patch = (labels if i == 0 else labels["mix_labels"][i - 1]).copy()
|
|
|
|
labels_patch = (labels if i == 0 else labels["mix_labels"][i - 1]).copy()
|
|
|
|
# Load image
|
|
|
|
# Load image
|
|
|
|
img = labels_patch["img"]
|
|
|
|
img = labels_patch["img"]
|
|
|
|
h, w = labels_patch["resized_shape"]
|
|
|
|
h, w = labels_patch.pop("resized_shape")
|
|
|
|
|
|
|
|
|
|
|
|
# place img in img4
|
|
|
|
# place img in img4
|
|
|
|
if i == 0: # top left
|
|
|
|
if i == 0: # top left
|
|
|
@ -184,11 +172,12 @@ class Mosaic(BaseMixTransform):
|
|
|
|
cls.append(labels["cls"])
|
|
|
|
cls.append(labels["cls"])
|
|
|
|
instances.append(labels["instances"])
|
|
|
|
instances.append(labels["instances"])
|
|
|
|
final_labels = {
|
|
|
|
final_labels = {
|
|
|
|
|
|
|
|
"im_file": mosaic_labels[0]["im_file"],
|
|
|
|
"ori_shape": mosaic_labels[0]["ori_shape"],
|
|
|
|
"ori_shape": mosaic_labels[0]["ori_shape"],
|
|
|
|
"resized_shape": (self.imgsz * 2, self.imgsz * 2),
|
|
|
|
"resized_shape": (self.imgsz * 2, self.imgsz * 2),
|
|
|
|
"im_file": mosaic_labels[0]["im_file"],
|
|
|
|
|
|
|
|
"cls": np.concatenate(cls, 0),
|
|
|
|
"cls": np.concatenate(cls, 0),
|
|
|
|
"instances": Instances.concatenate(instances, axis=0)}
|
|
|
|
"instances": Instances.concatenate(instances, axis=0),
|
|
|
|
|
|
|
|
"mosaic_border": self.border}
|
|
|
|
final_labels["instances"].clip(self.imgsz * 2, self.imgsz * 2)
|
|
|
|
final_labels["instances"].clip(self.imgsz * 2, self.imgsz * 2)
|
|
|
|
return final_labels
|
|
|
|
return final_labels
|
|
|
|
|
|
|
|
|
|
|
@ -213,7 +202,14 @@ class MixUp(BaseMixTransform):
|
|
|
|
|
|
|
|
|
|
|
|
class RandomPerspective:
|
|
|
|
class RandomPerspective:
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, border=(0, 0)):
|
|
|
|
def __init__(self,
|
|
|
|
|
|
|
|
degrees=0.0,
|
|
|
|
|
|
|
|
translate=0.1,
|
|
|
|
|
|
|
|
scale=0.5,
|
|
|
|
|
|
|
|
shear=0.0,
|
|
|
|
|
|
|
|
perspective=0.0,
|
|
|
|
|
|
|
|
border=(0, 0),
|
|
|
|
|
|
|
|
pre_transform=None):
|
|
|
|
self.degrees = degrees
|
|
|
|
self.degrees = degrees
|
|
|
|
self.translate = translate
|
|
|
|
self.translate = translate
|
|
|
|
self.scale = scale
|
|
|
|
self.scale = scale
|
|
|
@ -221,8 +217,9 @@ class RandomPerspective:
|
|
|
|
self.perspective = perspective
|
|
|
|
self.perspective = perspective
|
|
|
|
# mosaic border
|
|
|
|
# mosaic border
|
|
|
|
self.border = border
|
|
|
|
self.border = border
|
|
|
|
|
|
|
|
self.pre_transform = pre_transform
|
|
|
|
|
|
|
|
|
|
|
|
def affine_transform(self, img):
|
|
|
|
def affine_transform(self, img, border):
|
|
|
|
# Center
|
|
|
|
# Center
|
|
|
|
C = np.eye(3)
|
|
|
|
C = np.eye(3)
|
|
|
|
|
|
|
|
|
|
|
@ -255,7 +252,7 @@ class RandomPerspective:
|
|
|
|
# Combined rotation matrix
|
|
|
|
# Combined rotation matrix
|
|
|
|
M = T @ S @ R @ P @ C # order of operations (right to left) is IMPORTANT
|
|
|
|
M = T @ S @ R @ P @ C # order of operations (right to left) is IMPORTANT
|
|
|
|
# affine image
|
|
|
|
# affine image
|
|
|
|
if (self.border[0] != 0) or (self.border[1] != 0) or (M != np.eye(3)).any(): # image changed
|
|
|
|
if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any(): # image changed
|
|
|
|
if self.perspective:
|
|
|
|
if self.perspective:
|
|
|
|
img = cv2.warpPerspective(img, M, dsize=self.size, borderValue=(114, 114, 114))
|
|
|
|
img = cv2.warpPerspective(img, M, dsize=self.size, borderValue=(114, 114, 114))
|
|
|
|
else: # affine
|
|
|
|
else: # affine
|
|
|
@ -341,6 +338,10 @@ class RandomPerspective:
|
|
|
|
Args:
|
|
|
|
Args:
|
|
|
|
labels(Dict): a dict of `bboxes`, `segments`, `keypoints`.
|
|
|
|
labels(Dict): a dict of `bboxes`, `segments`, `keypoints`.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
if self.pre_transform and "mosaic_border" not in labels:
|
|
|
|
|
|
|
|
labels = self.pre_transform(labels)
|
|
|
|
|
|
|
|
labels.pop("ratio_pad") # do not need ratio pad
|
|
|
|
|
|
|
|
|
|
|
|
img = labels["img"]
|
|
|
|
img = labels["img"]
|
|
|
|
cls = labels["cls"]
|
|
|
|
cls = labels["cls"]
|
|
|
|
instances = labels.pop("instances")
|
|
|
|
instances = labels.pop("instances")
|
|
|
@ -348,10 +349,11 @@ class RandomPerspective:
|
|
|
|
instances.convert_bbox(format="xyxy")
|
|
|
|
instances.convert_bbox(format="xyxy")
|
|
|
|
instances.denormalize(*img.shape[:2][::-1])
|
|
|
|
instances.denormalize(*img.shape[:2][::-1])
|
|
|
|
|
|
|
|
|
|
|
|
self.size = img.shape[1] + self.border[1] * 2, img.shape[0] + self.border[0] * 2 # w, h
|
|
|
|
border = labels.pop("mosaic_border", self.border)
|
|
|
|
|
|
|
|
self.size = img.shape[1] + border[1] * 2, img.shape[0] + border[0] * 2 # w, h
|
|
|
|
# M is affine matrix
|
|
|
|
# M is affine matrix
|
|
|
|
# scale for func:`box_candidates`
|
|
|
|
# scale for func:`box_candidates`
|
|
|
|
img, M, scale = self.affine_transform(img)
|
|
|
|
img, M, scale = self.affine_transform(img, border)
|
|
|
|
|
|
|
|
|
|
|
|
bboxes = self.apply_bboxes(instances.bboxes, M)
|
|
|
|
bboxes = self.apply_bboxes(instances.bboxes, M)
|
|
|
|
|
|
|
|
|
|
|
@ -513,8 +515,10 @@ class CopyPaste:
|
|
|
|
# Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy)
|
|
|
|
# Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy)
|
|
|
|
im = labels["img"]
|
|
|
|
im = labels["img"]
|
|
|
|
cls = labels["cls"]
|
|
|
|
cls = labels["cls"]
|
|
|
|
|
|
|
|
h, w = im.shape[:2]
|
|
|
|
instances = labels.pop("instances")
|
|
|
|
instances = labels.pop("instances")
|
|
|
|
instances.convert_bbox(format="xyxy")
|
|
|
|
instances.convert_bbox(format="xyxy")
|
|
|
|
|
|
|
|
instances.denormalize(w, h)
|
|
|
|
if self.p and len(instances.segments):
|
|
|
|
if self.p and len(instances.segments):
|
|
|
|
n = len(instances)
|
|
|
|
n = len(instances)
|
|
|
|
_, w, _ = im.shape # height, width, channels
|
|
|
|
_, w, _ = im.shape # height, width, channels
|
|
|
@ -605,7 +609,7 @@ class Format:
|
|
|
|
self.batch_idx = batch_idx # keep the batch indexes
|
|
|
|
self.batch_idx = batch_idx # keep the batch indexes
|
|
|
|
|
|
|
|
|
|
|
|
def __call__(self, labels):
|
|
|
|
def __call__(self, labels):
|
|
|
|
img = labels["img"]
|
|
|
|
img = labels.pop("img")
|
|
|
|
h, w = img.shape[:2]
|
|
|
|
h, w = img.shape[:2]
|
|
|
|
cls = labels.pop("cls")
|
|
|
|
cls = labels.pop("cls")
|
|
|
|
instances = labels.pop("instances")
|
|
|
|
instances = labels.pop("instances")
|
|
|
@ -654,7 +658,7 @@ class Format:
|
|
|
|
return masks, instances, cls
|
|
|
|
return masks, instances, cls
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def mosaic_transforms(dataset, imgsz, hyp):
|
|
|
|
def v8_transforms(dataset, imgsz, hyp):
|
|
|
|
pre_transform = Compose([
|
|
|
|
pre_transform = Compose([
|
|
|
|
Mosaic(dataset, imgsz=imgsz, p=hyp.mosaic, border=[-imgsz // 2, -imgsz // 2]),
|
|
|
|
Mosaic(dataset, imgsz=imgsz, p=hyp.mosaic, border=[-imgsz // 2, -imgsz // 2]),
|
|
|
|
CopyPaste(p=hyp.copy_paste),
|
|
|
|
CopyPaste(p=hyp.copy_paste),
|
|
|
@ -664,7 +668,7 @@ def mosaic_transforms(dataset, imgsz, hyp):
|
|
|
|
scale=hyp.scale,
|
|
|
|
scale=hyp.scale,
|
|
|
|
shear=hyp.shear,
|
|
|
|
shear=hyp.shear,
|
|
|
|
perspective=hyp.perspective,
|
|
|
|
perspective=hyp.perspective,
|
|
|
|
border=[-imgsz // 2, -imgsz // 2],
|
|
|
|
pre_transform=LetterBox(new_shape=(imgsz, imgsz)),
|
|
|
|
),])
|
|
|
|
),])
|
|
|
|
return Compose([
|
|
|
|
return Compose([
|
|
|
|
pre_transform,
|
|
|
|
pre_transform,
|
|
|
@ -675,23 +679,6 @@ def mosaic_transforms(dataset, imgsz, hyp):
|
|
|
|
RandomFlip(direction="horizontal", p=hyp.fliplr),]) # transforms
|
|
|
|
RandomFlip(direction="horizontal", p=hyp.fliplr),]) # transforms
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def affine_transforms(imgsz, hyp):
|
|
|
|
|
|
|
|
return Compose([
|
|
|
|
|
|
|
|
LetterBox(new_shape=(imgsz, imgsz)),
|
|
|
|
|
|
|
|
RandomPerspective(
|
|
|
|
|
|
|
|
degrees=hyp.degrees,
|
|
|
|
|
|
|
|
translate=hyp.translate,
|
|
|
|
|
|
|
|
scale=hyp.scale,
|
|
|
|
|
|
|
|
shear=hyp.shear,
|
|
|
|
|
|
|
|
perspective=hyp.perspective,
|
|
|
|
|
|
|
|
border=[0, 0],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
Albumentations(p=1.0),
|
|
|
|
|
|
|
|
RandomHSV(hgain=hyp.hsv_h, sgain=hyp.hsv_s, vgain=hyp.hsv_v),
|
|
|
|
|
|
|
|
RandomFlip(direction="vertical", p=hyp.flipud),
|
|
|
|
|
|
|
|
RandomFlip(direction="horizontal", p=hyp.fliplr),]) # transforms
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Classification augmentations -----------------------------------------------------------------------------------------
|
|
|
|
# Classification augmentations -----------------------------------------------------------------------------------------
|
|
|
|
def classify_transforms(size=224):
|
|
|
|
def classify_transforms(size=224):
|
|
|
|
# Transforms to apply if albumentations not installed
|
|
|
|
# Transforms to apply if albumentations not installed
|
|
|
|