- This topic has 2개 답변, 2명 참여, and was last updated 4 years, 5 months 전에 by 야곰.
-
글쓴이글
-
-
광현참가자
- 글작성 : 15
- 답글작성 : 26
안녕하세요 오늘은 카메라에 실시간 필터(real-time filter)를 적용해 보았습니다. 다만 실시간 필터 중에서도 apple이 미리 구현해 놓은 필터 중 하나인 comic effect 필터를 사용해 보았습니다.
실시간 핊터 구현에서 previewView의 구현이 이전 카메라 앱 구현에서 AVCaptureVideoPreviewLayer 를 사용했던 것과 달리 이번에는 AVCaptureVideoDataOutputSampleBufferDelegate 프로토콜을 채택해서 사용해야 합니다.
- AVCaptureVideoDataOutputSampleBufferDelegate 는 video data output 에서 sample buffer를 받아오며, 받아오는 video data output의 상태를 감시하는 메쏘드들을 가지고 있는 프로토콜입니다. 이 프로토콜에 있는 메쏘드들은 필수적으로 구현해야 하는 것은 아닙니다.
-
Sample buffer의 행동을 조작하는 method는 두가지가 있습니다.
func captureOutput(:didOutput:from:)
func captureOutput(:didDrop:from:)
-
실시간 필터 적용되는 preview를 구현하기 위해 필요한 메쏘드는
func captureOutput(:didOutput:from:)
입니다.
지난번 시간에서 해왔듯이 AVCaptureSession을 이용해야 합니다.
추가된 점
- 실시간 필터를 구현에 있어서 AVCaptureVideoPreviewLayer 를 사용하지 않을 것이기 때문에 AVCaptureVideoDataOutput 을 AVCaptureSession에 추가해야 합니다.
CIContext 를 사용해야 합니다. 코어 이미지 모든 프로세싱이 일어나는 부분으로 CIFilter를 사용시에 반드시 필요합니다.
func captureOutput(:didOutput:from:) 구현 부분 설명
-
첫 번째 인자에는 AVCaptureOutput 하위 클래스가 들어옵니다. 여기서는 아마 AVCaptureVideoDataOutput 가 들어올 거라고 생각됩니다. 그리고 AVCaptureOutput 은 최상위 추상 클래스입니다. 간단히 말하자면 AVCaptureSession의 addOutput에 인자가 될 수 있는 클래스들의 부모 클래스(AVCaptureVideoDataOutput, AVCapturePhotoOutput 등)입니다.
-
두 번째 인자는 CMSampleBuffer 클래스의 인스턴스 입니다. 여기서 CM은 apple의 CoreMedia framework를 말합니다. CMSampleBuffer는 변경할 수 없는 CMSampleBufferRef 객체에 대한 참조입니다. CMSampleBuffer 는 압축되어 있거나 압축되어 있지 않은 특정 미디어 타입(audio, video 등)의 samples 를 가지고 있습니다.
-
세 번째 인자로는 AVCaptureConnection 클래스 타입이 변수로 들어옵니다. AVCaptureSession에 있는 특정한 input 과 output 사이의 연결이라고 합니다. 다만
addInput(_:)
과addOutput(_:)
을 사용했다면 자동으로 모든 input 과 output에 연결이 생성된다고 합니다. -
두 번째인자 sampleBuffer 로부터
CMSampleBufferGetImageBuffer(_:)
를 이용해서 CVImageBuffer를 받아옵니다. -
CIFilter 를 이용해서 이미 구현된 필터를 불러옵니다. 이미 구현된 filter 들은
Core Image Filter Reference 에서 확인할 수 있습니다.
-
이제는 CIFilter를 통과할 CIImage가 필요합니다. CIImage 클래스는 Core Image filter 들을 거칠 또는 생산된 image를 말합니다. 상당히 많은 init이 존재하는 데
init(cvImageBuffer:) 를 이용합니다. -
CIComicEffect filter를 통과한 이미지를 얻기 위해서 CIContext의
createCGImage(_:from:)
함수를 이용해서 CGImage를 생성합니다. 첫 번째 인자는 filter를 통과할 CIImage 객체이고, 두 번째 인자는 CGImage가 그려질 영역으로 CGRect 객체 입니다.func captureOutput(_:didOutput:from:)
부분의 코드입니다.
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let videoPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer), let _ = CMSampleBufferGetFormatDescription(sampleBuffer) else {return} let comicEffect = CIFilter(name: "CIPhotoEffectMono") let cameraImage = CIImage(cvImageBuffer: videoPixelBuffer) // setValue 부분은 잘 이해가 되지 않습니다. comicEffect!.setValue(cameraImage, forKey: kCIInputImageKey) let cgImage = self.context.createCGImage(comicEffect!.outputImage!, from: cameraImage.extent)! DispatchQueue.main.async { let filteredImage = UIImage(cgImage: cgImage) self.imageView.image = filteredImage } }
참조 링크
이해 되지 않는 부분
-
func captureOutput(_:didOutput:from)
에서 commicEffect!.setValue 부분의 의미가 무엇인지 궁금합니다. -
또
func captureOutput(_:didOutput:from)
에서 DispatchQueue 코드의 의미는 UI 변경에 관한 코드라서 main thread 에서 실행해서 해야하는 걸로 생각하고 있는 데 맞게 생각하는 건지 궁금합니다.
전체 코드입니다.
import UIKit import AVFoundation // CIFilter(CIComicFilter) 실시간 적용하기 class ViewController: UIViewController{ @IBOutlet weak var imageView: UIImageView! var captureSession: AVCaptureSession? var backCamera: AVCaptureDevice? var frontCamera: AVCaptureDevice? var captureInput: AVCaptureInput? var photoOutput: AVCapturePhotoOutput? var photoSetting: AVCapturePhotoSettings? var videoOutput: AVCaptureVideoDataOutput? var orientation: AVCaptureVideoOrientation = .portrait let context = CIContext() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.setAuthorization() self.setSession() self.setDevice() self.setInputOutput() self.imageView.transform = self.imageView.transform.rotated(by: .pi / 2) } func setAuthorization() { let status = AVCaptureDevice.authorizationStatus(for: .video) switch status { case .authorized: print("Get Authorization Success") case .denied: AVCaptureDevice.requestAccess(for: .video) { access in if access { DispatchQueue.main.async { self.setInputOutput() } } } default: print("Get Authorization failed") } } func setSession(){ self.captureSession = AVCaptureSession() self.captureSession?.sessionPreset = .photo } func setDevice(){ self.backCamera = AVCaptureDevice.default(for: .video) // or using default(_:for:position:) // self.backCamera = AVCaptureDevice.default(.builtInDualWideCamera, for: .video, position: .back) } func setInputOutput(){ guard let captureDevice = self.backCamera else {return} do{ // Set photoInput self.captureInput = try AVCaptureDeviceInput(device: captureDevice) // Set photoOutput self.photoOutput = AVCapturePhotoOutput() self.photoOutput?.isHighResolutionCaptureEnabled = true // Set videoDataOutput self.videoOutput = AVCaptureVideoDataOutput() self.videoOutput?.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32BGRA)] self.videoOutput?.setSampleBufferDelegate(self, queue: DispatchQueue.main) guard let photoinput = self.captureInput else {return} guard let photoOutput = self.photoOutput else {return} guard let videoOutput = self.videoOutput else {return} self.captureSession?.addInput(photoinput) self.captureSession?.addOutput(photoOutput) self.captureSession?.addOutput(videoOutput) self.captureSession?.startRunning() }catch{ print(error.localizedDescription) } } } extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let videoPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer), let _ = CMSampleBufferGetFormatDescription(sampleBuffer) else {return} let comicEffect = CIFilter(name: "CIComicEffect") let cameraImage = CIImage(cvImageBuffer: videoPixelBuffer) comicEffect!.setValue(cameraImage, forKey: kCIInputImageKey) let cgImage = self.context.createCGImage(comicEffect!.outputImage!, from: cameraImage.extent)! DispatchQueue.main.async { let filteredImage = UIImage(cgImage: cgImage) self.imageView.image = filteredImage } } }2020-06-29 오후 10:24 #23274 -
-
-
글쓴이글
- 답변은 로그인 후 가능합니다.