import os import cv2 import numpy as np import argparse from skimage.filters import gaussian from scipy.ndimage.interpolation import map_coordinates from tqdm import tqdm from PIL import Image def single2uint(img): return np.uint8((img.clip(0, 1)*255.).round()) def uint2single(img): return np.float32(img/255.) def Laplacian_edge_detector(img): # input: [0, 1] # return: [0, 1] (H, W, 3) img = np.clip(img*255, 0, 255).astype(np.uint8) # (H, W, 3) img = cv2.GaussianBlur(img, (3, 3), 0) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img = cv2.Laplacian(img, cv2.CV_16S) # (H, W) img = cv2.convertScaleAbs(img) img = img.astype(np.float32) / 255. img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3) return img def Laplacian_edge_detector_uint8(img): # input: [0, 255] # return: [0, 255] (H, W, 3) img = cv2.GaussianBlur(img, (3, 3), 0) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img = cv2.Laplacian(img, cv2.CV_16S) # (H, W) img = cv2.convertScaleAbs(img) img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3) return img def Canny_edge_detector(img): # input: [0, 1] # return: [0, 1] (H, W, 3) img = np.clip(img*255, 0, 255).astype(np.uint8) # (H, W, 3) img = cv2.GaussianBlur(img, (3, 3), 0) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img = cv2.Canny(img, 50, 200) # (H, W) img = cv2.convertScaleAbs(img) img = img.astype(np.float32) / 255. img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3) return img def Canny_edge_detector_uint8(img): # input: [0, 255] # return: [0, 255] (H, W, 3) img = cv2.GaussianBlur(img, (3, 3), 0) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img = cv2.Canny(img, 50, 200) # (H, W) img = cv2.convertScaleAbs(img) img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3) return img def Sobel_edge_detector(img): # input: [0, 1] # return: [0, 1] (H, W, 3) img = np.clip(img*255, 0, 255).astype(np.uint8) # (H, W, 3) img = cv2.GaussianBlur(img, (3, 3), 0) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img = cv2.Sobel(img, cv2.CV_16S, 1, 1) # (H, W) img = cv2.convertScaleAbs(img) img = img.astype(np.float32) / 255. img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3) return img def erosion(img, kernel_size=5): kernel = np.ones((kernel_size, kernel_size), np.uint8) img = cv2.erode(img, kernel, iterations=1) return img def dilatation(img, kernel_size=5): kernel = np.ones((kernel_size, kernel_size), np.uint8) img = cv2.dilate(img, kernel, iterations=1) return img def opening(img): return dilatation(erosion(img)) def closing(img): return erosion(dilatation(img)) def morphological_gradient(img): return dilatation(img) - erosion(img) def top_hat(img): return img - opening(img) def black_hat(img): return closing(img) - img def adjust_contrast(image, clip_limit=2.0, tile_grid_size=(8, 8)): image = single2uint(image) lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(lab) clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size) l_eq = clahe.apply(l) lab_eq = cv2.merge((l_eq, a, b)) result = cv2.cvtColor(lab_eq, cv2.COLOR_LAB2BGR) result = uint2single(result) return result def embossing(img): kernel = np.array([[0, -1, -1], [1, 0, -1], [1, 1, 0]]) return cv2.filter2D(img, -1, kernel) def hough_transform_line_detection(img): img = single2uint(img) dst = cv2.Canny(img, 50, 200, apertureSize=3) cdst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR) lines = cv2.HoughLinesP(dst, 1, np.pi / 180, 230, None, 0, 0) if lines is not None: for i in range(0, len(lines)): rho = lines[i][0][0] theta = lines[i][0][1] a = np.cos(theta) b = np.sin(theta) x0 = a * rho y0 = b * rho pt1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a))) pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a))) cv2.line(img, pt1, pt2, (0, 0, 255), 3, cv2.LINE_AA) return uint2single(img) def hough_circle_detection(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 100, param1=100, param2=30, minRadius=50, maxRadius=200) circles = np.uint16(np.around(circles)) for i in circles[0, :]: cv2.circle(img, (i[0], i[1]), i[2], (0, 0, 255), 2) return img def disk(radius, alias_blur=0.1, dtype=np.float32): if radius <= 8: L = np.arange(-8, 8 + 1) ksize = (3, 3) else: L = np.arange(-radius, radius + 1) ksize = (5, 5) X, Y = np.meshgrid(L, L) aliased_disk = np.array((X ** 2 + Y ** 2) <= radius ** 2, dtype=dtype) aliased_disk /= np.sum(aliased_disk) # supersample disk to antialias return cv2.GaussianBlur(aliased_disk, ksize=ksize, sigmaX=alias_blur) def defocus_blur(image, level=(1, 0.1)): c = level kernel = disk(radius=c[0], alias_blur=c[1]) channels = [] for d in range(3): channels.append(cv2.filter2D(image[:, :, d], -1, kernel)) channels = np.array(channels).transpose((1, 2, 0)) # 3x64x64 -> 64x64x3 return np.clip(channels, 0, 1) def masks_CFA_Bayer(shape): pattern = "RGGB" channels = dict((channel, np.zeros(shape)) for channel in "RGB") for channel, (y, x) in zip(pattern, [(0, 0), (0, 1), (1, 0), (1, 1)]): channels[channel][y::2, x::2] = 1 return tuple(channels[c].astype(bool) for c in "RGB") def cfa4_to_rgb(CFA4): RGB = np.zeros((CFA4.shape[0]*2, CFA4.shape[1]*2, 3), dtype=np.uint8) RGB[0::2, 0::2, 0] = CFA4[:, :, 0] # R RGB[0::2, 1::2, 1] = CFA4[:, :, 1] # G on R row RGB[1::2, 0::2, 1] = CFA4[:, :, 2] # G on B row RGB[1::2, 1::2, 2] = CFA4[:, :, 3] # B return RGB def mosaic_CFA_Bayer(RGB): RGB = single2uint(RGB) R_m, G_m, B_m = masks_CFA_Bayer(RGB.shape[0:2]) mask = np.concatenate( (R_m[..., np.newaxis], G_m[..., np.newaxis], B_m[..., np.newaxis]), axis=-1 ) mosaic = np.multiply(mask, RGB) # mask*RGB CFA = mosaic.sum(2).astype(np.uint8) CFA4 = np.zeros((RGB.shape[0] // 2, RGB.shape[1] // 2, 4), dtype=np.uint8) CFA4[:, :, 0] = CFA[0::2, 0::2] CFA4[:, :, 1] = CFA[0::2, 1::2] CFA4[:, :, 2] = CFA[1::2, 0::2] CFA4[:, :, 3] = CFA[1::2, 1::2] rgb = cfa4_to_rgb(CFA4) rgb = uint2single(rgb) return rgb def simulate_barrel_distortion(image, k1=0.02, k2=0.01): height, width = image.shape[:2] mapx, mapy = np.meshgrid(np.arange(width), np.arange(height)) mapx = 2 * mapx / (width - 1) - 1 mapy = 2 * mapy / (height - 1) - 1 r = np.sqrt(mapx**2 + mapy**2) mapx = mapx * (1 + k1 * r**2 + k2 * r**4) mapy = mapy * (1 + k1 * r**2 + k2 * r**4) mapx = (mapx + 1) * (width - 1) / 2 mapy = (mapy + 1) * (height - 1) / 2 distorted_image = cv2.remap(image, mapx.astype(np.float32), mapy.astype(np.float32), cv2.INTER_LINEAR) return distorted_image def simulate_pincushion_distortion(image, k1=-0.02, k2=-0.01): height, width = image.shape[:2] mapx, mapy = np.meshgrid(np.arange(width), np.arange(height)) mapx = 2 * mapx / (width - 1) - 1 mapy = 2 * mapy / (height - 1) - 1 r = np.sqrt(mapx**2 + mapy**2) mapx = mapx * (1 + k1 * r**2 + k2 * r**4) mapy = mapy * (1 + k1 * r**2 + k2 * r**4) mapx = (mapx + 1) * (width - 1) / 2 mapy = (mapy + 1) * (height - 1) / 2 distorted_image = cv2.remap(image, mapx.astype(np.float32), mapy.astype(np.float32), cv2.INTER_LINEAR) return distorted_image def rgb2gray(rgb): return np.dot(rgb[..., :3], [0.2989, 0.5870, 0.1140]) def spatter(x, severity=1): c = [(0.65, 0.3, 4, 0.69, 0.6, 0), (0.65, 0.3, 3, 0.68, 0.6, 0), (0.65, 0.3, 2, 0.68, 0.5, 0), (0.65, 0.3, 1, 0.65, 1.5, 1), (0.67, 0.4, 1, 0.65, 1.5, 1)][severity - 1] x_PIL = x x = np.array(x, dtype=np.float32) / 255. liquid_layer = np.random.normal(size=x.shape[:2], loc=c[0], scale=c[1]) liquid_layer = gaussian(liquid_layer, sigma=c[2]) liquid_layer[liquid_layer < c[3]] = 0 if c[5] == 0: liquid_layer = (liquid_layer * 255).astype(np.uint8) dist = 255 - cv2.Canny(liquid_layer, 50, 150) dist = cv2.distanceTransform(dist, cv2.DIST_L2, 5) _, dist = cv2.threshold(dist, 20, 20, cv2.THRESH_TRUNC) dist = cv2.blur(dist, (3, 3)).astype(np.uint8) dist = cv2.equalizeHist(dist) ker = np.array([[-2, -1, 0], [-1, 1, 1], [0, 1, 2]]) dist = cv2.filter2D(dist, cv2.CV_8U, ker) dist = cv2.blur(dist, (3, 3)).astype(np.float32) m = cv2.cvtColor(liquid_layer * dist, cv2.COLOR_GRAY2BGRA) m /= np.max(m, axis=(0, 1)) m *= c[4] # water is pale turqouise color = np.concatenate((175 / 255. * np.ones_like(m[..., :1]), 238 / 255. * np.ones_like(m[..., :1]), 238 / 255. * np.ones_like(m[..., :1])), axis=2) color = cv2.cvtColor(color, cv2.COLOR_BGR2BGRA) if len(x.shape) < 3 or x.shape[2] < 3: add_spatter_color = cv2.cvtColor(np.clip(m * color, 0, 1), cv2.COLOR_BGRA2BGR) add_spatter_gray = rgb2gray(add_spatter_color) return (np.clip(x + add_spatter_gray, 0, 1) * 255).astype(np.uint8) else: x = cv2.cvtColor(x, cv2.COLOR_BGR2BGRA) return (cv2.cvtColor(np.clip(x + m * color, 0, 1), cv2.COLOR_BGRA2BGR) * 255).astype(np.uint8) else: m = np.where(liquid_layer > c[3], 1, 0) m = gaussian(m.astype(np.float32), sigma=c[4]) m[m < 0.8] = 0 x_rgb = np.array(x_PIL) # mud brown color = np.concatenate((63 / 255. * np.ones_like(x_rgb[..., :1]), 42 / 255. * np.ones_like(x_rgb[..., :1]), 20 / 255. * np.ones_like(x_rgb[..., :1])), axis=2) color *= m[..., np.newaxis] if len(x.shape) < 3 or x.shape[2] < 3: x *= (1 - m) return (np.clip(x + rgb2gray(color), 0, 1) * 255).astype(np.uint8) else: x *= (1 - m[..., np.newaxis]) return (np.clip(x + color, 0, 1) * 255).astype(np.uint8) # mod of https://gist.github.com/erniejunior/601cdf56d2b424757de5 def elastic_transform(image, severity=3): image = np.array(image, dtype=np.float32) / 255. shape = image.shape shape_size = shape[:2] sigma = np.array(shape_size) * 0.01 alpha = [250 * 0.05, 250 * 0.065, 250 * 0.085, 250 * 0.1, 250 * 0.12][ severity - 1] max_dx = shape[0] * 0.005 max_dy = shape[0] * 0.005 dx = (gaussian(np.random.uniform(-max_dx, max_dx, size=shape[:2]), sigma, mode='reflect', truncate=3) * alpha).astype( np.float32) dy = (gaussian(np.random.uniform(-max_dy, max_dy, size=shape[:2]), sigma, mode='reflect', truncate=3) * alpha).astype( np.float32) if len(image.shape) < 3 or image.shape[2] < 3: x, y = np.meshgrid(np.arange(shape[1]), np.arange(shape[0])) indices = np.reshape(y + dy, (-1, 1)), np.reshape(x + dx, (-1, 1)) else: dx, dy = dx[..., np.newaxis], dy[..., np.newaxis] x, y, z = np.meshgrid(np.arange(shape[1]), np.arange(shape[0]), np.arange(shape[2])) indices = np.reshape(y + dy, (-1, 1)), np.reshape(x + dx, (-1, 1)), np.reshape( z, (-1, 1)) return np.clip( map_coordinates(image, indices, order=1, mode='reflect').reshape( shape), 0, 1) * 255 def frost(x, severity=2): c = [(1, 0.4), (0.8, 0.6), (0.7, 0.7), (0.65, 0.7), (0.6, 0.75)][severity - 1] idx = np.random.randint(5) filename = [os.path.join("degradation_toolkit/frost", 'frost1.png'), os.path.join("degradation_toolkit/frost", 'frost2.png'), os.path.join("degradation_toolkit/frost", 'frost3.png'), os.path.join("degradation_toolkit/frost", 'frost4.jpg'), os.path.join("degradation_toolkit/frost", 'frost5.jpg'), os.path.join("degradation_toolkit/frost", 'frost6.jpg')][idx] frost = Image.open(filename) frost = frost.convert("RGB") frost = np.array(frost) # frost = cv2.imread(filename) frost = uint2single(frost) frost_shape = frost.shape x_shape = np.array(x).shape # resize the frost image so it fits to the image dimensions scaling_factor = 1 if frost_shape[0] >= x_shape[0] and frost_shape[1] >= x_shape[1]: scaling_factor = 1 elif frost_shape[0] < x_shape[0] and frost_shape[1] >= x_shape[1]: scaling_factor = x_shape[0] / frost_shape[0] elif frost_shape[0] >= x_shape[0] and frost_shape[1] < x_shape[1]: scaling_factor = x_shape[1] / frost_shape[1] elif frost_shape[0] < x_shape[0] and frost_shape[1] < x_shape[ 1]: # If both dims are too small, pick the bigger scaling factor scaling_factor_0 = x_shape[0] / frost_shape[0] scaling_factor_1 = x_shape[1] / frost_shape[1] scaling_factor = np.maximum(scaling_factor_0, scaling_factor_1) scaling_factor *= 1.1 new_shape = (int(np.ceil(frost_shape[1] * scaling_factor)), int(np.ceil(frost_shape[0] * scaling_factor))) frost_rescaled = cv2.resize(frost, dsize=new_shape, interpolation=cv2.INTER_CUBIC) # randomly crop x_start, y_start = np.random.randint(0, frost_rescaled.shape[0] - x_shape[ 0]), np.random.randint(0, frost_rescaled.shape[1] - x_shape[1]) if len(x_shape) < 3 or x_shape[2] < 3: frost_rescaled = frost_rescaled[x_start:x_start + x_shape[0], y_start:y_start + x_shape[1]] frost_rescaled = rgb2gray(frost_rescaled) else: frost_rescaled = frost_rescaled[x_start:x_start + x_shape[0], y_start:y_start + x_shape[1]][..., [2, 1, 0]] return c[0] * np.array(x) + c[1] * frost_rescaled