關(guān)鍵詞
濾鏡 GPUImage 顏色 Filter colorDistance 相機(jī) 識別 框架 紋理 渲染
本文所有示例代碼或Demo可以在此獲鹊擞取:https://github.com/WillieWangWei/SampleCode_GPUImage2_Usage
如果本文對你有所幫助片择,請給個Star??
相關(guān)文章
GPUImage2(二)濾鏡大全:圖像生成
GPUImage2(三)濾鏡大全:色彩調(diào)校
GPUImage2(四)濾鏡大全:圖像處理
GPUImage2(五)濾鏡大全:混合模式
GPUImage2(六)濾鏡大全:視覺特效
概述
GPUImage
是一個基于OpenGL ES 2.0
的開源的圖像處理庫献烦,作者是Brad Larson卖丸。GPUImage
將OpenGL ES
封裝為簡潔的Objective-C
或Swift
接口识椰,可以用來給圖像列牺、實時相機(jī)視頻、電影等添加濾鏡恃轩。對于諸如處理圖像或?qū)崨r視頻幀的大規(guī)模并行操作,GPU相對于CPU具有一些顯著的性能優(yōu)點黎做。在iPhone 4上叉跛,簡單的圖像濾鏡在GPU上的執(zhí)行速度比等效的基于CPU的濾鏡快100多倍。
目前它有兩個版本:
-
GPUImage蒸殿。開發(fā)者使用最多的版本筷厘,它于2012年最早推出,使用
Objective-C
編寫宏所,支持macOS
和iOS
酥艳。 -
GPUImage2。同一作者在2016年推出的版本楣铁,使用
Swift
編寫玖雁,是GPUImage
框架的第二代,支持macOS
盖腕、iOS
和Swift
代碼的Linux
或未來平臺赫冬。
本文以Swift
版的GPUImage2
為主題,從以下幾個方面進(jìn)行講解:
- 在項目中集成
- 特性
- 示例代碼
- 注意問題
在項目中集成
- 下載壓縮包文件溃列,下載地址劲厌。
- 解壓后目錄如下:
文件目錄
將framework
下的GPUImage-iOS.xcodeproj
項目和Source
文件夾復(fù)制到你的項目中。 - 在你的項目的
Build Phases
欄听隐,Target Dependency
中添加GPUImage
依賴补鼻。
Target Dependency
在下面的Link Binary With Libraries
中添加GPUImage
。
Link Binary With Libraries
點擊左上角的+
,選擇New Copy Files Phase
风范,在新建的Copy Files
中將Destination
選為Frameworks
咨跌,并在欄目中添加GPUImage.framework
。
Copy Files
確認(rèn)現(xiàn)在你的項目文件夾中存在GPUImage-iOS.xcodeproj
和Source
硼婿,像是這樣:
編譯條件
4.如果前幾步?jīng)]有問題锌半,現(xiàn)在Build
。稍等會提示成功寇漫,但出現(xiàn)了一些警告:
過期警告
這是因為使用了過期的函數(shù)刊殉,但暫時不會造成功能上的問題。如果你覺得不爽州胳,可以參考如何忽略警告记焊。
特性
GPUImage2
可以進(jìn)行多種模式的圖像處理,其邏輯類似于流水線的概念栓撞。流水線上有若干個工位(Filter
)遍膜,每個工位接收來自上一個工位的產(chǎn)品(Data
),完成此工序的加工(Processing
)后交給下一個工位(Target
)處理腐缤。產(chǎn)品從開始端(Input
)經(jīng)過整條流水線加工捌归,到達(dá)結(jié)束端(Output
)變?yōu)槌善贰?/p>
雖然功能和GPUImage
相似,但GPUImage2
使用了大量Swift
語言的特性岭粤,在命名規(guī)則惜索、代碼風(fēng)格上都產(chǎn)生了很大的差別,比如:
-->運(yùn)算符
-->
是GPUImage2
定義的一個中綴運(yùn)算符
剃浇,它將兩個對象像鏈條一樣串聯(lián)起來巾兆,用起來像是這樣:
camera --> basicOperation --> renderView
左邊的參數(shù)遵循ImageSource
協(xié)議,作為數(shù)據(jù)的輸入虎囚,右邊的參數(shù)遵循ImageConsumer
協(xié)議角塑,作為數(shù)據(jù)的輸出。這里的basicOperation
是BasicOperation
的一個實例淘讥,其父類ImageProcessingOperation
同時遵循ImageSource
和ImageConsumer
協(xié)議圃伶,所以它可以放在-->
的左邊或右邊。
-->
的運(yùn)算是左結(jié)合的蒲列,類似于GPUImage
中的addTarget
方法窒朋,但是-->
有一個返回值,就是右邊的參數(shù)蝗岖。在上面的示例中侥猩,先計算了前半部camera --> basicOperation
,然后右邊的參數(shù)basicOperation
作為返回值又參與了后半部basicOperation --> renderView
的計算抵赢。
-->
體現(xiàn)了鏈?zhǔn)骄幊痰乃枷肫劾停尨a更加優(yōu)雅唧取,在GPUImage2
有著大量運(yùn)用,這得益于Swift
強(qiáng)大的語法划提,關(guān)于Swift
中的高級運(yùn)算符枫弟,請看這里。
示例代碼
GPUImage2
主要提供了這些功能:
- 處理靜態(tài)圖片
- 操作組
- 實時視頻濾鏡
- 從視頻中捕獲圖片
- 編寫自定義的圖像處理操作
- 從靜態(tài)圖片中捕獲并添加濾鏡(即將實現(xiàn))
- 添加濾鏡并轉(zhuǎn)碼視頻(即將實現(xiàn))
準(zhǔn)備
導(dǎo)入頭文件
import GPUImage
import AVFoundation
聲明變量
var camera: Camera!
var basicOperation: BasicOperation!
var renderView: RenderView!
lazy var imageView: UIImageView = {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
imageView.image = UIImage(contentsOfFile: Bundle.main.path(forResource: "Yui", ofType: "jpg")!)
imageView.contentMode = .scaleAspectFit
return imageView
}()
初始化
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(imageView)
}
處理靜態(tài)圖片
創(chuàng)建濾鏡實例
// 創(chuàng)建一個BrightnessAdjustment顏色處理濾鏡
let brightnessAdjustment = BrightnessAdjustment()
brightnessAdjustment.brightness = 0.2
// 創(chuàng)建一個ExposureAdjustment顏色處理濾鏡
let exposureAdjustment = ExposureAdjustment()
exposureAdjustment.exposure = 0.5
使用GPUImage
對UIImage
提供的擴(kuò)展方法進(jìn)行便利濾鏡處理
// 1.使用GPUImage對UIImage的擴(kuò)展方法進(jìn)行濾鏡處理
var filteredImage: UIImage
// 1.1單一濾鏡
filteredImage = imageView.image!.filterWithOperation(brightnessAdjustment)
// 1.2多個濾鏡疊加
filteredImage = imageView.image!.filterWithPipeline { (input, output) in
input --> brightnessAdjustment --> exposureAdjustment --> output
}
// 不建議的
imageView.image = filteredImage
注意:如果要將圖片顯示在屏幕上或者進(jìn)行多次濾鏡處理時腔剂,以上方法會讓
Core Graphics
產(chǎn)生更多開銷媒区,建議使用處理鏈驼仪,最后指向RenderView
來顯示掸犬,如下:
// 2.使用管道處理
// 創(chuàng)建圖片輸入
let pictureInput = PictureInput(image: imageView.image!)
// 創(chuàng)建圖片輸出
let pictureOutput = PictureOutput()
// 給閉包賦值
pictureOutput.imageAvailableCallback = { image in
// 這里的image是處理完的數(shù)據(jù),UIImage類型
}
// 綁定處理鏈
pictureInput --> brightnessAdjustment --> exposureAdjustment --> pictureOutput
// 開始處理 synchronously: true 同步執(zhí)行 false 異步執(zhí)行绪爸,處理完畢后會調(diào)用imageAvailableCallback這個閉包
pictureInput.processImage(synchronously: true)
操作組
你可以將若干個BasicOperation
的實例包裝成一個OperationGroup
操作組湾碎,通過給閉包賦值來定義組內(nèi)濾鏡的處理流程,外部可以將OperationGroup
的實例作為一個獨立單位參與其他濾鏡處理奠货。
// MARK: - 操作組
func operationGroup() {
// 創(chuàng)建一個BrightnessAdjustment顏色處理濾鏡
let brightnessAdjustment = BrightnessAdjustment()
brightnessAdjustment.brightness = 0.2
// 創(chuàng)建一個ExposureAdjustment顏色處理濾鏡
let exposureAdjustment = ExposureAdjustment()
exposureAdjustment.exposure = 0.5
// 創(chuàng)建一個操作組
let operationGroup = OperationGroup()
// 給閉包賦值介褥,綁定處理鏈
operationGroup.configureGroup{input, output in
input --> brightnessAdjustment --> exposureAdjustment --> output
}
// 進(jìn)行濾鏡處理
imageView.image = imageView.image!.filterWithOperation(operationGroup)
}
實時視頻濾鏡
從相機(jī)中獲取圖像數(shù)據(jù),經(jīng)過濾鏡處理后實時的顯示在屏幕上递惋。
// MARK: - 實時視頻濾鏡
func CameraFiltering() {
// Camera的構(gòu)造函數(shù)是可拋出錯誤的
do {
// 創(chuàng)建一個Camera的實例柔滔,Camera遵循ImageSource協(xié)議,用來從相機(jī)捕獲數(shù)據(jù)
/// Camera的指定構(gòu)造器
///
/// - Parameters:
/// - sessionPreset: 捕獲視頻的分辨率
/// - cameraDevice: 相機(jī)設(shè)備萍虽,默認(rèn)為nil
/// - location: 前置相機(jī)還是后置相機(jī)睛廊,默認(rèn)為.backFacing
/// - captureAsYUV: 是否采集為YUV顏色編碼,默認(rèn)為true
/// - Throws: AVCaptureDeviceInput構(gòu)造錯誤
camera = try Camera(sessionPreset: AVCaptureSessionPreset1280x720,
cameraDevice: nil,
location: .backFacing,
captureAsYUV: true)
// Camera的指定構(gòu)造器是有默認(rèn)參數(shù)的杉编,可以只傳入sessionPreset參數(shù)
// camera = try Camera(sessionPreset: AVCaptureSessionPreset1280x720)
} catch {
print(error)
return
}
// 創(chuàng)建一個Luminance顏色處理濾鏡
basicOperation = Luminance()
// 創(chuàng)建一個RenderView的實例并添加到view上超全,用來顯示最終處理出的內(nèi)容
renderView = RenderView(frame: view.bounds)
view.addSubview(renderView)
// 綁定處理鏈
camera --> basicOperation --> renderView
// 開始捕捉數(shù)據(jù)
camera.startCapture()
// 結(jié)束捕捉數(shù)據(jù)
// camera.stopCapture()
}
從視頻中捕獲圖片
從視頻中獲取某一幀的圖片,可以以任一濾鏡節(jié)點作為數(shù)據(jù)源邓馒。
// MARK: - 從實時視頻中截圖圖片
func captureImageFromVideo() {
// 啟動實時視頻濾鏡
self.cameraFiltering()
// 設(shè)置保存路徑
guard let outputPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else { return }
let originalPath = outputPath + "/originalImage.png"
print("path: \(originalPath)")
let originalURL = URL(fileURLWithPath: originalPath)
let filteredPath = outputPath + "/filteredImage.png"
print("path: \(filteredPath)")
let filteredlURL = URL(fileURLWithPath: filteredPath)
// 延遲1s執(zhí)行嘶朱,防止截到黑屏
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(1)) {
// 保存相機(jī)捕捉到的圖片
self.camera.saveNextFrameToURL(originalURL, format: .png)
// 保存濾鏡后的圖片
self.basicOperation.saveNextFrameToURL(filteredlURL, format: .png)
// 如果需要處理回調(diào),有下面兩種寫法
let dataOutput = PictureOutput()
dataOutput.encodedImageFormat = .png
dataOutput.encodedImageAvailableCallback = {imageData in
// 這里的imageData是截取到的數(shù)據(jù)光酣,Data類型
}
self.camera --> dataOutput
let imageOutput = PictureOutput()
imageOutput.encodedImageFormat = .png
imageOutput.imageAvailableCallback = {image in
// 這里的image是截取到的數(shù)據(jù)疏遏,UIImage類型
}
self.camera --> imageOutput
}
}
編寫自定義的圖像處理操作
自定義濾鏡需要使用OpenGL著色語言
(GLSL)編寫Fragment Shader
(片段著色器),調(diào)用BasicOperation
的構(gòu)造器讀取寫好的文件救军,可以創(chuàng)建自定義濾鏡财异。
// MARK: - 編寫自定義的圖像處理操作
func customFilter() {
// 獲取文件路徑
let url = URL(fileURLWithPath: Bundle.main.path(forResource: "Custom", ofType: "fsh")!)
var customFilter: BasicOperation
do {
// 從文件中創(chuàng)建自定義濾鏡
customFilter = try BasicOperation(fragmentShaderFile: url)
} catch {
print(error)
return
}
// 進(jìn)行濾鏡處理
imageView.image = imageView.image!.filterWithOperation(customFilter)
}
Custom.fsh
文件像是這樣:
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{
highp vec2 sampleDivisor = vec2(1.0 / 200.0, 1.0 / 320.0);
//highp vec4 colorDivisor = vec4(colorDepth);
highp vec2 samplePos = textureCoordinate - mod(textureCoordinate, sampleDivisor);
highp vec4 color = texture2D(inputImageTexture, samplePos );
//gl_FragColor = texture2D(inputImageTexture, samplePos );
mediump vec4 colorCyan = vec4(85.0 / 255.0, 1.0, 1.0, 1.0);
mediump vec4 colorMagenta = vec4(1.0, 85.0 / 255.0, 1.0, 1.0);
mediump vec4 colorWhite = vec4(1.0, 1.0, 1.0, 1.0);
mediump vec4 colorBlack = vec4(0.0, 0.0, 0.0, 1.0);
mediump vec4 endColor;
highp float blackDistance = distance(color, colorBlack);
highp float whiteDistance = distance(color, colorWhite);
highp float magentaDistance = distance(color, colorMagenta);
highp float cyanDistance = distance(color, colorCyan);
mediump vec4 finalColor;
highp float colorDistance = min(magentaDistance, cyanDistance);
colorDistance = min(colorDistance, whiteDistance);
colorDistance = min(colorDistance, blackDistance);
if (colorDistance == blackDistance) {
finalColor = colorBlack;
} else if (colorDistance == whiteDistance) {
finalColor = colorWhite;
} else if (colorDistance == cyanDistance) {
finalColor = colorCyan;
} else {
finalColor = colorMagenta;
}
gl_FragColor = finalColor;
}
從靜態(tài)圖片中捕獲并添加濾鏡
作者暫未實現(xiàn)
添加濾鏡并轉(zhuǎn)碼視頻
作者暫未實現(xiàn)
注意問題
使用Cocoapods安裝
作者暫不支持。但是有網(wǎng)友制作了EVGPUImage2
這個倉庫來間接使用GPUImage2
缤言,有興趣可以嘗試一下宝当。
使用ACV文件創(chuàng)建濾鏡
在GPUImage
中可以通過ACV文件快速創(chuàng)建自定義濾鏡。AVC可以通過photoShop
進(jìn)行圖片顏色曲線處理得到胆萧,但是GPUImage2
暫未移植這個功能庆揩。
與Core Image比較
Core Image
是iOS內(nèi)置的圖像處理框架俐东,兩者相比各有優(yōu)點:
GPUImage 優(yōu)勢
- 最低支持 iOS 4.0,iOS 5.0 之后就支持自定義濾鏡订晌。
- 在低端機(jī)型上虏辫,GPUImage 有更好的表現(xiàn)。(這個我沒用真正的設(shè)備對比過锈拨,GPUImage 的主頁上是這么說的)
- GPUImage 在視頻處理上有更好的表現(xiàn)砌庄。
- GPUImage 的代碼完成公開,實現(xiàn)透明奕枢。
- 可以根據(jù)自己的業(yè)務(wù)需求娄昆,定制更加復(fù)雜的管線操作》毂颍可定制程度高萌焰。
Core Image 優(yōu)勢
- 官方框架,使用放心谷浅,維護(hù)方便扒俯。
- 支持 CPU 渲染,可以在后臺繼續(xù)處理和保存圖片一疯。
- 一些濾鏡的性能更強(qiáng)勁撼玄。例如由 Metal Performance Shaders 支持的模糊濾鏡等。
- 支持使用 Metal 渲染圖像墩邀。而 Metal 在 iOS 平臺上有更好的表現(xiàn)掌猛。
- 與 Metal,SpriteKit磕蒲,SceneKit留潦,Core Animation 等更完美的配合。
- 支持圖像識別功能辣往。包括人臉識別兔院、條形碼識別、文本識別等站削。
- 支持自動增強(qiáng)圖像效果坊萝,會分析圖像的直方圖,圖像屬性许起,臉部區(qū)域十偶,然后通過一組濾鏡來改善圖像效果。
- 支持對原生 RAW 格式圖片的處理园细。
- 濾鏡鏈的性能比 GPUImage 高惦积。(沒有驗證過,GPUImage 的主頁上是這么說的)猛频。
- 支持對大圖進(jìn)行處理狮崩,超過 GPU 紋理限制 (4096 * 4096)的時候蛛勉,會自動拆分成幾個小塊處理(Automatic tiling)。GPUImage 當(dāng)處理超過紋理限制的圖像時候睦柴,會先做判斷诽凌,壓縮成最大紋理限制的圖像,導(dǎo)致圖像質(zhì)量損失坦敌。
總結(jié)
GPUImage
是一套主流的圖像處理框架侣诵,很多直播、美圖APP都采用此技術(shù)狱窘,當(dāng)你的項目是以Swift
為主時杜顺,GPUImage2
就是你的首選。
當(dāng)然训柴,你可以根據(jù)業(yè)務(wù)需要決定使用GPUImage
還是Core Image
哑舒,它們都是相當(dāng)成熟的工具。
本文所有示例代碼或Demo可以在此獲然媚佟:https://github.com/WillieWangWei/SampleCode_GPUImageUsage.git
如果本文對你有所幫助,請給個Star??
相關(guān)文章
GPUImage2(二)濾鏡大全:圖像生成
GPUImage2(三)濾鏡大全:色彩調(diào)校
GPUImage2(四)濾鏡大全:圖像處理
GPUImage2(五)濾鏡大全:混合模式
GPUImage2(六)濾鏡大全:視覺特效
參考資料:
https://github.com/BradLarson/GPUImage2
https://colin1994.github.io/2016/10/21/Core-Image-OverView/