GPUImage2的使用


GPUImage是一個基於OpenGL ES 2.0的開源的圖像處理庫,作者是Brad Larson。GPUImage將OpenGL ES封裝為簡潔的Objective-C或Swift接口,可以用來給圖像、實時相機視頻、電影等添加濾鏡。對於諸如處理圖像或實況視頻幀的大規模並行操作,GPU相對於CPU具有一些顯着的性能優點。在iPhone 4上,簡單的圖像濾鏡在GPU上的執行速度比等效的基於CPU的濾鏡快100多倍,與Core Image (ios5.0的一部分)相比,GPUImage允許您編寫自己的自定義濾鏡,支持部署到ios4.0,並且有一個更簡單的界面。然而,它目前缺乏核心圖像的一些更高級的特征,比如面部檢測等功能。

 我發現在創建和使用OpenGLES過程中需要編寫大量的樣板代碼。因此,GPUImage封裝了處理圖像和視頻時會遇到的許多常見任務,這樣就不需要關心OpenGL ES 2.0的基礎了。

 GPUImage有三個版本,GPUImage是基於OpenGL ES 使用OC語言寫的,對於OC的項目可以集成並使用它,GPUImage2是用Swift語言寫的基於OpenGL ES2.0,GPUImage3是基於蘋果的圖像渲染框架Metal封裝的,語言也是swift,可以根據自己的需求集成不同版本的GPUImage,本篇介紹GPUImage2使用,語言為Swift。

一、處理靜態圖片使用濾鏡

class StillImageViewController: UIViewController {
    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: "hulu", ofType: "jpg")!)
        imageView.contentMode = .scaleAspectFit
        
        return imageView
    }()
    var slider: UISlider = {
        
        let slider = UISlider(frame: CGRect(x: 20, y: SCREEN_HEIGHT - 30, width: SCREEN_WIDTH - 40, height: 20))
        return slider
    }()
    var filter:BasicOperation!
    var pictureInput : PictureInput!
    var filterModel:FilterModel = FilterModel(name: "BrightnessAdjustment 亮度",
                filterType: .basicOperation,
                range: (-1.0, 1.0, 0.0),
                initCallback: {BrightnessAdjustment()},
                valueChangedCallback: nil)
    let renderView = RenderView(frame:CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT - 85))
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "圖片濾鏡"
        view.backgroundColor = .white
        view.addSubview(imageView)
        view.addSubview(renderView)
        renderView.backgroundColor = .white
        renderView.isHidden = true
        view.addSubview(slider)
        slider.isHidden = true
        slider.addTarget(self, action: #selector(sliderValueChanged(slider:)), for: .valueChanged)
        pictureInput = PictureInput(image: imageView.image!)
        
        let filterButton = UIButton(frame: CGRect(x: 90, y: UIScreen.main.bounds.height - 80, width: 150, height: 40))
        filterButton.setTitle("選擇濾鏡", for: UIControl.State.normal)
        filterButton.center.x = view.center.x
        filterButton.backgroundColor = .gray
        filterButton.addTarget(self, action: #selector(ChoseFilters(btn:)), for: .touchUpInside)
        view.addSubview(filterButton)

    }

    @objc func ChoseFilters(btn:UIButton) {
        imageView.isHidden = true
        renderView.isHidden = false
        
        let fvc = FilterListTableViewController()
        fvc.filterBlock =  {  [weak self] filterModel in
            guard let `self` = self else {
                return
            }
            self.filterModel = filterModel
            
            self.setupFilterChain(filterModel: filterModel)
            
        }
        self.navigationController?.pushViewController(fvc, animated: true)
        
    }
    func setupFilterChain(filterModel:FilterModel) {
        title = filterModel.name
        //           pictureInput = PictureInput(image: MaYuImage)
        slider.minimumValue = filterModel.range?.0 ?? 0
        slider.maximumValue = filterModel.range?.1 ?? 0
        slider.value = filterModel.range?.2 ?? 0
        let filterObject = filterModel.initCallback()
        
        pictureInput.removeAllTargets()
        self.filter?.removeAllTargets()
        
        switch filterModel.filterType! {
            
        case .imageGenerators:
           imageView.image = imageView.image
        case .basicOperation:
            if let actualFilter = filterObject as? BasicOperation {
                self.filter = actualFilter
                pictureInput --> filter --> renderView
            }
            
        case .operationGroup:
            if let actualFilter = filterObject as? OperationGroup {
                pictureInput --> actualFilter --> renderView
            }
            
        case .blend:
            if let actualFilter = filterObject as? BasicOperation {
                self.filter = actualFilter
                let blendImgae = PictureInput(image: flowerImage)
                blendImgae --> actualFilter
                pictureInput --> filter --> renderView
                blendImgae.processImage()
                
            }
            
        case .custom:
            
            filterModel.customCallback!(pictureInput, filterObject, renderView)
            filter = filterObject as? BasicOperation
        }
        
        pictureInput.processImage()
        
        self.sliderValueChanged(slider: slider)
    }
    
    @objc func sliderValueChanged(slider: UISlider) {
        
        if let actualCallback = filterModel.valueChangedCallback {
            actualCallback(filter, slider.value)
            slider.isHidden = false
        } else {
            slider.isHidden = true
        }
        
        if filterModel.filterType! != .imageGenerators {
            pictureInput.processImage()
        }
    }
    func filteringImage() {
        
        // 創建一個BrightnessAdjustment顏色處理濾鏡
        let brightnessAdjustment = BrightnessAdjustment()
        brightnessAdjustment.brightness = 0.2
        
        // 創建一個ExposureAdjustment顏色處理濾鏡
        let exposureAdjustment = ExposureAdjustment()
        exposureAdjustment.exposure = 0.5
        
        // 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
        
        // 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)
    }
    
}

二、拍攝照片使用濾鏡

class TakePhotoViewController: UIViewController {
    
    var filterModel:FilterModel = FilterModel(name: "BrightnessAdjustment 亮度",
                                              filterType: .basicOperation,
                                              range: (-1.0, 1.0, 0.5),
                                              initCallback: {BrightnessAdjustment()},
                                              valueChangedCallback: { (filter, value) in
                                                (filter as! BrightnessAdjustment).brightness = value
    })
    var picture:PictureInput!
    var filter:BasicOperation!
    var camera: Camera!
    var movieOutput:MovieOutput? = nil
    var movie: MovieInput!
    var renderView: RenderView!
    
    var takeButton : UIButton!
    var filterButton : UIButton!
    var reTakeButton : UIButton!
    var slider: UISlider = {
        
        let slider = UISlider(frame: CGRect(x: 20, y: SCREEN_HEIGHT - 30, width: SCREEN_WIDTH - 40, height: 20))
        return slider
    }()
    func creaatRenderView() -> RenderView{
        let renderView = RenderView(frame:CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT - 100))
        return renderView
    }
    lazy var imageView: UIImageView = {
        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT - 80))
        imageView.image = UIImage(contentsOfFile: Bundle.main.path(forResource: "hulu", ofType: "jpg")!)
        imageView.contentMode = .scaleAspectFit
        imageView.backgroundColor = .purple
        imageView.isHidden = true
        return imageView
    }()
    @objc func ChoseFilters(btn:UIButton) {
        
        let fvc = FilterListTableViewController()
        fvc.filterBlock =  {  [weak self] filterModel in
            guard let `self` = self else {
                return
            }
            self.filterModel = filterModel
            
            self.setupFilterChain(filterModel: filterModel)
            
        }
        self.navigationController?.pushViewController(fvc, animated: true)
        
    }
    func setupFilterChain(filterModel:FilterModel) {
        title = filterModel.name
        //           pictureInput = PictureInput(image: MaYuImage)
        slider.minimumValue = filterModel.range?.0 ?? 0
        slider.maximumValue = filterModel.range?.1 ?? 0
        slider.value = filterModel.range?.2 ?? 0
        let filterObject = filterModel.initCallback()
        
        camera.removeAllTargets()
        filter.removeAllTargets()
        renderView.sources.removeAtIndex(0)
        switch filterModel.filterType! {
            
        case .imageGenerators:
            filterObject as! ImageSource --> renderView
            
        case .basicOperation:
            if let actualFilter = filterObject as? BasicOperation {
                filter = actualFilter
                camera --> actualFilter --> renderView
                //                   pictureInput.processImage()
            }
            
        case .operationGroup:
            if let actualFilter = filterObject as? OperationGroup {
                camera --> actualFilter --> renderView
            }
            
        case .blend:
            if let actualFilter = filterObject as? BasicOperation {
                filter = actualFilter
                let blendImgae = PictureInput(image: flowerImage)
                blendImgae --> actualFilter
                camera --> actualFilter --> renderView
                blendImgae.processImage()
                
            }
            
        case .custom:
            filterModel.customCallback!(camera, filterObject, renderView)
            filter = filterObject as? BasicOperation
            
        }
        
        
        
        self.sliderValueChanged(slider: slider)
    }
    
    @objc func sliderValueChanged(slider: UISlider) {
        
        //           print("slider value: \(slider.value)")
        
        if let actualCallback = filterModel.valueChangedCallback {
            actualCallback(filter, slider.value)
            slider.isHidden = false
        } else {
            slider.isHidden = true
        }
        
        if filterModel.filterType! != .imageGenerators {
            
        }
    }
  
    //拍攝
    @objc func takePhoto() {
        takeButton.isHidden = true
        reTakeButton.isHidden = false
        
        
        // 設置保存路徑
        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)
        // 保存相機捕捉到的圖片
        self.camera.saveNextFrameToURL(originalURL, format: .png)

        // 保存濾鏡后的圖片
        self.filter.saveNextFrameToURL(filteredlURL, format: .png)

        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(100)) {
            self.renderView.isHidden = true
            self.imageView.isHidden = false
            self.imageView.image = UIImage(contentsOfFile: filteredPath)
        }
        
//        // 如果需要處理回調,有下面兩種寫法
//        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.imageView.image = image
//        }
//
//        self.camera --> imageOutput
        
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "拍照濾鏡"
        view.backgroundColor = .white
        
        slider.addTarget(self, action: #selector(sliderValueChanged(slider:)), for: .valueChanged)
        self.renderView = creaatRenderView()
        view.addSubview(renderView)
        view.addSubview(imageView)
        view.addSubview(slider)
        slider.isHidden = true
        
        if let fi = filterModel.initCallback() as? BasicOperation{
            filter = fi
        }else{
            filter = BrightnessAdjustment()
        
        }
        
        takeButton = UIButton(frame: CGRect(x: 20, y: UIScreen.main.bounds.height - 100, width:60, height: 60))
        takeButton.setTitle("拍攝", for: UIControl.State.normal)
        takeButton.backgroundColor = .gray
        takeButton.center.x = self.view.center.x
        takeButton.layer.cornerRadius = 30
        takeButton.addTarget(self, action: #selector(takePhoto), for: UIControl.Event.touchUpInside)
        view.addSubview(takeButton)
        
        filterButton = UIButton(frame: CGRect(x: UIScreen.main.bounds.width - 100, y: UIScreen.main.bounds.height - 90, width: 80, height: 40))
        filterButton.setTitle("選擇濾鏡", for: UIControl.State.normal)
        filterButton.backgroundColor = .gray
        filterButton.addTarget(self, action: #selector(ChoseFilters), for: .touchUpInside)
        view.addSubview(filterButton)
        
        reTakeButton = UIButton(frame: CGRect(x: UIScreen.main.bounds.width - 100, y: UIScreen.main.bounds.height - 90, width: 80, height: 40))
        reTakeButton.setTitle("重新拍攝", for: UIControl.State.normal)
        reTakeButton.backgroundColor = .gray
        reTakeButton.center.x = self.view.center.x
        reTakeButton.addTarget(self, action: #selector(retake), for: UIControl.Event.touchUpInside)
        view.addSubview(reTakeButton)
        
        reTakeButton.isHidden = true
        
        cameraFiltering()
    }
     @objc func retake() {
        takeButton.isHidden = false
        reTakeButton.isHidden = true
        renderView.isHidden = false
        imageView.isHidden = true
    }
    
    func cameraFiltering() {
        
        // Camera的構造函數是可拋出錯誤的
        do {
            camera = try Camera(sessionPreset: AVCaptureSession.Preset.hd1280x720,
                                cameraDevice: nil,
                                location: .backFacing,
                                captureAsYUV: true)
            
        } catch {
            print(error)
            return
        }
        // 綁定處理鏈
        camera --> renderView
        
        // 開始捕捉數據
        self.camera.startCapture()
        // 結束捕捉數據
        // camera.stopCapture()
        
    }
}

三、播放視頻時添加濾鏡

class PlayMoviewViewController: UIViewController {

    var filter:BasicOperation = BrightnessAdjustment()
    var renderView: RenderView!
    var filterModel:FilterModel = FilterModel(name: "BrightnessAdjustment 亮度",
                filterType: .basicOperation,
                range: (-1.0, 1.0, 0.0),
                initCallback: {BrightnessAdjustment()},
                valueChangedCallback: { (filter, value) in
                    (filter as! BrightnessAdjustment).brightness = value
    })
    var movie: MovieInput! = {
        let documentsDir = try! FileManager.default.url(for:.documentDirectory, in:.userDomainMask, appropriateFor:nil, create:true)
        let fileURL = URL(string:"test.mp4", relativeTo:documentsDir)!
        let movie = try? MovieInput(url:fileURL, playAtActualSpeed:true)
        return movie
        
    }()
    var slider: UISlider = {
        
        let slider = UISlider(frame: CGRect(x: 8, y: SCREEN_HEIGHT - 30, width: SCREEN_WIDTH - 18, height: 20))
        return slider
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "播放視頻添加濾鏡"
        view.backgroundColor = .white
        addbutton()
        
        slider.addTarget(self, action: #selector(sliderValueChanged(slider:)), for: .valueChanged)
        view.addSubview(slider)
        slider.isHidden = true
    }
    @objc func buttonClick(btn:UIButton){
        if btn.tag == 101 {
            
            
        }else if btn.tag == 102{
            playMovie(btn: btn)
        }else if btn.tag == 103{
            
            let fvc = FilterListTableViewController()
            fvc.filterBlock =  {  [weak self] filterModel in
                guard let `self` = self else {
                    return
                }
                self.filterModel = filterModel
                
                self.setupFilterChain(filterModel: filterModel)
                
            }
            self.navigationController?.pushViewController(fvc, animated: true)
            
        }
    }
    
     func setupFilterChain(filterModel:FilterModel) {
            title = filterModel.name
            //           pictureInput = PictureInput(image: MaYuImage)
            slider.minimumValue = filterModel.range?.0 ?? 0
            slider.maximumValue = filterModel.range?.1 ?? 0
            slider.value = filterModel.range?.2 ?? 0
            let filterObject = filterModel.initCallback()
            
            movie.removeAllTargets()
            filter.removeAllTargets()
            renderView.sources.removeAtIndex(0)
            switch filterModel.filterType! {
                
            case .imageGenerators:
                filterObject as! ImageSource --> renderView
                
            case .basicOperation:
                if let actualFilter = filterObject as? BasicOperation {
                    filter = actualFilter
                    movie --> actualFilter --> renderView
                    //                   pictureInput.processImage()
                }
                
            case .operationGroup:
                if let actualFilter = filterObject as? OperationGroup {
                    movie --> actualFilter --> renderView
                }
                
            case .blend:
                if let actualFilter = filterObject as? BasicOperation {
                    filter = actualFilter
                    let blendImgae = PictureInput(image: flowerImage)
                    blendImgae --> actualFilter
                    movie --> actualFilter --> renderView
                    blendImgae.processImage()
                    
                }
                
            case .custom:
                filterModel.customCallback!(movie, filterObject, renderView)
                filter = (filterObject as? BasicOperation)!
            }
            
            
            
            self.sliderValueChanged(slider: slider)
        }
           
           @objc func sliderValueChanged(slider: UISlider) {
               
    //           print("slider value: \(slider.value)")
               
               if let actualCallback = filterModel.valueChangedCallback {
                   actualCallback(filter, slider.value)
               } else {
                   slider.isHidden = true
               }
               
               if filterModel.filterType! != .imageGenerators {
                
               }
           }
    func addbutton() {
        let buttonX = UIButton(frame: CGRect.zero)
        buttonX.tag = 101
        buttonX.setTitle("選視頻", for: UIControl.State.normal)
        buttonX.addTarget(self, action: #selector(buttonClick(btn:)), for: UIControl.Event.touchUpInside)
        buttonX.backgroundColor = UIColor.gray
        let buttonY = UIButton(frame: CGRect.zero)
        
        buttonY.tag = 102
        buttonY.setTitle("播放", for: UIControl.State.normal)
        buttonY.addTarget(self, action: #selector(buttonClick(btn:)), for: UIControl.Event.touchUpInside)
        buttonY.backgroundColor = UIColor.gray
        
        let buttonZ = UIButton(frame: CGRect.zero)
        buttonZ.tag = 103
        buttonZ.setTitle("選濾鏡", for: UIControl.State.normal)
        buttonZ.addTarget(self, action: #selector(buttonClick(btn:)), for: UIControl.Event.touchUpInside)
        buttonZ.backgroundColor = UIColor.gray
        
        view.addSubview(buttonX)
        view.addSubview(buttonY)
        view.addSubview(buttonZ)
        
        buttonX.translatesAutoresizingMaskIntoConstraints = false
        buttonY.translatesAutoresizingMaskIntoConstraints = false
        buttonZ.translatesAutoresizingMaskIntoConstraints = false

        buttonY.widthAnchor.constraint(equalTo: buttonX.widthAnchor).isActive = true
        buttonZ.widthAnchor.constraint(equalTo: buttonX.widthAnchor).isActive = true

        buttonX.leftAnchor.constraint(equalTo: view.leftAnchor,constant: 20).isActive = true
        buttonX.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -5).isActive = true
        buttonX.heightAnchor.constraint(equalToConstant: 60).isActive = true

        buttonY.leftAnchor.constraint(equalTo: buttonX.rightAnchor,constant: 10).isActive = true
        buttonY.topAnchor.constraint(equalTo: buttonX.topAnchor).isActive = true
        buttonY.bottomAnchor.constraint(equalTo: buttonX.bottomAnchor).isActive = true

        buttonZ.leftAnchor.constraint(equalTo: buttonY.rightAnchor,constant: 10).isActive = true
        buttonZ.topAnchor.constraint(equalTo: buttonX.topAnchor).isActive = true
        buttonZ.bottomAnchor.constraint(equalTo: buttonX.bottomAnchor).isActive = true
        buttonZ.rightAnchor.constraint(equalTo: view.rightAnchor,constant: -20).isActive = true
    }

    //播放
    @objc func playMovie(btn:UIButton){
        btn.isSelected = !btn.isSelected
        if btn.isSelected {
            
            btn.setTitle("stop", for: UIControl.State.normal)
            if (movie == nil) {
                
                filter = SaturationAdjustment()
                movie --> filter --> renderView
                movie.start()
            }else{
                movie --> filter
            }
            
//            movie.runBenchmark = true
//
            
            
            
        }else{
            btn.setTitle("play", for: UIControl.State.normal)
//            movie.cancel()
            movie.removeAllTargets()
//            filter.removeAllTargets()
        }
    }
    

}

四、錄制視頻添加實時濾鏡

class VideoViewController: UIViewController {

    var filterModel:FilterModel = FilterModel(name: "BrightnessAdjustment 亮度",
                filterType: .basicOperation,
                range: (-1.0, 1.0, 0.0),
                initCallback: {BrightnessAdjustment()},
                valueChangedCallback: { (filter, value) in
                    (filter as! BrightnessAdjustment).brightness = value
    })
    var picture:PictureInput!
    var filter:BasicOperation!
    var camera: Camera!
    var movieOutput:MovieOutput? = nil
    var movie: MovieInput!
    var renderView: RenderView!
    var slider: UISlider = {
        
        let slider = UISlider(frame: CGRect(x: 8, y: SCREEN_HEIGHT - 30, width: SCREEN_WIDTH - 18, height: 20))
        return slider
    }()
    //RenderView
    func creaatRenderView() -> RenderView{
        let renderView = RenderView(frame:CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT - 50))
        let button = UIButton(frame: CGRect(x: 20, y: UIScreen.main.bounds.height - 100, width:80, height: 40))
        button.setTitle("開始錄制", for: UIControl.State.normal)
        button.backgroundColor = .gray
        button.addTarget(self, action: #selector(startVideo(btn:)), for: UIControl.Event.touchUpInside)
        renderView.addSubview(button)
        
        let filterButton = UIButton(frame: CGRect(x: 130, y: UIScreen.main.bounds.height - 100, width: 80, height: 40))
        filterButton.setTitle("選擇濾鏡", for: UIControl.State.normal)
        filterButton.backgroundColor = .gray
        filterButton.center.x = self.view.center.x
        filterButton.addTarget(self, action: #selector(ChoseFilters), for: .touchUpInside)
        renderView.addSubview(filterButton)
        
        let playButton = UIButton(frame: CGRect(x: UIScreen.main.bounds.width - 100, y: UIScreen.main.bounds.height - 100, width: 80, height: 40))
        playButton.setTitle("播放視頻", for: UIControl.State.normal)
        playButton.backgroundColor = .gray
        playButton.addTarget(self, action: #selector(playMovie(btn:)), for: UIControl.Event.touchUpInside)
        renderView.addSubview(playButton)
        
        return renderView
    }
    
    @objc func ChoseFilters(btn:UIButton) {
       
        let fvc = FilterListTableViewController()
        fvc.filterBlock =  {  [weak self] filterModel in
            guard let `self` = self else {
                return
            }
            self.filterModel = filterModel
            
            self.setupFilterChain(filterModel: filterModel)
            
        }
        self.navigationController?.pushViewController(fvc, animated: true)
        
    }
    func setupFilterChain(filterModel:FilterModel) {
        title = filterModel.name
        //           pictureInput = PictureInput(image: MaYuImage)
        slider.minimumValue = filterModel.range?.0 ?? 0
        slider.maximumValue = filterModel.range?.1 ?? 0
        slider.value = filterModel.range?.2 ?? 0
        let filterObject = filterModel.initCallback()
        
        camera.removeAllTargets()
        filter.removeAllTargets()
        renderView.sources.removeAtIndex(0)
        switch filterModel.filterType! {
            
        case .imageGenerators:
            filterObject as! ImageSource --> renderView
            
        case .basicOperation:
            if let actualFilter = filterObject as? BasicOperation {
                filter = actualFilter
                camera --> actualFilter --> renderView
                //                   pictureInput.processImage()
            }
            
        case .operationGroup:
            if let actualFilter = filterObject as? OperationGroup {
                camera --> actualFilter --> renderView
            }
            
        case .blend:
            if let actualFilter = filterObject as? BasicOperation {
                filter = actualFilter
                let blendImgae = PictureInput(image: flowerImage)
                blendImgae --> actualFilter
                camera --> actualFilter --> renderView
                blendImgae.processImage()
                
            }
            
        case .custom:
            filterModel.customCallback!(camera, filterObject, renderView)
            filter = filterObject as? BasicOperation
            
        }
        
        
        
        self.sliderValueChanged(slider: slider)
    }
       
       @objc func sliderValueChanged(slider: UISlider) {
           
//           print("slider value: \(slider.value)")
           
           if let actualCallback = filterModel.valueChangedCallback {
               actualCallback(filter, slider.value)
           } else {
               slider.isHidden = true
           }
           
           if filterModel.filterType! != .imageGenerators {
            
           }
       }
    //播放
    @objc func playMovie(btn:UIButton){
        
        let playVc = PlayMoviewViewController()
        self.navigationController?.pushViewController(playVc, animated: true)
    }
    //拍攝
    @objc func startVideo(btn:UIButton){
        btn.isSelected = !btn.isSelected
        if btn.isSelected {
            btn.setTitle("stop", for: UIControl.State.normal)
            do {
                let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! as NSString
                print(documentsPath)
                let documentsDir = try FileManager.default.url(for:.documentDirectory, in:.userDomainMask, appropriateFor:nil, create:true)
                let fileURL = URL(string:"test.mp4", relativeTo:documentsDir)!
                
                do {
                    try FileManager.default.removeItem(at:fileURL)
                } catch {
                    print("error")
                }
                movieOutput = try MovieOutput(URL:fileURL, size:Size(width:480, height:640), liveVideo:true)
                camera.audioEncodingTarget = movieOutput
                camera.removeAllTargets()
                camera --> filter --> movieOutput!
                movieOutput!.startRecording()
                
            } catch {
                fatalError("Couldn't initialize movie, error: \(error)")
            }
        }else{
            btn.setTitle("start", for: UIControl.State.normal)
            
            movieOutput?.finishRecording{
                self.camera.audioEncodingTarget = nil
                self.movieOutput = nil
            }
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "拍攝視頻"
        view.backgroundColor = .white
        
        slider.addTarget(self, action: #selector(sliderValueChanged(slider:)), for: .valueChanged)
        self.renderView = creaatRenderView()
        view.addSubview(renderView)
        view.addSubview(slider)
        slider.isHidden = true
        
        if let fi = filterModel.initCallback() as? BasicOperation{
            filter = fi
        }else{
            filter = BrightnessAdjustment()
        }
        cameraFiltering()
    }
    

    func cameraFiltering() {
        
        // Camera的構造函數是可拋出錯誤的
        do {
            camera = try Camera(sessionPreset: AVCaptureSession.Preset.hd1280x720,
                                cameraDevice: nil,
                                location: .backFacing,
                                captureAsYUV: true)
            
        } catch {
            print(error)
            return
        }
        // 綁定處理鏈
        camera --> renderView
        
        // 開始捕捉數據
        self.camera.startCapture()
        // 結束捕捉數據
        // camera.stopCapture()
        
    }

}

五、自定義濾鏡

 框架使用一系列協議來定義可以輸出要處理的圖像、接收要處理的圖像或同時執行這兩種操作的類型。它們分別是ImageSource、ImageConsumer和ImageProcessingOperation協議。任何類型都可以遵循這些協議,但通常使用類。許多常見的濾鏡和其他圖像處理操作可以被描述為BasicOperation類的子類。BasicOperation提供了從一個或多個圖像輸入中獲取圖像所需的大量內部代碼,使用指定的着色程序從這些輸入中繪制圖像,並將該圖像提供給所有目標。在基本操作上的變化,如紋理放大操作或兩個階段操作,提供額外的信息給着色程序,可能需要某些類型的操作。要構建一個簡單的單自定義濾鏡,甚至可能不需要創建自己的子類。當實例化一個基本操作時,你所需要做的就是提供一個片段着色器和輸入的數量:

     let myFilter = BasicOperation(fragmentShaderFile:MyFilterFragmentShaderURL, numberOfInputs:1)

     一個着色器程序是由匹配的頂點着色器和片段着色器組成的,它們被編譯並鏈接到一個程序中。默認情況下,框架根據輸入到操作中的圖像的數量使用一系列頂點着色器。通常,你所需要做的就是提供用於執行過濾或其他處理的自定義片段着色器。

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)
    }
    /*
    自定義片元着色器代碼
     precision highp float;
     varying vec2 TextureCoordsVarying;
     uniform sampler2D Texture;

     void main()
     {
         vec2 sampleDivisor = vec2(1.0 / 200.0, 1.0 / 320.0);
         //highp vec4 colorDivisor = vec4(colorDepth);
         
         vec2 samplePos = TextureCoordsVarying - mod(TextureCoordsVarying, sampleDivisor);
         vec4 color = texture2D(Texture, samplePos );
         
         //gl_FragColor = texture2D(Texture, samplePos );
         vec4 colorCyan = vec4(85.0 / 255.0, 1.0, 1.0, 1.0);
         vec4 colorMagenta = vec4(1.0, 85.0 / 255.0, 1.0, 1.0);
         vec4 colorWhite = vec4(1.0, 1.0, 1.0, 1.0);
         vec4 colorBlack = vec4(0.0, 0.0, 0.0, 1.0);
         
         vec4 endColor;
         float blackDistance = distance(color, colorBlack);
         float whiteDistance = distance(color, colorWhite);
         float magentaDistance = distance(color, colorMagenta);
         float cyanDistance = distance(color, colorCyan);
         
         vec4 finalColor;
         
         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;
     }
     */

六、濾鏡操作組

     如果希望將一系列操作分組到單個單元中以便傳遞,可以創建一個新的OperationGroup實例。OperationGroup提供了一個configureGroup屬性,它帶有一個閉包,該閉包指定了該組應該如何配置

unc 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)
    }

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 當處理超過紋理限制的圖像時候,會先做判斷,壓縮成最大紋理限制的圖像,導致圖像質量損失。

本文所有示例代碼github地址:https://github.com/duzhaoquan/UseGPUImage2

參考資料:GPUImage集成與使用:https://www.jianshu.com/p/1bcf38960dbb

GPUImage2:https://github.com/BradLarson/GPUImage2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM