Spaces:
Runtime error
Runtime error
| 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 | |