[Core Image Programming Guide] 이미지 처리하기

애플의 ‘Core Image Programming Guide — Processing Images’ 해석

naljin
31 min readMar 23, 2020

이미지를 처리한다’라는 것은 필터를 적용하는 것을 의미한다. 이미지 필터하나의 소프트웨어이다. input 이미지를 픽셀 단위로 검토하고 알고리즘을 통해 특정한 효과를 적용해서 output 이미지를 만들어낸다.

Core Image에서 이미지 처리CIFilterCIImage 클래스를 통해 이뤄진다. 필터를 적용하고 결과를 나타내기 위해서는 Core Image와 다른 시스템 프레임워크를 통합(integration)을 하거나 CIContext 클래스자신만의 렌더링 처리 과정을 만들어야한다. 이번 챕터에서는 이러한 클래스들을 사용할 때 필요한 주요 개념을 다룬다.

Overview

이미지 처리를 위해 Core Image를 사용하는 방법은 많다. Listing 1–1에서는 기본 예시를 보여주고 더 자세한 설명이 있는 링크를 제공한다.

Listing 1–1 이미지에 필터를 적용하는 기본적인 방법

import CoreImagelet context = CIContext() //1

let filter = CIFilter(name: "CISepiaTone")! //2
filter.setValue(0.8, forKey: kCIInputIntensityKey)let image = CIImage(contentsOfURL: myURL) //3filter.setValue(image, forKey: kCIInputImageKey)let result = filter.outputImage! //4let cgImage = context.createCGImage(result, from: result.extent) //5
  1. CIContext 객체를 생성한다 (default 옵션과 함께). 다른 시스템 프레임워크와의 통합을 통해 렌더링 할 수도 있기 때문에 Core Image context가 항상 필요한 것은 아니다. 자신만의 context를 만드는 것은 렌더링 과정과 렌더링에 포함된 리소스를 더 정밀하게 컨트롤할 수 있도록 한다. context는 무거운(heavyweight) 객체이기 때문에, 최대한 빨리 하나를 만들고 나면 이미지 처리가 필요할때 마다 그것을 재사용한다. (See Building Your Own Workflow with a Core Image Context.)
  2. 적용할 filter를 나타내는 CIFilter 객체를 생성하고 그것의 파라미터에 값을 준다. (See Filters Describe Image Processing Effects.)
  3. 처리될 이미지를 나타내는 CIImage 객체를 생성하고 그것을 filter의 input 파라미터로 넣는다. URL로부터 이미지 데이터를 읽어오는 것은 이미지 객체를 생성하는 많은 방법 중 하나이다. (See Images are the Input and Output of Filters.)
  4. 필터의 아웃풋을 나타내는 CIImage 객체를 얻는다. 이 시점에서 필터는 아직 시행되지 않은 상태이다. image 객체는 단지 특정한 filter, parameters, input을 가지고 어떻게 이미지를 생성할지에 대한 “레시피” 일 뿐이다. Core Image렌더링이 요청될 때 해당 레시피를 수행한다. (See Images are the Input and Output of Filters.)
  5. 아웃풋 이미지를 Core Graphics 이미지로 렌더링해서 보거나 파일로 저장가능하게 만든다. (See Building Your Own Workflow with a Core Image Context.)

Image는 Filter의 Input과 Output이다

CIFilter(Core Image filter)CIImage(Core Image images)를 처리 및 생산한다. CIImage이미지를 나타내는 변하지 않는 객체이다. 해당 객체는 직접적으로 이미지 비트맵 데이터를 나타내지 않는다. 대신 CIImage 객체는 이미지 생성을 위한 “레시피”이다. 어떤 레시피는 파일에서 이미지 로딩을 요청할 수도 있고 다른 레시피는 필터의 아웃풋을 나타낼수도 있다. Core Image는 디스플레이를 위해 이미지 렌더링이 요청될때에야 이러한 레시피들을 수행한다.

필터를 적용하기 위해서는 적용될 이미지를 나타내는 CIImage 객체를 (한개 또는 그 이상) 만들고 filter의 input 파라미터로 넣는다. Core Image image 객체는 이미지 데이터의 source를 이용하면 거의 만들 수 있다.

  • 로딩될 이미지 파일을 나타내는 URL이나 이미지 파일 데이터를 담고 있는 NSData 객체
  • Quartz2D, UIKit, 또는 AppKit 의 이미지 표현들(CGImageRef, UIImage, 또는 NSBitmapImageRep 객체)
  • Metal, OpenGL, 또는 OpenGL ES textures
  • CoreVideo image 나 pixel buffers (CVImageBufferRef 또는 CVPixelBufferRef)
  • 프로세스와 이미지 데이터를 공유하는 IOSurfaceRef 객체
  • 메모리에 있는 이미지 비트맵 데이터 (이러한 데이터를 가리키는 포인터나 요청에 따라 데이터를 제공하는 CIImageProvider 객체)

CIImage 객체를 만들 수 있는 모든 방법을 보기 위해서는 CIImage Class Reference를 확인하자.

CIImage 객체는 이미지 데이터를 포함하는 것이 아니라 어떻게 이미지를 생성할 것인지에 대한 정보를 담고 있기 때문에 filter output도 나타낼 수 있다. CIFilter outputImage 속성(CIImage 타입)은 필터를 실행할 때 필요한 처리 단계를 정의 / 저장하고 있다. 이러한 단계들은 display나 output을 위해 이미지 렌더링을 요청할때만 시행된다. 이러한 렌더링 요청을 CIContextrenderdraw 메소드를 통해 명시적으로 할 수도 있고(see Building Your Own Workflow with a Core Image Context) Core Image와 함께 동작하는 많은 시스템 프레임워크 중 하나를 사용해서 이미지를 보여주는 방법과 같이 암시적으로 할 수도 있다. (see Integrating with Other Frameworks).

렌더링 요청이 이뤄질때까지 처리를 미루는건 Core Image를 빠르고 효율적으로 만든다. 렌더링할 때 Core Image는 이미지에 하나 이상의 필터가 적용되어야 하는지 확인하고, 그렇다면 자동으로 다수의 “레시피”를 잇고 정렬해서 중복 작업을 제거한다. 이렇게 각 픽셀은 한번만 처리된다.

필터는 이미지 처리 효과(image processing effect)를 설명한다

CIFilter class의 인스턴스는 이미지 처리 효과와 그 정도를 컨트롤하기 위한 파라미터를 나타내는 변경 가능한 객체이다. 필터를 사용하기 위해서는 CIFilter 객체를 만들고 input 파라미터를 설정한 후에 output 이미지에 접근한다(see Images are the Input and Output of Filters below). 시스템이 이미 알고 있는 필터의 이름을 사용해서 필터 객체를 생성하려면 filterWithName: 생성자를 호출한다(see Querying the System for Filters or Core Image Filter Reference).

대부분의 필터는 하나 또는 그 이상의 input 파라미터를 통해 이미지 처리를 컨트롤한다. 각각의 input 파라미터는 NSNumber와 같이 데이터 타입을 명시하는 attribute class가 있다. input 파라미터는 optional하게 다른 attribute를 가질 수도 있다. 예를 들어 CIColorMonochrome 필터는 처리될 이미지, 색깔, 적용 강도와 같은 세개의 input 파라미터가 있다.

Filter 파라미터는 key-value쌍으로 정의된다. 파라미터를 사용하기 위해서 보통 valueForKey:setValue:forKey: 메소드를 이용하거나 Core Animation과 같이 key-value 코딩을 기반으로 하는 다른 feature를 이용한다. key는 속성을 정의하는 상수이고 valuekey와 연관된 세팅값이다. 보통 아래의 데이터 타입 중 하나를 이용해서 값을 세팅한다.

Table 1–1 Attribute value data types

중요: CIFilter 객체는 변경 가능하다. 따라서 다른 스레드 간에 안전한 공유를 보장할 수 없다. 각각의 스레드는 자신만의 CIFilter 객체를 만들어야한다. 하지만 필터의 inputoutput에 해당하는 CIImage 객체는 변경 불가능하기 때문에 스레드 간 전달도 안전하다.

복잡한 효과를 위한 필터 체이닝

모든 Core Image 필터는 output으로 CIImage 객체를 생성한다. 따라서 해당 객체를 다른 필터의 input으로 사용할 수 있다. 예를들어 Figure 1–1과 같은 필터 시퀀스는 이미지에 색깔 효과를 주고 은은한 효과를 준 뒤 자른다.

Figure 1–1

Core Image 는 필터 체인의 적용을 최적화한다. 체인안에 있는 각 CIImage 객체는 렌더링된 이미지가 아니라 렌더링의 “레시피”이다. Core Image는 각 filter를 독립적으로 시행할 필요 없다. 절대 보여질일 없는 중간 pixel 버퍼를 렌더링하는 것은 시간과 메모리를 낭비이다. 대신 필터를 하나의 operation으로 합치고 순서를 재배치함으로써 같은 결과를 더 효율적으로 도출해낸다. Figure 1-2는 Figure 1–1의 필터 체인 예시보다 더 정확한 rendition(?)을 보여준다.

Figure 1–2

Figure 1–2에서는 자르기 동작이 마지막에서 첫번째로 옮겨진걸 확인할 수 있다. 이는 원래 이미지의 넓은 면적을 잘라내기 때문에 해당 픽셀에 color나 bloom 효과를 주지 않아도 된다. 자르기를 첫번째로 시행함으로써 비싼 이미지 처리 과정을 최종 결과물로 보일 부분에만 적용할 수 있다.

Listing 1–2 는 위의 사진처럼 만들기 위해 어떻게 필터 체인을 set up하는지 보여준다.

Listing 1–2 필터 체인 만들기

func applyFilterChain(to image: CIImage) -> CIImage {// CIPhotoEffectInstant filter는 input image만 인자로 받는다let colorFilter = CIFilter(name: "CIPhotoEffectProcess", withInputParameters:[kCIInputImageKey: image])!// Pass the result of the color filter into the Bloom filter// and set its parameters for a glowy effect.let bloomImage = colorFilter.outputImage!.applyingFilter("CIBloom",withInputParameters: [kCIInputRadiusKey: 10.0,kCIInputIntensityKey: 1.0])// imageByCroppingToRect is a convenience method for// creating the CICrop filter and accessing its outputImage.let cropRect = CGRect(x: 350, y: 350, width: 150, height: 150)let croppedImage = bloomImage.cropped(to: cropRect)return croppedImage}

Listing 1–2 에서는 필터를 설정하고 결과물에 접근하는 편리한 메소드도 몇개 사용되었다. 요약하자면 필터를 적용하기 위해 이러한 메소드들을 독립적으로 혹은 필터 체인의 일부로 사용할 수 있다.

더 많은 옵션을 위한 특별한 필터 타입

대부분 내장 필터는 main input 이미지 위에서 동작하고 하나의 output 이미지를 생성한다. 하지만 흥미로운 효과들을 생성할 때나, 더 복잡한 workflow를 만들기 위해서 다른 필터와 결합할때 사용할 수 있는 다른 타입들도 있다.

  • compositing 필터는 사전 공식에 따라 두 이미지를 합친다. CISourceInCompositingCIMultiplyBlendMode 필터 등이 있고 더 많은 composing 필터를 알고 싶다면 CICategoryCompositeOperation 카테고리를 query하면 된다.
  • generator 필터는 input 이미지를 받지 않는다. 대신 scratch에서 새로운 이미지를 생성하기 위해 다른 input 파라미터를 받는다. 그 자체로 유용한 output를 생성할때도 있고 필터 체인에 결합해서 더 흥미로운 결과를 만들어낼때도 있다. 예를 들어 CIQRCodeGeneratorCICode128BarcodeGenerator필터는 바코드 이미지를 만들어낸다. 더 많은 generator 필터를 알고 싶다면 CICategoryGeneratorCICategoryGradient 카테고리를 query 한다.
  • reduction 필터는 input 이미지 위에서 동작하지만 기존의 방식대로 output image를 생성하기 보다는 output은 input image의 정보를 나타낸다. 예를들어 CIAreaMaximum 필터는 이미지의 특정 부분에서 가장 밝은 색상값을 나타낸다. 모든 필터는 반드시 CIImage 객체를 output으로 내보내야하기에 reduction 필터를 통해 생성되는 정보도 이미지이다. 하지만 보통 이 이미지를 나타내지는 않고 single-pixel이나 single-row 이미지를 읽거나 다른 필터의 input으로 사용한다. 더 많은 reduction 필터를 알고 싶다면 CICategoryReduction 카테고리를 query 한다.
  • transition 필터는 두개의 input 이미지를 받는다. 그리고 독립 변수에 따라 그 결과가 달라진다. 주로 시간을 변수로 둬서 하나의 이미지로 시작해서 다른 이미지로 끝나며 흥미로운 시각 효과로 진행되는 애니메이션을 만들 수 있게 한다. CIDissolveTransitionCICopyMachineTransition 와 같은 필터가 있고 더 많은 transition 필터를 알고 싶다면 CICategoryTransition 카테고리를 query 한다.

다른 프레임워크와 통합

Core Image는 iOS, macOS, tvOS의 몇몇 다른 기술들과 상호작용한다. 이러한 통합 덕분에 복잡한 렌더링 코드를 작성할 필요 없이 게임, 비디오, 이미지에 시각 효과를 손쉽게 더할 수 있다.

아래 섹션에서는 Core Image를 사용하는 일반적인 방법과 system framework가 제공하는 편리한 점을 다룬다.

UIKit 과 AppKit에서 사진 처리

UIKit 과 AppKit 은 사진에 Core Image 처리를 하는 쉬운 방법을 제공한다.

  • 여행 앱은 목적지에 대한 스톡 사진이 있을 것이다. 이것들에 필터를 적용해서 각 목적지의 상세 페이지에 subtle background를 만들 수 있다.
  • 소셜 앱은 유저의 아바타 사진에 기분을 나타내기 위해 필터를 적용할 수 있다.

Note: Core Image를 유저 인터페이스 디자인의 일부인 (macOS, iOS, 및 tvOS 시스템 인터페이스의 반투명한 사이드바, 툴바, 배경에서도 확인 가능한) 블러 효과를 만드는데 사용하지 마라. 대신 macOS에서는 NSVisualEffectView 를 iOS/tvOS에서는 UIVisualEffectView 클래스를 사용해서 자동으로 system appearance를 매칭하고 효과적인 실시간 렌더링을 처리한다.

iOS tvOS에서는 UIImage 객체 어디에나 Core Image 필터를 적용할 수 있다. Listing 1-3 는 image view에서 필터를 사용하는 간단한 메소드를 보여준다.

Listing 1–3 Applying a filter to an image view (iOS/tvOS)

class ViewController: UIViewController {let filter = CIFilter(name: "CISepiaTone",withInputParameters: [kCIInputIntensityKey: 0.5])!@IBOutlet var imageView: UIImageView!func displayFilteredImage(image: UIImage) {// input image에 대한 Core Image image 객체 생성let inputImage = CIImage(image: image)!// Set that image as the filter's input image parameter.filter.setValue(inputImage, forKey: kCIInputImageKey)// Get a UIImage representation of the filter's output and display it.imageView.image = UIImage(CIImage: filter.outputImage!)}}

AV Foundation으로 영상 처리

AVFoundation 프레임워크는 다양한 high level utilities를 제공함으로써 비디오 및 오디오 처리를 돕는다. 이 중에 AVVideoComposition 클래스를 사용해서 비디오와 오디오 트랙을 하나의 결과물로 결합, 수정할 수 있다. (결합에 관한 전반적인 정보를 알고 싶다면 Editing in AVFoundation Programming Guide.) Listing 1–4에 나와있듯이 AVVideoComposition 객체를 사용해서 비디오를 재생하거나 내보낼때 각 프레임에 Core Image filter를 적용할 수 있다.

Listing 1–4 영상에 필터 입히기

let filter = CIFilter(name: "CIGaussianBlur")!let composition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in// 이미지 가장자리는 블러되지 않도록 처리let source = request.sourceImage.clampingToExtent()filter.setValue(source, forKey: kCIInputImageKey)// 비디오 타이밍에 따라 필터 파라미터가 달라짐let seconds = CMTimeGetSeconds(request.compositionTime)filter.setValue(seconds * 10.0, forKey: kCIInputRadiusKey)// 원본 이미지 크기만큼 블러처리 된 부분을 잘라냄
let output = filter.outputImage!.cropping(to: request.sourceImage.extent)
// Provide the filter output to the compositionrequest.finish(with: output, context: nil)})

videoCompositionWithAsset:applyingCIFiltersWithHandler: 생성자를 통해 만들때, 비디오의 각 프레임에 필터 적용을 책임지는 핸들러를 제공해야한다. AVFoundation은 재생이나 내보낼때 자동적으로 핸들러를 호출한다. 이 핸들러에서는 제공된 AVAsynchronousCIImageFilteringRequest 객체를 사용해서 필터 처리되어야하는 비디오 프레임을 가져오고 (프레임 시간과 같은 보충 정보도), 그리고 필터 처리된 이미지를 제공한다.

생성된 비디오를 재생하기 위해 composition의 source로 사용된 asset으로 AVPlayerItem 객체를 만든다. 그리고 composition을 player item의 videoComposition 프로퍼티에 할당하면 된다. composition을 새로운 영상 파일로 내보내기 위해서는 같은 source asset에서 AVAssetExportSession 객체를 만들고 composition을 내보내기 세션의 videoComposition 속성에 할당한다.

Tip: Listing 1–4 는 또다른 유용한 Core Image 테크닉을 보여준다. default로 블러 필터는 이미지의 가장자리도 블러처리한다. 이러한 효과는 비디오에 필터 입힐때 등의 상황에서는 의도되지 않을 수도 있다.

이러한 효과를 피하기 위해서는 imageByClampingToExtent 메소드 (또는 CIAffineClamp filter)를 사용한다. 이를 통해 블러하기 전에 모든 방향으로 이미지의 가장자리 픽셀을 무한히 늘릴 수 있다. Clamping 은 이미지를 무한한 사이즈로 만들기 때문에 블러 후에 이미지를 잘라야한다.

SpriteKit과 SceneKit에서 게임 컨텐츠 처리

SpriteKit은 2D 게임이나 고도로 동적인 컨텐츠의 특성을 가진 앱을 만드는데 쓰이는 기술이다. SceneKit은 3D asset을 만들고, 3D 장면을 렌더링하고 보여주며 3D 게임을 만든다. (각 기술의 더 자세한 정보를 알기 위해서는 see SpriteKit Programming Guide and SceneKit Framework Reference.) 두 프레임워크 모두 장면에 대해(부분 / 전체 모두 가능) Core Image 처리를 적용하는 간편한 방법과 함께 고성능의 실시간 렌더링을 제공한다.

SpriteKit에서는 SKEffectNode 클래스를 사용해서 Core Image를 적용할 수 있다. SpriteKit 게임 템플릿의 Xcode 프로젝트를 생성한 후 GameScene 클래스에 touchesBegan:withEvent: 함수를 아래처럼 변경한다. (macOS 게임 템플릿에서는 mouseDown: 메소드 변경을 통해 비슷한 작업을 할 수 있다)

Listing 1–5 SpriteKit에 필터 적용

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {for touch in touches {let sprite = SKSpriteNode(imageNamed:"Spaceship")sprite.setScale(0.5)sprite.position = touch.location(in: self)sprite.run(.repeatForever(.rotate(byAngle: 1, duration:1)))let effect = SKEffectNode()effect.addChild(sprite)effect.shouldEnableEffects = trueeffect.filter = CIFilter(name: "CIPixellate",withInputParameters: [kCIInputScaleKey: 20.0])self.addChild(effect)}}

SKScene 클래스는 SKEffectNode 의 subclass이기 때문에 모든 SpriteKit scene에 Core Image 필터를 적용할 수 있다.

SceneKit에서 SCNNode 클래스의 filters 속성을 통해 모든 3D scene 요소에 Core Image 필터를 적용할 수 있다. SceneKit 게임 템플릿의 Xcode 프로젝트를 생성한 후 GameViewController 클래스의 viewDidLoad 부분을 아래와 같이 수정하면 직접 확인 가능하다.

Listing 1–6 SceneKit에 필터 적용

// Find this line in the template code:let ship = rootNode.childNode(withName: "ship", recursively: true)!// Add these lines after it:let pixellate = CIFilter(name: "CIPixellate",withInputParameters: [kCIInputScaleKey: 20.0])!ship.filters = [ pixellate ]

SpriteKit과 SceneKit 모두에서 뷰의 씬(scene)에 추가적인 시각효과를 더하기 위해 transition을 사용할 수 있다.(SpriteKit의 presentScene:transition: 와 SceneKit의 presentScene:withTransition:incomingPointOfView:completionHandler:). SKTransition클래스와transitionWithCIFilter:duration: 생성자를 사용해서 Core Image transition 필터를 통한 transition animation을 만들 수 있다.

Core Animation Layers 처리(macOS)

macOS에서CALayer-backed 뷰의 컨텐츠에 필터를 적용하고 싶을때나, 시간의 흐름에 따라 필터 파라미터가 변화하는 애니메이션을 위해filters 프로퍼티를 사용할 수 있다.

Core Image Context로 자신만의 Workflow 만들기

이전 섹션에서 다룬 기술들을 이용해서 Core Image 필터를 적용한다면, 각 프레임워크는 자동으로 Core Image가 이미지를 처리하고 결과를 렌더링할때 사용하는 기초 리소스를 관리한다. 이러한 방법은 워크플로에 대한 성능을 극대화하고 set up을 쉽게 한다.

하지만, 어떤 경우에는 스스로 CIContext 클래스를 사용하여 그 자원들을 신중히 관리할 것이다. 직접 CIContext를 관리함으로써, 정밀하게 앱의 성능 특성을 제어하거나 하위 수준의 렌더링 기술과 코어 이미지를 통합할 수 있다.

Core Image context는 CPU 또는 GPU 컴퓨팅 기술, 자원 그리고 필터를 처리하고 이미지를 생산하는데 필요한 설정을 나타낸다. 사용 가능한 context가 몇 종류 있기에 앱의 워크플로우와 사용할 다른 기술과 가장 잘 맞는 옵션을 선택해야한다. 아래 섹션에서는 몇가지 일반적인 시나리오를 다룬다. 모든 옵션의 set을 보기 위해서는 see CIContext Class Reference.

중요: Core Image context는 많은 자원과 상태를 관리하는 무거운 객체이다. context를 생성, 삭제를 반복하는 것은 성능적으로 비용이 많이 들기 때문에 여러 이미지 처리 작업을 수행할 계획이라면 초기에 context를 생성하고 저장해서 재사용한다.

Automatic Context로 렌더링

만약 다른 그래픽 기술과 어떻게 상호작용할지에 대한 제약이 없다면 Core Image context를 간단하게 만들어라. 기본적인 init 이나 initWithOptions 생성자를 사용한다. 이렇게 하면 Core Image는 자동으로 현재 디바이스와 선택된 option에 알맞게 CPU 또는 GPU 렌더링 기술을 선택함으로써 내부적으로 리소스를 관리한다. 이러한 방법은 처리된 이미지를 파일로 렌더링하는 등의 작업에 적합하다. (for example, with the writeJPEGRepresentationOfImage:toURL:colorSpace:options:error: method).

Note: 명시적으로 렌더링 대상이 정의되지 않은 context는 drawImage:inRect:fromRect: 메소드를 사용할 수 없다. 왜냐하면 사용 될 렌더링 대상에 따라 해당 메서드의 동작이 변경되기 때문이다. 대신 CIContext 에서 rendercreate로 시작하는 메소드를 사용해서 명시적 대상을 지정할 수 있다.

만약 실시간으로 Core Image 결과를 렌더링하고자 할때는 이러한 방법을 조심해야한다. — 즉, 애니메이션 전환 효과를 생성하거나, 영상이나 이미 초당 여러 번 렌더링된 시각적 콘텐츠를 처리하는 것을 의미한다. 생성된 CIContext 객체가 GPU를 이용해서 자동으로 렌더링할 수 있을지라도 렌더링된 결과를 나타내는 것은 CPU와 GPU 사이에 비싼 복사 작업이 포함될 것이기 때문이다.

Metal로 실시간 렌더링

Metal 프레임워크는 그래픽 렌더링 및 병렬 컴퓨팅 워크플로우에 대한 높은 성능을 자랑하면서 GPU 접근에 대한 낮은 오버헤드 액세스를 제공한다.

이러한 workflow는 이미지 처리에 필수적이기 때문에 Core Image는 가능한한 Metal 위에서 진행된다.

만약 Metal로 그래픽을 렌더링하는 앱을 만들거나 실시간 성능을 얻기 위해(라이브 영상과 같이 필터링된 결과를 움직이게 하거나 움직이는 input을 필터링 할때) Metal을 이용한다면 자신의 Core Image context를 만드는 Metal 기기를 사용해라.

Listing 1–7 과 Listing 1–8 은 MetalKit view (MTKView) 를 이용해서 Core Image output을 렌더링하는 예시이다. 해당 예시는 iOS나 tvOS를 위해 NSViewController대신 UIViewController subclass를 사용한다.

Listing 1–7 Core Image 렌더링을 위한 Metal view 세팅

class ViewController: UIViewController, MTKViewDelegate {  // 1// Metal resourcesvar device: MTLDevice!var commandQueue: MTLCommandQueue!var sourceTexture: MTLTexture!                         // 2// Core Image resourcesvar context: CIContext!let filter = CIFilter(name: "CIGaussianBlur")!let colorSpace = CGColorSpaceCreateDeviceRGB()override func viewDidLoad() {super.viewDidLoad()device = MTLCreateSystemDefaultDevice()            // 3commandQueue = device.newCommandQueue()let view = self.view as! MTKView                   // 4view.delegate = selfview.device = deviceview.framebufferOnly = falsecontext = CIContext(mtlDevice: device)             // 5// other setup}}

MetalKit은 뷰가 display되어야 할때마다 drawInMTKView 를 호출한다. (기본적으로 MetalKit 초당 이 방법을 60번까지 부를 수 있다. 자세한 내용은 뷰의 preferredFramesPerSecond 속성을 확인한다.) Listing 1–8은 이 Core Image context에서 렌더링을 위한 메소드의 기본 구현을 보여준다.

Listing 1–8 Metal view에서 Core Image 필터 그리기

public func draw(in view: MTKView) {if let currentDrawable = view.currentDrawable {              // 1let commandBuffer = commandQueue.commandBuffer()let inputImage = CIImage(mtlTexture: sourceTexture)!     // 2filter.setValue(inputImage, forKey: kCIInputImageKey)filter.setValue(20.0, forKey: kCIInputRadiusKey)context.render(filter.outputImage!,                      // 3to: currentDrawable.texture,commandBuffer: commandBuffer,bounds: inputImage.extent,colorSpace: colorSpace)commandBuffer.present(currentDrawable)                   // 4commandBuffer.commit()}}

이 예시는 Metal 을 이용해서 Core Image로 렌더링하는데 필요한 최소한의 코드를 나타낸다. 실제 앱에서는 Core Image로 관리되는 것의 앞뒤로 추가 렌더링을 수행하거나 Core Image output을 다른 texture로 렌더링한 다음에 그것을 또 다른 렌더링에 사용할 수 있다. Metal을 이용하는 더 자세한 정보는 Metal Programming Guide를 참조한다.

OpenGL나 OpenGL ES로 실시간 렌더링

Core Image 는 고성능을 위해 GPS 기반 렌더링의 OpenGL (macOS)나 OpenGL ES (iOS and tvOS)를 사용할 수 있다. 만약 Metal이 지원되지 않는 오래된 하드웨어를 사용하거나 OpenGL / OpenGL ES 워크플로우에 Core Image를 적용하고 싶다면 이 옵션을 고려해볼 수 있다.

각각의 시나리오에서 mageWithTexture:size:flipped:colorSpace: 생성자를 사용한다. 이를 통해 OpenGL 또는 OpenGL ES texture에서 CIImage 객체를 만들수 있다. 이미 GPU 메모리에 있는 이미지 데이터를 작업하는 것은 중복 복사 작업을 방지함으로써 성능을 높인다.

OpenGL / OpenGL ES에서 Core Image output을 렌더링하는 것은

OpenGL 또는 OpenGL ES에서 Core Image 출력을 렌더링하려면 GL context를 current로 설정하고 대상 프레임 버퍼를 설정한 다음drawImage:inRect:fromRect: method를 호출해라.

Quartz 2D로 CPU 기반 렌더링

앱이 실시간 성능을 요구하지 않고 CoreGraphics(예: UIKit 또는 AppKit의 drawRect 메소드)를 사용하여 뷰 콘텐츠를 그리는 경우에는 contextWithCGContext:options 생성자를 사용하여 이미 다른 drawing에 사용 중인 Core Graphics context와 직접 작동하는 Core Image context를 생성한다.(macOS에서는 현재 NSGraphicsContext 객체의 CIContext 속성을 사용) CoreGraphics 컨텍스트에 대한 자세한 내용은 Quartz 2D Programming Guide를 참조한다.

원문

--

--

No responses yet