版本記錄
| 版本號 | 時間 |
|---|---|
| V1.0 | 2022.05.29 星期日 |
前言
Core Image是IOS5中新加入的一個框架,里面提供了強(qiáng)大高效的圖像處理功能,用來對基于像素的圖像進(jìn)行操作與分析。還提供了很多強(qiáng)大的濾鏡,可以實(shí)現(xiàn)你想要的效果,下面我們就一起解析一下這個框架。感興趣的可以參考上面幾篇。
1. Core Image框架詳細(xì)解析(一) —— 基本概覽
2. Core Image框架詳細(xì)解析(二) —— Core Image濾波器參考
3. Core Image框架詳細(xì)解析(三) —— 關(guān)于Core Image
4. Core Image框架詳細(xì)解析(四) —— Processing Images處理圖像(一)
5. Core Image框架詳細(xì)解析(五) —— Processing Images處理圖像(二)
6. Core Image框架詳細(xì)解析(六) —— 圖像中的面部識別Detecting Faces in an Image(一)
7. Core Image框架詳細(xì)解析(七) —— 自動增強(qiáng)圖像 Auto Enhancing Images
8. Core Image框架詳細(xì)解析(八) —— 查詢系統(tǒng)中的過濾器 Querying the System for Filters
9. Core Image框架詳細(xì)解析(九) —— 子類化CIFilter:自定義效果的配方 Subclassing CIFilter: Recipes for Custom Effects(一)
10. Core Image框架詳細(xì)解析(十) —— 子類化CIFilter:自定義效果的配方 Subclassing CIFilter: Recipes for Custom Effects(二)
11. Core Image框架詳細(xì)解析(十一) —— 獲得最佳性能 Getting the Best Performance
12. Core Image框架詳細(xì)解析(十二) —— 使用反饋處理圖像 Using Feedback to Process Images
13. Core Image框架詳細(xì)解析(十三) —— 在寫一個自定義濾波器之前你需要知道什么?
14. Core Image框架詳細(xì)解析(十四) —— 創(chuàng)建自定義濾波器 Creating Custom Filters(一)
15. Core Image框架詳細(xì)解析(十五) —— 創(chuàng)建自定義濾波器 Creating Custom Filters(二)
16. Core Image框架詳細(xì)解析(十六) —— 包裝和加載圖像單元 Packaging and Loading Image Units
17. Core Image框架詳細(xì)解析(十七) —— 一個簡單說明和示例(一)
18. Core Image框架詳細(xì)解析(十八) —— 使用Metal Shading Language創(chuàng)建你自己的Core Image濾波器進(jìn)行像素級圖像處理(一)
19. Core Image框架詳細(xì)解析(十九) —— 使用Metal Shading Language創(chuàng)建你自己的Core Image濾波器進(jìn)行像素級圖像處理(二)
開始
首先看下主要內(nèi)容:
使用
Core Image和Swift學(xué)習(xí)圖像過濾效果的基礎(chǔ)知識。內(nèi)容來自翻譯。
接著看下寫作環(huán)境
Swift 5.5, iOS 15, Xcode 13
下面就是正文了
Core Image 是一個強(qiáng)大的框架,可讓您將過濾器應(yīng)用于圖像。 它提供各種效果,例如修改活力、色調(diào)或曝光(vibrancy, hue or exposure)。 它可以使用 CPU 或 GPU 快速處理圖像數(shù)據(jù)——速度足以實(shí)時處理視頻幀!
您可以將 Core Image 過濾器鏈接在一起,以一次將許多效果應(yīng)用于圖像或視頻幀。 許多過濾器組合成一個過濾器并應(yīng)用于圖像。 與通過每個過濾器處理圖像相比,這使得它非常有效,一次一個。
在本教程中,您將獲得使用 Core Image 的實(shí)踐經(jīng)驗(yàn)。 您將應(yīng)用一些不同的過濾器,并了解實(shí)時對圖像應(yīng)用炫酷效果是多么容易。
在開始之前,先看看 Core Image 框架中一些最重要的類:
-
CIContext。
CIContext完成core image的所有處理。 這有點(diǎn)像Core Graphics或OpenGL上下文。 -
CIImage。 此類保存圖像數(shù)據(jù)。 一個
UIImage、一個圖像文件或像素?cái)?shù)據(jù)都可以創(chuàng)建它。 -
CIFilter。
CIFilter類有一個字典。 這定義了它所代表的特定過濾器的屬性。 濾波器的示例包括活力、顏色反轉(zhuǎn)、裁剪等等(vibrancy, color inversion, cropping)。
您將在此項(xiàng)目中使用這些類中的每一個。
1. CoreImageFun
打開CoreImageFun.xcodeproj 并運(yùn)行它。 這是一個簡單的應(yīng)用程序,一個帶有圖像和滑塊的單一屏幕。 滑塊還沒有做任何事情,但我們將使用它來展示 CIFilter 的功能。 您還會注意到屏幕右上角的相機(jī)按鈕。 您將在本教程后面使用它來調(diào)出圖像選擇器。

Image-Filtering Basics
您將首先通過 CIFilter 運(yùn)行圖像并將其顯示在屏幕上。每次你想對圖像應(yīng)用 CIFilter 時,你需要做四件事:
- 1) 創(chuàng)建一個
CIImage對象。一個CIImage有幾個初始化方法。在本教程中,您將使用CIImage(image:)從UIImage創(chuàng)建CIImage。瀏覽文檔documentation以了解更多創(chuàng)建 CIImage 的方法。 - 2) 創(chuàng)建一個
CIContext。CIContext可以是基于CPU或GPU的。CIContext的初始化成本很高,因此您可以重用它而不是一遍又一遍地創(chuàng)建它。輸出CIImage對象時總是需要一個。 - 3) 創(chuàng)建一個
CIFilter。創(chuàng)建過濾器時,您會在其上配置一些屬性,這些屬性取決于您使用的過濾器。 - 4) 獲取過濾器輸出。過濾器為您提供作為
CIImage的輸出圖像。您可以使用CIContext將其轉(zhuǎn)換為 UIImage,如下所示。
1. Applying Filter
在理論信息之后,是時候看看它是如何工作的了。將以下代碼添加到 ViewController.swift:
func applySepiaFilter(intensity: Float) {
// 1
guard let uiImage = UIImage(named: "image") else { return }
let ciImage = CIImage(image: uiImage)
// 2
guard let filter = CIFilter(name: "CISepiaTone") else { return }
// 3
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(intensity, forKey: kCIInputIntensityKey)
// 4
guard let outputImage = filter.outputImage else { return }
// 5
let newImage = UIImage(ciImage: outputImage)
imageView.image = newImage
}
這是代碼的作用。 它:
- 1) 創(chuàng)建一個
UIImage并使用它來創(chuàng)建一個CIImage。 - 2) 創(chuàng)建一個
CISepiTone類型的CIFilter。 這是棕褐色調(diào)的類型。 - 3)
CISepiaTone過濾器有兩個值。 首先是一個輸入圖像:kCIInputImageKey,它是一個CIImage實(shí)例。 其次,一個intensity:kCIInputIntensityKey,一個介于 0 和 1 之間的浮點(diǎn)值。如果沒有任何值,大多數(shù)過濾器都使用它們的默認(rèn)值。CIImage是一個例外。 這必須提供一個值,因?yàn)闆]有默認(rèn)值。 - 4) 使用
outputImage屬性從過濾器中獲取一個CIImage。 - 5) 將
CIImage轉(zhuǎn)回UIImage并將其顯示在image view中。
接下來,通過將以下內(nèi)容添加到 viewDidLoad()來調(diào)用添加的新方法:
applySepiaFilter(intensity: 0.5)
這會觸發(fā)強(qiáng)度值為 0.5 的圖像過濾。 在本教程的后面部分,您將使用滑塊來嘗試各種強(qiáng)度值。
構(gòu)建并運(yùn)行項(xiàng)目。 你會看到你的圖像被sepia tone filter過濾:

恭喜,你已經(jīng)很好地使用了 CIImage 和 CIFilters!
Putting it Into Context
在繼續(xù)之前,您應(yīng)該了解一項(xiàng)優(yōu)化。
如上所述,您需要一個 CIContext 來應(yīng)用 CIFilter。 但是在上面的例子中沒有提到這個對象。 事實(shí)證明 UIImage(CIImage:) 為您完成了所有工作。 它創(chuàng)建一個 CIContext并使用它來過濾圖像。 這使得 Core Image API 很容易使用。
有一個主要缺點(diǎn):每次使用它都會創(chuàng)建一個新的 CIContext。 CIContext實(shí)例應(yīng)該可重用以提高性能。 如果要使用滑塊更新過濾器值,則每次更改過濾器時都必須創(chuàng)建一個新的 CIContext。 這種方法會很慢。
首先,將以下屬性添加到 ViewController:
let context = CIContext(options: nil)
CIContext 接受一個可選字典。 它指定諸如顏色格式或上下文是否應(yīng)該在 CPU 或 GPU 上運(yùn)行等選項(xiàng)。 對于這個應(yīng)用程序,默認(rèn)值很好,所以你為那個參數(shù)傳入 nil。
接下來,從 applySepiaFilter(intensity:)中刪除 Step 5 并將其替換為以下內(nèi)容:
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return }
imageView.image = UIImage(cgImage: cgImage)
在這里,您使用 CIContext 繪制一個 CGImage 并使用它來創(chuàng)建一個 UIImage 以顯示在image view中。
構(gòu)建并運(yùn)行。 確保它像以前一樣工作。

在這個例子中,自己處理 CIContext 創(chuàng)建沒有太大區(qū)別。您將了解為什么這樣做對性能很重要,因?yàn)槟谙乱还?jié)中設(shè)置了更改過濾器的能力。
Changing Filter Values
這很棒,但這只是你可以使用 Core Image 濾鏡做的事情的開始。是時候使用圖像下方那個漂亮的滑塊來改變?yōu)V鏡效果了。
您已經(jīng)為 CIContext 實(shí)例添加了一個屬性。現(xiàn)在,您將添加一個屬性來保存過濾器。
已經(jīng)有一個 IBAction 連接到滑塊的 Value Changed 操作。它被稱為 sliderValueChanged(_:)。在此方法中,您將在滑塊值更改時重做圖像過濾器。但是你不想重做整個過程。那將是非常低效的,并且會花費(fèi)太長時間。您需要更改類中的一些內(nèi)容,因此您需要保留在 applySepiaFilter(intensity:) 中創(chuàng)建的一些對象。
在context聲明的正下方添加以下屬性:
let filter = CIFilter(name: "CISepiaTone")!
接下來,在調(diào)用 applySepiaFilter(intensity:)之前將以下內(nèi)容添加到 viewDidLoad() 中:
guard let uiImage = UIImage(named: "image") else { return }
let ciImage = CIImage(image: uiImage)
filter.setValue(ciImage, forKey: kCIInputImageKey)
在這里,您將圖像設(shè)置給濾波器。 你在 applySepiaFilter(intensity:)中的前面這個。 但最好將其移動到 viewDidLoad() 以防止對每個滑塊值更改的調(diào)用。
您將一些代碼移至 viewDidLoad(),因此將 applySepiaFilter(intensity:)替換為以下內(nèi)容:
func applySepiaFilter(intensity: Float) {
filter.setValue(intensity, forKey: kCIInputIntensityKey)
guard let outputImage = filter.outputImage else { return }
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return }
imageView.image = UIImage(cgImage: cgImage)
}
最后,將以下內(nèi)容添加到 sliderValueChanged(_:):
applySepiaFilter(intensity: slider.value)
當(dāng)滑塊值改變時,applySepiaFilter(intensity:) 將以新的強(qiáng)度值運(yùn)行。
您的滑塊設(shè)置為默認(rèn)值:最小 0,最大 1,默認(rèn) 0.5。 多么方便! 這些恰好是這個 CIFilter 的正確值。
構(gòu)建并運(yùn)行。 您應(yīng)該有一個功能正常的實(shí)時滑塊,可以實(shí)時更改圖像的sepia值。

Getting Photos From the Photo Album
現(xiàn)在您可以立即更改過濾器的值,事情變得有趣了! 但是,如果您不喜歡這種花的形象怎么辦? 接下來,您將設(shè)置一個 UIImagePickerController 以從相冊中獲取圖片并進(jìn)入您的應(yīng)用程序,以便您可以使用它們。
已經(jīng)有一個 IBAction 連接到相機(jī)按鈕的 Touch Up Inside 動作。 它被稱為 loadPhoto()。 將以下代碼添加到 loadPhoto():
let picker = UIImagePickerController()
picker.delegate = self
present(picker, animated: true)
第一行代碼實(shí)例化了一個新的 UIImagePickerController。 將圖像選擇器的代理設(shè)置為 self(ViewController),然后展示選擇器。
這里有一個編譯器錯誤。 您需要聲明 ViewController 符合 UIImagePickerControllerDelegate 和 UINavigationControllerDelegate 協(xié)議。
在 ViewController.swift 的底部添加以下擴(kuò)展:
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
}
}
此代理方法返回所選圖像以及info字典中的一些相關(guān)信息。 有關(guān)此字典中各種數(shù)據(jù)的更多詳細(xì)信息,請查看documentation。
構(gòu)建并運(yùn)行該應(yīng)用程序,然后點(diǎn)擊按鈕,該按鈕將顯示圖片選擇器,其中包含您相冊中的照片。

選擇圖像什么都不做。 你即將改變這一點(diǎn)。 將以下代碼添加到 imagePickerController(_:didFinishPickingMediaWithInfo:):
//1
guard let selectedImage = info[.originalImage] as? UIImage else { return }
//2
let ciImage = CIImage(image: selectedImage)
filter.setValue(ciImage, forKey: kCIInputImageKey)
//3
applySepiaFilter(intensity: slider.value)
//4
dismiss(animated: true)
這是一個代碼分解。 它:
- 1) 使用
originalImageUIImagePickerController.InfoKey鍵檢索選擇圖像。 - 2) 將所選圖像應(yīng)用到濾鏡。
- 3) 使用強(qiáng)度的當(dāng)前滑塊值調(diào)用
applySepiaFilter(intensity:)。 這將更新image view。 - 4) 完成對所選圖像的過濾后關(guān)閉圖像選擇器。
構(gòu)建并運(yùn)行。 現(xiàn)在,您將能夠更新相冊中的任何圖像。

What About Image Metadata?
當(dāng)然,是時候談?wù)剤D像元數(shù)據(jù)了。 在手機(jī)上拍攝的圖像文件具有與之相關(guān)的各種數(shù)據(jù),例如 GPS 坐標(biāo)、圖像格式和方向。
尤其是方向是您需要保留的東西。 將 UIImage 加載到 CIImage 中,渲染到 CGImage 并轉(zhuǎn)換回 UIImage 會從圖像中剝離元數(shù)據(jù)。 為了保留方向,你需要記錄它,然后將它傳回 UIImage。
首先向 ViewController.swift 添加一個新屬性:
var orientation = UIImage.Orientation.up
接下來,在調(diào)用 applySepiaFilter(intensity:) 之前,將以下行添加到 imagePickerController(_:didFinishPickingMediaWithInfo:):
orientation = selectedImage.imageOrientation
這會將選定的圖像方向保存到屬性中。
最后,更改 imageView 對象中設(shè)置的 applySepiaFilter(intensity:) 中的行:
imageView.image = UIImage(cgImage: cgImage, scale: 1, orientation: orientation)
現(xiàn)在,如果您以非默認(rèn)方向拍攝照片,應(yīng)用程序會保留它。
What Other Filters Are Available?
CIFilter 最大的優(yōu)勢之一是鏈接過濾器的能力。 為此,您將創(chuàng)建一個專用方法來處理 CIImage 并對其進(jìn)行過濾,使其看起來像一張舊照片。
CIFilter API在macOS上有160多個過濾器,其中大部分在 iOS 上也可用。 現(xiàn)在也可以創(chuàng)建自定義過濾器。要查看任何可用的過濾器或?qū)傩?,請查?a target="_blank">documentation。
1. Creating Old Photo Filter
在 ViewController 中添加以下方法:
func applyOldPhotoFilter(intensity: Float) {
// 1
filter.setValue(intensity, forKey: kCIInputIntensityKey)
// 2
let random = CIFilter(name: "CIRandomGenerator")
// 3
let lighten = CIFilter(name: "CIColorControls")
lighten?.setValue(random?.outputImage, forKey: kCIInputImageKey)
lighten?.setValue(1 - intensity, forKey: kCIInputBrightnessKey)
lighten?.setValue(0, forKey: kCIInputSaturationKey)
// 4
guard let ciImage = filter.value(forKey: kCIInputImageKey) as? CIImage else { return }
let croppedImage = lighten?.outputImage?.cropped(to: ciImage.extent)
// 5
let composite = CIFilter(name: "CIHardLightBlendMode")
composite?.setValue(filter.outputImage, forKey: kCIInputImageKey)
composite?.setValue(croppedImage, forKey: kCIInputBackgroundImageKey)
// 6
let vignette = CIFilter(name: "CIVignette")
vignette?.setValue(composite?.outputImage, forKey: kCIInputImageKey)
vignette?.setValue(intensity * 2, forKey: kCIInputIntensityKey)
vignette?.setValue(intensity * 30, forKey: kCIInputRadiusKey)
// 7
guard let outputImage = vignette?.outputImage else { return }
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return }
imageView.image = UIImage(cgImage: cgImage, scale: 1, orientation: orientation)
}
這是正在做的事情 :
- 1) 在您之前使用的
sepia-tone中設(shè)置強(qiáng)度。 - 2) 建立一個過濾器,創(chuàng)建一個如下所示的隨機(jī)噪聲模式:

它不帶任何參數(shù)。您將使用此噪點(diǎn)模式為最終的“舊照片”外觀添加紋理。
3) 改變隨機(jī)噪聲發(fā)生器的輸出。您想將其更改為灰度并使其變亮一點(diǎn),因此效果不那么引人注目。輸入圖像鍵設(shè)置為隨機(jī)過濾器的
outputImage屬性。這是一種將一個過濾器的輸出作為下一個過濾器的輸入傳遞的便捷方式。4) 已
cropped(to:)獲取輸出CIImage并將其裁剪為提供的矩形。在這種情況下,您需要裁剪CIRandomGenerator過濾器的輸出,因?yàn)樗鼤恢背掷m(xù)下去。如果你在某個時候不裁剪它,你會得到一個錯誤,說過濾器有“an infinite extent”。CIImages實(shí)際上并不包含圖像數(shù)據(jù);他們描述了創(chuàng)建它的“配方”。直到您調(diào)用CIContext上的方法才處理數(shù)據(jù)。5) 結(jié)合
sepia和CIRandomGenerator過濾器的輸出。后者執(zhí)行與Adobe Photoshop圖層中的“Hard Light”設(shè)置相同的操作。Photoshop中的大多數(shù)(如果不是全部)濾鏡選項(xiàng)都可以使用Core Image實(shí)現(xiàn)。6) 在此合成輸出上運(yùn)行
vignette濾鏡,使照片的邊緣變暗。您可以使用強(qiáng)度值來設(shè)置此效果的半徑和強(qiáng)度。7) 獲取輸出圖像并將其設(shè)置給
image view。
2. Applying Old Photo Filter
這就是這個過濾器鏈的全部內(nèi)容。 您現(xiàn)在已經(jīng)了解這些過濾器鏈可能變得多么復(fù)雜。 您可以將 Core Image 濾鏡組合到這些類型的鏈中,這樣您就可以實(shí)現(xiàn)無窮無盡的各種效果。
如果您想查看所有這些操作,請將所有對 applySepiaFilter(intensity:) 的調(diào)用替換為 applyOldPhotoFilter(intensity:)。
構(gòu)建并運(yùn)行。 您應(yīng)該獲得更精致的舊照片效果,包括棕褐色、少許噪點(diǎn)和一些暗角。

親愛的讀者,這種噪音可能會更微妙,但如何完善取決于您。 現(xiàn)在,您可以使用 Core Image 的全部功能。 瘋了!
這大約涵蓋了使用 Core Image 過濾器的基礎(chǔ)知識。 這是一種非常方便的技術(shù),您應(yīng)該能夠使用它快速地對圖像應(yīng)用一些簡潔的過濾器。
此外,此站點(diǎn)上還有更多 Core Image 教程,包括:
您可以查看 Core Image filter reference documentation以獲取有關(guān)所有過濾器的說明。
后記
本篇主要講述了使用
Core Image和Swift學(xué)習(xí)圖像過濾效果的基礎(chǔ)知識,感興趣的給個贊或者關(guān)注~~~
