|
from imutils import face_utils |
|
import numpy as np |
|
import random |
|
import albumentations as alb |
|
from .DeepFakeMask import dfl_full, extended, components, facehull |
|
import cv2 |
|
|
|
def IoUfrom2bboxes(boxA, boxB): |
|
xA = max(boxA[0], boxB[0]) |
|
yA = max(boxA[1], boxB[1]) |
|
xB = min(boxA[2], boxB[2]) |
|
yB = min(boxA[3], boxB[3]) |
|
interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1) |
|
boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1) |
|
boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1) |
|
iou = interArea / float(boxAArea + boxBArea - interArea) |
|
return iou |
|
|
|
def reorder_landmark(landmark): |
|
landmark_add = np.zeros((13, 2)) |
|
for idx, idx_l in enumerate([77, 75, 76, 68, 69, 70, 71, 80, 72, 73, 79, 74, 78]): |
|
landmark_add[idx] = landmark[idx_l] |
|
landmark[68:] = landmark_add |
|
return landmark |
|
|
|
def get_dlib_landmarks(inp, dlib_face_detector, dlib_face_predictor): |
|
faces = dlib_face_detector(inp, 1) |
|
if len(faces)==0: |
|
raise Exception("No faces detected") |
|
landmarks=[] |
|
size_list=[] |
|
for face_idx in range(len(faces)): |
|
landmark = dlib_face_predictor(inp, faces[face_idx]) |
|
landmark = face_utils.shape_to_np(landmark) |
|
x0,y0=landmark[:,0].min(),landmark[:,1].min() |
|
x1,y1=landmark[:,0].max(),landmark[:,1].max() |
|
face_s=(x1-x0)*(y1-y0) |
|
size_list.append(face_s) |
|
landmarks.append(landmark) |
|
landmarks=np.concatenate(landmarks).reshape((len(size_list),)+landmark.shape) |
|
landmarks=landmarks[np.argsort(np.array(size_list))[::-1]] |
|
return landmarks |
|
|
|
def get_retina_bbox(inp,face_detector): |
|
faces = face_detector.predict_jsons(inp) |
|
landmarks=[] |
|
size_list=[] |
|
for face_idx in range(len(faces)): |
|
|
|
x0,y0,x1,y1=faces[face_idx]['bbox'] |
|
landmark=np.array([[x0,y0],[x1,y1]]+faces[face_idx]['landmarks']) |
|
face_s=(x1-x0)*(y1-y0) |
|
size_list.append(face_s) |
|
landmarks.append(landmark) |
|
landmarks=np.concatenate(landmarks).reshape((len(size_list),)+landmark.shape) |
|
landmarks=landmarks[np.argsort(np.array(size_list))[::-1]] |
|
|
|
return landmarks |
|
|
|
def random_get_hull(landmark,img, face_region): |
|
face_region = int(face_region) |
|
if face_region == 1: |
|
mask = dfl_full(landmarks=landmark.astype('int32'),face=img, channels=3).mask |
|
elif face_region == 2: |
|
mask = extended(landmarks=landmark.astype('int32'),face=img, channels=3).mask |
|
elif face_region == 3: |
|
mask = components(landmarks=landmark.astype('int32'),face=img, channels=3).mask |
|
else: |
|
mask = facehull(landmarks=landmark.astype('int32'),face=img, channels=3).mask |
|
return mask/255 |
|
|
|
class RandomDownScale(alb.core.transforms_interface.ImageOnlyTransform): |
|
def apply(self,img,**params): |
|
return self.randomdownscale(img) |
|
|
|
def randomdownscale(self,img): |
|
keep_ratio=True |
|
keep_input_shape=True |
|
H,W,C=img.shape |
|
ratio_list=[2,4] |
|
r=ratio_list[np.random.randint(len(ratio_list))] |
|
img_ds=cv2.resize(img,(int(W/r),int(H/r)),interpolation=cv2.INTER_NEAREST) |
|
if keep_input_shape: |
|
img_ds=cv2.resize(img_ds,(W,H),interpolation=cv2.INTER_LINEAR) |
|
|
|
return img_ds |
|
|
|
def get_source_transforms(): |
|
return alb.Compose([ |
|
alb.Compose([ |
|
alb.RGBShift((-20, 20), (-20, 20), (-20, 20), p=0.3), |
|
alb.HueSaturationValue( |
|
hue_shift_limit=(-0.3, 0.3), sat_shift_limit=(-0.3, 0.3), val_shift_limit=(-0.3, 0.3), p=1), |
|
alb.RandomBrightnessContrast( |
|
brightness_limit=(-0.1, 0.1), contrast_limit=(-0.1, 0.1), p=1), |
|
], p=1), |
|
|
|
alb.OneOf([ |
|
RandomDownScale(p=1), |
|
alb.Sharpen(alpha=(0.2, 0.5), lightness=(0.5, 1.0), p=1), |
|
], p=1), |
|
|
|
], p=1.) |
|
|
|
def randaffine(img, mask): |
|
f = alb.Affine( |
|
translate_percent={'x': (-0.03, 0.03), 'y': (-0.015, 0.015)}, |
|
scale=[0.95, 1/0.95], |
|
fit_output=False, |
|
p=1) |
|
|
|
g = alb.ElasticTransform( |
|
alpha=50, |
|
sigma=7, |
|
alpha_affine=0, |
|
p=1, |
|
) |
|
|
|
transformed = f(image=img, mask=mask) |
|
img = transformed['image'] |
|
|
|
mask = transformed['mask'] |
|
transformed = g(image=img, mask=mask) |
|
mask = transformed['mask'] |
|
return img, mask |
|
|
|
def get_blend_mask(mask): |
|
H,W=mask.shape |
|
size_h=np.random.randint(192,257) |
|
size_w=np.random.randint(192,257) |
|
mask=cv2.resize(mask,(size_w,size_h)) |
|
kernel_1=random.randrange(5,26,2) |
|
kernel_1=(kernel_1,kernel_1) |
|
kernel_2=random.randrange(5,26,2) |
|
kernel_2=(kernel_2,kernel_2) |
|
|
|
mask_blured = cv2.GaussianBlur(mask, kernel_1, 0) |
|
mask_blured = mask_blured/(mask_blured.max()) |
|
mask_blured[mask_blured<1]=0 |
|
|
|
mask_blured = cv2.GaussianBlur(mask_blured, kernel_2, np.random.randint(5,46)) |
|
mask_blured = mask_blured/(mask_blured.max()) |
|
mask_blured = cv2.resize(mask_blured,(W,H)) |
|
return mask_blured.reshape((mask_blured.shape+(1,))) |
|
|
|
|
|
def dynamic_blend(source,target,mask,blending_type, mixup_ratio=[0.25,0.5,0.75,1,1,1]): |
|
"""Performs dynamic blending of source and target, using the mask as the blending region |
|
|
|
Args: |
|
source: source image |
|
target: target image |
|
mask: mask image |
|
|
|
Returns: |
|
img_blended: blended image |
|
mask_blurred: augmented mask used for blending |
|
""" |
|
|
|
mask_blured = get_blend_mask(mask) |
|
mask_blured_copy = mask_blured.copy() |
|
|
|
if blending_type == "Poisson": |
|
|
|
b_mask = (mask_blured_copy * 255).astype(np.uint8) |
|
l, t, w, h = cv2.boundingRect(b_mask) |
|
center = (int(l + w / 2), int(t + h / 2)) |
|
img_blended = cv2.seamlessClone(source, target, b_mask, center, cv2.NORMAL_CLONE) |
|
else: |
|
|
|
blend_list=mixup_ratio |
|
blend_ratio = blend_list[np.random.randint(len(blend_list))] |
|
|
|
mask_blured_copy = mask_blured.copy() |
|
mask_blured_copy*=blend_ratio |
|
|
|
img_blended=(mask_blured_copy * source + (1 - mask_blured_copy) * target) |
|
|
|
return img_blended,mask_blured |
|
|
|
def get_transforms(): |
|
return alb.Compose([ |
|
|
|
alb.RGBShift((-20, 20), (-20, 20), (-20, 20), p=0.3), |
|
alb.HueSaturationValue( |
|
hue_shift_limit=(-0.3, 0.3), sat_shift_limit=(-0.3, 0.3), val_shift_limit=(-0.3, 0.3), p=0.3), |
|
alb.RandomBrightnessContrast( |
|
brightness_limit=(-0.3, 0.3), contrast_limit=(-0.3, 0.3), p=0.3), |
|
alb.ImageCompression(quality_lower=40, quality_upper=100, p=0.5), |
|
|
|
], |
|
additional_targets={f'image1': 'image'}, |
|
p=1.) |
|
|
|
|
|
def self_blending(img, landmark, blending_type, face_region): |
|
if np.random.rand() < 0.25: |
|
landmark = landmark[:68] |
|
mask = random_get_hull(landmark, img, face_region) |
|
if mask.shape[-1] == 3: |
|
mask = mask[:, :, 0] |
|
|
|
mask_copy = mask |
|
|
|
source_transforms = get_source_transforms() |
|
source = img.copy() |
|
source = source_transforms(image=source.astype(np.uint8))['image'] |
|
|
|
source_before_affine_transforms, mask_before_affine_transforms = source, mask |
|
source, mask = randaffine(source, mask) |
|
source_after_affine_transforms, mask_after_affine_transforms = source, mask |
|
|
|
img_blended, mask = dynamic_blend(source, img, mask, blending_type) |
|
img_blended = img_blended.astype(np.uint8) |
|
img = img.astype(np.uint8) |
|
|
|
return img, img_blended, mask, mask_copy, source_before_affine_transforms, mask_before_affine_transforms, source_after_affine_transforms, mask_after_affine_transforms |