liiistem-miniapp/sh/idcard/idcard.py

252 lines
9.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import argparse
import math
import cv2
from typing import List
import numpy as np
from alibabacloud_imageseg20191230.client import Client as imageseg20191230Client
from alibabacloud_facebody20191230.client import Client as facebody20191230Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_facebody20191230 import models as facebody_20191230_models
from alibabacloud_imageseg20191230 import models as imageseg_20191230_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient
from viapi.fileutils import FileUtils
import urllib.request
class Sample:
def __init__(self):
pass
@staticmethod
def create_client(
access_key_id: str,
access_key_secret: str,
) -> (imageseg20191230Client, facebody20191230Client):
"""
使用AK&SK初始化账号Client
@param access_key_id:
@param access_key_secret:
@return: Client
@throws Exception
"""
seg_config = open_api_models.Config(
# 您的 AccessKey ID,
access_key_id=access_key_id,
# 您的 AccessKey Secret,
access_key_secret=access_key_secret
)
face_config = open_api_models.Config(
# 您的 AccessKey ID,
access_key_id=access_key_id,
# 您的 AccessKey Secret,
access_key_secret=access_key_secret
)
# 访问的域名
seg_config.endpoint = f'imageseg.cn-shanghai.aliyuncs.com'
face_config.endpoint = f'facebody.cn-shanghai.aliyuncs.com'
return (imageseg20191230Client(seg_config), facebody20191230Client(face_config))
def id_pohto_demo(
args: List[str],
) -> None:
assert (args is not None and len(args.param) > 2), "parameters wrong, use -h for details!"
[sc_file, color, of_file] = [i for i in args.param]
# set id photo size
size = [295, 413]
# client init
seg_client, face_client = Sample.create_client(args.access_key_id, args.access_key_secret)
# generation source image url
input_url = generate_url(sc_file, args.access_key_id, args.access_key_secret)
# init segment_body_request
segment_body_request = imageseg_20191230_models.SegmentBodyRequest(
image_url=input_url
)
# init detect_face_request
detect_face_request = facebody_20191230_models.DetectFaceRequest(
image_url=input_url
)
runtime = util_models.RuntimeOptions()
try:
# 通过人脸SDK获取人脸105关键点信息
face_result = face_client.detect_face_with_options(detect_face_request, runtime)
landmarks = face_result.body.data.landmarks
landmarks = np.array(landmarks)
# 通过分割SDK获取分割png图
seg_result = seg_client.segment_body_with_options(segment_body_request, runtime)
print(seg_result.body.data.image_url)
rqt = urllib.request.urlopen(seg_result.body.data.image_url)
seg_img = np.asarray(bytearray(rqt.read()), dtype="uint8")
seg_img = cv2.imdecode(seg_img, cv2.IMREAD_UNCHANGED)
# 根据人脸关键点进行人脸对齐返回旋转后图像、面部中心点、旋转角度、旋转后新的landmarks值
rotated_img, eye_center, angle, landmarks = align_face(seg_img, landmarks)
# 输入旋转后图片、预期的证件照分辨率、landmarks、背景色返回半身像结果
png_img = corp_halfbody(rotated_img, landmarks, size)
# 证件照背景色替换
colors = {'red': (0, 0, 255, 255), 'blue': (255, 0, 0, 255), 'white': (255, 255, 255, 255)}
if type(color) is str:
color = colors[color]
rst_img = np.zeros((size[1], size[0], 3)) + color[0:3]
rst_img = image_merge_background(png_img[:, :, 0:3], png_img, rst_img)
# 保存结果到本地
cv2.imwrite(of_file, rst_img)
except Exception as error:
print(error.message)
# 如有需要,请打印 error
UtilClient.assert_as_string(error.message)
def align_face(image_array, landmarks):
""" align faces according to eyes position
:param image_array: numpy array of a single image
:param landmarks: dict of landmarks for facial parts as keys and tuple of coordinates as values
:return:
rotated_img: numpy array of aligned image
eye_center: tuple of coordinates for eye center
angle: degrees of rotation
"""
landmarks = np.resize(landmarks, (105, 2))
# get list landmarks of left and right eye
left_eye = landmarks[24:39]
right_eye = landmarks[40:55]
left_eye_center = np.mean(left_eye, axis=0).astype("int32")
right_eye_center = np.mean(right_eye, axis=0).astype("int32")
dy = right_eye_center[1] - left_eye_center[1]
dx = right_eye_center[0] - left_eye_center[0]
angle = math.atan2(dy, dx) * 180. / math.pi
# calculate the center of 2 eyes
eye_center = (int(left_eye_center[0] + right_eye_center[0]) // 2,
int(left_eye_center[1] + right_eye_center[1]) // 2)
# at the eye_center, rotate the image by the angle
rotate_matrix = cv2.getRotationMatrix2D(eye_center, angle, scale=1)
rotated_img = cv2.warpAffine(image_array, rotate_matrix, (image_array.shape[1], image_array.shape[0]))
rotated_landmarks = []
for landmark in landmarks:
rotated_landmark = rotate(origin=eye_center, point=landmark, angle=angle, row=image_array.shape[0])
rotated_landmarks.append(rotated_landmark)
return rotated_img, eye_center, angle, rotated_landmarks
def rotate(origin, point, angle, row):
""" rotate coordinates in image coordinate system
:param origin: tuple of coordinates,the rotation center
:param point: tuple of coordinates, points to rotate
:param angle: degrees of rotation
:param row: row size of the image
:return: rotated coordinates of point
"""
x1, y1 = point
x2, y2 = origin
y1 = row - y1
y2 = row - y2
angle = math.radians(angle)
x = x2 + math.cos(angle) * (x1 - x2) - math.sin(angle) * (y1 - y2)
y = y2 + math.sin(angle) * (x1 - x2) + math.cos(angle) * (y1 - y2)
y = row - y
return int(x), int(y)
def corp_halfbody(image_array, landmarks, size):
""" crop face according to eye,mouth and chin position
:param image_array: numpy array of a single image
:param size: single int value, size for w and h after crop
:param landmarks: dict of landmarks for facial parts as keys and tuple of coordinates as values
:return:
cropped_img: numpy array of cropped image
left, top: left and top coordinates of cropping
"""
crop_size = [0, 0, size[1], size[0]]
scal = size[1] / 4 / abs(landmarks[98][1] - landmarks[56][1])
image = cv2.resize(image_array, np.multiply((image_array.shape[0:2])[::-1], scal).astype(int))
landmarks = np.multiply(np.array(landmarks), scal)
x_center = landmarks[98][0]
crop_size[0:2] = [x_center - size[0] / 2, x_center + size[0] / 2]
y_center = (landmarks[98][1] + landmarks[56][1]) / 2
crop_size[2:4] = [y_center - size[1] / 2, y_center + size[1] / 2]
left, right, top, bottom = [round(i) for i in crop_size]
left = max(0, left)
top = max(0, top)
right = min(image.shape[1], right)
bottom = min(image.shape[0], bottom)
cropped_img = image[top:bottom, left:right]
left, right, top, bottom = [round(i) for i in crop_size]
bottom = size[1]
top = size[1] - cropped_img.shape[0]
left = -min(0, left)
right = min(cropped_img.shape[1] + left, size[0])
png_img = np.zeros((size[1], size[0], 4))
png_img[top:bottom, left:right] = cropped_img[:, :]
return png_img
def image_merge_background(sc_image, png_image, bg_image):
""" merge foreground to background image
:param sc_image: numpy array of the source image
:param png_image: numpy array of the segmented result with same size of sc_image
:param bg_image: numpy array of the background image with same size of sc_image
:return:
rst_image: numpy array of merged image
"""
assert (sc_image is not None and png_image is not None and bg_image is not None), "read image input error!"
h, w, c = sc_image.shape
# keep sc_image, png_image and bg_image same size
viapi_image = cv2.resize(png_image, (w, h))
bg_image = cv2.resize(bg_image, (w, h))
if len(viapi_image.shape) == 2:
mask = viapi_image[:, :, np.newaxis]
elif viapi_image.shape[2] == 4:
mask = viapi_image[:, :, 3:4]
elif viapi_image.shape[2] == 3:
mask = viapi_image[:, :, 0:1]
else:
raise Exception("invalid image mask!")
mask = mask / 255.0
# merge background
sc_image = sc_image.astype(float)
bg_image = bg_image.astype(float)
rst_image = (sc_image - bg_image) * mask + bg_image
rst_image = np.clip(rst_image, 0, 255)
return rst_image
# see https://help.aliyun.com/document_detail/155645.html for more detail
def generate_url(image_path, access_key_id, access_key_secret):
file_utils = FileUtils(access_key_id, access_key_secret)
oss_url = file_utils.get_oss_url(image_path, "jpeg", True)
return oss_url
def define_options():
parser = argparse.ArgumentParser(description='Human segmentation examples.')
parser.add_argument('-i', '--access_key_id', default="LTAI5tE6gkXQqJBbCxFfo4hv", help="oss access key id")
parser.add_argument('-s', '--access_key_secret', default="pziJRQfXlev3vEIY4dHuB6C7E73Uv6", help="access key secret")
parser.add_argument('-p', '--param', nargs=3
, default=['./data/source.jpeg', 'blue', 'result2.jpg']
, type=str
, help=('source_image_path background_color[red blue white] result_path'))
args = parser.parse_args()
return args
if __name__ == '__main__':
args = define_options()
id_pohto_demo(args)