利用机器学习完成动漫人脸识别与贴图

一、引言

这只是我随意做的一段小脚本而已,功能是检测动漫人物的人脸,并在指定位置替换上自己预备好的贴图。
机器学习的原理我几乎一窍不通,全程只是普通地调库,没什么特别的技术含量。

代码主要借助了 python Anime Face Detector库(点击此处跳转)中动漫人脸识别的功能,这个库可以很方便地标记动漫人脸的特征点,之后手动将预设的贴图在指定位置贴上去就行了。

所有代码我都上传到我的 gitee 仓库了,需要的可以去下载:https://gitee.com/oldprincess/xiaoxile

二、代码

2.1 环境配置

我使用的是 python 3.7,首先需要安装库Anime Face Detector
这个库安装前需要下载许多依赖库,例如torchopencv-python等等,所以可能会有些久(我使用清华的镜像源花了15分钟左右)

pip install anime-face-detector

在代码运行时还需要对应的模型文件,它会自动下载,也可以手动去 github 上下载库Anime Face Detector作者提供的模型文件(点击此处跳转),此处需要的文件只有mmdet_anime-face_yolov3.pthmmpose_anime-face_hrnetv2.pth

模型文件应放置在 <当前工作目录>\checkpoints\目录下

2.2 需要import的库

import torch
import pathlib
import cv2
import math
from anime_face_detector import create_detector

torchpathlib只是用于修改模型的默认路径,在默认情况下它会位于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_DOTLEFT_FACERIGHT_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()
posted @ 2022-04-29 22:07  kentle  阅读(1126)  评论(0编辑  收藏  举报