**基于Swfit 实现人脸变老**
## 1.项目简介
自己实现的一个人脸变老的方案,项目代码和算法相关均由 **Swift** 实现,最终的效果图如下:
![](http://www.writebug.com/myres/static/uploads/2021/10/21/b02efbec1ca0b5507e8bedf2b96f4ff6.writebug)
## 2.实现步骤
该方案实现的原理是将一张**预制作好的皱纹纹理**“贴在”原图的人脸区域上,听起来很简单,不过在具体实现上则需要考虑不少问题,让我们从后往前去推导哪些要需要解决的问题:首先,预制作好的皱纹纹理如何和原图中的人脸**自然的贴合**?考虑到不同原图中的人脸肤色和亮度会有很大的差异,如果针对不同的肤色来提供不同的皱纹纹理显然是不可行的。其次,预制好的皱纹纹理的五官区域明显是和原图中的人脸不符合,那么就需要针对不同的人脸特征点来对皱纹纹理进行**复杂变形**。考虑到以上种种,本方案的实现步骤分为以下三步:
- 1、识别图片中的人脸区域并提取人脸特征点
- 2、根据人脸特征点来对皱纹纹理的各区域进行复杂变形
- 3、将变形后的皱纹纹理自然的贴合在原图识别出的人脸区域上
让我们一步步来实现:
## 3.识别人脸关键点
这一步的实现方案比较简单,借助的是 [Face++](https://link.juejin.cn?target=https%3A%2F%2Fwww.faceplusplus.com.cn%2F) 平台的技术实现,只需要简单的申请注册就可以免费使用人脸识别功能,客户端只需要上传图片调用相关的Api即可,返回的人脸识别特征点信息大致如下图所示(图片源自Face++):
![](http://www.writebug.com/myres/static/uploads/2021/10/21/a5e14e0e4245ba9cd9ee8be35d81f9fb.writebug)
## 4.对皱纹纹理进行变形处理
#### **4.1提取皱纹纹理特征点坐标**
变形前需要先获取皱纹纹理上对应的人脸特征点坐标,由于皱纹纹理是提前准备的,所以可以直接通过[获取图片点坐标工具](https://link.juejin.cn?target=https%3A%2F%2Fwww.mobilefish.com%2Fservices%2Frecord_mouse_coordinates%2Frecord_mouse_coordinates.php)来提取特性点坐标数据:
![](http://www.writebug.com/myres/static/uploads/2021/10/21/34f3061ca3a527791146b80064eb9310.writebug)
#### 4.2变形算法实现
考虑到这是基于特征点的复杂变形,所以皱纹纹理图片的渲染选择了用 [OpenGL](https://link.juejin.cn?target=https%3A%2F%2Fwww.opengl.org%2F),iOS SDK 提供了封装好的 [GLKit](https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.apple.com%2Fdocumentation%2Fglkit) 来方便使用 OpenGL ,只需要创建一个 `GLKViewController`:
![](http://www.writebug.com/myres/static/uploads/2021/10/21/bf784d88bf49f3f9aecccef1c6fc6efc.writebug)
然后重写 `glkView` 方法:
```swift
import UIKit
import GLKit
class FaceGLKViewController: GLKViewController {
···
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
···
}
···
}
```
新建一个 `ImageMesh` 类,用来记录皱纹纹理内坐标网格点信息:
```swift
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 = 0
var xy: [vector_float2]? = nil
var ixy: [vector_float2]? = nil
convenience init(vd: Int, hd: Int) {
self.init()
verticalDivisions = vd
horizontalDivisions = hd
numVertices = (verticalDivisions + 1) * (horizontalDivisions + 1)
indexArrSize = 2 * verticalDivisions * (horizontalDivisions + 1)
verticesArr = [Float](repeating: 0.0, count: 2 * indexArrSize)
textureCoordsArr = [Float](repeating: 0.0, count: 2 * indexArrSize)
vertexIndices = [Int](repeating: 0, count: indexArrSize)
xy = [vector_float2](repeating: [0.0, 0.0], count: numVertices)
ixy = [vector_float2](repeating: [0.0, 0.0], count: numVertices)
var count = 0
for i in 0..<verticalDivisions {
for j in 0...horizontalDivisions {
vertexIndices![count] = (i + 1) * (horizontalDivisions + 1) + j; count += 1
vertexIndices![count] = i * (horizontalDivisions + 1) + j; count += 1
}
}
let xIncrease = 1.0 / Float(horizontalDivisions)
let yIncrease = 1.0 / Float(verticalDivisions)
count = 0
for i in 0..<verticalDivisions {
for j in 0...horizontalDivisions {
let currX = Float(j) * xIncrease;
let currY = 1 - Float(i) * yIncrease;
textureCoordsArr![count] = currX; count += 1
textureCoordsArr![count] = currY - yIncrease; count += 1
textureCoordsArr![count] = currX; count += 1
textureCoordsArr![count] = currY; count += 1
}
}
}
···
}
```
然后调用 Opengl Api 完成渲染工作:
```swift
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
// 透明背景
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
glBlendFunc(GLenum(GL_SRC_ALPHA), GLenum(GL_ONE_MINUS_SRC_ALPHA));
glEnable(GLenum(GL_BLEND));
if (isSetup) {
renderImage()
}
}
func renderImage() {
self.effect?.texture2d0.name = (mainImage?.texture?.name)!
self.effect?.texture2d0.enabled = GLboolean(truncating: true)
self.effect?.prepareToDraw()
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue))
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.texCoord0.rawValue))
glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 8, mainImage?.verticesArr)
glVertexAttribPointer(GLuint(GLKVertexAttrib.texCoord0.rawValue), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 8, mainImage?.textureCoordsArr)
for i in 0..<(mainImage?.verticalDivisions)! {
glDrawArrays(GLenum(GL_TRIANGLE_STRIP), GLint(i * (self.mainImage!.horizontalDivisions * 2 + 2)), GLsizei(self.mainImage!.horizontalDivisions * 2 + 2))
}
}
复制代码
```
接下来是实现基于关键点的变形,变形的算法实现是根据 [Image Deformation Using Moving Least Squares](https://link.juejin.cn?target=http%3A%2F%2Ffaculty.cs.tamu.edu%2Fschaefer%2Fresearch%2Fmls.pdf) 论文来编写的,论文的内容和推导过程比较简洁,侧重于给出最终的数学公式,有兴趣的可以去详读。为了方便,本方案用 Swift 来实现该算法。以皱纹纹理上的特征点作为变形原点, Face++ 返回的人脸特征点作为变形目标点,对皱纹纹理进行变形:
```swift
func setupImage(image: UIImage, width: CGFloat, height: CGFloat, original_vertices: [float2], target_vertices: [float2]) {
let _ = mainImage?.loadImage(image: image, width: width, height: height)
setupViewSize()
let count = target_vertices.count
var p = original_vertices
// 转换坐标系
for i in 0..<count {
p[i] = [p[i].x - Float(image.size.width / 2), Float(image.size.height / 2) - p[i].y]
p[i] = [p[i].x * Float(width) / Float(image.size.width), p[i].y * Float(height) / Float(image.size.height)]
}
let q = target_vertices
var w = [Float](repeating: 0.0, count: count)
// 计算变形权重
for i in 0..<(self.mainImage?.numVertices)! {
var ignore = false
for j in 0..<count {
let distanceSquare = ((self.mainImage?.ixy![i])! - p[j]).squaredNorm()
if distanceSquare < 10e-6 {
self.mainImage?.xy![i] = p[j]
ignore = true
}
w[j] = 1 / distanceSquare
}
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
该方案实现的原理是将一张预制作好的皱纹纹理“贴在”原图的人脸区域上,听起来很简单,不过在具体实现上则需要考虑不少问题,让我们从后往前去推导哪些要需要解决的问题:首先,预制作好的皱纹纹理如何和原图中的人脸自然的贴合?考虑到不同原图中的人脸肤色和亮度会有很大的差异,如果针对不同的肤色来提供不同的皱纹纹理显然是不可行的。其次,预制好的皱纹纹理的五官区域明显是和原图中的人脸不符合,那么就需要针对不同的人脸特征点来对皱纹纹理进行复杂变形。 详细介绍参考:https://blog.csdn.net/newlw/article/details/132575136
资源推荐
资源详情
资源评论
收起资源包目录
基于Swfit实现的人脸变老软件.zip (31个子文件)
基于Swfit实现的人脸变老软件
faceagingdemo-master
.DS_Store 6KB
FaceAgingDemo.xcodeproj
project.pbxproj 20KB
project.xcworkspace
xcshareddata
IDEWorkspaceChecks.plist 238B
contents.xcworkspacedata 158B
Podfile 113B
LICENSE 1KB
FaceAgingDemo.xcworkspace
xcshareddata
IDEWorkspaceChecks.plist 238B
contents.xcworkspacedata 231B
.gitignore 2KB
README.md 12KB
Podfile.lock 409B
FaceAgingDemo
Utils
RoundCornerImageView.swift 370B
vector_float2+Norm.swift 487B
ImageUtil.swift 538B
FaceApi
FaceBean.swift 2KB
FaceService.swift 2KB
Base.lproj
LaunchScreen.storyboard 2KB
FaceAging.storyboard 11KB
FaceGLKViewController.swift 7KB
ImageMesh.swift 4KB
AppDelegate.swift 432B
FaceAgingController.swift 4KB
Info.plist 2KB
float3+Invalid.swift 356B
Assets.xcassets
.DS_Store 6KB
Wrinkle.imageset
Contents.json 156B
aging90.png 342KB
Contents.json 62B
AppIcon.appiconset
Contents.json 2KB
Face.imageset
Contents.json 154B
woman.jpg 61KB
共 31 条
- 1
资源评论
shejizuopin
- 粉丝: 1w+
- 资源: 1300
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功