import os import numpy as np import random import cv2 import math from scipy import special from skimage import restoration import torch from torch.nn import functional as F from torchvision.utils import make_grid def uint2single(img): return np.float32(img/255.) def single2uint(img): return np.uint8((img.clip(0, 1)*255.).round()) def img2tensor(imgs, bgr2rgb=True, float32=True): """Numpy array to tensor. Args: imgs (list[ndarray] | ndarray): Input images. bgr2rgb (bool): Whether to change bgr to rgb. float32 (bool): Whether to change to float32. Returns: list[tensor] | tensor: Tensor images. If returned results only have one element, just return tensor. """ def _totensor(img, bgr2rgb, float32): if img.shape[2] == 3 and bgr2rgb: if img.dtype == 'float64': img = img.astype('float32') img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = torch.from_numpy(img.transpose(2, 0, 1)) if float32: img = img.float() return img if isinstance(imgs, list): return [_totensor(img, bgr2rgb, float32) for img in imgs] else: return _totensor(imgs, bgr2rgb, float32) def tensor2img(tensor, rgb2bgr=True, out_type=np.uint8, min_max=(0, 1)): """Convert torch Tensors into image numpy arrays. After clamping to [min, max], values will be normalized to [0, 1]. Args: tensor (Tensor or list[Tensor]): Accept shapes: 1) 4D mini-batch Tensor of shape (B x 3/1 x H x W); 2) 3D Tensor of shape (3/1 x H x W); 3) 2D Tensor of shape (H x W). Tensor channel should be in RGB order. rgb2bgr (bool): Whether to change rgb to bgr. out_type (numpy type): output types. If ``np.uint8``, transform outputs to uint8 type with range [0, 255]; otherwise, float type with range [0, 1]. Default: ``np.uint8``. min_max (tuple[int]): min and max values for clamp. Returns: (Tensor or list): 3D ndarray of shape (H x W x C) OR 2D ndarray of shape (H x W). The channel order is BGR. """ if not (torch.is_tensor(tensor) or (isinstance(tensor, list) and all(torch.is_tensor(t) for t in tensor))): raise TypeError(f'tensor or list of tensors expected, got {type(tensor)}') if torch.is_tensor(tensor): tensor = [tensor] result = [] for _tensor in tensor: _tensor = _tensor.squeeze(0).float().detach().cpu().clamp_(*min_max) _tensor = (_tensor - min_max[0]) / (min_max[1] - min_max[0]) n_dim = _tensor.dim() if n_dim == 4: img_np = make_grid(_tensor, nrow=int(math.sqrt(_tensor.size(0))), normalize=False).numpy() img_np = img_np.transpose(1, 2, 0) if rgb2bgr: img_np = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR) elif n_dim == 3: img_np = _tensor.numpy() img_np = img_np.transpose(1, 2, 0) if img_np.shape[2] == 1: # gray image img_np = np.squeeze(img_np, axis=2) else: if rgb2bgr: img_np = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR) elif n_dim == 2: img_np = _tensor.numpy() else: raise TypeError(f'Only support 4D, 3D or 2D tensor. But received with dimension: {n_dim}') if out_type == np.uint8: # Unlike MATLAB, numpy.unit8() WILL NOT round by default. img_np = (img_np * 255.0).round() img_np = img_np.astype(out_type) result.append(img_np) if len(result) == 1: result = result[0] return result def get_noise(img, value=10): noise = np.random.uniform(0, 256, img.shape[0:2]) v = value * 0.01 noise[np.where(noise < (256 - v))] = 0 k = np.array([[0, 0.1, 0], [0.1, 8, 0.1], [0, 0.1, 0]]) noise = cv2.filter2D(noise, -1, k) '''cv2.imshow('img',noise) cv2.waitKey() cv2.destroyWindow('img')''' return noise def rain_blur(noise, length=10, angle=0, w=1): trans = cv2.getRotationMatrix2D((length / 2, length / 2), angle - 45, 1 - length / 100.0) dig = np.diag(np.ones(length)) k = cv2.warpAffine(dig, trans, (length, length)) k = cv2.GaussianBlur(k, (w, w), 0) blurred = cv2.filter2D(noise, -1, k) cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX) blurred = np.array(blurred, dtype=np.uint8) rain = np.expand_dims(blurred, 2) blurred = np.repeat(rain, 3, 2) return blurred def add_rain(img,value): if np.max(img) > 1: pass else: img = img*255 w, h, c = img.shape h = h - (h % 4) w = w - (w % 4) img = img[0:w, 0:h, :] w = np.random.choice([3, 5, 7, 9, 11], p=[0.2, 0.2, 0.2, 0.2, 0.2]) length = np.random.randint(30, 41) angle = np.random.randint(-45, 45) noise = get_noise(img, value=value) rain = rain_blur(noise, length=length, angle=angle, w=w) img = img.astype('float32') + rain np.clip(img, 0, 255, out=img) img = img/255.0 return img def add_rain_range(img, value_min, value_max): value = np.random.randint(value_min, value_max) if np.max(img) > 1: pass else: img = img*255 w, h, c = img.shape h = h - (h % 4) w = w - (w % 4) img = img[0:w, 0:h, :] w = np.random.choice([3, 5, 7, 9, 11], p=[0.2, 0.2, 0.2, 0.2, 0.2]) length = np.random.randint(30, 41) angle = np.random.randint(-45, 45) noise = get_noise(img, value=value) rain = rain_blur(noise, length=length, angle=angle, w=w) img = img.astype('float32') + rain np.clip(img, 0, 255, out=img) img = img/255.0 return img def add_Poisson_noise(img, level=2): # input range[0, 1] vals = 10**(level) img = np.random.poisson(img * vals).astype(np.float32) / vals img = np.clip(img, 0.0, 1.0) return img def add_Gaussian_noise(img, level=20): # input range[0, 1] noise_level = level / 255.0 noise_map = np.random.normal(loc=0.0, scale=1.0, size=img.shape)*noise_level img += noise_map img = np.clip(img, 0.0, 1.0) return img def add_Gaussian_noise_range(img, min_level=10, max_level=50): # input range[0, 1] level = random.uniform(min_level, max_level) noise_level = level / 255.0 noise_map = np.random.normal(loc=0.0, scale=1.0, size=img.shape)*noise_level img += noise_map img = np.clip(img, 0.0, 1.0) return img def add_sp_noise(img, snr=0.95, salt_pro=0.5): # input range[0, 1] output = np.copy(img) for i in range(img.shape[0]): for j in range(img.shape[1]): rdn = random.random() if rdn < snr: output[i][j] = img[i][j] else: rdn = random.random() if rdn < salt_pro: output[i][j] = 1 else: output[i][j] = 0 return output def add_JPEG_noise(img, level): quality_factor = level img = single2uint(img) _, encimg = cv2.imencode('.jpg', img, [int(cv2.IMWRITE_JPEG_QUALITY), quality_factor]) img = cv2.imdecode(encimg, 1) img = uint2single(img) return img def add_JPEG_noise_range(img, level_min, level_max): quality_factor = random.randint(level_min, level_max) img = single2uint(img) _, encimg = cv2.imencode('.jpg', img, [int(cv2.IMWRITE_JPEG_QUALITY), quality_factor]) img = cv2.imdecode(encimg, 1) img = uint2single(img) return img def circular_lowpass_kernel(cutoff, kernel_size, pad_to=0): """2D sinc filter, ref: https://dsp.stackexchange.com/questions/58301/2-d-circularly-symmetric-low-pass-filter Args: cutoff (float): cutoff frequency in radians (pi is max) kernel_size (int): horizontal and vertical size, must be odd. pad_to (int): pad kernel size to desired size, must be odd or zero. """ assert kernel_size % 2 == 1, 'Kernel size must be an odd number.' kernel = np.fromfunction( lambda x, y: cutoff * special.j1(cutoff * np.sqrt( (x - (kernel_size - 1) / 2) ** 2 + (y - (kernel_size - 1) / 2) ** 2)) / ((2 * np.pi * np.sqrt( (x - (kernel_size - 1) / 2) ** 2 + (y - (kernel_size - 1) / 2) ** 2)) + 1e-9), [kernel_size, kernel_size]) kernel[(kernel_size - 1) // 2, (kernel_size - 1) // 2] = cutoff ** 2 / (4 * np.pi) kernel = kernel / np.sum(kernel) if pad_to > kernel_size: pad_size = (pad_to - kernel_size) // 2 kernel = np.pad(kernel, ((pad_size, pad_size), (pad_size, pad_size))) return kernel def filter2D(img, kernel): """PyTorch version of cv2.filter2D Args: img (Tensor): (b, c, h, w) kernel (Tensor): (b, k, k) """ k = kernel.size(-1) b, c, h, w = img.size() if k % 2 == 1: img = F.pad(img, (k // 2, k // 2, k // 2, k // 2), mode='reflect') else: raise ValueError('Wrong kernel size') ph, pw = img.size()[-2:] if kernel.size(0) == 1: # apply the same kernel to all batch images img = img.view(b * c, 1, ph, pw) kernel = kernel.view(1, 1, k, k) return F.conv2d(img, kernel, padding=0).view(b, c, h, w) else: img = img.view(1, b * c, ph, pw) kernel = kernel.view(b, 1, k, k).repeat(1, c, 1, 1).view(b * c, 1, k, k) return F.conv2d(img, kernel, groups=b * c).view(b, c, h, w) def sinc(img, kernel_size,omega_c): sinc_kernel = circular_lowpass_kernel(omega_c, kernel_size, pad_to=21) sinc_kernel = torch.FloatTensor(sinc_kernel) img = filter2D(img,sinc_kernel) return img def add_ringing(img): # input: [0, 1] img = img2tensor([img])[0].unsqueeze(0) ks = 15 omega_c = round(1.2, 2) img = sinc(img, ks, omega_c) img = torch.clamp((img * 255.0).round(), 0, 255) / 255. img = tensor2img(img, min_max=(0, 1)) img = img/255.0 return img def low_light(img, lum_scale): img = img*lum_scale return img def low_light_range(img): lum_scale = random.uniform(0.1, 0.5) img = img*lum_scale return img def iso_GaussianBlur(img, window, sigma): img = cv2.GaussianBlur(img.copy(), (window, window), sigma) return img def iso_GaussianBlur_range(img, window, min_sigma=2, max_sigma=4): sigma = random.uniform(min_sigma, max_sigma) img = cv2.GaussianBlur(img.copy(), (window, window), sigma) return img def add_resize(img): ori_H, ori_W = img.shape[0], img.shape[1] rnum = np.random.rand() if rnum > 0.8: # up sf1 = random.uniform(1, 2) elif rnum < 0.7: # down sf1 = random.uniform(0.2, 1) else: sf1 = 1.0 img = cv2.resize(img, (int(sf1*img.shape[1]), int(sf1*img.shape[0])), interpolation=random.choice([1, 2, 3])) img = cv2.resize(img, (int(ori_W), int(ori_H)), interpolation=random.choice([1, 2, 3])) img = np.clip(img, 0.0, 1.0) return img def r_l(img): img = img2tensor([img],bgr2rgb=False)[0].unsqueeze(0) psf = np.ones((1, 1, 5, 5)) psf = psf / psf.sum() img = img.numpy() img = np.pad(img, ((0, 0), (0, 0), (7, 7), (7, 7)), 'linear_ramp') img = restoration.richardson_lucy(img, psf, 1) img = img[:, :, 7:-7, 7:-7] img = torch.from_numpy(img) img = img.squeeze(0).numpy().transpose(1, 2, 0) return img def inpainting(img,l_num,l_thick): ori_h, ori_w = img.shape[0], img.shape[1] mask = np.zeros((ori_h, ori_w, 3), np.uint8) col = random.choice(['white', 'black']) while (l_num): x1, y1 = random.randint(0, ori_w), random.randint(0, ori_h) x2, y2 = random.randint(0, ori_w), random.randint(0, ori_h) pts = np.array([[x1, y1], [x2, y2]], np.int32) pts = pts.reshape((-1, 1, 2)) mask = cv2.polylines(mask, [pts], 0, (1, 1, 1), l_thick) l_num -= 1 if col == 'white': img = np.clip(img + mask, 0, 1) else: img = np.clip(img - mask, 0, 1) return img