Spaces:
Sleeping
Sleeping
import sys | |
import os | |
import random | |
import glob | |
import torch | |
from skimage import io | |
from skimage import transform as ski_transform | |
from skimage.color import rgb2gray | |
import scipy.io as sio | |
from scipy import interpolate | |
import numpy as np | |
import matplotlib.pyplot as plt | |
from torch.utils.data import Dataset, DataLoader | |
from torchvision import transforms, utils | |
from torchvision.transforms import Lambda, Compose | |
from torchvision.transforms.functional import adjust_brightness, adjust_contrast, adjust_saturation, adjust_hue | |
from utils.utils import cv_crop, cv_rotate, draw_gaussian, transform, power_transform, shuffle_lr, fig2data, generate_weight_map | |
from PIL import Image | |
import cv2 | |
import copy | |
import math | |
from imgaug import augmenters as iaa | |
class AddBoundary(object): | |
def __init__(self, num_landmarks=68): | |
self.num_landmarks = num_landmarks | |
def __call__(self, sample): | |
landmarks_64 = np.floor(sample['landmarks'] / 4.0) | |
if self.num_landmarks == 68: | |
boundaries = {} | |
boundaries['cheek'] = landmarks_64[0:17] | |
boundaries['left_eyebrow'] = landmarks_64[17:22] | |
boundaries['right_eyebrow'] = landmarks_64[22:27] | |
boundaries['uper_left_eyelid'] = landmarks_64[36:40] | |
boundaries['lower_left_eyelid'] = np.array([landmarks_64[i] for i in [36, 41, 40, 39]]) | |
boundaries['upper_right_eyelid'] = landmarks_64[42:46] | |
boundaries['lower_right_eyelid'] = np.array([landmarks_64[i] for i in [42, 47, 46, 45]]) | |
boundaries['noise'] = landmarks_64[27:31] | |
boundaries['noise_bot'] = landmarks_64[31:36] | |
boundaries['upper_outer_lip'] = landmarks_64[48:55] | |
boundaries['upper_inner_lip'] = np.array([landmarks_64[i] for i in [60, 61, 62, 63, 64]]) | |
boundaries['lower_outer_lip'] = np.array([landmarks_64[i] for i in [48, 59, 58, 57, 56, 55, 54]]) | |
boundaries['lower_inner_lip'] = np.array([landmarks_64[i] for i in [60, 67, 66, 65, 64]]) | |
elif self.num_landmarks == 98: | |
boundaries = {} | |
boundaries['cheek'] = landmarks_64[0:33] | |
boundaries['left_eyebrow'] = landmarks_64[33:38] | |
boundaries['right_eyebrow'] = landmarks_64[42:47] | |
boundaries['uper_left_eyelid'] = landmarks_64[60:65] | |
boundaries['lower_left_eyelid'] = np.array([landmarks_64[i] for i in [60, 67, 66, 65, 64]]) | |
boundaries['upper_right_eyelid'] = landmarks_64[68:73] | |
boundaries['lower_right_eyelid'] = np.array([landmarks_64[i] for i in [68, 75, 74, 73, 72]]) | |
boundaries['noise'] = landmarks_64[51:55] | |
boundaries['noise_bot'] = landmarks_64[55:60] | |
boundaries['upper_outer_lip'] = landmarks_64[76:83] | |
boundaries['upper_inner_lip'] = np.array([landmarks_64[i] for i in [88, 89, 90, 91, 92]]) | |
boundaries['lower_outer_lip'] = np.array([landmarks_64[i] for i in [76, 87, 86, 85, 84, 83, 82]]) | |
boundaries['lower_inner_lip'] = np.array([landmarks_64[i] for i in [88, 95, 94, 93, 92]]) | |
elif self.num_landmarks == 19: | |
boundaries = {} | |
boundaries['left_eyebrow'] = landmarks_64[0:3] | |
boundaries['right_eyebrow'] = landmarks_64[3:5] | |
boundaries['left_eye'] = landmarks_64[6:9] | |
boundaries['right_eye'] = landmarks_64[9:12] | |
boundaries['noise'] = landmarks_64[12:15] | |
elif self.num_landmarks == 29: | |
boundaries = {} | |
boundaries['upper_left_eyebrow'] = np.stack([ | |
landmarks_64[0], | |
landmarks_64[4], | |
landmarks_64[2] | |
], axis=0) | |
boundaries['lower_left_eyebrow'] = np.stack([ | |
landmarks_64[0], | |
landmarks_64[5], | |
landmarks_64[2] | |
], axis=0) | |
boundaries['upper_right_eyebrow'] = np.stack([ | |
landmarks_64[1], | |
landmarks_64[6], | |
landmarks_64[3] | |
], axis=0) | |
boundaries['lower_right_eyebrow'] = np.stack([ | |
landmarks_64[1], | |
landmarks_64[7], | |
landmarks_64[3] | |
], axis=0) | |
boundaries['upper_left_eye'] = np.stack([ | |
landmarks_64[8], | |
landmarks_64[12], | |
landmarks_64[10] | |
], axis=0) | |
boundaries['lower_left_eye'] = np.stack([ | |
landmarks_64[8], | |
landmarks_64[13], | |
landmarks_64[10] | |
], axis=0) | |
boundaries['upper_right_eye'] = np.stack([ | |
landmarks_64[9], | |
landmarks_64[14], | |
landmarks_64[11] | |
], axis=0) | |
boundaries['lower_right_eye'] = np.stack([ | |
landmarks_64[9], | |
landmarks_64[15], | |
landmarks_64[11] | |
], axis=0) | |
boundaries['noise'] = np.stack([ | |
landmarks_64[18], | |
landmarks_64[21], | |
landmarks_64[19] | |
], axis=0) | |
boundaries['outer_upper_lip'] = np.stack([ | |
landmarks_64[22], | |
landmarks_64[24], | |
landmarks_64[23] | |
], axis=0) | |
boundaries['inner_upper_lip'] = np.stack([ | |
landmarks_64[22], | |
landmarks_64[25], | |
landmarks_64[23] | |
], axis=0) | |
boundaries['outer_lower_lip'] = np.stack([ | |
landmarks_64[22], | |
landmarks_64[26], | |
landmarks_64[23] | |
], axis=0) | |
boundaries['inner_lower_lip'] = np.stack([ | |
landmarks_64[22], | |
landmarks_64[27], | |
landmarks_64[23] | |
], axis=0) | |
functions = {} | |
for key, points in boundaries.items(): | |
temp = points[0] | |
new_points = points[0:1, :] | |
for point in points[1:]: | |
if point[0] == temp[0] and point[1] == temp[1]: | |
continue | |
else: | |
new_points = np.concatenate((new_points, np.expand_dims(point, 0)), axis=0) | |
temp = point | |
points = new_points | |
if points.shape[0] == 1: | |
points = np.concatenate((points, points+0.001), axis=0) | |
k = min(4, points.shape[0]) | |
functions[key] = interpolate.splprep([points[:, 0], points[:, 1]], k=k-1,s=0) | |
boundary_map = np.zeros((64, 64)) | |
fig = plt.figure(figsize=[64/96.0, 64/96.0], dpi=96) | |
ax = fig.add_axes([0, 0, 1, 1]) | |
ax.axis('off') | |
ax.imshow(boundary_map, interpolation='nearest', cmap='gray') | |
#ax.scatter(landmarks[:, 0], landmarks[:, 1], s=1, marker=',', c='w') | |
for key in functions.keys(): | |
xnew = np.arange(0, 1, 0.01) | |
out = interpolate.splev(xnew, functions[key][0], der=0) | |
plt.plot(out[0], out[1], ',', linewidth=1, color='w') | |
img = fig2data(fig) | |
plt.close() | |
sigma = 1 | |
temp = 255-img[:,:,1] | |
temp = cv2.distanceTransform(temp, cv2.DIST_L2, cv2.DIST_MASK_PRECISE) | |
temp = temp.astype(np.float32) | |
temp = np.where(temp < 3*sigma, np.exp(-(temp*temp)/(2*sigma*sigma)), 0 ) | |
fig = plt.figure(figsize=[64/96.0, 64/96.0], dpi=96) | |
ax = fig.add_axes([0, 0, 1, 1]) | |
ax.axis('off') | |
ax.imshow(temp, cmap='gray') | |
plt.close() | |
boundary_map = fig2data(fig) | |
sample['boundary'] = boundary_map[:, :, 0] | |
return sample | |
class AddWeightMap(object): | |
def __call__(self, sample): | |
heatmap= sample['heatmap'] | |
boundary = sample['boundary'] | |
heatmap = np.concatenate((heatmap, np.expand_dims(boundary, axis=0)), 0) | |
weight_map = np.zeros_like(heatmap) | |
for i in range(heatmap.shape[0]): | |
weight_map[i] = generate_weight_map(weight_map[i], | |
heatmap[i]) | |
sample['weight_map'] = weight_map | |
return sample | |
class ToTensor(object): | |
"""Convert ndarrays in sample to Tensors.""" | |
def __call__(self, sample): | |
image, heatmap, landmarks, boundary, weight_map= sample['image'], sample['heatmap'], sample['landmarks'], sample['boundary'], sample['weight_map'] | |
# swap color axis because | |
# numpy image: H x W x C | |
# torch image: C X H X W | |
if len(image.shape) == 2: | |
image = np.expand_dims(image, axis=2) | |
image_small = np.expand_dims(image_small, axis=2) | |
image = image.transpose((2, 0, 1)) | |
boundary = np.expand_dims(boundary, axis=2) | |
boundary = boundary.transpose((2, 0, 1)) | |
return {'image': torch.from_numpy(image).float().div(255.0), | |
'heatmap': torch.from_numpy(heatmap).float(), | |
'landmarks': torch.from_numpy(landmarks).float(), | |
'boundary': torch.from_numpy(boundary).float().div(255.0), | |
'weight_map': torch.from_numpy(weight_map).float()} | |
class FaceLandmarksDataset(Dataset): | |
"""Face Landmarks dataset.""" | |
def __init__(self, img_dir, landmarks_dir, num_landmarks=68, gray_scale=False, | |
detect_face=False, enhance=False, center_shift=0, | |
transform=None,): | |
""" | |
Args: | |
landmark_dir (string): Path to the mat file with landmarks saved. | |
img_dir (string): Directory with all the images. | |
transform (callable, optional): Optional transform to be applied | |
on a sample. | |
""" | |
self.img_dir = img_dir | |
self.landmarks_dir = landmarks_dir | |
self.num_lanmdkars = num_landmarks | |
self.transform = transform | |
self.img_names = glob.glob(self.img_dir+'*.jpg') + \ | |
glob.glob(self.img_dir+'*.png') | |
self.gray_scale = gray_scale | |
self.detect_face = detect_face | |
self.enhance = enhance | |
self.center_shift = center_shift | |
if self.detect_face: | |
self.face_detector = MTCNN(thresh=[0.5, 0.6, 0.7]) | |
def __len__(self): | |
return len(self.img_names) | |
def __getitem__(self, idx): | |
img_name = self.img_names[idx] | |
pil_image = Image.open(img_name) | |
if pil_image.mode != "RGB": | |
# if input is grayscale image, convert it to 3 channel image | |
if self.enhance: | |
pil_image = power_transform(pil_image, 0.5) | |
temp_image = Image.new('RGB', pil_image.size) | |
temp_image.paste(pil_image) | |
pil_image = temp_image | |
image = np.array(pil_image) | |
if self.gray_scale: | |
image = rgb2gray(image) | |
image = np.expand_dims(image, axis=2) | |
image = np.concatenate((image, image, image), axis=2) | |
image = image * 255.0 | |
image = image.astype(np.uint8) | |
if not self.detect_face: | |
center = [450//2, 450//2+0] | |
if self.center_shift != 0: | |
center[0] += int(np.random.uniform(-self.center_shift, | |
self.center_shift)) | |
center[1] += int(np.random.uniform(-self.center_shift, | |
self.center_shift)) | |
scale = 1.8 | |
else: | |
detected_faces = self.face_detector.detect_image(image) | |
if len(detected_faces) > 0: | |
box = detected_faces[0] | |
left, top, right, bottom, _ = box | |
center = [right - (right - left) / 2.0, | |
bottom - (bottom - top) / 2.0] | |
center[1] = center[1] - (bottom - top) * 0.12 | |
scale = (right - left + bottom - top) / 195.0 | |
else: | |
center = [450//2, 450//2+0] | |
scale = 1.8 | |
if self.center_shift != 0: | |
shift = self.center * self.center_shift / 450 | |
center[0] += int(np.random.uniform(-shift, shift)) | |
center[1] += int(np.random.uniform(-shift, shift)) | |
base_name = os.path.basename(img_name) | |
landmarks_base_name = base_name[:-4] + '_pts.mat' | |
landmarks_name = os.path.join(self.landmarks_dir, landmarks_base_name) | |
if os.path.isfile(landmarks_name): | |
mat_data = sio.loadmat(landmarks_name) | |
landmarks = mat_data['pts_2d'] | |
elif os.path.isfile(landmarks_name[:-8] + '.pts.npy'): | |
landmarks = np.load(landmarks_name[:-8] + '.pts.npy') | |
else: | |
landmarks = [] | |
heatmap = [] | |
if landmarks != []: | |
new_image, new_landmarks = cv_crop(image, landmarks, center, | |
scale, 256, self.center_shift) | |
tries = 0 | |
while self.center_shift != 0 and tries < 5 and (np.max(new_landmarks) > 240 or np.min(new_landmarks) < 15): | |
center = [450//2, 450//2+0] | |
scale += 0.05 | |
center[0] += int(np.random.uniform(-self.center_shift, | |
self.center_shift)) | |
center[1] += int(np.random.uniform(-self.center_shift, | |
self.center_shift)) | |
new_image, new_landmarks = cv_crop(image, landmarks, | |
center, scale, 256, | |
self.center_shift) | |
tries += 1 | |
if np.max(new_landmarks) > 250 or np.min(new_landmarks) < 5: | |
center = [450//2, 450//2+0] | |
scale = 2.25 | |
new_image, new_landmarks = cv_crop(image, landmarks, | |
center, scale, 256, | |
100) | |
assert (np.min(new_landmarks) > 0 and np.max(new_landmarks) < 256), \ | |
"Landmarks out of boundary!" | |
image = new_image | |
landmarks = new_landmarks | |
heatmap = np.zeros((self.num_lanmdkars, 64, 64)) | |
for i in range(self.num_lanmdkars): | |
if landmarks[i][0] > 0: | |
heatmap[i] = draw_gaussian(heatmap[i], landmarks[i]/4.0+1, 1) | |
sample = {'image': image, 'heatmap': heatmap, 'landmarks': landmarks} | |
if self.transform: | |
sample = self.transform(sample) | |
return sample | |
def get_dataset(val_img_dir, val_landmarks_dir, batch_size, | |
num_landmarks=68, rotation=0, scale=0, | |
center_shift=0, random_flip=False, | |
brightness=0, contrast=0, saturation=0, | |
blur=False, noise=False, jpeg_effect=False, | |
random_occlusion=False, gray_scale=False, | |
detect_face=False, enhance=False): | |
val_transforms = transforms.Compose([AddBoundary(num_landmarks), | |
AddWeightMap(), | |
ToTensor()]) | |
val_dataset = FaceLandmarksDataset(val_img_dir, val_landmarks_dir, | |
num_landmarks=num_landmarks, | |
gray_scale=gray_scale, | |
detect_face=detect_face, | |
enhance=enhance, | |
transform=val_transforms) | |
val_dataloader = torch.utils.data.DataLoader(val_dataset, | |
batch_size=batch_size, | |
shuffle=False, | |
num_workers=6) | |
data_loaders = {'val': val_dataloader} | |
dataset_sizes = {} | |
dataset_sizes['val'] = len(val_dataset) | |
return data_loaders, dataset_sizes | |