[Swift通天遁地]8、媒體與動畫-(4)給相機添加CoreImage濾鏡效果

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公衆號:山青詠芝(shanqingyongzhi)
➤博客園地址:山青詠芝(https://www.cnblogs.com/strengthen/
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:http://www.javashuo.com/article/p-dneegqra-mc.html 
➤若是連接不是山青詠芝的博客園地址,則多是爬取做者的文章。
➤原文已修改更新!強烈建議點擊原文地址閱讀!支持做者!支持原創!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★html

目錄:[Swift]通天遁地Swiftgit

本文將演示如何給相機添加實時的濾鏡效果。github

首先打開項目的配置文件【Info.plist】,在空白區域點擊鼠標右鍵,彈出右鍵菜單。swift

選擇【Add Row】添加行命令,添加一行配置選項。api

在【Key】鍵輸入框輸入相機的訪問標識:【Application Category緩存

在【Value】值輸入框輸入當應用程序訪問相機設備時的提示語:微信

【Requires access to the camera】session

在左側的項目導航區,打開視圖控制器的代碼文件【ViewController.swift】框架

如今開始編寫代碼,在應用程序中使用相機設備,並給相機添加實時濾鏡。ide

須要使用真機進行調試。

  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, width: 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 }
相關文章
相關標籤/搜索