你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS8 Core Image In Swift:自動改善圖像以及內置濾鏡的使用

iOS8 Core Image In Swift:自動改善圖像以及內置濾鏡的使用

編輯:IOS開發綜合

基於iOS SDK 8.0以及Xcode 6 Beta 6。

Core Image是一個很強大的框架。它可以讓你簡單地應用各種濾鏡來處理圖像,比如修改鮮艷程度, 色澤, 或者曝光。 它利用GPU(或者CPU)來非常快速、甚至實時地處理圖像數據和視頻的幀。並且隱藏了底層圖形處理的所有細節,通過提供的API就能簡單的使用了,無須關心OpenGL或者OpenGL ES是如何充分利用GPU的能力的,也不需要你知道GCD在其中發揮了怎樣的作用,Core Image處理了全部的細節。

Core Image框架給我們提供了這些東西:

 

內置的圖片濾鏡特征檢測能力(如人臉識別)支持自動改善圖像能利用多個濾鏡組合成一個自定義濾鏡

 

 

自動改善圖像

先上一個簡單的例子。用Single View Application的工程模板建立一個工程,這個工程建好後只有一個AppDelegate和一個ViewController,另外還有一個Main.storyboard,在Main.storyboard裡已經准備好一個ViewController了,我們在這個ViewController裡放置一個UIImageView,調整其frame:

/

除此之外,因為UIImageView是默認拉伸圖片的,我們不想讓它變形,把它的ContentMode設置為Aspect Fit

最後在拖兩個按鈕進來,一個用於顯示原圖,一個用於自動改善圖像,整個ViewController看起來像這樣:

/

接下來在ViewController中增加對應的IBAction方法以及IBOutlet屬性:

 

class ViewController: UIViewController {

@IBOutlet var imageView: UIImageView!

 

lazy var originalImage: UIImage = {

return UIImage(named: Image)

}()

......

一個連接到Storyboard的imageView,還有一個只懶加載一次的originalImage屬性,這個屬性在後面會用到很多次,這裡有我整個工程用到的image。

 

然後在ViewDidLoad裡像這樣:

 

override func viewDidLoad() {

super.viewDidLoad()

self.imageView.layer.shadowOpacity = 0.8

self.imageView.layer.shadowColor = UIColor.blackColor().CGColor

self.imageView.layer.shadowOffset = CGSize(width: 1, height: 1)

 

self.imageView.image = originalImage

}

只做了兩件事:一是給imageView加上了陰影邊框,只是為了好看;二是把originalImage賦值給imageView。

一行代碼顯示原圖:

 

@IBAction func showOriginalImage() {

self.imageView.image = originalImage

}

 

下面是自動改善的代碼,我先貼出來,再慢慢說,有一個直觀的效果會更有興趣一些:

 

@IBAction func autoAdjust() {

var inputImage = CIImage(image: originalImage)

let filters = inputImage.autoAdjustmentFilters() as [CIFilter]

for filter: CIFilter in filters {

filter.setValue(inputImage, forKey: kCIInputImageKey)

inputImage = filter.outputImage

}

self.imageView.image = UIImage(CIImage: inputImage)

}

把IBAction和IBOutlet連接對之後,運行起來應該就能看到以下效果了:

/

點擊自動改善就能看到效果了,可能運行會有點慢,因為我們還沒做優化,如果覺得改善效果不明顯的話,可以多點擊幾次原圖來對比一下。

雖然有些問題,但是已經不妨礙我們繼續探索了。上面的自動改善代碼顯式地用到了兩個類(我為什麼在這裡要用顯式這個詞?):CIImage和CIFilter,其中:

 

CIImage:只保存能構建圖像的原始數據,是一個模型對象。CIFilter:濾鏡,不同的CIFilter實例能表示不同的濾鏡效果,不同的濾鏡所能設置的參數也不盡相同。另外Core Image的自動改善功能能智能的對圖像的柱狀圖(histogram?)、人臉區域以及元數據進行分析,只需要傳入一個Image作為輸入參數,就能得到一組能使圖像改善的濾鏡。CIImage的實例能通過UIImage來得到,然後通過兩個API:autoAdjustmentFilters和autoAdjustmentFiltersWithOptions:取到能使圖像得到改善的濾鏡數組,在大多數情況下,你可能會用提供Option字典的那個API,因為你能設置:圖片的方向,這對CIRedEyeCorrection、CIFaceBalance等濾鏡來說格外重要,因為Core Image需要精確的對面部進行識別。是否只需要消除紅眼(設置kCIImageAutoAdjustEnhance為false)。是否使用除了消除紅眼以外的所有的濾鏡(設置kCIImageAutoAdjustRedEye為false)。如果你想提供Option字典,那麼可以這樣使用:
NSDictionary *options = @{ CIDetectorImageOrientation :
                 [[image properties] valueForKey:kCGImagePropertyOrientation] };
NSArray *adjustments = [myImage autoAdjustmentFiltersWithOptions:options];
在這個例子中,我就不傳入Option字典了,因為不涉及圖片方向的問題。想知道自動改善功能用了哪些濾鏡?只需要把filter對象打印出來即可,一般來說,會是這5個濾鏡:CIRedEyeCorrection:修復因相機的閃光燈導致的各種紅眼CIFaceBalance:調整膚色CIVibrance:在不影響膚色的情況下,改善圖像的飽和度CIToneCurve:改善圖像的對比度CIHighlightShadowAdjust:改善陰影細節大部分情況下這些濾鏡就夠用了。之前也提到了,不同的CIFilter會有不同的參數,如果我們想知道CIFilter具體有哪些參數,可以調用它自己的inputKeys方法,得到它支持的輸入參數的列表,或者調用它的outputKeys方法,得到它的輸出參數的列表(一般我們只用outpuntImage就行了),又或者直接調用它的attributes方法,得到它的所有信息,包括它的名字、所屬的分類、輸入參數、輸出參數、各參數的取值范圍以及默認值等。調用CIFilter的inputKeys方法可以看到它的輸入參數:

 

for filter: CIFilter in filters {

let inputKeys = filter.inputKeys()

print(filter.name())

println(inputKeys)

...

 

打印結果:

 

CIFaceBalance[inputImage, inputOrigI, inputOrigQ, inputStrength, inputWarmth]
CIVibrance[inputImage, inputAmount]
CIToneCurve[inputImage, inputPoint0, inputPoint1, inputPoint2, inputPoint3, inputPoint4]
CIHighlightShadowAdjust[inputImage, inputRadius, inputShadowAmount, inputHighlightAmount]

幾乎所有的濾鏡都有inputImage這個輸入參數,我們可以直接用系統預置的各種key來設置參數(如kCIInputImageKey),系統已經預置了大部分常用的key,如果你發現有的key系統沒有預置,你可以直接使用獲取的key名的字符串來作key,如:

 

filter.setValue(inputImage, forKey: kCIInputImageKey)

//兩者設置方式完全一樣

filter.setValue(inputImage, forKey: inputImage)

對自動改善圖像功能來說,我們不需要知道太多的細節,設置inputImage就可以了。

 

接下來填坑。

上面的代碼有兩個問題:一是在每次使用自動改善的過程中感覺到明顯的慢;二是圖像在自動改善後變形了。原圖和改善後的圖像對比:

//

我把UIImageView的contentMode設置為Aspect Fit,就是不希望圖片發生變形,而是按等比例縮放,如果把UIImageView的背景色設置為紅色的話,在顯示原圖的時候可以看到上下均有紅色,而改善之後的圖片就沒有紅色。由於蘋果表示UIImage是完全支持CIImage的,所以文檔中並沒有指出出現這個問題的原因,我參考了下面這個貼子:

http://stackoverflow.com/questions/15878060/setting-uiimageview-content-mode-after-applying-a-cifilter

上面有說通過UIImage(CIImage:)方法得到的UIImage並不是一個基於CGImage的標准UIImage,所以不能按一般的顯示規則去理解,因此我們要換種方式去得到一個真正的UIImage,解決方法放在下面再說。

在之前介紹CIImage和CIFilter的時候我們用到顯式這個詞,因為在代碼裡可以直觀的看到使用了這兩個類,CIImage提供圖像的信息,CIFilter提供濾鏡,Core Image還需要另一個對象把兩者粘合起來,這個對象就是CIContext

CIContext是Core Image處理圖像的關鍵,它和Core Graphics的CGContext類似但又與之不同,CIContext可以被重用,不必每次都新建一個,同時在輸出CIImage的時候又必須有一個。在上面的例子中我們沒有使用CIContext,但在調用UIImage(CIImage:)的時候Core Image隱式地在內部使用了CIContext,也就是把我們需要手動做的工作自動地完成了。但是這就有了一個問題,在每次調用UIImage(CIImage:)的時候它都會重新創建一個CIContext對象,這在用完即毀的情況下不會造成很大的影響,但在反復地使用濾鏡的過程中就很影響性能了,為了防止這種情況,我們把CIContext對象重用起來,讓ViewController持有一個懶加載的屬性:

 

lazy var context: CIContext = {

return CIContext(options: nil)

}()

 

CIContext在初始化的時候需要一個字典,可以通過kCIContextUseSoftwareRenderer創建一個基於CPU的CIContext對象,默認是創建基於GPU的CIContext對象,不同之處在於GPU的CIContext對象處理起來會更快,而基於CPU的CIContext對象除了支持更大的圖像以外,還能在後台處理。我們傳nil創建基於GPU的CIContext對象就可以了。

/

有了可重用的CIContext對象,在創建UIImage的時候需要這樣:

 

@IBAction func autoAdjust() {

var inputImage = CIImage(image: originalImage)

let filters = inputImage.autoAdjustmentFilters() as [CIFilter]

for filter: CIFilter in filters {

filter.setValue(inputImage, forKey: kCIInputImageKey)

inputImage = filter.outputImage

}

//self.imageView.image = UIImage(CIImage: inputImage)

let cgImage = context.createCGImage(inputImage, fromRect: inputImage.extent())

self.imageView.image = UIImage(CGImage: cgImage)

}

雖然在第一次執行自動改善的時候會有點慢(因為要創建CIContext對象),但是在反復執行的情況下性能改善了很多,而且這樣一來還解決了第二個問題,即ContentMode的問題。如果沒有特殊情況,應該總是用這種方式創建一個CGImage,再把CGImage轉成UIImage。

 

使用各種內置濾鏡

通過CIFilter的類方法filterNamesInCategory()可以得到屬於某個類別下的所有濾鏡:

func showFiltersInConsole() {

let filterNames = CIFilter.filterNamesInCategory(kCICategoryBuiltIn)

println(filterNames.count)

println(filterNames)

for filterName in filterNames {

let filter = CIFilter(name: filterName as String)

let attributes = filter.attributes()

println(attributes)

}

}

在這個方法裡傳入的類別參數是kCICategoryBuiltIn,表示會輸出iOS8 Core Image的所有濾鏡:/
共有127種。當然了,並不是所有的濾鏡都是常用的,我們可以通過kCICategoryColorEffect這個key取到一些常見的濾鏡,就像iOS7相機應用裡的一樣,這些濾鏡一般不需要設置什麼參數(其中有些也可以設置不同的參數),只需要同上文一樣,設置inputImage就行了。把這個類別下的濾鏡打印一些出來看看:/
我選中了其中一個濾鏡,這是一個單色濾鏡,雖然看上去內容很多,但並不復雜,CIAttributeFilterDisplayName是它的顯示名,inputImage是參數,每個參數的詳細信息通過一個字典來展示,其中包括了這個參數的類型(CIAttributeTypeImage),以及參數的Class(CIImage),之後是這個濾鏡所屬的類別,一個濾鏡可以同時屬於多個類別,最後是這個濾鏡在實例化的時候需要提供的字符串,即CIPhotoEffectMono。它上面的CIPhotoEffectInstant以及下面的CIPhotoEffectNoir、CIPhotoEffectProcess與之類似,都只需要一個inputImage參數,是不是很簡單?這些濾鏡的參數已經被內部設置好了,我們直接使用即可。為了方便使用,我們給ViewController持有一個CIFilter屬性,在其他的方法中實例化CIFilter,同時用一個公共的方法來顯示打上濾鏡後的圖像:

class ViewController: UIViewController {

@IBOutlet var imageView: UIImageView!

 

lazy var originalImage: UIImage = {

return UIImage(named: Image)

}()

lazy var context: CIContext = {

return CIContext(options: nil)

}()

var filter: CIFilter!

......

// MARK: - 懷舊

@IBAction func photoEffectInstant() {

filter = CIFilter(name: CIPhotoEffectInstant)

outputImage()

}

// MARK: - 黑白

@IBAction func photoEffectNoir() {

filter = CIFilter(name: CIPhotoEffectNoir)

outputImage()

}

// MARK: - 色調

@IBAction func photoEffectTonal() {

filter = CIFilter(name: CIPhotoEffectTonal)

outputImage()

}

// MARK: - 歲月

@IBAction func photoEffectTransfer() {

filter = CIFilter(name: CIPhotoEffectTransfer)

outputImage()

}

// MARK: - 單色

@IBAction func photoEffectMono() {

filter = CIFilter(name: CIPhotoEffectMono)

outputImage()

}

// MARK: - 褪色

@IBAction func photoEffectFade() {

filter = CIFilter(name: CIPhotoEffectFade)

outputImage()

}

// MARK: - 沖印

@IBAction func photoEffectProcess() {

filter = CIFilter(name: CIPhotoEffectProcess)

outputImage()

}

// MARK: - 鉻黃

@IBAction func photoEffectChrome() {

filter = CIFilter(name: CIPhotoEffectChrome)

outputImage()

}

func outputImage() {

println(filter)

let inputImage = CIImage(image: originalImage)

filter.setValue(inputImage, forKey: kCIInputImageKey)

let outputImage = filter.outputImage

let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())

self.imageView.image = UIImage(CGImage: cgImage)

}

這些都寫好後,在UI上把各種按鈕及touch事件綁定好,UI看起來像這樣:

/

運行後各種濾鏡效果:

/

以上就是對Core Image內置濾鏡的簡單使用,如果你不需要對濾鏡做更加細粒度控制的話,上述方法就夠用了。

 

GitHub下載地址

 

UPDATED:

另外不知道有沒有人注意到我在注釋裡用到了// MARK: -,這和Objective-C的#pragma mark -作用一樣。

 

 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved