旷视研究院 · 2021年08月18日

技术的真相 | 从AR口红试妆了解人工智能试妆技术

随着人工智能技术的不断发展,各大美颜软件的妆容变得越来越“自然无痕迹”,大家可能会好奇这背后的技术原理是什么。因此,本期“技术的真相”将通过简易的AR口红试妆带大家走进虚拟试妆技术。

image.png

一、 项目简介

在刚刚过去的七夕选礼物的重要仪式中,大家有没有在挑选口红时感到茫然,就怕一不小心买成“直男”色号。其实,在许多电商平台都已经具备了试妆功能,点击按钮之后就能够尝试口红、睫毛、腮红、眉毛、眼影等多种美妆产品的试戴效果,实现足不出户就能选到心仪的美妆产品,而这种功能的背后就是虚拟试妆算法。

虚拟试妆算法是一种典型的计算机视觉与计算机图形学相结合的技术,它通过检测面部区域的关键点,在特定区域使用AR增强现实的渲染技术,最终可以实现模拟化妆效果,能够在电商、直播、短视频等平台具有广泛的应用。本文将从零开始搭建一个简单的AR口红试妆系统,帮助大家加深对于虚拟试妆技术的理解。

二、 技术实现

通常虚拟试妆算法主要包含检测上妆区域以及妆容渲染两个步骤,下面本文将分别介绍实现方案。

image.png
01 检测上妆区域

1. 人脸关键点检测

在上妆之前,我们需要知道在哪些位置进行上妆,因此,我们首先对于输入的图片进行人脸的关键点检测。

目前市面上有多家视觉厂商提供了人脸关键点检测的功能,本文选择使用了Face++人工智能开放平台提供的人脸关键点检测(https://www.faceplusplus.com....。注册控制台账号,创建API Key,下载SDK开始实现。对于试妆来说关键点数量越多,最终的试妆效果越好,本文以介绍流程为主,因此使用的关键点数量较少,有兴趣的读者可以选择使用Face++提供的人脸稠密关键点。

image.png

(图:可视化人脸关键点)

2. 人脸关键点平滑

通常我们使用的AR试妆系统均为实时系统,因此,我们需要将视频流拆解成单帧图像输入到人脸关键点检测网络,这导致输出的视频中人脸关键点会有一定的抖动。其中一方面是因为预测的关键点与实际关键点位置有小幅度的偏差,导致视频中相邻帧之间预测点位差异超过了实际点位的差异。另一方面,视频中人眼观察的静止与图像实际的静止状态存在差异,实际图像在像素级别存在差异,而模型对这种图像微小差异不够鲁棒。为了缓解关键点抖动问题,我们需要对人脸关键点进行平滑后处理,这里我们选择使用一阶的卡尔曼滤波进行处理,其中有两点需要注意:

(1)使用关键点的移动距离来估计系统过程噪声。如果检测关键点与前一个时刻的预测点的距离差别较小,则变化大概率由于检测误差引起的,需要降低过程噪声,反之当两者距离差别较大时,则需要提高系统的过程噪声。这样的处理也可以从另一个角度来理解,当检测关键点与前一个时刻的预测值距离较大时,说明关键点的真实值进行了移动,这时我们很难观察到关键点是否有抖动,通过提高系统过程噪声误差,能够减少预测的关键点与真实的关键点之间的距离,从而提高关键点的跟随性。而当检测的关键点与前一个时刻预测值的距离较小时,关键点的抖动较为明显,适当降低系统的过程误差,能够减弱观测输入带来的影响。

(2)根据实际模型估计系统观测误差R。通常系统观测误差R与关键点检测模型具有较强的相关性,当系统观测误差R的增大,预测关键点的抖动效果会减弱,同时会产生“拖影”效果,为了准确的估计系统观测误差,往往需要根据实际的使用效果进行调节,使其能够在抖动与“拖影”中取得平衡。

关键点卡尔曼滤波


#系统观测误差,根据模型进行调节
R = 0.05
#输入为前一个时刻的预测值、前一个时刻的预测误差,当前时刻的观测值
#输出为当前时刻的预测值、当前时刻的预测误差
def kalman_filter(last_predict_point, last_P, input_point):
    #通过当前观测值与前一时刻预测值的距离,计算系统过程噪声
    distance = dis(input_point - last_predict_point)
    alpha = clamp(distance/max_distance, 0.0, 1.0)
    #系统过程噪声误差
    Q = alpha + eps
    #系统预测误差
    P = last_P + Q
    #卡尔曼增益
    K = P / (P + R)
    #当前时刻的预测值
    cnt_predict_point = last_predict_point + K * (input_point - last_predict_point)
    #更新当前时刻预测误差
    cnt_P = (1 - K)* P
    return cnt_predict_point, cnt_P

02 妆容渲染

1. 人像美化

如果简单的对输入图片进行口红渲染,会使得效果非常突兀。通常小姐姐们在涂口红前,都需要上一层粉底,以达到提亮肤色、衬托口红的效果,因此我们在妆容渲染之前也需要进行一步人像美化。人像美化主要包含磨皮、美白、瘦脸、大眼等功能,本文将主要实现磨皮、美白功能。

磨皮

目前较为通用的磨皮算法的框架如下图,对于输入图像,首先进行肤色的检测得到肤色的区域图,然后对于输入图像进行相应的滤波算法。最后,根据肤色区域将原图与滤波后的图像进行融合,在肤色区域使用滤波后的图片,其他区域使用输入图像,通常为了提升融合效果,会先对肤色检测的结果进行高斯模糊,再根据系数进行融合。

image.png

皮肤检测

又名肤色检测、皮肤概率检测。在磨皮算法中,通常需要确定图像中磨皮的位置,以达到精确磨皮的效果。之所以选择根据肤色检测结果而不是人脸检测区域进行磨皮,是因为人脸检测只能确定面部区域,这会导致脖子与面部交界的位置出现明显区别,影响效果。为了解决这个问题,一种比较简单的想法是统计大量皮肤像素的颜色数据,分析皮肤颜色的取值范围,得到基于RGB空间的肤色判断条件如下:


R>95&&G>40&&B>20&&R>G&&R>B&&max(R,G,B)−min(R,G,B)>15&&abs(R−G)>15

如果输入像素颜色满足该条件,即为肤色像素,通常我们会生成一个与原图相同大小的mask,并对mask进行高斯blur,用于滤波图像与原图的融合。

image.png

 (图:皮肤检测区域)

滤波算法

这里使用的滤波算法主要作用是在平滑皮肤的同时,保留五官的细节,因此需要选择一些能够保留边缘信息的滤波算法。目前可以做磨皮的保边滤波主要有以下几种:表面模糊、双边滤波、导向滤波等。考虑到性能与效果的平衡,我们选择使用表面模糊的算法。表面模糊是一种Photoshop中常用的滤波算法,其思想是计算像素邻域内不同像素的加权平均,对于边缘处的像素,权重比较小,能够较为完整的保留;对于非边缘处的区域,权重比较大,平滑的效果较好。假设像素I(i,j)I(i,j)周围半径R大小的邻域为S,邻域内的像素为I(k,l)I(k,l),则滤波后的像素值为:

image.png

表面模糊处理有2个参数,即模糊半径R和模糊阈值Y,前者确定模糊的范围,后者确定模糊的程度,代码实现如下:

表面模糊


#表面模糊Surface Blur
#references:https://blog.csdn.net/shinian1987/article/details/78345408
def sur_blur ( I_in, thre=20, half_size=10):
    I_out = I_in * 1.0
    row, col = I_in.shape
    w_size = half_size * 2 + 1
    for ii in range (half_size, row-1-half_size):
        for jj in range (half_size, col-1-half_size):
            aa = I_in [ii-half_size:ii+half_size+1, jj-half_size : jj+half_size+1]
            p0 = I_in [ii, jj]
            mask_1 = numpy.matlib.repmat(p0, w_size, w_size)
            mask_2 = 1-abs(aa-mask_1)/(2.5*thre);
            mask_3 = mask_2 * (mask_2 > 0)
            t1 = aa * mask_3
            I_out[ii, jj] = t1.sum()/mask_3.sum()

磨皮效果如下:

image.png

(图:磨皮效果,左:原图,右:磨皮后)

美白

美白算法主要的目的为提亮肤色,一个比较常见的方式是通过颜色查找表方式将肤色映射到理想的范围。颜色查找表的原理是对于每一个给定的RGB颜色,都能够在查找表中找到一个颜色与之对应,并将映射后的颜色使用一张图像的方式进行存储。我们使用的查找表如下图:

image.png

使用查找表的代码如下:

查找表


#获取滤镜颜色
#输入为图像、查找表、图像中位置
#输出为对应位置的颜色值
def getBGR(img, table, i, j):
    #获取图像颜色
    b, g, r = img[i][j]
    #计算标准颜色表中颜色的位置坐标
    x = int(g/4 + int(b/32) * 64)
    y = int(r/4 + int((b%32) / 4) * 64)
    #返回滤镜颜色表中对应的颜色
    return table[x][y]

#简单使用代码
img = cv2.imread('input.png')
lut_map = cv2.imread('lut.png')
rows, cols = img.shape[:2]
dst = np.zeros((rows, cols, 3), dtype="uint8")
for i in range(rows):
    for j in range(cols):
        dst[i][j] = getBGR(img, lut_map, i, j)

最终效果如下:

image.png

其中左边为原图,右边为增加美白滤镜后的图片,美白不宜添加过渡,容易喧宾夺主。

2.妆容渲染

为了保证妆容渲染能够达到实时的效果,通常需要使用OpenGL的技术进行渲染,这里将简单介绍妆容渲染的主要流程。

素材生成,需要根据指定的颜色、材质等信息,生成对应的素材,如需要形状等静态效果只需要加载静态图片资源作为纹理即可,如需要光效等动态效果则需要在shader中进行实现。

位置对齐,通常将人脸关键点的位置作为顶点坐标,将素材作为纹理,关键点对应的位置作为纹理坐标进行渲染,以达到将素材位置与实际位置对齐的效果。

颜色融合,这一步会将口红mask与底色进行融合,通常在shader中进行实现,融合的方式有多种,对于口红来说,可以使用透明度融合的方式,但口红mask透明度尽量控制在30%以下,以保证效果较为自然,也可以尝试选用其他的融合方式如叠加等。

三、 总结与展望

本文简单介绍了口红虚拟试妆系统的主要流程,并提供了部分功能的核心代码,其他区域的妆容渲染大同小异,读者可以自行进行尝试。由于每个人对于美的定义都有所区别,读者也可以根据个人的需求调整美妆效果。未来将进一步介绍肤质检测相关算法,敬请期待。

四、 参考文献

1. Face++开放平台:https://github.com/FacePlusPl...

2. 卡尔曼滤波:https://www.bzarg.com/p/how-a...

3. 人脸关键点:https://zhuanlan.zhihu.com/p/...

4. 美颜美妆:https://blog.csdn.net/dQCFKyQ...

首发:旷视研究院
作者:孙宇超

专栏文章推荐

欢迎关注旷视研究院极术社区专栏,定期更新最新旷视研究院成果
加入旷视:career@megvii.com
推荐阅读
关注数
7696
内容数
164
专注旷视研究院学术论文解读推送,涵盖计算机视觉,文字识别等
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息