2022-06-28 07:36:21 +00:00
|
|
|
import os
|
|
|
|
import random as rd
|
|
|
|
|
|
|
|
import albumentations as A
|
|
|
|
import numpy as np
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
|
|
|
|
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-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-28 14:36:50 +00:00
|
|
|
for pos in positions:
|
|
|
|
mask.paste(paste_mask, pos, paste_mask)
|
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
|
|
|
|
|
|
|
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-28 14:36:50 +00:00
|
|
|
# generate some positions
|
2022-06-28 07:36:21 +00:00
|
|
|
positions = []
|
|
|
|
for _ in range(rd.randint(1, self.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-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"
|