Core Image編程指南翻譯二(圖像處理)

示例代碼下載

圖像處理

圖像處理意味著應(yīng)用濾鏡 - 圖像濾鏡是一個(gè)軟件,它逐個(gè)像素地檢查輸入圖像,并在算法上應(yīng)用一些效果以創(chuàng)建輸出圖像。在Core Image中,圖像處理依賴于CIFilter和CIImage類,它們描述了濾鏡及其輸入和輸出。要應(yīng)用濾鏡并顯示或?qū)С鼋Y(jié)果,您可以使用Core Image和其他系統(tǒng)框架之間的集成,或者使用CIContext類創(chuàng)建自己的渲染工作流。本章介紹了使用這些類來(lái)應(yīng)用濾鏡和呈現(xiàn)結(jié)果的關(guān)鍵概念。

概述

在應(yīng)用中,有許多方法可以使用Core Image進(jìn)行圖像處理。清單1-1顯示了一個(gè)基本示例,并提供了本章中進(jìn)一步說(shuō)明的地方。

清單1-1 將濾鏡應(yīng)用于圖像的基礎(chǔ)

import CoreImage
 
let context = CIContext()                                           // 1
 
let filter = CIFilter(name: "CISepiaTone")!                         // 2
filter.setValue(0.8, forKey: kCIInputIntensityKey)
let image = CIImage(contentsOfURL: myURL)                           // 3
filter.setValue(image, forKey: kCIInputImageKey)
let result = filter.outputImage!                                    // 4
let cgImage = context.createCGImage(result, from: result.extent)    // 5

這段代碼的作用:

  1. 創(chuàng)建一個(gè)CIContext對(duì)象(使用默認(rèn)選項(xiàng))。您并不總是需要自己的Core Image上下文 - 通常您可以與管理渲染的其他系統(tǒng)框架集成。通過(guò)創(chuàng)建自己的上下文,您可以更精確地控制渲染過(guò)程和渲染所涉及的資源。上下文是重量級(jí)對(duì)象,因此如果您創(chuàng)建了一個(gè),請(qǐng)盡早執(zhí)行,并在每次需要處理圖像時(shí)重復(fù)使用它。
  2. 實(shí)例化CIFilter表示要應(yīng)用的濾鏡的對(duì)象,并為其參數(shù)提供值。
  3. 創(chuàng)建CIImage表示要處理的圖像的對(duì)象,并將其作為輸入圖像參數(shù)提供給濾鏡。從URL讀取圖像數(shù)據(jù)只是創(chuàng)建圖像對(duì)象的眾多方法之一。
  4. 獲取CIImage表示濾鏡輸出的對(duì)象。此時(shí)濾鏡尚未執(zhí)行步驟 - 圖像對(duì)象是“配置”說(shuō)明如何使用指定濾鏡,參數(shù)和輸入圖像來(lái)創(chuàng)建圖像。Core Image僅在您請(qǐng)求渲染時(shí)執(zhí)行此“配置”。
  5. 將輸出圖像渲染為可以顯示或保存到文件的Core Graphics圖像。

圖像是濾鏡的輸入和輸出

Core Image濾鏡處理并生成Core Image圖像。一個(gè)CIImage實(shí)例是表示圖像的一個(gè)不可變的對(duì)象。這些對(duì)象不直接表示圖像位圖數(shù)據(jù) - 相反,CIImage對(duì)象是用于產(chǎn)生圖像的“配置”。一個(gè)配置可能要求從文件加載圖像; 另一個(gè)可能代表濾鏡或?yàn)V鏡的輸出。僅當(dāng)您請(qǐng)求渲染圖像以供顯示或輸出時(shí),Core Image才會(huì)執(zhí)行這些配置。

要應(yīng)濾鏡,請(qǐng)創(chuàng)建一個(gè)或多個(gè)CIImage表示要由濾鏡處理的圖像對(duì)象,并將它們分配給濾鏡的輸入?yún)?shù)(例如kCIInputImageKey)。您幾乎可以從任何圖像數(shù)據(jù)源創(chuàng)建Core Image圖像對(duì)象,包括:

  • 引用要加載的圖像文件的URL或NSData包含圖像文件數(shù)據(jù)的對(duì)象
  • Quartz2D,UIKit中,或者AppKit圖像表示(CGImageRef,UIImage,或NSBitmapImageRep對(duì)象)
  • Metal,OpenGL或OpenGL ES紋理
  • CoreVideo圖像或像素緩沖區(qū)(CVImageBufferRef或CVPixelBufferRef)
  • IOSurfaceRef 在進(jìn)程之間共享圖像數(shù)據(jù)的對(duì)象
  • 內(nèi)存中的圖像位圖數(shù)據(jù)(指向此類數(shù)據(jù)的指針,或CIImageProvider按需提供數(shù)據(jù)的對(duì)象)

有關(guān)創(chuàng)建CIImage對(duì)象的完整方法列表,請(qǐng)參閱CIImage類參考。

因?yàn)镃IImage對(duì)象描述了如何生成圖像(而不是包含圖像數(shù)據(jù)),所以它也可以表示濾鏡的輸出。當(dāng)您訪問(wèn)CIFilter對(duì)象的outputImage屬性時(shí),Core Image僅識(shí)別并存儲(chǔ)執(zhí)行濾鏡所需的步驟。僅當(dāng)您請(qǐng)求渲染圖像以供顯示或輸出時(shí),才會(huì)執(zhí)行這些步驟。您可以使用其中一個(gè)CIContextrender或多個(gè)draw方法顯式請(qǐng)求渲染(請(qǐng)參閱使用Core Image Context構(gòu)建您自己的工作流),或者通過(guò)使用的眾多系統(tǒng)框架之一與Core Image一起工作顯示圖像來(lái)隱式請(qǐng)求渲染(請(qǐng)參閱其他框架集成)。

延遲處理直到渲染時(shí)使Core Image快速高效。在渲染時(shí),Core Image可以查看是否需要將多個(gè)濾鏡應(yīng)用于圖像。如果是這樣,它會(huì)自動(dòng)連接多個(gè)“配置”并組織它們以消除冗余操作,這樣每個(gè)像素只處理一次而不是多次。

濾鏡描述圖像處理效果

CIFilter該類的實(shí)例是表示圖像處理效果的可變對(duì)象以及控制該效果行為的任何參數(shù)。要使用濾鏡,可以創(chuàng)建一個(gè)CIFilter對(duì)象,設(shè)置其輸入?yún)?shù),然后訪問(wèn)其輸出圖像(請(qǐng)參閱下面的圖像是濾鏡的輸入和輸出)。filterWithName:使用系統(tǒng)已知的濾鏡名稱調(diào)用初始化程序以實(shí)例化濾鏡對(duì)象(請(qǐng)參閱查詢系統(tǒng)濾鏡或Core Image濾鏡參考)。

大多數(shù)濾鏡都有一個(gè)或多個(gè)輸入?yún)?shù),可讓您控制處理的完成方式。每個(gè)輸入?yún)?shù)都有一個(gè)屬性類,用于指定其數(shù)據(jù)類型,例如NSNumber。輸入?yún)?shù)可以選擇具有其他屬性,例如其默認(rèn)值,允許的最小值和最大值,參數(shù)的顯示名稱以及CIFilter類參考中描述的其他屬性。例如,CIColorMonochrome濾鏡有三個(gè)輸入?yún)?shù) - 要處理的圖像,單色和顏色強(qiáng)度。

濾鏡參數(shù)定義為鍵值對(duì); 要使用參數(shù),通常使用valueForKey:和setValue:forKey:鍵或基于鍵值編碼構(gòu)建的其他功能(例如Core Animation)。鍵是標(biāo)識(shí)屬性的常量,值是與鍵關(guān)聯(lián)的設(shè)置。Core Image屬性值通常使用屬性值數(shù)據(jù)類型中列出的數(shù)據(jù)類型之一。

表1-1 屬性值數(shù)據(jù)類型

數(shù)據(jù)類型 對(duì)象 描述
字符串 NSString 文本,通常用于顯示給用戶
浮點(diǎn)值 NSNumber 標(biāo)量值,例如強(qiáng)度級(jí)別或半徑
矢量 CIVector 一組浮點(diǎn)值,可指定位置,大小,矩形或未標(biāo)記的顏色分量值
顏色 CIColor 一組顏色組件值,標(biāo)記有指定如何解釋它們的顏色空間
圖片 CIImage 一個(gè)圖像; 請(qǐng)參閱圖像是濾鏡的輸入和輸出
變換 NSData, NSAffineTransform 要應(yīng)用于圖像的坐標(biāo)轉(zhuǎn)換

重要說(shuō)明: CIFilter對(duì)象是可變的,因此您無(wú)法在不同的線程之間安全地共享它們。每個(gè)線程都必須創(chuàng)建自己的CIFilter對(duì)象。但是,濾鏡的輸入和輸出CIImage對(duì)象是不可變的,因此可以安全地在線程之間傳遞。

用于復(fù)雜效果的鏈接濾鏡

每個(gè)Core Image濾鏡都會(huì)生成一個(gè)輸出CIImage對(duì)象,因此您可以將此對(duì)象用作另一個(gè)濾鏡的輸入。例如,圖1-1中所示的濾鏡序列將顏色效果應(yīng)用于圖像,然后添加發(fā)光效果,最后從結(jié)果中裁剪出一個(gè)部分。

圖1-1 通過(guò)連接濾鏡輸入和輸出構(gòu)建濾鏡鏈


image

Core Image優(yōu)化了此類濾鏡鏈的應(yīng)用,以快速有效地呈現(xiàn)結(jié)果。CIImage鏈中的每個(gè)對(duì)象都不是完全渲染的圖像,而只是用于渲染的“配置”。Core Image不需要單獨(dú)執(zhí)行每個(gè)濾鏡,浪費(fèi)時(shí)間和內(nèi)存渲染中間像素緩沖區(qū),這些緩沖區(qū)永遠(yuǎn)不會(huì)被看到。相反,Core Image將濾鏡組合到一個(gè)操作中,甚至可以在以不同順序應(yīng)用濾鏡時(shí)重新組織濾鏡,從而更有效地生成相同的結(jié)果。圖1-2顯示了圖1-1中示例濾鏡鏈的更準(zhǔn)確的再現(xiàn)。

圖1-2 Core Image將濾鏡鏈優(yōu)化為單個(gè)操作


image

請(qǐng)注意,在圖1-2中,裁剪操作已從最后移動(dòng)到第一個(gè)。該濾鏡導(dǎo)致原始圖像的大部分區(qū)域被裁剪出最終輸出。因此,無(wú)需對(duì)這些像素應(yīng)用顏色和銳化濾鏡。通過(guò)首先執(zhí)行裁剪,Core Image確保昂貴的圖像處理操作僅適用于最終輸出中可見(jiàn)的像素。

清單1-2顯示了如何設(shè)置如上所示的濾鏡鏈。

清單1-2 創(chuàng)建濾鏡鏈

func applyFilterChain(to image: CIImage) -> CIImage {
    // CIPhotoEffectInstant建濾鏡僅采用輸入圖像
    let colorFilter = CIFilter(name: "CIPhotoEffectProcess", withInputParameters:
        [kCIInputImageKey: image])!
    
    //將顏色濾鏡的結(jié)果傳遞給Bloom濾鏡并為發(fā)光效果設(shè)置其參數(shù)。
    let bloomImage = colorFilter.outputImage!.applyingFilter("CIBloom",
                                                             withInputParameters: [
                                                                kCIInputRadiusKey: 10.0,
                                                                kCIInputIntensityKey: 1.0
        ])
    
    // imageByCroppingToRect是一種方便的方法創(chuàng)建CICrop濾鏡并訪問(wèn)其outputImage。
    let cropRect = CGRect(x: 350, y: 350, width: 150, height: 150)
    let croppedImage = bloomImage.cropping(to: cropRect)
    
    return croppedImage
}

清單1-2還顯示了一些用于配置濾鏡和訪問(wèn)其結(jié)果的便捷方法。總之,您可以使用以下任何方法單獨(dú)或作為濾鏡鏈的一部分應(yīng)用濾鏡:

  • CIFilter使用filterWithName:初始化程序創(chuàng)建實(shí)例,使用setValue:forKey:方法設(shè)置參數(shù)(包括要處理的圖像kCIInputImageKey),并使用outputImage屬性訪問(wèn)輸出圖像。(見(jiàn)清單1-1。)
  • 使用初始化程序CIFilter在一次調(diào)用中創(chuàng)建實(shí)例并設(shè)置其參數(shù)(包括輸入圖像)filterWithName:withInputParameters:,然后使用該outputImage屬性訪問(wèn)輸出。(參見(jiàn)清單1-2中的colorFilter示例。)
  • CIFilter通過(guò)將imageByApplyingFilter:withInputParameters:方法用于CIImage對(duì)象,應(yīng)用濾鏡而不創(chuàng)建實(shí)例。(參見(jiàn)清單1-2中的bloomImage示例。)
  • 對(duì)于某些常用的濾鏡操作,例如裁剪和應(yīng)用坐標(biāo)變換,請(qǐng)使用CIImage中列出的其他實(shí)例方法通過(guò)修改現(xiàn)有圖像創(chuàng)建圖像。(參見(jiàn)清單1-2中的croppedImage示例。)

使用特殊濾鏡類型獲得更多選項(xiàng)

大多數(shù)內(nèi)置Core Image濾鏡都在主輸入圖像上運(yùn)行(可能還有影響處理的附加輸入圖像)并創(chuàng)建單個(gè)輸出圖像。但是,您可以使用其他幾種類型來(lái)創(chuàng)建有趣的效果,或者與其他濾鏡結(jié)合使用以生成更復(fù)雜的工作流程。

  • 一個(gè)合成(或混合)的濾鏡根據(jù)預(yù)先設(shè)定的公式組合兩個(gè)圖像。例如:

    • 述CISourceInCompositing濾鏡組合圖像,使得僅在是不透明的區(qū)域輸入圖像在輸出圖像中可見(jiàn)。
    • 所述CIMultiplyBlendMode濾鏡從兩個(gè)圖像乘以像素顏色,產(chǎn)生變暗的輸出圖像。

    有關(guān)合成濾鏡的完整列表,請(qǐng)查詢CICategoryCompositeOperation類別。

注意: 您可以通過(guò)對(duì)每個(gè)圖像應(yīng)用幾何調(diào)整來(lái)排列輸入圖像,然后再對(duì)其進(jìn) 請(qǐng)參閱CICategoryGeometryAdjustment濾鏡類別或imageByApplyingTransform:方法。

  • 一個(gè)濾鏡生成器不采取任何輸入圖像。相反,這些濾鏡使用其他輸入?yún)?shù)從頭開(kāi)始創(chuàng)建新圖像。這些生成器產(chǎn)生的輸出圖像可以單獨(dú)使用,也可以同其他濾鏡組合在濾鏡鏈中以產(chǎn)生更有趣的圖像。內(nèi)置Core Image濾鏡中的一些示例包括:

    • CIQRCodeGenerator和CICode128BarcodeGenerator等濾鏡生成對(duì)指定輸入數(shù)據(jù)進(jìn)行編碼的條形碼圖像。
    • 像CIConstantColorGenerator,CICheckerboardGenerator和CILinearGradient這樣的濾鏡可以從指定的顏色生成簡(jiǎn)單的過(guò)程圖像。您可以將這些與其他濾鏡結(jié)合使用以獲得有趣的效果 - 例如,CIRadialGradient濾鏡可以創(chuàng)建一個(gè)遮罩,以便與CIMaskedVariableBlur濾鏡一起使用。
    • 像CILenticularHaloGenerator和CISunbeamsGenerator這樣的濾鏡可以創(chuàng)建獨(dú)立的視覺(jué)效果 - 將這些濾鏡與合成濾鏡結(jié)合起來(lái),為圖像添加特殊效果。

    要查找生成器濾鏡,請(qǐng)查詢CICategoryGenerator和CICategoryGradient類別。

  • 一個(gè)縮減濾鏡操作在輸入圖像上,并不是在傳統(tǒng)意義上創(chuàng)建輸出圖像,而是輸出描述有關(guān)輸入圖像的信息。例如:

    • 所述CIAreaMaximum濾鏡輸出表示在圖像的指定區(qū)域中的最亮的所有像素的顏色的單個(gè)顏色值。
    • 所述CIAreaHistogram濾鏡輸出關(guān)于用于在圖像的指定區(qū)域中的每個(gè)強(qiáng)度值的像素的數(shù)量的信息。

    所有Core Image濾鏡都必須生成一個(gè)CIImage對(duì)象作為輸出,因此縮減濾鏡產(chǎn)生的信息仍然是圖像。但是,您通常不顯示這些圖像 - 而是從單像素或單行圖像中讀取顏色值,或?qū)⑵溆糜谄渌麨V鏡的輸入。
    有關(guān)縮減濾鏡的完整列表,請(qǐng)查詢CICategoryReduction類別。

  • 一個(gè)過(guò)渡濾鏡采用了兩個(gè)輸入的圖像和變量,輸出圖像為他們之間的一個(gè)特定的變化點(diǎn),該變量是時(shí)間,所以可以使用一個(gè)過(guò)渡濾鏡來(lái)創(chuàng)建一個(gè)的動(dòng)畫以一個(gè)圖像開(kāi)始,以另一個(gè)圖像結(jié)束,并且從一個(gè)到另一個(gè)使用有趣的視覺(jué)效果執(zhí)行。Core Image提供了幾個(gè)內(nèi)置的過(guò)渡濾鏡,包括:

    • CIDissolveTransition濾鏡產(chǎn)生一個(gè)簡(jiǎn)單的交叉疊,從一個(gè)圖像褪色到另一個(gè)圖像。
    • CICopyMachineTransition濾鏡模擬復(fù)印機(jī),滑動(dòng)明亮的光的條跨越一個(gè)圖像到另一個(gè)圖像。
      有關(guān)過(guò)渡圖像的完整列表,請(qǐng)查詢CICategoryTransition類別。

與其他框架集成

Core Image可與iOS,macOS和tvOS中的其他幾種技術(shù)進(jìn)行互操作。由于這種緊密集成,您可以使用Core Image輕松地在應(yīng)用程序的用戶界面中為游戲,視頻或圖像添加視覺(jué)效果,而無(wú)需構(gòu)建復(fù)雜的渲染代碼。以下部分介紹了在應(yīng)用程序中使用Core Image的幾種常用方法,以及為每種方法提供的便利系統(tǒng)框架。

在UIKit和AppKit中處理靜止圖像

UIKit和AppKit提供了簡(jiǎn)單的方法來(lái)向靜態(tài)圖像添加Core Image處理,無(wú)論這些圖像是出現(xiàn)在應(yīng)用程序的UI中還是其工作流程的一部分。例如:

  • 旅行應(yīng)用程序可能會(huì)在列表中顯示目的地的照片,然后對(duì)這些圖像應(yīng)用濾鏡,以便為每個(gè)目的地的詳細(xì)信息頁(yè)面創(chuàng)建微妙的背景。
  • 社交應(yīng)用可以對(duì)用戶頭像圖片應(yīng)用濾鏡以代表每個(gè)帖子的心情。
  • 一個(gè)攝影的應(yīng)用程序可能會(huì)允許用戶在拍攝定制具有濾鏡的圖像,或顯示的照片的應(yīng)用程序擴(kuò)展提供添加效果在用戶照片庫(kù)中的圖片(見(jiàn)應(yīng)用程序擴(kuò)展編程指南中的照片編輯)。

注意: 請(qǐng)勿使用Core Image創(chuàng)建作為用戶界面設(shè)計(jì)一部分的模糊效果(如半透明側(cè)邊欄,工具欄以及macOS,iOS和tvOS系統(tǒng)界面的背景中所見(jiàn))。相反,請(qǐng)參閱NSVisualEffectView(macOS)或UIVisualEffectView(iOS / tvOS)類,它們自動(dòng)匹配系統(tǒng)外觀并提供高效的實(shí)時(shí)渲染。

在iOS和tvOS中,您可以在使用UIImage對(duì)象的任何位置應(yīng)用Core Image濾鏡。清單1-3顯示了使用帶有圖像視圖的濾鏡的簡(jiǎn)單方法。

清單1-3 將濾鏡應(yīng)用于圖像視圖(iOS / tvOS)

class ViewController: UIViewController {
    let filter = CIFilter(name: "CISepiaTone",
                          withInputParameters: [kCIInputIntensityKey: 0.5])!
    @IBOutlet var imageView: UIImageView!
    
    func displayFilteredImage(image: UIImage) {
        // 使用輸入圖像創(chuàng)建一個(gè)Core Image對(duì)象
        let inputImage = CIImage(image: image)!
        // 設(shè)置為濾鏡的輸入圖像參數(shù)
        filter.setValue(inputImage, forKey: kCIInputImageKey)
        // 用UIImage表示并顯示濾鏡的輸出
        imageView.image = UIImage(CIImage: filter.outputImage!)
    }
}

在macOS中,使用該initWithBitmapImageRep:方法從位圖創(chuàng)建CIImage圖像對(duì)象,可在任何支持NSImage對(duì)象的地方使用NSCIImageRep類創(chuàng)建圖像。

與AV Foundation處理視頻

AVFoundation框架提供了許多用于處理視頻和音頻內(nèi)容的高級(jí)實(shí)用程序。其中包括AVVideoComposition類,您可以使用它將視頻和音頻軌道組合或編輯到單個(gè)演示文稿中。(有關(guān)合成的一般信息,請(qǐng)參閱“ AVFoundation編程指南中的編輯”。)您可以使用AVVideoComposition對(duì)象在播放或?qū)С銎陂g將Core Image濾鏡應(yīng)用于視頻的每個(gè)幀,如清單1-4所示。

清單1-4 將濾鏡應(yīng)用于視頻合成

let filter = CIFilter(name: "CIGaussianBlur")!
let composition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in
    
    // 避免邊緣模糊
    let source = request.sourceImage.clampingToExtent()
    filter.setValue(source, forKey: kCIInputImageKey)
    
    // 根據(jù)視頻的時(shí)間改變?yōu)V鏡參數(shù)
    let seconds = CMTimeGetSeconds(request.compositionTime)
    filter.setValue(seconds * 10.0, forKey: kCIInputRadiusKey)
    
    // 裁剪為原始圖像大小
    let output = filter.outputImage!.cropping(to: request.sourceImage.extent)
    
    // 提供濾鏡輸出
    request.finish(with: output, context: nil)
})

使用videoCompositionWithAsset:applyingCIFiltersWithHandler:初始化程序創(chuàng)建合成時(shí),您將提供一個(gè)處理程序,負(fù)責(zé)將濾鏡應(yīng)用于每個(gè)視頻幀。AVFoundation在播放或?qū)С銎陂g自動(dòng)調(diào)用您的處理程序。在處理程序中,首先使用提供的AVAsynchronousCIImageFilteringRequest對(duì)象來(lái)檢索要使用濾鏡的視頻幀(以及補(bǔ)充信息,如幀時(shí)間),然后提供濾鏡后的圖像以供合成使用。

要使用創(chuàng)建的視頻合成進(jìn)行播放,請(qǐng)使用與合成源相同的資源創(chuàng)建AVPlayerItem對(duì)象,然后將合成分配給播放器項(xiàng)的videoComposition屬性。要將合成導(dǎo)出到新的影片文件,請(qǐng)從同一源資源創(chuàng)建AVAssetExportSession對(duì)象,然后將合成分配給導(dǎo)出會(huì)話的videoComposition屬性。

提示: 清單1-4還顯示了另一種有用的Core Image技術(shù)。默認(rèn)情況下,模糊濾鏡還會(huì)通過(guò)模糊圖像像素以及(在濾鏡的圖像處理空間中)環(huán)繞圖像的透明像素來(lái)柔化圖像的邊緣。在某些情況下,例如過(guò)濾視頻時(shí),這種效果可能是不合需要的。

要避免此效果,請(qǐng)使用imageByClampingToExtent方法(或CIAffineClamp濾鏡)在模糊之前在所有方向上無(wú)限延伸圖像的邊緣像素。Clamping會(huì)創(chuàng)建無(wú)限大小的圖像,因此您還應(yīng)該在模糊后裁剪圖像。

使用SpriteKit和SceneKit處理游戲內(nèi)容

SpriteKit是一種用于構(gòu)建2D游戲和其他類型應(yīng)用程序的技術(shù),這些應(yīng)用程序具有高度動(dòng)態(tài)的動(dòng)畫內(nèi)容; SceneKit用于處理3D資源,渲染和動(dòng)畫3D場(chǎng)景以及構(gòu)建3D游戲。(有關(guān)每種技術(shù)的更多信息,請(qǐng)參閱SpriteKit編程指南和SceneKit框架參考。)兩種框架都提供高性能的實(shí)時(shí)渲染,可以通過(guò)簡(jiǎn)單的方法將Core Image處理添加到場(chǎng)景的全部或部分。

在SpriteKit中,您可以使用SKEffectNode類添加Core Image濾鏡。要查看正在使用的此類的示例,請(qǐng)使用Game模板(對(duì)于iOS或tvOS)創(chuàng)建一個(gè)新的Xcode項(xiàng)目,選擇SpriteKit作為游戲技術(shù),并修改法GameScene類中的touchesBegan:withEvent:方法以使用清單1-5中的代碼。(對(duì)于macOS Game模板,您可以對(duì)該mouseDown:方法進(jìn)行類似的修改。)

清單1-5 在SpriteKit中應(yīng)用濾鏡

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)
    }
}

請(qǐng)注意,SKScene類本身是SKEffectNode子類,因此您還可以將Core Image濾鏡應(yīng)用于整個(gè)SpriteKit場(chǎng)景。

在SceneKit中,SCNNode類的filters屬性可以將Core Image濾鏡應(yīng)用于3D場(chǎng)景的任何元素。要查看此屬性,請(qǐng)使用Game模板(對(duì)于iOS,tvOS或macOS)創(chuàng)建一個(gè)新的Xcode項(xiàng)目,選擇SceneKit作為游戲技術(shù),并修改GameViewController類中的viewDidLoad方法以使用清單1-6中的代碼。

清單1-6 在SceneKit中應(yīng)用濾鏡

// 在模塊中找到這一行
let ship = rootNode.childNode(withName: "ship", recursively: true)!
 
// 添加如下代碼
let pixellate = CIFilter(name: "CIPixellate",
                         withInputParameters: [kCIInputScaleKey: 20.0])!
ship.filters = [ pixellate ]

您還可以在SceneKit節(jié)點(diǎn)上設(shè)置濾鏡參數(shù)的動(dòng)畫 - 有關(guān)詳細(xì)信息,請(qǐng)參閱該filters屬性的參考文檔。

在SpriteKit和SceneKit中,您可以使用過(guò)渡來(lái)更改視圖的場(chǎng)景,增加視覺(jué)效果。(請(qǐng)參閱SpriteKit的presentScene:transition: 方法和SceneKit的presentScene:withTransition:incomingPointOfView:completionHandler:方法。)使用SKTransition類及其transitionWithCIFilter:duration:初始化程序從任何Core Image過(guò)渡濾鏡創(chuàng)建過(guò)渡動(dòng)畫。

處理核心動(dòng)畫層(macOS)

在macOS中,您可以使用filters屬性將濾鏡應(yīng)用于任何CALayer視圖的內(nèi)容,并添加隨時(shí)間變化的濾鏡參數(shù)的動(dòng)畫。見(jiàn)濾鏡加到OS X瀏覽視覺(jué)效果和先進(jìn)的動(dòng)畫技巧的核心動(dòng)畫編程指南。

使用Core Image Context構(gòu)建您自己的工作流

使用上一節(jié)中列出的技術(shù)應(yīng)用Core Image濾鏡時(shí),這些框架會(huì)自動(dòng)管理Core Image用于處理圖像和呈現(xiàn)結(jié)果以供顯示的基礎(chǔ)資源。這種方法既可以最大限度地提高這些工作流程的性能,又可以更輕松地進(jìn)行設(shè)置。但是,在某些情況下,使用CIContext該類自己管理這些資源更為謹(jǐn)慎。通過(guò)直接管理Core Image上下文,您可以精確控制應(yīng)用程序的性能特征,或者將Core Image與較低級(jí)別的呈現(xiàn)技術(shù)集成。

Core Image Context表示執(zhí)行濾鏡和生成圖像所需的CPU或GPU計(jì)算技術(shù),資源和設(shè)置。有幾種上下文可供選擇,因此您應(yīng)該選擇最適合您應(yīng)用程序工作流程的選項(xiàng)以及您可能正在使用的其他技術(shù)。以下部分討論了一些常見(jiàn)的情況; 有關(guān)完整的選項(xiàng)集,請(qǐng)參閱CIContext類參考。

重要: Core Image Context是管理大量資源和狀態(tài)的重量級(jí)對(duì)象。反復(fù)創(chuàng)建和銷毀上下文會(huì)產(chǎn)生很大的性能成本,因此如果您計(jì)劃執(zhí)行多個(gè)圖像處理操作,請(qǐng)盡早創(chuàng)建上下文并將其存儲(chǔ)以供將來(lái)重用。

使用自動(dòng)上下文進(jìn)行渲染

如果您對(duì)應(yīng)用程序與其他圖形技術(shù)的互操作性沒(méi)有限制,那么創(chuàng)建Core Image上下文很簡(jiǎn)單:只需使用基本init或initWithOptions:初始化程序。執(zhí)行此操作時(shí),Core Image會(huì)自動(dòng)在內(nèi)部管理資源,根據(jù)當(dāng)前設(shè)備和您指定的任何選項(xiàng)選擇適當(dāng)或最佳的CPU或GPU渲染技術(shù)。此方法非常適合于渲染已處理圖像以輸出到文件(例如,使用writeJPEGRepresentationOfImage:toURL:colorSpace:options:error:方法)的任務(wù)。

注意: 沒(méi)有顯式指定的渲染目標(biāo)的上下文不能使用該drawImage:inRect:fromRect:方法,因?yàn)樵摲椒ǖ男袨闀?huì)根據(jù)使用的渲染目標(biāo)而更改。而是使用CIContext以名稱開(kāi)頭render或create的方法來(lái)指定顯式目標(biāo)。

如果您想要實(shí)時(shí)渲染Core Image結(jié)果,即在濾鏡參數(shù)中設(shè)置動(dòng)畫效果,生成動(dòng)畫過(guò)渡效果,或者處理已經(jīng)每秒渲染多次的視頻或其他可視內(nèi)容,請(qǐng)小心使用此方法。

用Metal實(shí)時(shí)渲染

Metal框架提供對(duì)GPU的低開(kāi)銷訪問(wèn),實(shí)現(xiàn)圖形渲染和并行計(jì)算工作流的高性能。此類工作流程是圖像處理不可或缺的一部分,因此Core Image盡可能在Metal上構(gòu)建。如果您正在構(gòu)建使用Metal渲染圖形的應(yīng)用程序,或者如果您想利用Metal來(lái)獲取動(dòng)畫濾鏡輸出或過(guò)濾動(dòng)畫輸入(例如實(shí)時(shí)視頻)的實(shí)時(shí)性能,請(qǐng)使用Metal設(shè)備創(chuàng)建Core Image上下文。

清單1-7和清單1-8顯示了使用MetalKit視圖(MTKView)渲染Core Image輸出的示例。(重要步驟在每個(gè)清單中編號(hào),然后進(jìn)行描述。)

清單1-7 使用Core Image渲染設(shè)置Metal視圖

class ViewController: UIViewController, MTKViewDelegate {  // 1
    
    // Metal資源
    var device: MTLDevice!
    var commandQueue: MTLCommandQueue!
    var sourceTexture: MTLTexture!                         // 2
    
    // Core Image資源
    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
        
        // 其他設(shè)置
    }
}
  1. 此示例使用UIViewControlleriOS或tvOS 的子類。要與macOS一起使用,請(qǐng)NSViewController改為使用子類。
  2. 該sourceTexture屬性包含一個(gè)Metal紋理,其中包含要由濾鏡處理的圖像。此示例未顯示加載紋理的內(nèi)容,因?yàn)橛性S多方法可以填充紋理 - 例如,您可以使用MTKTextureLoader類加載圖像文件,或使用紋理作為您自己的早期渲染過(guò)程的輸出。
  3. 創(chuàng)建渲染所需的Metal對(duì)象 - MTLDevice表示要使用的GPU 的對(duì)象,以及在該GPU上執(zhí)行渲染和計(jì)算命令的命令隊(duì)列。(此命令隊(duì)列可以處理由Core Image編碼的渲染或計(jì)算命令以及來(lái)自您自己的任何其他渲染通道的命令。)
  4. 配置MetalKit視圖。

要點(diǎn): 將Metal視圖,圖層或紋理用作Core Image渲染目標(biāo)時(shí),始終將framebufferOnly屬性設(shè)置為NO。

  1. 創(chuàng)建與視圖使用相同的Metal設(shè)備的Core Image上下文。通過(guò)共享Metal資源,Core Image可以處理紋理內(nèi)容并渲染到視圖,而無(wú)需將圖像數(shù)據(jù)復(fù)制到單獨(dú)的CPU或GPU內(nèi)存緩沖區(qū)的性能成本。CIContext對(duì)象的創(chuàng)建成本很高,因此您只需執(zhí)行一次,并在每次處理圖像時(shí)重復(fù)使用它。

drawInMTKView:每次視圖需要顯示時(shí),MetalKit都會(huì)調(diào)用該方法。(默認(rèn)情況下,MetalKit可以每秒調(diào)用此方法多達(dá)60次。有關(guān)詳細(xì)信息,請(qǐng)參閱視圖的preferredFramesPerSecond屬性。)清單1-8顯示了從Core Image上下文渲染的方法的基本實(shí)現(xiàn)。

清單1-8 在Metal視圖中使用Core Image濾鏡繪圖

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可繪制紋理和用于編碼渲染命令的命令緩沖區(qū)。
  2. 配置濾鏡的輸入?yún)?shù),包括Metal紋理中的輸入圖像。此示例使用常量參數(shù),但請(qǐng)記住,此方法每秒最多運(yùn)行60次 - 您可以利用此機(jī)會(huì)隨時(shí)間更改濾鏡參數(shù)以創(chuàng)建平滑動(dòng)畫。
  3. 告訴Core Image上下文將濾鏡輸出渲染到視圖的可繪制紋理中。bounds參數(shù)告訴Core Image要繪制的圖像的哪個(gè)部分 - 此示例使用輸入圖像的尺寸。
  4. 當(dāng)命令緩沖區(qū)完成執(zhí)行時(shí),告訴Metal顯示渲染的圖像。

此示例僅顯示使用Metal渲染Core Image所需的最少代碼。在實(shí)際應(yīng)用程序中,您可能會(huì)在Core Image管理的渲染之前或之后執(zhí)行其他渲染過(guò)程,或者將Core Image輸出渲染到輔助紋理中,并在另一個(gè)渲染過(guò)程中使用該紋理。有關(guān)使用Metal繪圖的更多信息,請(qǐng)參閱“Metal編程指南”。

使用OpenGL或OpenGL ES進(jìn)行實(shí)時(shí)渲染

Core Image還可以使用OpenGL(macOS)或OpenGL ES(iOS和tvOS)進(jìn)行基于GPU的高性能渲染。如果您需要支持Metal不可用的舊硬件,或者您希望將Core Image集成到現(xiàn)有的OpenGL或OpenGL ES工作流程中,請(qǐng)使用此選項(xiàng)。

  • 如果使用OpenGL ES(在iOS或tvOS中)繪制,請(qǐng)使用contextWithEAGLContext:options:初始化程序從用于渲染的EAGLContext創(chuàng)建Core Image上下文。
  • 如果使用OpenGL(在macOS中)繪制,請(qǐng)使用contextWithCGLContext:pixelFormat:colorSpace:options:初始化程序從用于渲染的OpenGL上下文創(chuàng)建Core Image上下文。(有關(guān)像素格式的重要詳細(xì)信息,請(qǐng)參閱該方法的參考文檔。)

在任一情況下,使用imageWithTexture:size:flipped:colorSpace:初始化程序從OpenGL或OpenGL ES紋理創(chuàng)建CIImage對(duì)象。使用GPU內(nèi)存中已有的圖像數(shù)據(jù)可以通過(guò)刪除冗余復(fù)制操作來(lái)提高性能。

要在OpenGL或OpenGL ES中渲染Core Image輸出,請(qǐng)使GL上下文變?yōu)楫?dāng)前并設(shè)置目標(biāo)幀緩沖,然后調(diào)用drawImage:inRect:fromRect:方法。

使用Quartz 2D進(jìn)行基于CPU的渲染

如果您的應(yīng)用程序不需要實(shí)時(shí)性能并使用CoreGraphics繪制視圖內(nèi)容(例如,在drawRect:UIKit或AppKit視圖的方法中),請(qǐng)使用contextWithCGContext:options:初始化程序創(chuàng)建直接與Core Graphics上下文一起使用的Core Image上下文'已經(jīng)用于其他繪圖。(在macOS中,請(qǐng)改用CIContext當(dāng)前NSGraphicsContext對(duì)象的屬性。)有關(guān)CoreGraphics上下文的信息,請(qǐng)參閱“ Quartz 2D編程指南”。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容