GPUImage2(一)集成與使用

關鍵詞


濾鏡 GPUImage 顏色 Filter colorDistance 相機 識別 框架 紋理 渲染

本文所有示例代碼或Demo可以在此獲?。?a target="_blank" rel="nofollow">https://github.com/WillieWangWei/SampleCode_GPUImage2_Usage

如果本文對你有所幫助,請給個Star??

相關文章
GPUImage2(二)濾鏡大全:圖像生成
GPUImage2(三)濾鏡大全:色彩調校
GPUImage2(四)濾鏡大全:圖像處理
GPUImage2(五)濾鏡大全:混合模式
GPUImage2(六)濾鏡大全:視覺特效

概述


GPUImage是一個基于OpenGL ES 2.0的開源的圖像處理庫,作者是Brad Larson。GPUImageOpenGL ES封裝為簡潔的Objective-CSwift接口,可以用來給圖像、實時相機視頻、電影等添加濾鏡。對于諸如處理圖像或實況視頻幀的大規模并行操作,GPU相對于CPU具有一些顯著的性能優點。在iPhone 4上,簡單的圖像濾鏡在GPU上的執行速度比等效的基于CPU的濾鏡快100多倍。

目前它有兩個版本:

  1. GPUImage。開發者使用最多的版本,它于2012年最早推出,使用Objective-C編寫,支持macOSiOS。
  2. GPUImage2。同一作者在2016年推出的版本,使用Swift編寫,是GPUImage框架的第二代,支持macOS、iOSSwift代碼的Linux或未來平臺。

本文以Swift版的GPUImage2為主題,從以下幾個方面進行講解:

  • 在項目中集成
  • 特性
  • 示例代碼
  • 注意問題

在項目中集成


  1. 下載壓縮包文件,下載地址。
  2. 解壓后目錄如下:
    文件目錄

    framework下的GPUImage-iOS.xcodeproj項目和Source文件夾復制到你的項目中。
  3. 在你的項目的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

    確認現在你的項目文件夾中存在GPUImage-iOS.xcodeprojSource,像是這樣:
    編譯條件

    4.如果前幾步沒有問題,現在Build。稍等會提示成功,但出現了一些警告:
    過期警告

    這是因為使用了過期的函數,但暫時不會造成功能上的問題。如果你覺得不爽,可以參考如何忽略警告。

特性


GPUImage2可以進行多種模式的圖像處理,其邏輯類似于流水線的概念。流水線上有若干個工位(Filter),每個工位接收來自上一個工位的產品(Data),完成此工序的加工(Processing)后交給下一個工位(Target)處理。產品從開始端(Input)經過整條流水線加工,到達結束端(Output)變為成品。

處理流程

雖然功能和GPUImage相似,但GPUImage2使用了大量Swift語言的特性,在命名規則、代碼風格上都產生了很大的差別,比如:

-->運算符

-->GPUImage2定義的一個中綴運算符,它將兩個對象像鏈條一樣串聯起來,用起來像是這樣:

camera --> basicOperation --> renderView

左邊的參數遵循ImageSource協議,作為數據的輸入,右邊的參數遵循ImageConsumer協議,作為數據的輸出。這里的basicOperationBasicOperation的一個實例,其父類ImageProcessingOperation同時遵循ImageSourceImageConsumer協議,所以它可以放在-->的左邊或右邊。
-->的運算是左結合的,類似于GPUImage中的addTarget方法,但是-->有一個返回值,就是右邊的參數。在上面的示例中,先計算了前半部camera --> basicOperation,然后右邊的參數basicOperation作為返回值又參與了后半部basicOperation --> renderView的計算。
-->體現了鏈式編程的思想,讓代碼更加優雅,在GPUImage2有著大量運用,這得益于Swift強大的語法,關于Swift中的高級運算符,請看這里。

示例代碼


GPUImage2主要提供了這些功能:

  • 處理靜態圖片
  • 操作組
  • 實時視頻濾鏡
  • 從視頻中捕獲圖片
  • 編寫自定義的圖像處理操作
  • 從靜態圖片中捕獲并添加濾鏡(即將實現)
  • 添加濾鏡并轉碼視頻(即將實現)
準備

導入頭文件

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)
}
處理靜態圖片

創建濾鏡實例

// 創建一個BrightnessAdjustment顏色處理濾鏡
let brightnessAdjustment = BrightnessAdjustment()
brightnessAdjustment.brightness = 0.2

// 創建一個ExposureAdjustment顏色處理濾鏡
let exposureAdjustment = ExposureAdjustment()
exposureAdjustment.exposure = 0.5

使用GPUImageUIImage提供的擴展方法進行便利濾鏡處理

// 1.使用GPUImage對UIImage的擴展方法進行濾鏡處理
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

注意:如果要將圖片顯示在屏幕上或者進行多次濾鏡處理時,以上方法會讓Core Graphics產生更多開銷,建議使用處理鏈,最后指向RenderView來顯示,如下:

// 2.使用管道處理

// 創建圖片輸入
let pictureInput = PictureInput(image: imageView.image!)
// 創建圖片輸出
let pictureOutput = PictureOutput()
// 給閉包賦值
pictureOutput.imageAvailableCallback = { image in
    // 這里的image是處理完的數據,UIImage類型
}
// 綁定處理鏈
pictureInput --> brightnessAdjustment --> exposureAdjustment --> pictureOutput
// 開始處理 synchronously: true 同步執行 false 異步執行,處理完畢后會調用imageAvailableCallback這個閉包
pictureInput.processImage(synchronously: true)
操作組

你可以將若干個BasicOperation的實例包裝成一個OperationGroup操作組,通過給閉包賦值來定義組內濾鏡的處理流程,外部可以將OperationGroup的實例作為一個獨立單位參與其他濾鏡處理。

// MARK: - 操作組
func operationGroup() {
    
    // 創建一個BrightnessAdjustment顏色處理濾鏡
    let brightnessAdjustment = BrightnessAdjustment()
    brightnessAdjustment.brightness = 0.2
    
    // 創建一個ExposureAdjustment顏色處理濾鏡
    let exposureAdjustment = ExposureAdjustment()
    exposureAdjustment.exposure = 0.5
    
    // 創建一個操作組
    let operationGroup = OperationGroup()
    
    // 給閉包賦值,綁定處理鏈
    operationGroup.configureGroup{input, output in
        input --> brightnessAdjustment --> exposureAdjustment --> output
    }

    // 進行濾鏡處理
    imageView.image = imageView.image!.filterWithOperation(operationGroup)
}
實時視頻濾鏡

從相機中獲取圖像數據,經過濾鏡處理后實時的顯示在屏幕上。

// MARK: - 實時視頻濾鏡
func CameraFiltering() {
    
    // Camera的構造函數是可拋出錯誤的
    do {
        // 創建一個Camera的實例,Camera遵循ImageSource協議,用來從相機捕獲數據
        
        /// Camera的指定構造器
        ///
        /// - Parameters:
        ///   - sessionPreset: 捕獲視頻的分辨率
        ///   - cameraDevice: 相機設備,默認為nil
        ///   - location: 前置相機還是后置相機,默認為.backFacing
        ///   - captureAsYUV: 是否采集為YUV顏色編碼,默認為true
        /// - Throws: AVCaptureDeviceInput構造錯誤
        camera = try Camera(sessionPreset: AVCaptureSessionPreset1280x720,
                            cameraDevice: nil,
                            location: .backFacing,
                            captureAsYUV: true)
        
        // Camera的指定構造器是有默認參數的,可以只傳入sessionPreset參數
        // camera = try Camera(sessionPreset: AVCaptureSessionPreset1280x720)
        
    } catch {
        
        print(error)
        return
    }
    
    // 創建一個Luminance顏色處理濾鏡
    basicOperation = Luminance()
    
    // 創建一個RenderView的實例并添加到view上,用來顯示最終處理出的內容
    renderView = RenderView(frame: view.bounds)
    view.addSubview(renderView)
    
    // 綁定處理鏈
    camera --> basicOperation --> renderView
    
    // 開始捕捉數據
    camera.startCapture()
    
    // 結束捕捉數據
    // camera.stopCapture()
}
從視頻中捕獲圖片

從視頻中獲取某一幀的圖片,可以以任一濾鏡節點作為數據源。

// MARK: - 從實時視頻中截圖圖片
func captureImageFromVideo() {
    
    // 啟動實時視頻濾鏡
    self.cameraFiltering()
    
    // 設置保存路徑
    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執行,防止截到黑屏
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(1)) {
        
        // 保存相機捕捉到的圖片
        self.camera.saveNextFrameToURL(originalURL, format: .png)
        
        // 保存濾鏡后的圖片
        self.basicOperation.saveNextFrameToURL(filteredlURL, format: .png)
        
        // 如果需要處理回調,有下面兩種寫法
        
        let dataOutput = PictureOutput()
        dataOutput.encodedImageFormat = .png
        dataOutput.encodedImageAvailableCallback = {imageData in
            // 這里的imageData是截取到的數據,Data類型
        }
        self.camera --> dataOutput
        
        let imageOutput = PictureOutput()
        imageOutput.encodedImageFormat = .png
        imageOutput.imageAvailableCallback = {image in
            // 這里的image是截取到的數據,UIImage類型
        }
        self.camera --> imageOutput
    }
}
編寫自定義的圖像處理操作

自定義濾鏡需要使用OpenGL著色語言(GLSL)編寫Fragment Shader(片段著色器),調用BasicOperation的構造器讀取寫好的文件,可以創建自定義濾鏡。

// MARK: - 編寫自定義的圖像處理操作
func customFilter() {
    
    // 獲取文件路徑
    let url = URL(fileURLWithPath: Bundle.main.path(forResource: "Custom", ofType: "fsh")!)
    
    var customFilter: BasicOperation
    
    do {
        // 從文件中創建自定義濾鏡
        customFilter = try BasicOperation(fragmentShaderFile: url)
    } catch {
        
        print(error)
        return
    }
    
    // 進行濾鏡處理
    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;
}
從靜態圖片中捕獲并添加濾鏡

作者暫未實現

添加濾鏡并轉碼視頻

作者暫未實現

注意問題

使用Cocoapods安裝

作者暫不支持。但是有網友制作了EVGPUImage2這個倉庫來間接使用GPUImage2,有興趣可以嘗試一下。

使用ACV文件創建濾鏡

GPUImage中可以通過ACV文件快速創建自定義濾鏡。AVC可以通過photoShop進行圖片顏色曲線處理得到,但是GPUImage2暫未移植這個功能。

與Core Image比較

Core Image是iOS內置的圖像處理框架,兩者相比各有優點:

GPUImage 優勢

  • 最低支持 iOS 4.0,iOS 5.0 之后就支持自定義濾鏡。
  • 在低端機型上,GPUImage 有更好的表現。(這個我沒用真正的設備對比過,GPUImage 的主頁上是這么說的)
  • GPUImage 在視頻處理上有更好的表現。
  • GPUImage 的代碼完成公開,實現透明。
  • 可以根據自己的業務需求,定制更加復雜的管線操作。可定制程度高。

Core Image 優勢

  • 官方框架,使用放心,維護方便。
  • 支持 CPU 渲染,可以在后臺繼續處理和保存圖片。
  • 一些濾鏡的性能更強勁。例如由 Metal Performance Shaders 支持的模糊濾鏡等。
  • 支持使用 Metal 渲染圖像。而 Metal 在 iOS 平臺上有更好的表現。
  • 與 Metal,SpriteKit,SceneKit,Core Animation 等更完美的配合。
  • 支持圖像識別功能。包括人臉識別、條形碼識別、文本識別等。
  • 支持自動增強圖像效果,會分析圖像的直方圖,圖像屬性,臉部區域,然后通過一組濾鏡來改善圖像效果。
  • 支持對原生 RAW 格式圖片的處理。
  • 濾鏡鏈的性能比 GPUImage 高。(沒有驗證過,GPUImage 的主頁上是這么說的)。
  • 支持對大圖進行處理,超過 GPU 紋理限制 (4096 * 4096)的時候,會自動拆分成幾個小塊處理(Automatic tiling)。GPUImage 當處理超過紋理限制的圖像時候,會先做判斷,壓縮成最大紋理限制的圖像,導致圖像質量損失。

總結

GPUImage是一套主流的圖像處理框架,很多直播、美圖APP都采用此技術,當你的項目是以Swift為主時,GPUImage2就是你的首選。
當然,你可以根據業務需要決定使用GPUImage還是Core Image,它們都是相當成熟的工具。

本文所有示例代碼或Demo可以在此獲?。?a target="_blank" rel="nofollow">https://github.com/WillieWangWei/SampleCode_GPUImageUsage.git

如果本文對你有所幫助,請給個Star??

相關文章
GPUImage2(二)濾鏡大全:圖像生成
GPUImage2(三)濾鏡大全:色彩調校
GPUImage2(四)濾鏡大全:圖像處理
GPUImage2(五)濾鏡大全:混合模式
GPUImage2(六)濾鏡大全:視覺特效

參考資料:
https://github.com/BradLarson/GPUImage2
https://colin1994.github.io/2016/10/21/Core-Image-OverView/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容