
前言:Core Image是一個強大的框架,可讓您輕松地將過濾器應(yīng)用于圖像。您可以獲得各種各樣的效果,如修改活力,色調(diào)或曝光。它可以使用CPU或GPU來處理圖像數(shù)據(jù),并且速度非???- 足以實現(xiàn)視頻幀的實時處理!
核心圖像濾鏡也可以鏈接在一起,以一次將多個效果應(yīng)用于圖像或視頻幀。多個濾波器被組合成應(yīng)用于圖像的單個濾波器。與通過每個過濾器一次處理圖像相比,這樣做非常有效。
入門
在開始之前,讓我們來討論Core Image框架中的一些最重要的類:
CIContext。核心圖像的所有處理都以CIContext完成。這與Core Graphics或OpenGL上下文有些相似。
CIImage。該類保存圖像數(shù)據(jù)。它可以從UIImage,從圖像文件或從像素數(shù)據(jù)創(chuàng)建。
CIFilter。CIFilter類有一個字典,用于定義它所代表的特定過濾器的屬性。過濾器的例子是振動,顏色反轉(zhuǎn),裁剪等等。
基本圖像過濾
通過簡單地運行您的圖像CIFilter并在屏幕上顯示圖像來開始。每次想要將CIFilter應(yīng)用于圖像時,有4個步驟:
1、創(chuàng)建一個CIImage對象。CIImage有幾種初始化方法,包括:CIImage(CIImage(contentsOf: ),CIImage(data :),CIImage(CGImage :),CIImage(bitmapData:bytesPerRow:size:format:colorSpace :)等幾個。最常用的是用CIImage(contentsOf: )
2、創(chuàng)建CIContext。CIContext可以是基于CPU或GPU的。CIContext初始化相對耗費資源,因此您可以重用它,而不是一遍又一遍地創(chuàng)建它。輸出CIImage對象時,您將始終需要一個。
3、創(chuàng)建一個CIFilter。創(chuàng)建過濾器時,您可以配置依賴于您使用的過濾器的許多屬性。
4、獲取過濾器輸出。過濾器為您提供輸出圖像作為CIImage - 您可以使用CIContext將其轉(zhuǎn)換為UIImage,如下所示
// 1
let fileURL = Bundle.main.url(forResource: "beauty", withExtension: "jpg")
// 2
let beginImage = CIImage(contentsOf: fileURL!)
// 3
let filter = CIFilter (name: "CISepiaTone")
filter?.setValue(beginImage, forKey: kCIInputImageKey)
filter?.setValue(0.5, forKey: kCIInputIntensityKey)
// 4
let newImage = UIImage(ciImage: (filter?.outputImage)!)
self.imageView.image = newImage;
我們先來看看這一節(jié):
1、此行創(chuàng)建一個<code>NSURL</code>對象,該對象保存圖像文件的路徑。
2、接下來,使用<code>CIImage(contentsOf: )</code>構(gòu)造函數(shù)創(chuàng)建您的CIImage。
3、接下來,您將創(chuàng)建您的<code>CIFilter</code>對象。<code>CIFilter</code>構(gòu)造函數(shù)使用過濾器的名稱,并指定該過濾器的鍵和值的字典。每個過濾器將有自己唯一的密鑰和一組有效的值。所述<code>CISepiaTone</code>過濾器只需兩個值:<code>KCIInputImageKey</code>(一個CIImage)和在0和1之間的<code>kCIInputIntensityKey</code> ,你給該值0.5。大多數(shù)過濾器具有默認值,如果沒有提供值,將使用該值。一個例外是<code>CIImage</code>,這是必須提供的,因為沒有默認。
4、將<code>CIImage</code>從過濾器中恢復(fù)與使用該<code>outputImage</code>屬性一樣簡單。一旦輸出<code>CIImage</code>,您將需要將其轉(zhuǎn)換為<code>UIImage</code>。該<code>UIImage(ciImage:)</code>構(gòu)造函數(shù)轉(zhuǎn)換了<code>CIImage</code>到<code>UIImage</code>。一旦將其轉(zhuǎn)換為<code>UIImage</code>,您只需將其顯示在您之前添加的<code>imageView</code>中。
運行該項目,您將看到由深褐色濾鏡過濾的圖像。

置于上下文
在您繼續(xù)了解之前,您應(yīng)該了解一個優(yōu)化。
我之前提到你需要一個CIContext應(yīng)用CIFilter,但在上面的例子中沒有提到這個對象。事實證明,<code>UIImage(ciImage:)code</code>構(gòu)造函數(shù)為您做所有的工作。它創(chuàng)建CIContext并使用它來執(zhí)行過濾圖像的工作。這使得使用Core Image API非常簡單。
有一個主要的缺點 - CIContext每次使用時都會創(chuàng)建一個新的。CIContext實例旨在可重用以提高性能。如果要使用滑塊來更新過濾器值,就像在本教程中所做的那樣,每次更改過濾器時都會創(chuàng)建一個新的CIContext將太慢了。
我們這樣做是正確的。從viewDidLoad()添加的代碼中刪除步驟4 ,并將其替換為以下內(nèi)容:
// 1
let context = CIContext(options:nil)
// 2
let cgimg = context.createCGImage(filter!.outputImage!, from: filter!.outputImage!.extent)
// 3
let newImage = UIImage(cgImage: cgimg!)
self.imageView.image = newImage;
再次,我們一起來看看這一節(jié)。
在這里,您設(shè)置CIContext對象并使用它來繪制CGImage。該CIContext(options:)構(gòu)造采用指定的選項一個NSDictionary如色彩格式,或上下文是否應(yīng)在CPU或GPU上運行。對于這個應(yīng)用程序,默認值是好的,所以你傳遞為nil為該參數(shù)。
<code>createCGImage(outputImage:from:)</code>使用提供的CIImage在上下文中調(diào)用將返回一個新的CGImage實例。
接下來,您使用<code>UIImage(cgImage:)</code>構(gòu)造函數(shù)從新創(chuàng)建的CGImage創(chuàng)建UIImage,而不是像以前一樣直接從CIImage創(chuàng)建。注意,在完成它之后,不需要明確地釋放CGImage,就像在Objective-C中一樣。在Swift中,ARC可以自動釋放Core Foundation對象。
構(gòu)建和運行,并確保它像以前一樣工作。
在這個例子中,自己處理CIContext的創(chuàng)建并沒有太多的區(qū)別。但在下一節(jié)中,您將看到為什么這對于性能很重要,因為您實現(xiàn)了動態(tài)修改過濾器的功能!
更改過濾器值
下面增加滑塊,每次滑塊更改時,都需要使用不同的值重做圖像過濾器。但是,您不想重做整個過程,這將是非常低效的,并且需要太長時間。您將需要更改類中的一些內(nèi)容,以便您可以保留在viewDidLoad方法中創(chuàng)建的一些對象。
如果為了重新使用CIContext,而每次重新創(chuàng)建它程序?qū)⑦\行非常緩慢。
添加一些實例變量才能完成此任務(wù)。將以下三個屬性添加到ViewController類中:
var context: CIContext!
var filter: CIFilter!
var beginImage: CIImage!
更改代碼,因此<code>viewDidLoad()</code>使用這些屬性,而不是聲明新的局部變量,如下所示:
beginImage = CIImage(contentsOf: fileURL!)
filter = CIFilter (name: "CISepiaTone")
filter?.setValue(beginImage, forKey: kCIInputImageKey)
filter?.setValue(0.5, forKey: kCIInputIntensityKey)
let outputImage = filter.outputImage
context = CIContext(options:nil)
let cgimg = context.createCGImage(filter!.outputImage!, from: filter!.outputImage!.extent)
實現(xiàn)changeValue方法。在CIFilter字典中改變<code>inputIntensity</code>值。
一旦你改變了這個值,你需要重復(fù)幾個步驟:
1、從CIFilter獲取輸出CIImage。
2、將CIImage轉(zhuǎn)換為CGImage。
3、將CGImage轉(zhuǎn)換為UIImage,并將其顯示在圖像視圖中。
將創(chuàng)建一個方法<code>amountSliderValueChanged(sender :)</code>:
@IBAction func amountSliderValueChange(_ sender: UISlider) {
let sliderValue = sender.value
filter.setValue(sliderValue, forKey: kCIInputIntensityKey)
let outputImage = filter.outputImage
let cgimg = context.createCGImage(outputImage!, from: outputImage!.extent)
let newImage = UIImage(cgImage: cgimg!)
self.imageView.image = newImage
}

老相片效果
在這個Demo中,會得到一個更精致的老照片效果,完成與棕褐色,一點噪音和一些暈影
func oldPhoto (img: CIImage, withAmount intensity: Float) -> CIImage {
//1 CISepiaTone 棕褐色調(diào)
let sepia = CIFilter(name: "CISepiaTone")
sepia?.setValue(img, forKey: kCIInputImageKey)
sepia?.setValue(intensity, forKey: "inputIntensity")
//2 設(shè)置一個過濾器,創(chuàng)建一個隨機噪聲模式
let random = CIFilter(name: "CIRandomGenerator")
//3 改變隨機噪聲發(fā)生器的輸出
let lighten = CIFilter(name:"CIColorControls")
lighten?.setValue(random?.outputImage, forKey:kCIInputImageKey)
lighten?.setValue(1 - intensity, forKey:"inputBrightness")
lighten?.setValue(0, forKey:"inputSaturation")
//4 cropping(to rect: CGRect)輸出CIImage并將其作用到所提供的rect
let croppedImage = lighten?.outputImage?.cropping(to: beginImage.extent)
//5 將棕褐色濾鏡的輸出與CIRandomGenerator濾鏡的輸出相結(jié)合。
let composite = CIFilter(name:"CIHardLightBlendMode")
composite?.setValue(sepia?.outputImage, forKey:kCIInputImageKey)
composite?.setValue(croppedImage, forKey:kCIInputBackgroundImageKey)
//6 合成輸出上運行暈影濾鏡(vignette filter),使照片的邊緣變暗
let vignette = CIFilter(name:"CIVignette")
vignette?.setValue(composite?.outputImage, forKey:kCIInputImageKey)
vignette?.setValue(intensity * 2, forKey:"inputIntensity")
vignette?.setValue(intensity * 30, forKey:"inputRadius")
//7 返回濾鏡的輸出
return vignette!.outputImage!
}
效果圖:

解析以上代碼:
1、像在簡單的場景中所做的一樣,設(shè)置棕褐色濾鏡。您在方法中傳入浮點值以設(shè)置深色效果的強度。該值將由滑塊提供。
2、設(shè)置一個過濾器,創(chuàng)建一個如下所示的隨機噪聲模式:
CIRandomGenerator
它不需要任何參數(shù)。您將使用這種噪音模式將紋理添加到最終的“舊照片”外觀。
3、改變隨機噪聲發(fā)生器的輸出。你想把它改成灰度,并減輕一點點,所以效果不那么戲劇化。您會注意到,輸入圖像鍵被設(shè)置為隨機過濾器的outputImage屬性。這是一個方便的方式來傳遞一個過濾器的輸出作為下一個的輸入。
4、 cropping(to rect: CGRect)輸出CIImage并將其作用到所提供的rect。在這種情況下,您需要裁剪CIRandomGenerator過濾器的輸出,因為它無限制地打磚塊。如果您在某些時候沒有裁剪,就會出現(xiàn)一個錯誤,表示過濾器具有“無限長度”。CIImages實際上并不包含圖像數(shù)據(jù),它們描述了創(chuàng)建它的“配方”。直到你在CIContext上調(diào)用一個方法來實際處理數(shù)據(jù)。
5、將棕褐色濾鏡的輸出與CIRandomGenerator濾鏡的輸出相結(jié)合。該過濾器執(zhí)行與Photoshop圖層中的“硬光”設(shè)置完全相同的操作。使用Core Image可以實現(xiàn)Photoshop中的大多數(shù)濾鏡選項。
6、在此合成輸出上運行暈影濾鏡,使照片的邊緣變暗。您正在使用滑塊的值來設(shè)置此效果的半徑和強度。
7、返回最后一個過濾器的輸出。
