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

posted @ 2020-07-08 10:47  不停奔跑的蜗牛  阅读(1768)  评论(0编辑  收藏  举报