philm-iOS-wiki
  • 介绍
  • 网络层
    • 说明
  • UI
    • 说明
    • 在ios7以前使用ColorSpace的坑
    • UITableView偏移异常问题
    • drawRect时单独设置文字阴影无效
    • Xcode9下相册访问权限问题
    • 避免同时点击多个Button
    • scroll上的button延迟响应问题
    • uibutton触发边界事件
    • ios 11 上tableview 改动
    • YYImage 显示指定区域的图片
  • 数据持久化
    • 说明
  • 其它
    • 取消延迟执行之坑
    • NSString 转换 float 的精度问题
  • 每周阅读
    • 目录
    • 深入思考NSNotification
    • gitBook使用小助手
    • iOS App签名的原理
    • 响应链
    • iOS10跳转系统到设置页
    • SDWebImage下载高清图内存问题
    • iOS圆角避免离屏渲染
    • 常用的延时调用
    • iOS 神经网络
    • SDWebImage缓存策略
    • 3Dtouch
    • 为什么 Objective-C 对象存储在堆上而不是栈上
    • 深入浅出理解视频编码H264结构
    • CATextLayer学习
    • cocoaPods
    • 任意网站支持RSS
    • Metal简介
    • 动态更改icon
    • CAReplicatorLayer
    • 增加点击间隔
    • 勒索病毒当道的时代
    • iOS常用宏定义
    • Metal实现YUV转RGB渲染视频
    • 获取当前下载的app及下载进度
    • OpenGL ES 三种类型修饰 uniform attribute varying
    • 技术部门引入OKR
    • 基于runloop的线程保活、销毁与通信
    • 深入理解哈希表
    • TOLL-FREE BRIDGING 和 UNMANAGED
    • 开发者能拿到的标识符
    • Swift自定义LOG
    • 系统通知整理
    • iOS 中的 imageIO 与 image 解码
    • CGImageRef基本介绍及方法说明
    • Swift 3.0 语法
    • webview加载部分网页
    • 在CAAnimation中暂停动画
    • 把代码迁移到协调器上
    • ios11API更新整理
    • 非越狱iOS设备的远程控制实现原理
    • 关于本地化
    • swift命名空间
    • CoreML与coremltools体验
    • 力学动画
    • Swift 4官方文档中文版: The Basic(上)
    • swift 中的KVO用法
    • GPUImage的图像形变设计(简单形变部分)
    • iOS响应式架构
    • 移动端图片上传旋转、压缩的解决方案
    • AVFoundation使用指南AVAssert使用
    • 过渡动画
    • 谈谈 MVX 中的 Model
    • AVFoundation编程-AVPlayer使用
    • GPUImage的图像形变设计(复杂形变部分)
    • What's New in LLVM 9
    • ios的事件机制
    • GPUImage源码解读(一)
    • GPUImage源码解读(二)
    • iOS 启动优化
    • 模块化 Swift 中的状态
    • swift中的let和var背后的编程模式
    • Swift Runtime动态性分析
    • RAC下的响应式编程
    • GPUImage源码解读(三)
    • 如何准确判断webView是否加载完成
    • NSObject的+load和+initialize详解
    • ios8以后设置启动图
    • GPUImage源码解读(四)
    • Swift自动闭包
    • IOS11新特性
    • GPUImage源码解读(五)
    • 理解 OC 内部的消息调用、消息转发、类和对象
    • 修饰符
    • IOS 切面统计事件解耦
    • GPUImage源码解读(六)
    • CoreImage介绍
    • 影响Core Animation性能的原因
    • Instruments中的动画工具选项介绍
    • GPUImage源码解读(七)
    • Xcode 7新的特性Lightweight Generics 轻量级泛型与__kindof修饰符
    • GPUImage源码解读(八)
    • Core Image之自定 Filter
    • iOS通用链接
    • 谈nonatomic非线程安全问题
    • 深拷贝与浅拷贝
    • CIKernel 介绍
    • iOS11适配
    • GPUImage源码解读(九)
    • CVPixelBufferCreate使用的坑
    • ios一窥并发底层
    • ARKit进阶:物理世界
    • ARKit的工作原理及流程介绍
    • UI线程卡顿监控
    • FBKVOController使用
    • GPUImage源码解读(十)
    • WKWebView在ios11崩溃问题解决方法
    • 微信iOS SQLite源码优化实践
    • HEIF 和 HEVC 研究
    • 谈谈 iOS 中图片的解压缩
    • 提升 iOS 开发效率! Xcode 9 内置模拟器的9个技巧
    • ObjC和JavaScript的交互,在恰当的时机注入对象
    • iOS数据保护
    • iOS11中网络层的一些变化(Session707&709脱水版)
    • GPUImage源码解读(十一)
    • 一种避免 iOS 内存碎片的方法
    • pods的原理
    • GPUImage源码解读(十二)
    • GPUImage源码解读(十三)
    • iOS 11 Layout的新特性
    • iOS应用瘦身方法思路整理
    • GPUImage源码解读(十四)
    • CAEmitterLayer属性介绍
    • 浅析移动蜂窝网络的特点及其省电方案
    • 如何在 table view 中添加 3D Touch Peek & Pop 功能
    • iOS中锁的介绍与使用
    • NSLog效率低下的原因及尝试lldb断点打印Log
    • GPUImage源码解读(十五)
    • GPUImage源码解读(十六)
    • CADisplayLink
    • GPUImage源码解读(十七)
    • CADisplayLink
    • 老生常谈category增加属性的几种操作
    • 30行代码演示dispatch_once死锁
    • GPUImage源码解读(十八)
    • YYImage设计思路
    • GPUImage源码解读(十九)
    • 深入理解Tagged Pointer
    • iOS 11:WKWebView内容过滤规则详解
    • Swift语法对编译速度的影响
    • GPUImage源码解读(二十)
    • GPUImage源码解读(二十一)
    • iOS App间常用的五种通信方式
    • YYCache深入学习
    • 冲顶大会插件
    • iOS高性能图片架构与设计
    • YUV颜色编码解析
    • iOS传感器:App前后台切换后,获取敏感信息使用touch ID进行校验
    • GPUImage源码解读(二十二)
    • GPUImage源码解读(二十三)
    • 从零开始的机器学习 - Machine Learning(一)
    • 从零开始的机器学习 - Machine Learning(二)
    • GPUImage源码解读(二十四)
    • Objective-C消息转发机制
    • iOS 程序 main 函数之前发生了什么
    • MMKV--基于 mmap 的 iOS 高性能通用 key-value 组件
    • Objective-C 消息发送与转发机制原理
    • 谈Objective-C block的实现
    • GPUImage源码解读(二十五)
    • podfile语法
    • 轻量级低风险 iOS 热更新方案
    • 使用objection来模块化开发iOS项目
    • swift 中delegate的使用注意
    • 使用appledoc自动生成api文档
    • UITextChecker的使用
    • ARKit 如何给SCNNode贴Gif图片
    • Unity与iOS平台交互和原生插件开发
    • SceneKit编程珠玑
Powered by GitBook
On this page
  • Part3:基于自定义vertices的局部图像形变设计
  • 小结:
  • GPU受限:指传输到GPU的用于计算的数据太大,部分GPU可能无法支持
  1. 每周阅读

GPUImage的图像形变设计(复杂形变部分)

PreviousAVFoundation编程-AVPlayer使用NextWhat's New in LLVM 9

Last updated 7 years ago

在上一部分,我们介绍了两种简单形变的GPUImage实现方式,包括自定义FragmentShader,和自定义顶点数组。这一部分,我们将介绍更为复杂的一些图像形变的实现。

Part3:基于自定义vertices的局部图像形变设计

区别于Part2中的自定义vertices和fragment数组的简单图像形变,这里的自定义vertices数组不仅仅局限于图像4个顶点,而是可以任意指定的,从而可以达到对图像的局部区域进行细微的形变调整。这里,我们以调整用户的脸型,从而达到蛇精脸的效果为例,如下图所示:

对于用户图像的人脸区域,我们分隔成若干个三角形切片,然后通过调节这些三角形的顶点来实现形变。具体的做法是:

1) 得到原始三角形顶点位置(原始特征点,图中红色点)

2) 得到需要形变后的三角形顶点位置(形变特征点,图中蓝色点)

3) 通过设置vertices和textureCoordinates来调整三角形(vertices对应新地形变后特征点,textureCoordinates对应原图的原始特征点)

4) 通过OpenGL绘制相应的三角形,即得到形变后的图像

这里需要单独设置的内容相比简单形变要复杂一些,需要同时设置多组vertices和textureCoordinates,并且在绘制三角形时也应该绘制GL_TRIANGLES而非GL_TRIANGLE_STRIP,这是因为很难得到连续顶点的三角形数组。具体代码如下:

这里mVertex和mFragment都是nTriangles32个值(nTriangle个三角形,每个三角形3个顶点,每个顶点2个float值)

另外需要注意的是三角形划分,必须保证一个固定不变的区域内所有面积都要有所覆盖,否则会形成空洞(对于上图的例子,需要在最外围设置一个正方形,保持正方形的4条边不动的情况下,调整正方形区域内的顶点,从而可以达到形变后的图像任然连续这一个结果)。

如上图所示,需要外框点保持不变,通过调节内部的0~98号点(内部点),来实现形变后的图像与原图保持连续。

Part4:基于网格形变的自定义vertices全局图像形变设计

对于Part3中的自定义顶点的方法来实现图像形变而言,需要确定三角形的具体分割,并且仅支持线性的位置调整,对于非线性的位置调整(比如大眼,越离眼睛中心形变越大)则支持能力较弱,这时候就需要使用这里的基于网格形变的自定义vertices全局图像形变方法来进行图像形变了。

这种方法的本质思想是:对于图片上的每一个像素,手动计算出该像素在新的图片中的位置,并且将该像素值填充至该位置。然而,单独计算每一个像素点的位置需要大量的计算资源,无法达到实时处理的性能,为此,通过对图片进行分块,每一块都是一个小三角形。通过对小三角形顶点的位置调整,来大致近似每一个点的位置移动,从而便于OpenGL进行渲染。具体的分块示意图如下所示:

从上图可以看出,当分块足够多时,变相相当于逐像素计算新的位置;而当位置足够少时(比如只有1*1的顶点),则退化为普通的顶点坐标变换。

那么,具体应该如何计算每一个点在新图像中的位置呢?这里给出常用的2种方法:

1) MLS方法:利用论文《Image Deformation Using Moving Least Squares》中的方法,当已知某些点在新图中的新位置之后(锚定点),对于每个像素点,可以依据该像素点与锚定点之间的关系,计算得到该像素点在新图像中的位置,从而达到形变的目的。下图是MLS算法的一个示例:

2) 基于规则的点位置计算:也是最传统的点计算方法。该方法通过设定一些具体的规则(比如,某个像素A的邻域内点往方向v移动x个像素,则对于任意一个像素点,判断它与A之间的关系,如果落在A的邻域内,则往v方向移动x个像素)。这些规则可以很简单(移动、扩大、收缩),也可以很复杂(按指定路径移动,非线性移动),从而可以组合出各种效果。比如Part3中的瘦脸,也可以对脸部轮廓的像素进行移动来实现近似的效果。具体的效果如下图所示,左边是原图,右边是每个网格点移动后形变产生的图片。

上面两种是比较常用的点移动方法。对于点位置的计算,可以放在CPU端进行,再将计算结果赋值给vertices数组,也可以直接传默认的vertices数组,将点位置的计算交给GPU来做。如果通过GPU来计算点的位置,可以获得GPU的并行加速能力,缺点是需要传输额外的数据给GPU(MLS算法需要传点的映射关系;规则方法需要传规则本身),因此各有优缺点。这里举例通过GPU来计算MLS算法中的点位置的变化:

小结:

GPUImage提供了很方便的接口供我们来对图像进行形变的操作。对于简单的形变,可以通过自定义vertices数组来实现,也可以通过改写FragmentShader来实现;对于复杂的形变,可以同时自定义vertices和textureCoordinates数组来通过自定义贴三角形的方式来实现,也可以通过将图像分割成网格状,再绘制每一个小三角形的方式来实现。

下面是各种方式的时间复杂度以及代码复杂度:(假设图像宽度w,高度h)

GPU受限:指传输到GPU的用于计算的数据太大,部分GPU可能无法支持