You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

119 lines
3.6 KiB

# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import cv2
import math
import numpy as np
from PIL import Image
from .dlib_utils import detect, landmarks
def align_crop(image: Image):
faces = detect(image)
assert len(faces) > 0, 'Cannot detect face!!!'
face = get_max_face(faces)
lms = landmarks(image, face)
lms = lms[:, ::-1]
image = np.array(image)
image_align, landmarks_align = align(image, lms)
image_crop = crop(image_align, landmarks_align)
return image_crop
def get_max_face(faces):
if len(faces) == 1:
return faces[0]
else:
# find max face
areas = []
for face in faces:
left = face.left()
top = face.top()
right = face.right()
bottom = face.bottom()
areas.append((bottom - top) * (right - left))
max_face_index = np.argmax(areas)
return faces[max_face_index]
def align(image, lms):
# rotation angle
left_eye_corner = lms[36]
right_eye_corner = lms[45]
radian = np.arctan((left_eye_corner[1] - right_eye_corner[1]) /
(left_eye_corner[0] - right_eye_corner[0]))
# image size after rotating
height, width, _ = image.shape
cos = math.cos(radian)
sin = math.sin(radian)
new_w = int(width * abs(cos) + height * abs(sin))
new_h = int(width * abs(sin) + height * abs(cos))
# translation
Tx = new_w // 2 - width // 2
Ty = new_h // 2 - height // 2
# affine matrix
M = np.array([[cos, sin, (1 - cos) * width / 2. - sin * height / 2. + Tx],
[-sin, cos, sin * width / 2. + (1 - cos) * height / 2. + Ty]])
image_rotate = cv2.warpAffine(
image, M, (new_w, new_h), borderValue=(255, 255, 255))
landmarks = np.concatenate([lms, np.ones((lms.shape[0], 1))], axis=1)
landmarks_rotate = np.dot(M, landmarks.T).T
return image_rotate, landmarks_rotate
def crop(image, lms):
lms_top = np.min(lms[:, 1])
lms_bottom = np.max(lms[:, 1])
lms_left = np.min(lms[:, 0])
lms_right = np.max(lms[:, 0])
# expand bbox
top = int(lms_top - 0.8 * (lms_bottom - lms_top))
bottom = int(lms_bottom + 0.3 * (lms_bottom - lms_top))
left = int(lms_left - 0.3 * (lms_right - lms_left))
right = int(lms_right + 0.3 * (lms_right - lms_left))
if bottom - top > right - left:
left -= ((bottom - top) - (right - left)) // 2
right = left + (bottom - top)
else:
top -= ((right - left) - (bottom - top)) // 2
bottom = top + (right - left)
image_crop = np.ones((bottom - top + 1, right - left + 1, 3),
np.uint8) * 255
h, w = image.shape[:2]
left_white = max(0, -left)
left = max(0, left)
right = min(right, w - 1)
right_white = left_white + (right - left)
top_white = max(0, -top)
top = max(0, top)
bottom = min(bottom, h - 1)
bottom_white = top_white + (bottom - top)
image_crop[top_white:bottom_white + 1, left_white:right_white + 1] = image[
top:bottom + 1, left:right + 1].copy()
return image_crop