Core Image框架详细解析(五) —— Processing Images处理图像(二)

版本记录

版本号 时间
V1.0 2018.01.27

前言

Core Image是IOS5中新加入的一个框架,里面提供了强大高效的图像处理功能,用来对基于像素的图像进行操作与分析。还提供了很多强大的滤镜,可以实现你想要的效果,下面我们就一起解析一下这个框架。感兴趣的可以参考上面几篇。
1. Core Image框架详细解析(一) —— 基本概览
2. Core Image框架详细解析(二) —— Core Image滤波器参考
3. Core Image框架详细解析(三) —— 关于Core Image
4. Core Image框架详细解析(四) —— Processing Images处理图像(一)

Integrating with Other Frameworks - 与其他框架集成

Core Image可与iOS,macOS和tvOS中的其他几项技术进行互操作。 由于这种紧密集成,您可以使用Core Image轻松地将视觉效果添加到应用程序用户界面中的游戏,视频或图像,而无需构建复杂的渲染代码。 以下部分介绍了在应用程序中使用Core Image的几种常用方法,以及便利性系统框架为每个方法提供的内容。

1. Processing Still Images in UIKit and AppKit - 处理UIKit和AppKit中的静止图像

UIKit和AppKit提供了简单的方法来添加Core Image处理静止图像,无论这些图像出现在您的应用程序的用户界面或是其工作流程的一部分。 例如:

  • 一个旅游应用程序可能会列出目的地的stock摄影,然后对这些图像应用过滤器,为每个目的地的详细信息页面创建一个微妙的背景。
  • 社交应用可以将过滤器应用于用户头像图片以指示每个帖子的心情。
  • 一个摄影应用程序可能允许用户在捕捉时使用过滤器自定义图像,或者提供一个照片应用程序扩展,用于为用户照片库中的照片添加效果(请参阅App Extension Programming Guide中的Photo Editing)。

Note: Don’t use Core Image to create blur effects that are part of a user interface design (like those seen in the translucent sidebars, toolbars, and backgrounds of the macOS, iOS, and tvOS system interfaces). Instead, see the NSVisualEffectView (macOS) or UIVisualEffectView (iOS/tvOS) classes, which automatically match the system appearance and provide efficient real-time rendering. 注意:不要使用Core Image创建模糊效果,这些效果是用户界面设计的一部分(如在MacOS,iOS和tvOS系统界面的半透明侧边栏,工具栏和背景中所见)。 相反,请参阅NSVisualEffectView(macOS)或UIVisualEffectView(iOS / tvOS)类,它们自动匹配系统外观并提供高效的实时渲染。

在iOS和tvOS中,您可以在使用UIImage对象的任何位置应用Core Image过滤器。 Listing1-3显示了一个使用带有图像视图的过滤器的简单方法

// Listing 1-3  Applying a filter to an image view (iOS/tvOS)

class ViewController: UIViewController {
    let filter = CIFilter(name: "CISepiaTone",
                          withInputParameters: [kCIInputIntensityKey: 0.5])!
    @IBOutlet var imageView: UIImageView!
    
    func displayFilteredImage(image: UIImage) {
        // Create a Core Image image object for the input image.
        let inputImage = CIImage(image: image)!
        // Set that image as the filter's input image parameter.
        filter.setValue(inputImage, forKey: kCIInputImageKey)
        // Get a UIImage representation of the filter's output and display it.
        imageView.image = UIImage(CIImage: filter.outputImage!)
    }
}

在macOS中,使用initWithBitmapImageRep:方法从位图图像和NSCIImageRep类创建CIImage对象来创建可以在任何支持NSImage对象的地方使用的图像。

2. Processing Video with AV Foundation - 和AV Foundation一起处理视频

AVFoundation框架提供了许多用于处理视频和音频内容的高级实用程序。 其中包括AVVideoComposition类,您可以使用它来将视频和音频轨道合并或编辑为单个演示文稿。 (有关合成的一般信息,请参阅AVFoundation Programming Guide中的Editing。)在播放或导出期间,您可以使用AVVideoComposition对象将Core Image滤镜应用于视频的每一帧,如Listing1-4所示

// Listing 1-4  Applying a filter to a video composition

let filter = CIFilter(name: "CIGaussianBlur")!
let composition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in
    
    // Clamp to avoid blurring transparent pixels at the image edges
    let source = request.sourceImage.clampingToExtent()
    filter.setValue(source, forKey: kCIInputImageKey)
    
    // Vary filter parameters based on video timing
    let seconds = CMTimeGetSeconds(request.compositionTime)
    filter.setValue(seconds * 10.0, forKey: kCIInputRadiusKey)
    
    // Crop the blurred output to the bounds of the original image
    let output = filter.outputImage!.cropping(to: request.sourceImage.extent)
    
    // Provide the filter output to the composition
    request.finish(with: output, context: nil)
})

当您使用videoCompositionWithAsset:applyingCIFiltersWithHandler:初始化器创建一个composition,您提供了一个负责将滤镜应用于每个视频帧的处理程序handler。 AVFoundation会在播放或导出过程中自动调用您的处理程序。 在处理程序中,首先使用提供的AVAsynchronousCIImageFilteringRequest对象来检索要过滤的视频帧(以及补充信息,如帧时间),然后提供过滤后的图像供composition使用。

要使用创建的视频composition进行回放,请使用与composition来源相同的素材资源创建AVPlayerItem对象,然后将composition指定给播放器项目的videoComposition属性。 要将合成输出到新的电影文件,请从相同的素材资源创建一个AVAssetExportSession对象,然后将composition分配给导出会话的videoComposition属性。

注意:提示:Listing 1-4还显示了另一个有用的Core Image技术。 默认情况下,模糊滤镜还会通过将图像像素与(在滤镜的图像处理空间中)的图像周围的透明像素一起模糊来柔化图像的边缘。 在某些情况下,例如在过滤视频时,这种效果可能是不理想的。为了避免这种效果,可以使用imageByClampingToExtent方法(或CIAffineClamp滤镜)在模糊之前在所有方向无限扩展图像的边缘像素。 Clamping创建不确定大小的图像,所以你也应该在模糊后裁剪图像。

3. Processing Game Content with SpriteKit and SceneKit - 与SpriteKit和SceneKit一起处理游戏内容

SpriteKit是一款用于构建2D游戏和其他类型的应用程序的技术,这些应用程序具有高度动态的动画内容。 SceneKit用于处理3D资源,渲染和动画3D场景,以及构建3D游戏。 (有关每种技术的更多信息,请参阅SpriteKit Programming GuideSceneKit Framework Reference。)这两种框架都提供了高性能的实时渲染,可以轻松地将Core Image处理添加到全部或部分场景。

在SpriteKit中,您可以使用SKEffectNode类添加Core Image过滤器。 要查看正在使用的此类的示例,请使用Game template(适用于iOS或tvOS)创建一个新的Xcode项目,选择SpriteKit作为游戏技术,并修改GameScene类中的touchesBegan:withEvent:方法,代码如Listing 1-5中所列。 (对于macOSGame template,您可以对mouseDown:方法进行类似的修改。)

// Listing 1-5  Applying filters in SpriteKit

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch in touches {
        let sprite = SKSpriteNode(imageNamed:"Spaceship")
        sprite.setScale(0.5)
        sprite.position = touch.location(in: self)
        sprite.run(.repeatForever(.rotate(byAngle: 1, duration:1)))
        
        let effect = SKEffectNode()
        effect.addChild(sprite)
        effect.shouldEnableEffects = true
        effect.filter = CIFilter(name: "CIPixellate",
                                 withInputParameters: [kCIInputScaleKey: 20.0])
        
        self.addChild(effect)
    }
}

请注意,SKScene类本身是SKEffectNode的子类,因此您也可以将Core Image过滤器应用于整个SpriteKit场景。

在SceneKit中,SCNNode类的filters属性可以将Core Image滤镜应用到3D场景的任何元素。 若要查看此属性,请使用Game template(iOS,tvOS或macOS)创建一个新的Xcode项目,选择SceneKit作为游戏技术,并修改GameViewController类中的viewDidLoad方法,以使用Listing 1-6。

// Listing 1-6  Applying filters in SceneKit

// Find this line in the template code:
let ship = rootNode.childNode(withName: "ship", recursively: true)!
 
// Add these lines after it:
let pixellate = CIFilter(name: "CIPixellate",
                         withInputParameters: [kCIInputScaleKey: 20.0])!
ship.filters = [ pixellate ]

您还可以在SceneKit节点上动画滤波器效果,有关详细信息,请参阅filters属性的参考文档。

在SpriteKit和SceneKit中,您都可以使用转换来添加视觉效果来更改视图的场景。 (请参阅SpriteKit的presentScene:transition:方法和SceneKit的presentScene:withTransition:incomingPointOfView:completionHandler:方法。)使用SKTransition类及其 transitionWithCIFilter:duration:初始化器从任何Core Image transition过滤器创建过渡动画。

4. Processing Core Animation Layers (macOS) - 处理核心动画层(macOS)

在macOS中,可以使用filters属性将过滤器应用于任何CALayer支持的视图的内容,并添加随时间变化的过滤器参数的动画。 请参阅Core Animation Programming Guide中的Filters Add Visual Effects to OS X ViewsAdvanced Animation Tricks


Building Your Own Workflow with a Core Image Context - 用Core Image上下文构建自己的工作流程

当您使用上一节中列出的技术应用Core Image过滤器时,这些框架会自动管理Core Image用于处理图像和渲染结果以供显示的底层资源。这种方法既可以最大限度地提高这些工作流程的性能,又可以使其更容易设置。但是,在某些情况下,使用CIContext类自己管理这些资源更为谨慎。通过直接管理Core Image上下文,您可以精确地控制应用程序的性能特征,或将Core Image与更低级别的渲染技术集成。

Core Image上下文表示执行过滤器和生成图像所需的CPU或GPU计算技术,资源和设置。有几种上下文可用,所以您应该选择最适合您的应用程序的工作流程以及您可能正在使用的其他技术的选项。以下各节讨论一些常见的情况。有关完整的选项集,请参阅CIContext Class Reference

Important: A Core Image context is a heavyweight object managing a large amount of resources and state. Repeatedly creating and destroying contexts has a large performance cost, so if you plan to perform multiple image processing operations, create a context early on and store it for future reuse. 重要提示:Core Image上下文是管理大量资源和状态的重量级对象。 反复创建和销毁上下文具有较高的性能成本,因此如果您计划执行多个图像处理操作,请尽早创建上下文并将其存储起来以供将来重用。

1. Rendering with an Automatic Context - 使用自动上下文进行渲染

如果您对应用程序如何与其他图形技术的互操作性没有限制,则创建Core Image上下文很简单:只需使用基本的initinitWithOptions:初始化工具。 当您这样做时,Core Image将自动管理内部资源,根据当前设备和您指定的任何选项,选择合适的或最佳可用的CPU或GPU渲染技术。 这种方法非常适用于渲染处理后的图像以输出到文件(例如,使用writeJPEGRepresentationOfImage:toURL:colorSpace:options:error:方法)的任务。

Note: A context without an explicitly specified rendering destination cannot use the drawImage:inRect:fromRect: method, because that method’s behavior changes depending on the rendering destination in use. Instead, use the CIContext methods whose names begin with render or create to specify an explicit destination. 注意:没有显式指定渲染目标的上下文无法使用drawImage:inRect:fromRect:方法,因为该方法的行为根据使用的渲染目标而改变。 相反,使用名称以render或create开头的CIContext方法指定一个显式的目的地。

如果您打算实时渲染Core Image结果(即动画化滤镜参数中的更改,产生动画过渡效果,或者处理视频或其他每秒渲染多次的视觉内容),请谨慎使用此方法。 即使使用此方法创建的CIContext对象可以使用GPU自动渲染,但呈现渲染结果可能会涉及CPU和GPU内存之间昂贵的复制操作。

2. Real-Time Rendering with Metal - Metal的实时渲染

Metal框架提供对GPU的低开销访问,为图形渲染和并行计算工作流程提供了高性能。 这样的工作流程对于图像处理是不可或缺的,所以Core Image尽可能地建立在Metal上。 如果您正在构建使用Metal渲染图形的应用程序,或者如果要利用Metal实现动画过滤器输出或过滤动画输入(如实况视频)的实时性能,请使用Metal设备创建Core Image上下文。

Listing 1-7和Listing 1-8显示了使用MetalKit视图(MTKView)呈现Core Image输出的示例。 (重要的步骤在每个列表中都有编号并在后面进行描述。

// Listing 1-7  Setting up a Metal view for Core Image rendering

class ViewController: UIViewController, MTKViewDelegate {  // 1
    
    // Metal resources
    var device: MTLDevice!
    var commandQueue: MTLCommandQueue!
    var sourceTexture: MTLTexture!                         // 2
    
    // Core Image resources
    var context: CIContext!
    let filter = CIFilter(name: "CIGaussianBlur")!
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        device = MTLCreateSystemDefaultDevice()            // 3
        commandQueue = device.newCommandQueue()
        
        let view = self.view as! MTKView                   // 4
        view.delegate = self
        view.device = device
        view.framebufferOnly = false
        
        context = CIContext(mtlDevice: device)             // 5
        
        // other setup
    }
}
    1. 这个例子使用iOS或tvOS的UIViewController子类。 要使用macOS,请改为NSViewController的子类。
    1. sourceTexture属性包含一个包含要由过滤器处理的图像的Metal纹理。 这个例子并没有显示加载纹理的内容,因为有很多方法来填充纹理,例如,你可以使用MTKTextureLoader类加载一个图像文件,或者使用纹理作为你自己早期渲染的输出。
    1. 创建渲染所需的Metal对象 - 表示要使用的GPU的MTLDevice对象,以及在该GPU上执行渲染和计算命令的命令队列。 (这个命令队列可以处理由Core Image编码的渲染或计算命令,以及来自您自己的任何额外渲染过程的命令)。
    1. 配置MetalKit视图。

Important: Always set the framebufferOnly property to NO when using a Metal view, layer, or texture as a Core Image rendering destination. 重要提示:在使用Metal视图,图层或纹理作为Core Image渲染目标时,始终将framebufferOnly属性设置为NO。

    1. 创建一个使用与视图相同的Metal设备的Core Image上下文。 通过共享Metal资源,Core Image可以处理纹理内容并渲染到视图中,而不需要将图像数据复制到单独的CPU或GPU内存缓冲区,也不需要执行成本。 CIContext对象创建起来非常昂贵,所以只做一次实例化,每次处理图像时并重复使用它。

每次视图需要显示时,MetalKit都会调用drawInMTKView:方法。 (默认情况下,MetalKit可能每秒钟调用这个方法多达60次,详细信息请参阅视图的preferredFramesPerSecond属性。)Listing 1-8显示了从Core Image上下文中渲染的方法的基本实现。

// Listing 1-8  Drawing with Core Image filters in a Metal view

public func draw(in view: MTKView) {
    if let currentDrawable = view.currentDrawable {              // 1
        let commandBuffer = commandQueue.commandBuffer()
        
        let inputImage = CIImage(mtlTexture: sourceTexture)!     // 2
        filter.setValue(inputImage, forKey: kCIInputImageKey)
        filter.setValue(20.0, forKey: kCIInputRadiusKey)
        
        context.render(filter.outputImage!,                      // 3
            to: currentDrawable.texture,
            commandBuffer: commandBuffer,
            bounds: inputImage.extent,
            colorSpace: colorSpace)
        
        commandBuffer.present(currentDrawable)                   // 4
        commandBuffer.commit()
    }
}
    1. 获取一个Metal可绘制纹理渲染成一个命令缓冲区来编码渲染命令。
    1. 配置过滤器的输入参数,包括来自Metal纹理的输入图像。 此示例使用常量参数,但请记住,此方法每秒最多运行60次,您可以使用此机会随时间改变滤波器参数以创建流畅的动画。
    1. 告诉Core Image上下文将过滤器输出渲染到视图的可绘制纹理中。 bounds参数告诉Core Image要绘制图像的哪个部分 - 本例使用输入图像的尺寸。
    1. 当命令缓冲区结束执行时,告诉Metal显示渲染的图像。

此示例仅显示了使用Metal进行Core Image渲染所需的最少代码。 在真实的应用程序中,您可能会在Core Image管理的之前或之后执行额外的渲染过程,或者将Core Image输出渲染为次要纹理,并在另一个渲染过程中使用该纹理。 有关使用Metal进行绘图的更多信息,请参阅Metal Programming Guide

3. Real-Time Rendering with OpenGL or OpenGL ES - 使用OpenGL或OpenGL ES进行实时渲染

Core Image还可以使用OpenGL(macOS)或OpenGL ES(iOS和tvOS)进行基于GPU的高性能渲染。 如果您需要支持Metal不能使用的旧硬件,或者要将Core Image集成到现有的OpenGL或OpenGL ES工作流程中,请使用此选项。

在任一情况下,使用imageWithTexture:size:flipped:colorSpace:初始化器从OpenGL或OpenGL ES纹理创建CIImage对象。 处理GPU内存中已有的图像数据可通过消除冗余复制操作来提高性能。

要在OpenGL或OpenGL ES中渲染Core Image输出,请使您的GL上下文处于当前状态并设置目标帧缓冲区,然后调用drawImage:inRect:fromRect:方法。

4. CPU-Based Rendering with Quartz 2D - Quartus 2D的基于CPU的渲染

如果您的App不需要实时性能并使用CoreGraphics绘制视图内容(例如,在UIKit或AppKit视图的drawRect:方法中),请使用contextWithCGContext:options:初始化器来创建的Core Image上下文,这个上下文与您已经用于其他绘图的Core Graphics上下文一起工作。 (在macOS中,改为使用当前NSGraphicsContext对象的CIContext属性。)有关CoreGraphics上下文的信息,请参阅Quartz 2D Programming Guide

后记

本篇已结束,后面更精彩~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容