Last active
October 13, 2020 04:26
-
-
Save sizhky/25ef17e99db72167ea7d3d1b777c47fb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
import cv2 | |
random_transform_args = { | |
'rotation_range': 10, | |
'zoom_range': 0.05, | |
'shift_range': 0.05, | |
'random_flip': 0.4, | |
} | |
def get_training_data(images, batch_size): | |
indices = np.random.randint(len(images), size=batch_size) | |
for i, index in enumerate(indices): | |
image = images[index] | |
image = random_transform(image, **random_transform_args) | |
warped_img, target_img = random_warp(image) | |
if i == 0: | |
warped_images = np.empty((batch_size,) + warped_img.shape, warped_img.dtype) | |
target_images = np.empty((batch_size,) + target_img.shape, warped_img.dtype) | |
warped_images[i] = warped_img | |
target_images[i] = target_img | |
return warped_images, target_images | |
def random_transform(image, rotation_range, zoom_range, shift_range, random_flip): | |
h, w = image.shape[0:2] | |
rotation = np.random.uniform(-rotation_range, rotation_range) | |
scale = np.random.uniform(1 - zoom_range, 1 + zoom_range) | |
tx = np.random.uniform(-shift_range, shift_range) * w | |
ty = np.random.uniform(-shift_range, shift_range) * h | |
mat = cv2.getRotationMatrix2D((w // 2, h // 2), rotation, scale) | |
mat[:, 2] += (tx, ty) | |
result = cv2.warpAffine(image, mat, (w, h), borderMode=cv2.BORDER_REPLICATE) | |
if np.random.random() < random_flip: | |
result = result[:, ::-1] | |
return result | |
# get pair of random warped images from aligened face image | |
def random_warp(image): | |
assert image.shape == (256, 256, 3) | |
range_ = np.linspace(128 - 80, 128 + 80, 5) | |
mapx = np.broadcast_to(range_, (5, 5)) | |
mapy = mapx.T | |
mapx = mapx + np.random.normal(size=(5, 5), scale=5) | |
mapy = mapy + np.random.normal(size=(5, 5), scale=5) | |
interp_mapx = cv2.resize(mapx, (80, 80))[8:72, 8:72].astype('float32') | |
interp_mapy = cv2.resize(mapy, (80, 80))[8:72, 8:72].astype('float32') | |
warped_image = cv2.remap(image, interp_mapx, interp_mapy, cv2.INTER_LINEAR) | |
src_points = np.stack([mapx.ravel(), mapy.ravel()], axis=-1) | |
dst_points = np.mgrid[0:65:16, 0:65:16].T.reshape(-1, 2) | |
mat = umeyama(src_points, dst_points, True)[0:2] | |
target_image = cv2.warpAffine(image, mat, (64, 64)) | |
return warped_image, target_image | |
def umeyama(src, dst, estimate_scale): | |
"""Estimate N-D similarity transformation with or without scaling. | |
Parameters | |
---------- | |
src : (M, N) array | |
Source coordinates. | |
dst : (M, N) array | |
Destination coordinates. | |
estimate_scale : bool | |
Whether to estimate scaling factor. | |
Returns | |
------- | |
T : (N + 1, N + 1) | |
The homogeneous similarity transformation matrix. The matrix contains | |
NaN values only if the problem is not well-conditioned. | |
References | |
---------- | |
.. [1] "Least-squares estimation of transformation parameters between two | |
point patterns", Shinji Umeyama, PAMI 1991, DOI: 10.1109/34.88573 | |
""" | |
num = src.shape[0] | |
dim = src.shape[1] | |
# Compute mean of src and dst. | |
src_mean = src.mean(axis=0) | |
dst_mean = dst.mean(axis=0) | |
# Subtract mean from src and dst. | |
src_demean = src - src_mean | |
dst_demean = dst - dst_mean | |
# Eq. (38). | |
A = np.dot(dst_demean.T, src_demean) / num | |
# Eq. (39). | |
d = np.ones((dim,), dtype=np.double) | |
if np.linalg.det(A) < 0: | |
d[dim - 1] = -1 | |
T = np.eye(dim + 1, dtype=np.double) | |
U, S, V = np.linalg.svd(A) | |
# Eq. (40) and (43). | |
rank = np.linalg.matrix_rank(A) | |
if rank == 0: | |
return np.nan * T | |
elif rank == dim - 1: | |
if np.linalg.det(U) * np.linalg.det(V) > 0: | |
T[:dim, :dim] = np.dot(U, V) | |
else: | |
s = d[dim - 1] | |
d[dim - 1] = -1 | |
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V)) | |
d[dim - 1] = s | |
else: | |
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V.T)) | |
if estimate_scale: | |
# Eq. (41) and (42). | |
scale = 1.0 / src_demean.var(axis=0).sum() * np.dot(S, d) | |
else: | |
scale = 1.0 | |
T[:dim, dim] = dst_mean - scale * np.dot(T[:dim, :dim], src_mean.T) | |
T[:dim, :dim] *= scale | |
return T |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment