2022-06-28 07:36:21 +00:00
|
|
|
import os
|
|
|
|
import random as rd
|
|
|
|
|
|
|
|
import albumentations as A
|
|
|
|
import numpy as np
|
2022-06-29 13:01:57 +00:00
|
|
|
from PIL import Image, ImageEnhance
|
2022-06-28 07:36:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
class RandomPaste(A.DualTransform):
|
|
|
|
"""Paste an object on a background.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
TODO
|
|
|
|
|
|
|
|
Targets:
|
|
|
|
image, mask
|
|
|
|
|
|
|
|
Image types:
|
|
|
|
uint8
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
nb,
|
|
|
|
path_paste_img_dir,
|
|
|
|
path_paste_mask_dir,
|
2022-06-28 14:36:50 +00:00
|
|
|
scale_range=(0.1, 0.2),
|
2022-06-28 07:36:21 +00:00
|
|
|
always_apply=True,
|
|
|
|
p=1.0,
|
|
|
|
):
|
|
|
|
super().__init__(always_apply, p)
|
|
|
|
self.path_paste_img_dir = path_paste_img_dir
|
|
|
|
self.path_paste_mask_dir = path_paste_mask_dir
|
2022-06-28 14:36:50 +00:00
|
|
|
self.scale_range = scale_range
|
2022-06-28 07:36:21 +00:00
|
|
|
self.nb = nb
|
|
|
|
|
|
|
|
@property
|
|
|
|
def targets_as_params(self):
|
|
|
|
return ["image"]
|
|
|
|
|
|
|
|
def apply(self, img, positions, paste_img, paste_mask, **params):
|
2022-06-28 14:36:50 +00:00
|
|
|
# convert img to Image, needed for `paste` function
|
|
|
|
img = Image.fromarray(img)
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-30 21:28:38 +00:00
|
|
|
# paste spheres
|
2022-06-28 14:36:50 +00:00
|
|
|
for pos in positions:
|
|
|
|
img.paste(paste_img, pos, paste_mask)
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-28 14:36:50 +00:00
|
|
|
return np.asarray(img.convert("RGB"))
|
2022-06-28 07:36:21 +00:00
|
|
|
|
|
|
|
def apply_to_mask(self, mask, positions, paste_mask, **params):
|
2022-06-28 14:36:50 +00:00
|
|
|
# convert mask to Image, needed for `paste` function
|
|
|
|
mask = Image.fromarray(mask)
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-30 21:28:38 +00:00
|
|
|
# binarize the mask -> {0, 1}
|
|
|
|
paste_mask_bin = paste_mask.point(lambda p: 1 if p > 10 else 0)
|
|
|
|
|
|
|
|
# paste spheres
|
2022-06-28 14:36:50 +00:00
|
|
|
for pos in positions:
|
2022-06-30 21:28:38 +00:00
|
|
|
mask.paste(paste_mask, pos, paste_mask_bin)
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-28 14:36:50 +00:00
|
|
|
return np.asarray(mask.convert("L"))
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-29 09:22:23 +00:00
|
|
|
@staticmethod
|
|
|
|
def overlap(positions, x1, y1, w, h):
|
|
|
|
for x2, y2 in positions:
|
|
|
|
if x1 + w >= x2 and x1 <= x2 + w and y1 + h >= y2 and y1 <= y2 + h:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2022-06-28 07:36:21 +00:00
|
|
|
def get_params_dependent_on_targets(self, params):
|
2022-06-28 14:36:50 +00:00
|
|
|
# choose a random image inside the image folder
|
2022-06-28 07:36:21 +00:00
|
|
|
filename = rd.choice(os.listdir(self.path_paste_img_dir))
|
|
|
|
|
2022-06-28 14:36:50 +00:00
|
|
|
# load the "paste" image
|
|
|
|
paste_img = Image.open(
|
|
|
|
os.path.join(
|
|
|
|
self.path_paste_img_dir,
|
|
|
|
filename,
|
|
|
|
)
|
|
|
|
).convert("RGBA")
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-28 14:36:50 +00:00
|
|
|
# load its respective mask
|
|
|
|
paste_mask = Image.open(
|
|
|
|
os.path.join(
|
|
|
|
self.path_paste_mask_dir,
|
|
|
|
filename,
|
2022-06-28 07:36:21 +00:00
|
|
|
)
|
2022-06-28 14:36:50 +00:00
|
|
|
).convert("LA")
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-28 14:36:50 +00:00
|
|
|
# load the target image
|
2022-06-28 07:36:21 +00:00
|
|
|
target_img = params["image"]
|
2022-06-28 14:36:50 +00:00
|
|
|
target_shape = np.array(target_img.shape[:2], dtype=np.uint)
|
|
|
|
paste_shape = np.array(paste_img.size, dtype=np.uint)
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-28 14:36:50 +00:00
|
|
|
# compute the minimum scaling to fit inside target image
|
|
|
|
min_scale = np.min(target_shape / paste_shape)
|
|
|
|
|
|
|
|
# randomize the relative scaling
|
|
|
|
scale = rd.uniform(*self.scale_range)
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-28 14:36:50 +00:00
|
|
|
# rotate the image and its mask
|
|
|
|
angle = rd.uniform(0, 360)
|
|
|
|
paste_img = paste_img.rotate(angle, expand=True)
|
|
|
|
paste_mask = paste_mask.rotate(angle, expand=True)
|
|
|
|
|
|
|
|
# scale the "paste" image and its mask
|
|
|
|
paste_img = paste_img.resize(
|
|
|
|
tuple((paste_shape * min_scale * scale).astype(np.uint)),
|
|
|
|
resample=Image.Resampling.LANCZOS,
|
|
|
|
)
|
|
|
|
paste_mask = paste_mask.resize(
|
|
|
|
tuple((paste_shape * min_scale * scale).astype(np.uint)),
|
|
|
|
resample=Image.Resampling.LANCZOS,
|
2022-06-28 07:36:21 +00:00
|
|
|
)
|
|
|
|
|
2022-06-28 14:36:50 +00:00
|
|
|
# update paste_shape after scaling
|
|
|
|
paste_shape = np.array(paste_img.size, dtype=np.uint)
|
2022-06-28 07:36:21 +00:00
|
|
|
|
2022-06-29 13:01:57 +00:00
|
|
|
# change brightness randomly
|
|
|
|
filter = ImageEnhance.Brightness(paste_img)
|
|
|
|
paste_img = filter.enhance(rd.uniform(0.5, 1.5))
|
|
|
|
|
2022-06-28 14:36:50 +00:00
|
|
|
# generate some positions
|
2022-06-28 07:36:21 +00:00
|
|
|
positions = []
|
2022-06-29 09:22:23 +00:00
|
|
|
NB = rd.randint(1, self.nb)
|
2022-06-30 13:54:36 +00:00
|
|
|
while len(positions) < NB:
|
2022-06-28 14:36:50 +00:00
|
|
|
x = rd.randint(0, target_shape[0] - paste_shape[0])
|
|
|
|
y = rd.randint(0, target_shape[1] - paste_shape[1])
|
2022-06-29 08:16:26 +00:00
|
|
|
|
|
|
|
# check for overlapping
|
2022-06-29 09:22:23 +00:00
|
|
|
if RandomPaste.overlap(positions, x, y, paste_shape[0], paste_shape[1]):
|
|
|
|
continue
|
2022-06-29 08:16:26 +00:00
|
|
|
|
2022-06-28 07:36:21 +00:00
|
|
|
positions.append((x, y))
|
|
|
|
|
|
|
|
params.update(
|
|
|
|
{
|
|
|
|
"positions": positions,
|
|
|
|
"paste_img": paste_img,
|
|
|
|
"paste_mask": paste_mask,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
return params
|
|
|
|
|
|
|
|
def get_transform_init_args_names(self):
|
2022-06-28 14:36:50 +00:00
|
|
|
return "scale_range", "path_paste_img_dir", "path_paste_mask_dir"
|