import cv2 import os from random import randint, seed import numpy as np class MaskGenerator(): def __init__(self, height, width, channels=3, rand_seed=None, filepath=None): """Convenience functions for generating masks to be used for inpainting training Arguments: height {int} -- Mask height width {width} -- Mask width Keyword Arguments: channels {int} -- Channels to output (default: {3}) rand_seed {[type]} -- Random seed (default: {None}) filepath {[type]} -- Load masks from filepath. If None, generate masks with OpenCV (default: {None}) """ self.height = height self.width = width self.channels = channels self.filepath = filepath # If filepath supplied, load the list of masks within the directory self.mask_files = [] if self.filepath: filenames = [f for f in os.listdir(self.filepath)] self.mask_files = [f for f in filenames if any(filetype in f.lower() for filetype in ['.jpeg', '.png', '.jpg'])] print(">> Found {} masks in {}".format(len(self.mask_files), self.filepath)) # Seed for reproducibility if rand_seed: seed(rand_seed) def _generate_mask(self): """Generates a random irregular mask with lines, circles and elipses""" img = np.zeros((self.height, self.width, self.channels), np.uint8) # Set size scale size = int((self.width + self.height) * 0.03) if self.width < 64 or self.height < 64: raise Exception("Width and Height of mask must be at least 64!") # Draw random lines for _ in range(randint(1, 20)): x1, x2 = randint(1, self.width), randint(1, self.width) y1, y2 = randint(1, self.height), randint(1, self.height) thickness = randint(3, size) cv2.line(img,(x1,y1),(x2,y2),(1,1,1),thickness) # Draw random circles for _ in range(randint(1, 20)): x1, y1 = randint(1, self.width), randint(1, self.height) radius = randint(3, size) cv2.circle(img,(x1,y1),radius,(1,1,1), -1) # Draw random ellipses for _ in range(randint(1, 20)): x1, y1 = randint(1, self.width), randint(1, self.height) s1, s2 = randint(1, self.width), randint(1, self.height) a1, a2, a3 = randint(3, 180), randint(3, 180), randint(3, 180) thickness = randint(3, size) cv2.ellipse(img, (x1,y1), (s1,s2), a1, a2, a3,(1,1,1), thickness) return 1-img def _load_mask(self, rotation=True, dilation=True, cropping=True): """Loads a mask from disk, and optionally augments it""" # Read image mask = cv2.imread(os.path.join(self.filepath, np.random.choice(self.mask_files, 1, replace=False)[0])) # Random rotation if rotation: rand = np.random.randint(-180, 180) M = cv2.getRotationMatrix2D((mask.shape[1]/2, mask.shape[0]/2), rand, 1.5) mask = cv2.warpAffine(mask, M, (mask.shape[1], mask.shape[0])) # Random dilation if dilation: rand = np.random.randint(5, 47) kernel = np.ones((rand, rand), np.uint8) mask = cv2.erode(mask, kernel, iterations=1) # Random cropping if cropping: x = np.random.randint(0, mask.shape[1] - self.width) y = np.random.randint(0, mask.shape[0] - self.height) mask = mask[y:y+self.height, x:x+self.width] return (mask > 1).astype(np.uint8) def sample(self, random_seed=None): """Retrieve a random mask""" if random_seed: seed(random_seed) if self.filepath and len(self.mask_files) > 0: return self._load_mask() else: return self._generate_mask()