xuehongyang
ser
83d8d3c
import copy
import glob
import math
import os
import random
import sys
import cv2
import matplotlib.pyplot as plt
import numpy as np
import scipy.io as sio
import torch
from imgaug import augmenters as iaa
from PIL import Image
from scipy import interpolate
from skimage import io
from skimage import transform as ski_transform
from skimage.color import rgb2gray
from torch.utils.data import DataLoader
from torch.utils.data import Dataset
from torchvision import transforms
from torchvision import utils
from torchvision.transforms import Compose
from torchvision.transforms import Lambda
from torchvision.transforms.functional import adjust_brightness
from torchvision.transforms.functional import adjust_contrast
from torchvision.transforms.functional import adjust_hue
from torchvision.transforms.functional import adjust_saturation
from utils.utils import cv_crop
from utils.utils import cv_rotate
from utils.utils import draw_gaussian
from utils.utils import fig2data
from utils.utils import generate_weight_map
from utils.utils import power_transform
from utils.utils import shuffle_lr
from utils.utils import transform
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