133 lines
4.2 KiB
Python
133 lines
4.2 KiB
Python
"""
|
|
Align face and image sizes
|
|
"""
|
|
import cv2
|
|
import numpy as np
|
|
|
|
def positive_cap(num):
|
|
""" Cap a number to ensure positivity
|
|
|
|
:param num: positive or negative number
|
|
:returns: (overflow, capped_number)
|
|
"""
|
|
if num < 0:
|
|
return 0, abs(num)
|
|
else:
|
|
return num, 0
|
|
|
|
def roi_coordinates(rect, size, scale):
|
|
""" Align the rectangle into the center and return the top-left coordinates
|
|
within the new size. If rect is smaller, we add borders.
|
|
|
|
:param rect: (x, y, w, h) bounding rectangle of the face
|
|
:param size: (width, height) are the desired dimensions
|
|
:param scale: scaling factor of the rectangle to be resized
|
|
:returns: 4 numbers. Top-left coordinates of the aligned ROI.
|
|
(x, y, border_x, border_y). All values are > 0.
|
|
"""
|
|
rectx, recty, rectw, recth = rect
|
|
new_height, new_width = size
|
|
mid_x = int((rectx + rectw/2) * scale)
|
|
mid_y = int((recty + recth/2) * scale)
|
|
roi_x = mid_x - int(new_width/2)
|
|
roi_y = mid_y - int(new_height/2)
|
|
|
|
roi_x, border_x = positive_cap(roi_x)
|
|
roi_y, border_y = positive_cap(roi_y)
|
|
return roi_x, roi_y, border_x, border_y
|
|
|
|
def scaling_factor(rect, size):
|
|
""" Calculate the scaling factor for the current image to be
|
|
resized to the new dimensions
|
|
|
|
:param rect: (x, y, w, h) bounding rectangle of the face
|
|
:param size: (width, height) are the desired dimensions
|
|
:returns: floating point scaling factor
|
|
"""
|
|
new_height, new_width = size
|
|
rect_h, rect_w = rect[2:]
|
|
height_ratio = rect_h / new_height
|
|
width_ratio = rect_w / new_width
|
|
scale = 1
|
|
if height_ratio > width_ratio:
|
|
new_recth = 0.8 * new_height
|
|
scale = new_recth / rect_h
|
|
else:
|
|
new_rectw = 0.8 * new_width
|
|
scale = new_rectw / rect_w
|
|
return scale
|
|
|
|
def resize_image(img, scale):
|
|
""" Resize image with the provided scaling factor
|
|
|
|
:param img: image to be resized
|
|
:param scale: scaling factor for resizing the image
|
|
"""
|
|
cur_height, cur_width = img.shape[:2]
|
|
new_scaled_height = int(scale * cur_height)
|
|
new_scaled_width = int(scale * cur_width)
|
|
|
|
return cv2.resize(img, (new_scaled_width, new_scaled_height))
|
|
|
|
|
|
def de_resize_image(img, scale):
|
|
""" Resize image with the provided scaling factor
|
|
|
|
:param img: image to be resized
|
|
:param scale: scaling factor for resizing the image
|
|
"""
|
|
cur_height, cur_width = img.shape[:2]
|
|
new_scaled_height = int(cur_height / scale)
|
|
new_scaled_width = int(cur_width / scale)
|
|
|
|
return cv2.resize(img, (new_scaled_width, new_scaled_height))
|
|
|
|
def resize_align(img, points, size):
|
|
""" Resize image and associated points, align face to the center
|
|
and crop to the desired size
|
|
|
|
:param img: image to be resized
|
|
:param points: *m* x 2 array of points
|
|
:param size: (height, width) tuple of new desired size
|
|
"""
|
|
new_height, new_width = size # 910 910
|
|
|
|
# Resize image based on bounding rectangle
|
|
rect = cv2.boundingRect(np.array([points], np.int32)) # face rect
|
|
scale = scaling_factor(rect, size)
|
|
img = resize_image(img, scale)
|
|
|
|
# Align bounding rect to center
|
|
cur_height, cur_width = img.shape[:2]
|
|
roi_x, roi_y, border_x, border_y = roi_coordinates(rect, size, scale)
|
|
roi_h = np.min([new_height-border_y, cur_height-roi_y])
|
|
roi_w = np.min([new_width-border_x, cur_width-roi_x])
|
|
|
|
# Crop to supplied size
|
|
crop = np.zeros((new_height, new_width, 3), img.dtype)
|
|
crop[border_y:border_y+roi_h, border_x:border_x+roi_w] = (
|
|
img[roi_y:roi_y+roi_h, roi_x:roi_x+roi_w])
|
|
|
|
# Scale and align face points to the crop
|
|
points[:, 0] = (points[:, 0] * scale) + (border_x - roi_x)
|
|
points[:, 1] = (points[:, 1] * scale) + (border_y - roi_y)
|
|
|
|
return (crop, points)
|
|
|
|
def restore_image(img, points, size, crop):
|
|
|
|
new_height, new_width = size # 910 910
|
|
# Resize image based on bounding rectangle
|
|
rect = cv2.boundingRect(np.array([points], np.int32)) # face rect
|
|
scale = scaling_factor(rect, size)
|
|
img = resize_image(img, scale)
|
|
|
|
# Align bounding rect to center
|
|
cur_height, cur_width = img.shape[:2]
|
|
roi_x, roi_y, border_x, border_y = roi_coordinates(rect, size, scale)
|
|
roi_h = np.min([new_height-border_y, cur_height-roi_y])
|
|
roi_w = np.min([new_width-border_x, cur_width-roi_x])
|
|
|
|
img[roi_y:roi_y + roi_h, roi_x:roi_x + roi_w] = crop[border_y:border_y + roi_h, border_x:border_x + roi_w]
|
|
img = de_resize_image(img, scale)
|
|
return img |