| | import cv2
|
| | import os
|
| | import os.path as osp
|
| | import numpy as np
|
| | from PIL import Image
|
| | import torch
|
| | from torch.hub import download_url_to_file, get_dir
|
| | from urllib.parse import urlparse
|
| |
|
| |
|
| | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| |
|
| |
|
| | def download_pretrained_models(file_ids, save_path_root):
|
| | import gdown
|
| |
|
| | os.makedirs(save_path_root, exist_ok=True)
|
| |
|
| | for file_name, file_id in file_ids.items():
|
| | file_url = 'https://drive.google.com/uc?id='+file_id
|
| | save_path = osp.abspath(osp.join(save_path_root, file_name))
|
| | if osp.exists(save_path):
|
| | user_response = input(f'{file_name} already exist. Do you want to cover it? Y/N\n')
|
| | if user_response.lower() == 'y':
|
| | print(f'Covering {file_name} to {save_path}')
|
| | gdown.download(file_url, save_path, quiet=False)
|
| |
|
| | elif user_response.lower() == 'n':
|
| | print(f'Skipping {file_name}')
|
| | else:
|
| | raise ValueError('Wrong input. Only accepts Y/N.')
|
| | else:
|
| | print(f'Downloading {file_name} to {save_path}')
|
| | gdown.download(file_url, save_path, quiet=False)
|
| |
|
| |
|
| |
|
| | def imwrite(img, file_path, params=None, auto_mkdir=True):
|
| | """Write image to file.
|
| |
|
| | Args:
|
| | img (ndarray): Image array to be written.
|
| | file_path (str): Image file path.
|
| | params (None or list): Same as opencv's :func:`imwrite` interface.
|
| | auto_mkdir (bool): If the parent folder of `file_path` does not exist,
|
| | whether to create it automatically.
|
| |
|
| | Returns:
|
| | bool: Successful or not.
|
| | """
|
| | if auto_mkdir:
|
| | dir_name = os.path.abspath(os.path.dirname(file_path))
|
| | os.makedirs(dir_name, exist_ok=True)
|
| | return cv2.imwrite(file_path, img, params)
|
| |
|
| |
|
| | 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 load_file_from_url(url, model_dir=None, progress=True, file_name=None):
|
| | """Ref:https://github.com/1adrianb/face-alignment/blob/master/face_alignment/utils.py
|
| | """
|
| | if model_dir is None:
|
| | hub_dir = get_dir()
|
| | model_dir = os.path.join(hub_dir, 'checkpoints')
|
| |
|
| | os.makedirs(os.path.join(ROOT_DIR, model_dir), exist_ok=True)
|
| |
|
| | parts = urlparse(url)
|
| | filename = os.path.basename(parts.path)
|
| | if file_name is not None:
|
| | filename = file_name
|
| | cached_file = os.path.abspath(os.path.join(ROOT_DIR, model_dir, filename))
|
| | if not os.path.exists(cached_file):
|
| | print(f'Downloading: "{url}" to {cached_file}\n')
|
| | download_url_to_file(url, cached_file, hash_prefix=None, progress=progress)
|
| | return cached_file
|
| |
|
| |
|
| | def scandir(dir_path, suffix=None, recursive=False, full_path=False):
|
| | """Scan a directory to find the interested files.
|
| | Args:
|
| | dir_path (str): Path of the directory.
|
| | suffix (str | tuple(str), optional): File suffix that we are
|
| | interested in. Default: None.
|
| | recursive (bool, optional): If set to True, recursively scan the
|
| | directory. Default: False.
|
| | full_path (bool, optional): If set to True, include the dir_path.
|
| | Default: False.
|
| | Returns:
|
| | A generator for all the interested files with relative paths.
|
| | """
|
| |
|
| | if (suffix is not None) and not isinstance(suffix, (str, tuple)):
|
| | raise TypeError('"suffix" must be a string or tuple of strings')
|
| |
|
| | root = dir_path
|
| |
|
| | def _scandir(dir_path, suffix, recursive):
|
| | for entry in os.scandir(dir_path):
|
| | if not entry.name.startswith('.') and entry.is_file():
|
| | if full_path:
|
| | return_path = entry.path
|
| | else:
|
| | return_path = osp.relpath(entry.path, root)
|
| |
|
| | if suffix is None:
|
| | yield return_path
|
| | elif return_path.endswith(suffix):
|
| | yield return_path
|
| | else:
|
| | if recursive:
|
| | yield from _scandir(entry.path, suffix=suffix, recursive=recursive)
|
| | else:
|
| | continue
|
| |
|
| | return _scandir(dir_path, suffix=suffix, recursive=recursive)
|
| |
|
| |
|
| | def is_gray(img, threshold=10):
|
| | img = Image.fromarray(img)
|
| | if len(img.getbands()) == 1:
|
| | return True
|
| | img1 = np.asarray(img.getchannel(channel=0), dtype=np.int16)
|
| | img2 = np.asarray(img.getchannel(channel=1), dtype=np.int16)
|
| | img3 = np.asarray(img.getchannel(channel=2), dtype=np.int16)
|
| | diff1 = (img1 - img2).var()
|
| | diff2 = (img2 - img3).var()
|
| | diff3 = (img3 - img1).var()
|
| | diff_sum = (diff1 + diff2 + diff3) / 3.0
|
| | if diff_sum <= threshold:
|
| | return True
|
| | else:
|
| | return False
|
| |
|
| | def rgb2gray(img, out_channel=3):
|
| | r, g, b = img[:,:,0], img[:,:,1], img[:,:,2]
|
| | gray = 0.2989 * r + 0.5870 * g + 0.1140 * b
|
| | if out_channel == 3:
|
| | gray = gray[:,:,np.newaxis].repeat(3, axis=2)
|
| | return gray
|
| |
|
| | def bgr2gray(img, out_channel=3):
|
| | b, g, r = img[:,:,0], img[:,:,1], img[:,:,2]
|
| | gray = 0.2989 * r + 0.5870 * g + 0.1140 * b
|
| | if out_channel == 3:
|
| | gray = gray[:,:,np.newaxis].repeat(3, axis=2)
|
| | return gray
|
| |
|
| |
|
| | def calc_mean_std(feat, eps=1e-5):
|
| | """
|
| | Args:
|
| | feat (numpy): 3D [w h c]s
|
| | """
|
| | size = feat.shape
|
| | assert len(size) == 3, 'The input feature should be 3D tensor.'
|
| | c = size[2]
|
| | feat_var = feat.reshape(-1, c).var(axis=0) + eps
|
| | feat_std = np.sqrt(feat_var).reshape(1, 1, c)
|
| | feat_mean = feat.reshape(-1, c).mean(axis=0).reshape(1, 1, c)
|
| | return feat_mean, feat_std
|
| |
|
| |
|
| | def adain_npy(content_feat, style_feat):
|
| | """Adaptive instance normalization for numpy.
|
| |
|
| | Args:
|
| | content_feat (numpy): The input feature.
|
| | style_feat (numpy): The reference feature.
|
| | """
|
| | size = content_feat.shape
|
| | style_mean, style_std = calc_mean_std(style_feat)
|
| | content_mean, content_std = calc_mean_std(content_feat)
|
| | normalized_feat = (content_feat - np.broadcast_to(content_mean, size)) / np.broadcast_to(content_std, size)
|
| | return normalized_feat * np.broadcast_to(style_std, size) + np.broadcast_to(style_mean, size) |