1. 程式人生 > >[Swift通天遁地]八、媒體與動畫-(4)給相機添加CoreImage濾鏡效果

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

puts 結束 展示 plist 視圖 類庫 access 捕捉 att

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

首先打開項目的配置文件【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, 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 }

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