前段时间,产品要求做一个图片美化——人脸变老的功能,经过网上一番搜索,没看到有完整方案开源实现可供参考,一些相关的博客大都是简单的提及一些思路和给出一些效果图,于是在借鉴了一些前人的思路之后,自己实现了一个人脸变老的方案,项目代码和算法相关均由 Swift 实现, 现将具体的实现步骤和核心的代码分享一下。完整的 Demo 代码会附在本文末尾,最终的效果图如下:
人脸变老实现
该方案实现的原理是将一张预制作好的皱纹纹理“贴在”原图的人脸区域上,听起来很简单,不过在具体实现上则需要考虑不少问题,让我们从后往前去推导哪些要需要解决的问题:首先,预制作好的皱纹纹理如何和原图中的人脸自然的贴合?考虑到不同原图中的人脸肤色和亮度会有很大的差异,如果针对不同的肤色来提供不同的皱纹纹理显然是不可行的。其次,预制好的皱纹纹理的五官区域明显是和原图中的人脸不符合,那么就需要针对不同的人脸特征点来对皱纹纹理进行复杂变形。考虑到以上种种,本方案的实现步骤分为以下三步:
- 1、识别图片中的人脸区域并提取人脸特征点
- 2、根据人脸特征点来对皱纹纹理的各区域进行复杂变形
- 3、将变形后的皱纹纹理自然的贴合在原图识别出的人脸区域上
让我们一步步来实现:
识别人脸关键点
这一步的实现方案比较简单,借助的是 Face++ 平台的技术实现,只需要简单的申请注册就可以免费使用人脸识别功能,客户端只需要上传图片调用相关的Api即可,返回的人脸识别特征点信息大致如下图所示(图片源自Face++):
对皱纹纹理进行变形处理
提取皱纹纹理特征点坐标
变形前需要先获取皱纹纹理上对应的人脸特征点坐标,由于皱纹纹理是提前准备的,所以可以直接通过获取图片点坐标工具来提取特性点坐标数据:
变形算法实现
考虑到这是基于特征点的复杂变形,所以皱纹纹理图片的渲染选择了用 OpenGL,iOS SDK 提供了封装好的 GLKit 来方便使用 OpenGL ,只需要创建一个 GLKViewController
:
glkView
方法:
import UIKit
import GLKit
class FaceGLKViewController: GLKViewController {
···
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
···
}
···
}
复制代码
新建一个 ImageMesh
类,用来记录皱纹纹理内坐标网格点信息:
class ImageMesh: NSObject {
var verticalDivisions = 0
var horizontalDivisions = 0
var indexArrSize = 0
var vertexIndices: [Int]? = nil
// Opengl坐标点数组
var verticesArr: [Float]? = nil
var textureCoordsArr: [Float]? = nil
var texture: GLKTextureInfo? = nil
var image_width: Float = 0.0
var image_height: Float = 0.0
var numVertices: Int