利用机器学习完成动漫人脸识别与贴图
一、引言
这只是我随意做的一段小脚本而已,功能是检测动漫人物的人脸,并在指定位置替换上自己预备好的贴图。
机器学习的原理我几乎一窍不通,全程只是普通地调库,没什么特别的技术含量。
代码主要借助了 python Anime Face Detector
库(点击此处跳转)中动漫人脸识别的功能,这个库可以很方便地标记动漫人脸的特征点,之后手动将预设的贴图在指定位置贴上去就行了。
所有代码我都上传到我的 gitee 仓库了,需要的可以去下载:https://gitee.com/oldprincess/xiaoxile
二、代码
2.1 环境配置
我使用的是 python 3.7,首先需要安装库Anime Face Detector
这个库安装前需要下载许多依赖库,例如torch
,opencv-python
等等,所以可能会有些久(我使用清华的镜像源花了15分钟左右)
pip install anime-face-detector
在代码运行时还需要对应的模型文件,它会自动下载,也可以手动去 github 上下载库Anime Face Detector
作者提供的模型文件(点击此处跳转),此处需要的文件只有mmdet_anime-face_yolov3.pth
和mmpose_anime-face_hrnetv2.pth
模型文件应放置在 <当前工作目录>\checkpoints\
目录下
2.2 需要import的库
import torch
import pathlib
import cv2
import math
from anime_face_detector import create_detector
torch
和pathlib
只是用于修改模型的默认路径,在默认情况下它会位于C:\User\<用户名>\.cache\torch\hub
处(在我的机器上是这个路径),此处将其修改为当前的工作目录
cv2
用于进行图像读取和处理,包括缩放、旋转等等,math
库用于根据特征点坐标计算旋转角度,而anime_face_detector
就是识别器
2.3 全局设定
# set torch.hub dir
torch.hub.set_dir(str(pathlib.Path().cwd()))
MOUTH_DOT = [24, 25, 26, 27] # mouth key dot
LEFT_FACE, RIGHT_FACE = 1, 3 # face key dot
detector = create_detector(device='cpu') # init detector
smile = cv2.imread('smile.png', cv2.IMREAD_UNCHANGED) # load smile pic
第一行将 torch 的路径设置为当前的工作目录
接下来的 MOUTH_DOT
,LEFT_FACE
,RIGHT_FACE
是特征点的索引,分别是 4 个嘴部特征脸,左脸,右脸
之后的 detector
创建了一个识别器,因为我的电脑并没有配置 cuda
环境,所以 device
参数设置为 cpu
之后将事先准备好的 smile.png
读入
2.4 函数
此处是主要的处理函数 paste_smile
,即识别图片中的脸部,并在对应坐标贴图
使用一个 3 通道的 img
图片为输入,首先使用之前初始化的识别器产生识别结果 preds
,它包含多个面部的特征点信息,其中的 keypoints
就是特征点(坐标为浮点数)
对嘴巴的 4 个特征点,通过计算均值确定坐标;而对于脸部的特征点,直接采用对应的结果
def paste_smile(img):
preds = detector(img) # detector anime face
# every face in img
for pred in preds:
dots = pred['keypoints'] # key dots
# average mouth dot pos
mouth = (
int(sum([dots[i][0] for i in MOUTH_DOT]) // 4),
int(sum([dots[i][1] for i in MOUTH_DOT]) // 4)
) # mouth dot
# face dots, pos[0] is width, pos[1] is height
l_face = (int(dots[LEFT_FACE][0]), int(dots[LEFT_FACE][1])) # left face dot
r_face = (int(dots[RIGHT_FACE][0]), int(dots[RIGHT_FACE][1])) # right face dot
接着,需要对读入的图片进行缩放与旋转处理。计算面部宽度face_width
,之后的图片宽度使用人物面部宽度一半
face_width = abs(l_face[0] - r_face[0]) # calculate face size
# resize smile pic
ratio = 0.5 * (face_width / smile.shape[1]) # shape = (height, width, ...)
img2 = cv2.resize(smile, (int(smile.shape[1] * ratio), int(smile.shape[0] * ratio)))
img2_height, img2_width, _ = img2.shape # shape = (height, width, ...)
接着,对图片进行旋转。如果左右侧脸的高度不一致,那么就对贴图进行旋转,通过atan
函数计算出旋转角
# rotate pic
if l_face[0] != r_face[0]:
# calculate angle
theta = - math.atan((r_face[1] - l_face[1]) / (r_face[0] - l_face[0])) / math.pi * 180
# rotate smile
matrix = cv2.getRotationMatrix2D((img2_width // 2, img2_height // 2), theta, 1.0)
img2 = cv2.warpAffine(img2, matrix, (img2_width, img2_height))
最后,进行图片贴图。获取贴图透明通道的信息,将非透明部分贴到原图中
# copy smile
alpha = img2[:, :, 3] # alpha channel
for w in range(img2_width):
for h in range(img2_height):
if alpha[h, w] != 0:
s_h = mouth[1] - (img2_height // 2) + h # pos height
s_w = mouth[0] - (img2_width // 2) + w # pos width
if (0 <= s_h < img.shape[0]) and (0 <= s_w < img.shape[1]):
img[s_h, s_w, 0:3] = img2[h, w, 0:3]
return img
2.5 demo
此处将上述代码作为 xiaoxile
模块,使用时,只需要 import 并调用其 paste_smile
函数即可
import xiaoxile
import cv2
img = cv2.imread('pic.jpg')
img2 = xiaoxile.paste_smile(img)
cv2.imshow('', img2)
cv2.waitKey()