写点什么

Core Image:iOS 图像处理技术追踪

2020 年 8 月 09 日

Core Image:iOS图像处理技术追踪

Core Image是苹果官方提供的图像处理框架,通过丰富的built-in(内置)或自定义Filter(过滤器)高效处理静态图片、动态图片或视频。开发者还可以通过构造Filter链或自定义Core Image Kernel来实现更丰富的效果。

在WWDC20中,苹果官方针对Core Image技术在以下三方面做了优化:Core Image对视频/动图的支持、基于Metal构建Core Image (CI) Kernel以及Core Image的Debug支持。

这三方面会在下文逐一提到,文末笔者也会浅谈Core Image在手淘图片库中的应用可能以及对Core Image技术的展望。


优化 Core Image 对视频/动图的支持


创建 CIContext


创建 CIContext 时,需要遵循一个 view 一个 context 的原则。由于视频的每一帧都会发生变化,将 CIContext 的 cacheIntermediates 属性设置为 false 可以大大减少内存消耗。


如果在使用 Core Image 时将同时运用 Metal(作为输入或输出),通过设置 MTLCommandQueue 属性创建 CIContext 将会是较好选择。在不使用 MTLCommandQueue 的情况下,每一个 Metal 或 CoreImage 执行的任务都在不同队列中并以 wait 命令分隔开,导致任务执行效率低。通过设置 MTLCommandQueue 创建的 CIContext 和相应的 Metal 任务在同一队列中,能提高 app 的运行效率。



图一:不使用 MTLCommandQueue 的工作流程



图二:使用 MTLCommandQueue 的工作流程


编写 Core Image Kernel(在 Metal 中实现)


为了将效果处理得更丰富,通过 Metal 来实现自定义 CI Kernel 是个高效的选择。苹果官方提供了的许多方便部署的内置工具(都通过 Metal 实现),如内置 CI 滤镜。通过 Metal 实现自定义 CI Kernel,不仅 app 的 runtime 编译时间将会大大减少(这段工作会移至 app 构建完成后进行),开发者还能获得高性能语言特性(如 gather-reads、group-writes、半精度浮点数)、高效开发体验(如缩进检查、缩进高光)等功能。


选择合适的 View 类


如果要对视频/动图应用特效,静态内容 View 如 UIImageView 或 NSImageView 应当被避免。AVPlayerView 和 MetalKit View(MTKView)是个两个不错的选择。前者为简单选择,后者为进阶选择。


使用 AVPlayerView 时,需要创建 AVMutableVideoComposition 对象,CI 滤镜在 block 中执行图像处理任务。在进行断点 debug 时,通过点击 CIImage 对象地址右侧的眼睛图示可以浏览 CI 滤镜处理流程的详细信息。


官方提供的案例中,Core Image 还将 10 位的 HDR 视频帧数据自动从 HLG 转化成了 Core Image working space。



图三:CI Image 断点测试中展现的处理流程


使用 MTKView 时,开发者需要以 frame 和 device 作为参数重载 init 方法。VIew 对应的 CIContext 也将在 init 函数中被创建。如果我们在 macOS 中开发支持 HDR 的 view,color-Pixel-Format 属性需要被设定为 rgba16Float,wants-Extended-Dynamic-Range-Content 属性需要被设定为 true。设定完 init 方法后,开发者需要实现 draw-in view 方法。需要注意的是,此处并未直接将 Metal 材质传入 CIRenderDestination 函数,而是创建了一个会返回 texture 的 block。这使得 CIContext 能在前面的帧尚未完成时将 Metal 工作入队。之后该方法会执行渲染任务(至指定目的地)并创建 command buffer 将当前绘制结果渲染至 view。


本人也亲自尝试了通过 Core Image 处理视频的整个流程。以下案例使用 CIVortexDistortion 滤镜对视频进行逐帧处理并渲染,展示内容包含核心代码、原视频、CI 滤镜处理后视频以及断点测试的滤镜逐帧处理图示。


let filepath: String? = Bundle.main.path(forResource: "test_video", ofType: "MOV")let fileURL = URL.init(fileURLWithPath: filepath!)let asset = AVAsset(url: fileURL)let item = AVPlayerItem(asset: asset)item.videoComposition = AVMutableVideoComposition(asset: asset) { request in    let filter = CIFilter(name: "CIVortexDistortion")    filter?.setValue(request.sourceImage, forKey: kCIInputImageKey)    filter?.setValue(NSNumber(400), forKey: "inputAngle")    filter?.setValue(NSNumber(1200), forKey: "inputRadius")    filter?.setValue(CIVector(x: 700, y: 400), forKey: "inputCenter")    let out = filter?.outputImage    request.finish(with: out ?? request.sourceImage, context: nil)}avPlayer = AVPlayer(playerItem: item)
复制代码


测试核心代码


http://mpvideo.qpic.cn/0bf2pyaamaaapeahvnrdnbpva7wdaz7aabqa.f10002.mp4?dis_k=0f04353de60ce504c1ffc8be4dd1484b&dis_t=1596367310


原视频(笔者本人取材)


http://mpvideo.qpic.cn/0b78siaacaaa3mah22zdnrpvbewdagjaaaia.f10002.mp4?dis_k=fc45165fd0e34a1f15afa120e5501099&dis_t=1596367310


添加 CIVortexDistortion 滤镜后的视频



图四:断点调试时 Core Image 对每帧的处理流程


基于 Metal 构建 Core Image Kernel


使用 CI Kernel 有诸多优势,包括上文提及的缩短 runtime 编译时间、高性能语言特性(如 gather-reads、group-writes、半精度浮点数)、高效开发体验(如缩进检查、缩进高光)。基于 Metal 构建 CI Kernel 有 5 步流程,会在下文进行逐一介绍。


在项目中增加自定义构建规则


苹果官方推荐在项目 target 中增加两项自定义构建规则。第一个构建规则针对以“.ci.metal”为后缀名的文件。该构建规则会创建一个以“.ci.air”为后缀名的二进制输出文件。



图五:针对“*.ci.metal”文件的构建规则


第二个构建规则针对以“.ci.air”为后缀名的文件(上一个构建规则的输出结果)。该构建规则会在 app 的资源文件夹内创建以“.ci.metallib”为后缀名的输出文件。



图六:针对“*.ci.air”文件的构建规则


在项目中增加.ci.metal 资源


在 Xcode 提供的创建面板中选择 Metal File 即可。开发者对 Metal File 进行命名时需要以“.ci”作为后缀名,这样项目中新生成的文件会以“.ci.metal”作为后缀名。


编写 Metal Kernel


便携 Metal Kernel 需要 include CoreImage.h 头文件,用来使用 Metal 和 Core Image 提供的各种类。官方提供的范例编写了一个 CIColorKernel,输入参数为 coreimage::samle_t 对象(表示输入图片的一个像素)、time 和 coreimage::destination 对象,返回 float4 像素。



图七:苹果官方提供的代码范例:Metal Kernel 编写


苹果官方为开发者提供了描述 CI Kernel 中 Metal Shader 语言的文档,详情见「 Metal Shading Language for Core Image Kernels」① 。


加载 Kernel 并应用于新图像(基于 Swift)


Kernel 会被 CI 滤镜的子类使用。苹果官方推荐开发者在实例化滤镜的 CIKernel 对象时使用静态属性(static property),这种情况下加载 metallib 资源的工作仅会执行一次(在首次需要时)。CI 滤镜的子类也必须重载输出图片的属性,Kernel 将在 getter 中进行图像处理并创建新图像。



图八:苹果官方提供的代码范例:Kernel 加载与使用


Core Image 的 Debug 支持


苹果官方在 WWDC20 详细介绍了 Debug 特性:CI_PRINT_TREE。


什么是 CI_PRINT_TREE


CI_PRINT_TREE 的基础框架与 Xcode 提供的 Core Image Quick Look 支持相同。Core Image Quick Look 为开发者提供了快捷可视化的 Core Image 图片(详见上文图三),而 CI_PRINT_TREE 支持几种不同的模式和选项用来查看 Core Image 如何优化和渲染图像。


如何启用 CI_PRINT_TREE


苹果官方提供了 CI_PRINT_TREE 的两种启动方式。最常用的方法是编辑 Xcode target scheme,在 Arugments 窗体下的环境变量列表中加入 CI_PRINT_TREE 并设置值。另一种方法是在 Terminal.app 中通过命令行启动 CI_PRINT_TREE(需要在执行应用程序前设定)。




图九:启用 CI_PRINT_TREE 的两种方式


如何控制 CI_PRINT_TREE


CI_PRINT_TREE 的字符串格式为“<graph type> <output type> <options>”


  • graph type: 表示Core Image render的若干stage,包括type-1初始图像(有助于查看被使用的色彩空间)、type-2优化后的图像(有助于查看core image对render的优化效果)、type-4级联图像(有助于查看各stage如何级联于GPU程序,以便了解render需要多少中间缓存)以及type-7(输出图像type1、2和4)。



图十:苹果官方对 graph type 四个 stage 的描述


  • output type: 输出格式可以是pdf或png。在macOS上trees会被存储在临时项目文件夹,在iOS上trees会被存储在文档(Documents)目录下。如果output type没有确定,core image会把tree以紧凑文本格式输出在标准输出(stdout)。通过设置CI_LOG_FILE=“oslog”,文本也可以前往Console.app(在iOS开发中更为方便)。

  • options: 对于CI_PRINT_TREE,开发者可以设定额外的选项。如通过设定context==name来限制输出(仅输出名字相同的context),或是通过设定frame-n来框定具体输出context的哪一帧。更多option及详情请见图十一。设定option对debug能提供很大帮助,但也需谨慎使用,因为生产这些文件需要额外的时间和内存。



图十一:苹果官方提供的 option



图十二:type 设定为 7 时 tmp 文件夹下的文件


如何获得 CI_PRINT_TREE 文件


在 macOS 中,开发者只需要进入“/tmp”文件夹就能找到生成的 CI_PRINT_TREE 文件。需要注意的是沙盒应用会使用特有的临时存储文件夹。


在 iOS 中,开发者需要将 Custom iOS Target Properties 中的“Application supports iTunes file sharing”项设为 YES(图十三)。这样生成的 CI_PRINT_TREE 文件可以在连接中的 iOS 设备上被找到并拖拽至 macOS 存储中。



图十三:Custom iOS Target Properties 中进行设置


如何解释 CI_PRINT_TREE 文件


读 CI_PRINT_TREE 时,需要遵循以下规则:


  • 输入在底层,输出在顶层

  • 绿色节点代表卷曲内核(warp kernel),红色节点代表颜色内核(color kernel)



图十四:绿色节点与红色节点示例


  • 在树的初始位置(initial tree)很容易找到颜色搭配节点(colormatch nodes),里面记录了搭配前后的色彩空间名称。苹果官方提供的案例为ITUR_2100_HLG_to_workingspace,即HLG色彩空间转化为Core Image线性色彩空间。



图十五:苹果官方案例中 initial tree 对色彩空间的描述


  • 每个节点会显示Region of Interest(ROI),表示该节点在render中被使用的范围。


如果开发者在 CI_PRINT_TREE 控制字符串中选择 type-4 并在 option 中设定 dump-intermediates,产生的级联图片会展示中间缓存的每一次 pass(除了 output pass)及其耗时、像素点数量和像素点格式(用来查找耗时大、占内存大的 pass)。这对 render 内追踪错误非常有帮助。如果树中没有展示中间图,那么说明这张图在先前渲染的时候已被缓存,因此 Core Image 没有渲染它的必要。



图十六:设定 dump-intermediates 的 debug 效果展示


Core Image 在手淘图片库中的应用可能


手淘图片库中的 CDN 图片适配处理库(TBCDNImage)的核心目的是为不同终端设备、网络环境下的图片展示提供最优解。目前考虑的维度主要是终端设备硬件和网络状态,考虑的参数则是图片尺寸、压缩比率、锐化等图片属性。随着苹果在 Core Image、端智能(CoreML)、硬件支持(自研芯片)等方面进行技术提升,手淘的 CDN 图片适配处理库可以考虑增加“图片内容”作为新的维度,增加亮度、对比度、滤镜、图片种类等新参数。以下为部分应用场景:


  • 识别亮度较暗的图片,提升亮度做CDN图片适配处理

  • 判断图片内容种类(如美食),根据不同内容种类的图片增加适合的滤镜做CDN图片适配处理

  • 根据移动终端设备屏幕亮度(或深/浅色模式)修改图片色调做CDN图片适配处理,达到护眼效果


对 Core Image 技术的展望


总结全文,WWDC20 对 Core Image 技术的提升主要在三方面:


  • 优化CI对视频/动图的支持,包括开发流程简化、逐帧处理性能提升等。

  • 允许开发者更自由的构建Core Image Kernel,使CI的特效处理更加丰富

  • 针对CI开发流程提供更高效的Debug支持


随着苹果未来自研芯片的底层硬件支持将提供视频流流畅的逐帧处理与渲染。笔者认为 Core Image 技术将会在以下场景有较大应用价值:


  • 直播滤镜/特效功能原生化(摆脱自研或第三方API),实现质量更高的实时滤镜渲染

  • 视频拍摄增加滤镜功能(如淘宝或咸鱼的商品视频录制)


参考:


https://developer.apple.com/metal/MetalCIKLReference6.pdf


https://github.com/duzhaoquan/ImagesVideoFilters


本文转载自公众号淘系技术(ID:AlibabaMTT)。


原文链接


https://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650409336&idx=1&sn=f630ee519d7b9fe89807f06484e614b9&chksm=8396c160b4e14876c10c3fd7ac34935582b64f398cb1fd46522bc2ad7c44b06a1602a6f07eb5&scene=27#wechat_redirect


2020 年 8 月 09 日 10:00871

评论

发布
暂无评论
发现更多内容

专科程序员与本科程序员之间有什么区别?薪资待遇又差多少?

码农月半

spring 程序员 程序员人生 Java 面试 程序员成长

架构师训练营 -week5 命题作业

J.Spring

极客大学架构师训练营

授权专利争夺正当时

CECBC区块链专委会

数据隐私 授权专利 平台应用服务

一致性Hash算法——架构师训练营第5周

架构 极客大学架构师训练营 一致性Hash算法 第5周作业 负载均衡算法

ConcurrentHashMap里面也有死循环

无予且行

Java jdk Java 面试 jdk8

ARTS|Week 6 合并有序列表、团队、MIME类型和IIS

Puran

LeetCode ARTS 打卡计划

面试官:既然CPU有MESI,为什么 JMM 还需要volatile关键字?

犬来八荒

Java JVM 硬件 java面试

区块链+金融赋能高原特色农业重点产业

CECBC区块链专委会

打破信息孤岛 区块链+咖啡 特色农业 咖云链

第五周作业

秦宝齐

学习

第一个Spring程序(代码篇)

JavaPub

spring

极客大学架构师训练营 系统架构 消息队列 数据库备份 第10课 听课总结

John(易筋)

负载均衡 极客时间 极客大学 极客大学架构师训练营 消息队列

【思考】互联网厂商争夺企业市场

superman

企业中台 互联网

spring 那点事儿——让你少走弯路

爱java爱自己

Spring Cloud Spring Boot

游戏夜读 | 简单认识一下爬虫

game1night

一文搞懂分布式消息中间件设计

小隐乐乐

消息队列

Git【入门】这一篇就够了

JavaPub

spring

一篇告诉你什么是Spring

JavaPub

spring

LeetCode | 7. Merge Two Sorted Lists 合并两个有序列表

Puran

Python C# 算法 LeetCode

你真的理解透彻高并发了吗?来看看架构师眼里的高并发

小谈

Java 面试 高并发 高并发系统设计

如何站在架构师的角度做框架

小新

Java 集合 框架

面试中必问的JVM应该怎么学(面试题含答案)

猿灯塔

PHP实现一致性哈希算法

任小龙

解读 java 并发队列 BlockingQueue

猿灯塔

Java

在Windows上使用IIS来托管站点

Puran

windows IIS Server

80%会问到的18个Dubbo面试题,快来看看你都掌握了吗

小新

Java 程序员 架构 面试 dubbo

总结

Mr.Monkey

架构师训练营第五周学习总结

张明森

Java架构-Apache POI Excel

猿灯塔

唯一路径的动态规划解法,阿里巴巴架构演化路径 John 易筋 ARTS 打卡 Week 07

John(易筋)

动态规划 ARTS 打卡计划 系统架构演化 唯一路径

Python设计模式 单例模式

早睡蟒

Python 面向对象 设计模式 单例模式

ARTS Week6

丽子

飞猪Flutter技术演进及业务改造的实践与思考

飞猪Flutter技术演进及业务改造的实践与思考

Core Image:iOS图像处理技术追踪-InfoQ