zoukankan      html  css  js  c++  java
  • [Swift通天遁地]八、媒体与动画-(4)给相机添加CoreImage滤镜效果

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
    ➤微信公众号:山青咏芝(shanqingyongzhi)
    ➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/
    ➤GitHub地址:https://github.com/strengthen/LeetCode
    ➤原文地址:https://www.cnblogs.com/strengthen/p/10353871.html 
    ➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
    ➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

    目录:[Swift]通天遁地Swift

    本文将演示如何给相机添加实时的滤镜效果。

    首先打开项目的配置文件【Info.plist】,在空白区域点击鼠标右键,弹出右键菜单。

    选择【Add Row】添加行命令,添加一行配置选项。

    在【Key】键输入框输入相机的访问标识:【Application Category

    在【Value】值输入框输入当应用程序访问相机设备时的提示语:

    【Requires access to the camera】

    在左侧的项目导航区,打开视图控制器的代码文件【ViewController.swift】

    现在开始编写代码,在应用程序中使用相机设备,并给相机添加实时滤镜。

    需要使用真机进行调试。

      1 import UIKit
      2 //引入需要使用到的类库,用来添加滤镜效果。
      3 import CoreImage
      4 //引入需要使用到的类库,用来对视频的采样进行处理
      5 import AVFoundation
      6 
      7 //给当前的类添加协议,使用该协议,可以获得相机设备中的实时输出的数据流。
      8 class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate{
      9     
     10     //添加一个滤镜,它将在代理协议中的方法中被使用,
     11     //从而对视频流实时添加滤镜效果。
     12     var filter: CIFilter!
     13     //该属性用来存储在协议方法中获得的图像。
     14     //当用户点击截图按钮时,从而获得当前视频流中的截图。
     15     var cgImage: CGImage!
     16     //该属性用来展示应用滤镜后的视频流截图
     17     var videoLayer: CALayer!
     18     //当用户点击截图按钮时,展示视频流的截图
     19     var imageView : UIImageView!
     20     //使用该属性获得相机设备的数据流
     21     var avCaptureSession: AVCaptureSession!
     22     //该属性可以将应用滤镜后的图像,转换成CGImage格式的图像,
     23     //并提交给视频层进行展示。
     24     var context: CIContext = {
     25         //返回一个指定接口的上下文对象
     26         return CIContext(eaglContext: EAGLContext(api: EAGLRenderingAPI.openGLES2)!, options: nil)
     27     }()
     28     
     29     //在视图加载完成的方法中,对部分属性进行初始化操作。
     30     override func viewDidLoad() {
     31         super.viewDidLoad()
     32         
     33         //初始化滤镜对象,该滤镜可以使用图像显示复古、暖色调的艺术风格。
     34         filter = CIFilter(name: "CIPhotoEffectTransfer")
     35         //调用生成界面的方法,对程序的界面进行初始化操作。
     36         buildUI()
     37         //对用于捕捉视频流的对象进行初始化操作
     38         buildSession()
     39     }
     40     
     41     //添加一个方法,用来创建应用程序的界面
     42     func buildUI()
     43     {
     44         //对视图层进行初始化操作
     45         videoLayer = CALayer()
     46         //设置视图层的锚点点位置在原点
     47         videoLayer.anchorPoint = CGPoint.zero
     48         //保持视图层的尺寸和骗你干嘛的尺寸相同
     49         videoLayer.bounds = view.bounds
     50         //将视图层添加到根视图的层中
     51         self.view.layer.insertSublayer(videoLayer, at: 0)
     52         
     53         //创建一个图像视图对象,该图像视图将用来展示从视频流中,
     54         //获得应用滤镜后的截图,它的尺寸也跟屏幕尺寸相同。
     55         imageView = UIImageView(frame: view.bounds)
     56         //将图像视图添加到根视图中。
     57         self.view.addSubview(imageView)
     58         
     59         //添加一个按钮,当用户点击该按钮时,获得视频流中的应用滤镜后的截图。
     60         let button = UIButton(frame: CGRect(x: 0, y: 420,  320, height: 60))
     61         //设置按钮在正常状态下的标题文字
     62         button.setTitle("Capture", for: .normal)
     63         //同时设置按钮的背景颜色为黑色。
     64         button.backgroundColor = UIColor.black
     65         //给按钮控件绑定点击事件
     66         button.addTarget(self, action: #selector(ViewController.captureScreen), for: .touchUpInside)
     67         //将按钮添加到根视图
     68         self.view.addSubview(button)
     69     }
     70     
     71     //添加一个方法,用来响应按钮的点击事件
     72     func buildSession()
     73     {
     74         //对获得数据流的对象进行初始化操作
     75         avCaptureSession = AVCaptureSession()
     76         //通过调用对象的开始配置方法,开始对各种参数进行配置。
     77         avCaptureSession.beginConfiguration()
     78         //设置获得质量较高的视频流和音频流。
     79         avCaptureSession.sessionPreset = AVCaptureSession.Preset.high
     80         
     81         //获得当前的相机设备
     82         let captureDevice = AVCaptureDevice.default(for: .video)
     83        //初始化一个视频捕捉设备输入对象
     84         let deviceInput = try! AVCaptureDeviceInput(device: captureDevice!)
     85         //当相机设备处于可用状态时,
     86         if avCaptureSession.canAddInput(deviceInput)
     87         {
     88             //设置视频流的输入设备为相机设备
     89             avCaptureSession.addInput(deviceInput)
     90         }
     91         
     92         //获得视频捕捉数据输出对象,
     93         //该对象用于从视频流中,获取未经压缩的帧
     94         let dataOutput = AVCaptureVideoDataOutput()
     95         //设置视频帧的格式为32位的RGBA格式
     96         dataOutput.videoSettings = ([kCVPixelBufferPixelFormatTypeKey as AnyHashable : Int(kCVPixelFormatType_32BGRA)] as! [String : Any])
     97         //设置自动丢弃由于视频延迟等因素,而造成延迟等的视频帧。
     98         dataOutput.alwaysDiscardsLateVideoFrames = true
     99         
    100         //将数据输出对象,添加到视频捕捉对象的数据输出端口
    101         if avCaptureSession.canAddOutput(dataOutput)
    102         {
    103             avCaptureSession.addOutput(dataOutput)
    104         }
    105         
    106         //创建一个串行的任务队列
    107         let queue = DispatchQueue(label: "VideoQueue", attributes: .concurrent)
    108         //设置数据输出对象的采样换成代理,位当前的视图控制器对象,
    109         //并使用穿好的任务队列
    110         dataOutput.setSampleBufferDelegate(self, queue: queue)
    111         
    112         //通过调用视频捕捉对象的提交配置方法,结束对各种参数的配置。
    113         avCaptureSession.commitConfiguration()
    114         //调用开始运行方法,开始使用相机设备捕捉视频。
    115         avCaptureSession.startRunning()
    116     }
    117     
    118     //实现协议中的代理方法,以实时检测视频流,
    119     //并给视频流实时添加滤镜。
    120     func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)
    121     {
    122         //添加一个自动释放池
    123         autoreleasepool
    124             {
    125                 //将采样的流数据,转换成图像缓存对象
    126                 let imgBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
    127                 //将图像缓存对象进行格式的转换,
    128                 //以便给图像添加滤镜。
    129                 var ciImage = CIImage(cvPixelBuffer: imgBuffer)
    130                 
    131                 //将数据流转换格式后,就可以应用框架中的众多滤镜。
    132                 //首先设置滤镜的输入图像,为流数据转换格式后的对象。
    133                 self.filter.setValue(ciImage, forKey: kCIInputImageKey)
    134                 //获得应用滤镜后所输出的图像。
    135                 ciImage = self.filter.outputImage!
    136                 
    137                 //获得当前设备的朝向
    138                 let orientation = UIDevice.current.orientation
    139                 //因为两种坐标系统的原点不同,所以需要对视频流中的应用滤镜后的截图,进行旋转操作。
    140                 if orientation == UIDeviceOrientation.portrait
    141                 {
    142                     ciImage = ciImage.transformed(by: CGAffineTransform(rotationAngle: CGFloat(Double.pi / -2.0)))
    143                 }
    144                 //处理设备在竖立状态,主键在上的情况。
    145                 else if orientation == UIDeviceOrientation.portraitUpsideDown
    146                 {
    147                     ciImage = ciImage.transformed(by: CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2.0)))
    148                 }
    149                 //处理设备在横向状态,主键在右的情况。
    150                 else if (orientation == UIDeviceOrientation.landscapeRight)
    151                 {
    152                     ciImage = ciImage.transformed(by: CGAffineTransform(rotationAngle: CGFloat(Double.pi)))
    153                 }
    154                 //将调整方向后的图像,赋予应用的属性,供按钮控件的点击事件使用。
    155                 self.cgImage = self.context.createCGImage(ciImage, from: ciImage.extent)
    156                 
    157                 //返回主线程,在主线程中刷新界面上的内容,
    158                 DispatchQueue.main.sync(execute:
    159                     {
    160                         //将视频层的内容属性,设置为应用滤镜后的图像。
    161                         self.videoLayer.contents = self.cgImage
    162                 })
    163         }
    164     }
    165     
    166     //添加一个方法,用来响应按钮的点击事件
    167     @objc func captureScreen(_ sender: UIButton)
    168     {
    169         //当按钮被点击时,首先中止视频流的传递。
    170         avCaptureSession.stopRunning()
    171         //将用来显示时流的图层,从父层中移除。
    172         videoLayer.removeFromSuperlayer()
    173         //隐藏当前的按钮控件
    174         sender.isHidden = true
    175         
    176         //设置图像视图的内容模式,图片按一定比例缩放,
    177         //直到在长度或者宽度达到图像视图的边界为止。
    178         imageView.contentMode = .scaleAspectFit
    179         //将应用滤镜后的截图,赋予当前根视图中的图像视图,
    180         //在屏幕上显示来自视频流的截图。
    181         imageView.image = UIImage(cgImage: self.cgImage)
    182     }
    183     
    184     override func didReceiveMemoryWarning() {
    185         super.didReceiveMemoryWarning()
    186         // Dispose of any resources that can be recreated.
    187     }
    188 }
  • 相关阅读:
    Java操作Excel之POI简单例子
    机器学习之KNN算法
    机器学习之sklearn数据集
    数据分析之matplotlib
    数据分析之pandas
    数据分析之numpy
    python模块contextlib
    前端jsonp解决跨域问题
    django media和static配置
    Django之数据库迁移和创建
  • 原文地址:https://www.cnblogs.com/strengthen/p/10353871.html
Copyright © 2011-2022 走看看