""" 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 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 # Resize image based on bounding rectangle rect = cv2.boundingRect(np.array([points], np.int32)) 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)