티스토리 뷰
Core Image tutorial Part. 2
(Ray wenderlich - Core Image tutorial: Getting Started 번역)
바쁜 사람들을 위한 1편 요약 혹은 복습)
- 코어 이미지는 여러분에게 쉽게 필터들을 이미지에 적용하게 해주는 강력한 프레임워크.
- Vibrane, hue, exposure 를 수정하는 것과 같은 모든 종류의 효과를 얻을 수 있음
- CPU 또는 GPU 들 중 하나를 이미지 데이터를 처리하기 위해 사용 할 수 있고 아주 빠름.
- 비디오 프레임의 실시간 프로세싱을 하는데 충분
- 코어 이미지 필터는 또한 이미지나 비디오 프레임에 다양한 효과를 한번에 적용하기 위해서 필터들을 함께 연결지을 수 있음.
* 다양한 필터들은 이미지에 적용되는 하나의 필터로 합쳐지는 것
* 한번에 하나씩 각각의 필터들을 통해 이미지를 처리하는 것에 비교해서 매우 효율적
1. CIContext
- 코어 이미지의 모든 프로세싱은 CIContext 내에서 수행 됩니다. 이것은 코어 그래픽스 혹은 OpenGL 컨텍스트와 다소 유사합니다.
2. CIImage
- 이 클래스는 이미지 데이터를 가지고 있습니다. UIImage, 이미지 파일, pixel data 로부터 생성됩니다.
3. CIFilter
- 이 클래스는 딕셔너리를 가지는데 해당 필터가 나타내는 특정 필터의 속성을 정의하고 있어요.
필터의 예시로는 vibrance, color inversion, cropping(생동감, 반전, 자르기) 등이 있습니다.
CIFilter 를 이미지에 적용하길 원할 때, 우리는 다음의 4가지 것들이 필요하다.
1. CIImage 객체를 만든다.
- CIImage 는 몇 개의 초기화 메소드를 가진다. 대표적으로,
* CIImage(contentsOfURL:),
* CIImage(data:),
* CIImage(CGImage:),
* CIImage(bitmapData:bytesPerRow:size:format:colorSpace:) 등등...
- 우리는 이것들 중 대부분의 경우, 첫 번째 메소드를 사용할 것이다.
2. CIContext 를 만든다.
- CIContext 는 CPU 혹 은 GPU 를 기반으로 한다.
CIContext 는 상대적으로 초기화 하는데 비용이 많이 들기 때문에, 이것을 반복적으로 만들기 보다는 오히려 재사용 한다.
CIImage 객체를 아웃풋(출력)을 하려고 할 때는, 우리는 항상 하나를 필요로 하게 될 것이다.
3. CIFilter 를 만든다
- 필터를 만들어낼 때마다, 많은 수의 프로퍼티를 구성해야 하는데 이는 우리가 사용하고자 하는 필터에 의존하게 됩니다.
4. 필터를 적용한 아웃풋을 얻는다.
- 필터는 사용자에게 아웃풋 이미지(필터 적용 결과)를 CIImage 로 주는데, 사용자는 이것을 UIImage 로 변환할 수 있습니다.
CIContext 를 사용해서요.
2편 시작)
Putting It Into Context
살펴보기 전에, 우리는 '최적화' 에 대해 알아야 합니다.
1편에서 전술한 것처럼 우린 "CIFilter" 를 적용하기 위해서는 "CIContext" 가 필요해요. 하지만 앞선 예시에서는 이 객체에 대한 언급이 없었어요. UIImage(CIImage:) 생성자가 모든 작업을 수행했어요. 얘는 CIContext 를 생성하고 이것을 이미지 필터링 작업을 수행하는데 사용하죠. 얘는 코어 이미지 API 를 사용하는 것을 매우 쉽게 만들어줘요.
하지만 큰 단점이 있어요. 얘는 자신이 사용될 때마다 새로운 CIContext 를 만들어내요. CIContext 인스턴스는 퍼포먼스를 증가시키기 위해 재사용 될 친구였어요. 만약 필터 값을 업데이트하기 위해 silder 객체를 사용하길 원한다면(+ 뒤에서 해볼거에요 !), 새로운 CIContext 를 필터를 변경할 때마다 매번 생성하는 것은 너무나 느린 방식이 될 거에요.
이것을 적절하게 만들어 봅시다. 코드의 4번째 부분을 삭제하고 다음의 코드로 대체해 보죠.
다시 섹션별로 살펴보도록 하죠(새로운 부분)
1. 여기에 CIContext 객체를 설정하고 CGImage 를 그리기 위해 사용합니다. "CIContext(options:)" 생성자는 컬러 포맷과 같은 옵션들을 지정하는 NSDictionary 를 가지며, context 가 CPU 혹은 GPU 상에서 작동하는지 여부를 지정합니다. 이번 앱에서 기본 값은 괜찮으니 argument 로 nil 을 전달하겠습니다.
2. "createCGImage(_ :, from:)" 을 CIImage 와 함께 문맥 상에서 호출하는 것은 새로운 CGImage 인스턴스를 리턴할 것입니다.
3. 다음으로 "UIImage(cgImage:)" 생성자를 전처럼 CIImage 로부터 직접 생성하는 것 대신 새롭게 생성된 CGImage 로 부터 UIImage 를 생성하기 위해 사용합니다. (+ 추가 : 이 부분에서, 오브젝티브-C 에서는 메모리 관리 문제가 있었는데, 스위프트에서는 ARC 가 자동적으로 코어 파운데이션 객체들을 릴리즈 해줄 수 있으니 걱정 마세요)
실행한다면, 전과 똑같이 작동했음을 볼 수 있습니다.
지금까지 예시에서는 CIContext 의 생성을 직접 핸들링하는 것은 많은 차이를 만들어내지는 않습니다.
그러나 다음 세션에서, 우리는 왜 이것이 성능에서 중요한지 보게 될 것입니다. 물론 필터를 동적으로 수정하는 어빌리티를 구현해 볼거에요!
Changing Filter Values
필터의 값을 바꾸는 것은 대단하지만, 단지 코어 이미지 필터로 사용자가 할 수 있는 것의 시작일 뿐입니다.
슬라이더를 추가하고 설정해서 실시간으로 필터 값을 조정해보도록 해볼게요..
이미지 뷰의 오토레이아웃을 변경하겠습니다.
(스토리보드 지문은 패스하겠습니다. 원문에서 확인하세용)
(https://www.raywenderlich.com/2305-core-image-tutorial-getting-started)
슬라이더의 값이 변화할 때마다, 다른 값을 가진 이미지 필터를 다시 작업해야 합니다..
하지만, 우린 이 전체 프로세스를 다시 수행하길 원하지 않아요. 매우 비효율적이고 너무 오래 걸리거든요.
이를 해결하기 위해, 우리는 클래스 내의 몇 가지 내용을 바꿀 것입니다.
왜냐면 viedDidLoad 메소드 내에 우리가 만든 객체의 일부를 유지할 수 있도록 하기 위함입니다.
우리가 수행하길 원하는 가장 중요한 내용은 CIContext 를 우리가 사용하길 원할 때마다 재사용하는 것이에요.
만약 우리가 이것을 매번 재생성한다면, 우리의 프로그램은 매우 느리게 작동할 거에요.
(기존 코드를 보면) 붙잡은 상태로(= 재사용 안하고) 있는 다른 것은 CIFilter 와 원래의 이미지를 가지고 있는 CIImage 입니다.
모든 아웃풋에 대해 우리가 새로운 CIImage 를 필요로 하게 될 것이지만, 시작 이미지는 일정하게 유지됩니다.
이 작업을 수행하기 위한 인스턴수 변수들을 몇 개 더해볼게요. 다음의 3개의 프로퍼티를 뷰컨트롤러 클래스에 더해주세요.
이 값들을 암시적으로 언래핑된 옵셔널로 '!' 문법을 사용해서 선언했습니다.
왜냐하면 viewDidLoad 까지 우리는 초기화 하지 않을 것이기 때문이죠.
우리는 '?' 도 사용할 수 있었지만, 코드가 디자인된 이 방식('!')이 우리가 이 값들을 사용할 때에 옵셔널이; nil 이 되는 것을 예방할 것이라는 걸 알아야 합니다. (+ 물론 안전성은 떨어지겠죠...)
암시적 언래핑 문법은 어느 곳에서나 '!' 마크 없이도 옵셔널을 읽어들이기 훨씬 쉽게 만들어 줍니다.
viewDidLoad 내의 코드를 변경하여 새로운 로컬 변수들을 선언하는 것 대신 이 프로퍼티들을 사용하세요.
(+ viewDidLoad 에 선언했던 지역 변수들이 삭제되고 코드가 좀 더 깔끔해졌습니다.)
이제 changeValue 메소드를 구현하겠습니다.
이 메소드에서 구현할 내용은 CIFilter 딕셔너리의 내 "inputIntensity" 키의 값을 바꾸는 것입니다.
(+ 슬라이더를 이용해서 필터에 대한 강도를 조절할 예정이거든요)
일단 이 값을 바꾸게 되면 우리는 몇 가지 단계를 반복해야 합니다.
1. CIFilter 로부터 CIImage 아웃풋을 얻습니다.
2. CIImage 를 CGImage 로 변환합니다.
3. CGImage 를 UIImage 로 변환하고 image view 내에 표시합니다.
"amountSliderValueChanged(sender:)" 함수를 다음과 같이 바꾸도록 하겠습니다.
메소드 정의 부분에서 AnyObject 에서 UISilder 로 argument 타입을 변경한 것을 주목하세요.
오직 이 메소드를 UISlider 로부터 값을 검색하기 위해 사용할 것입니다.
만약 AnyObject 로 argument 타입을 남겨둔다면, 이것을 UISlider 로 캐스팅을 해야하거나 다음 라인해서 에러를 던질 것입니다.
우리는 slider 로부터 값을 검색합니다 (슬라이더는 Float 값을 리턴하죠).
우리의 slider 는 기본 값이 설정되어 있는데, 최저가 0.0, 최고가 1.0, 기본값이 0.5 입니다.
CIFilter 는 메소드를 가지는데, 이 메소드는 우리에게 CIFilter 의 dictionary 내의 다른 키들에 대한 값들을 설정하는 것을 가능하게 해줍니다. 우리의 slider 로부터 얻고자 하는 것이면 무엇이든 간에, 우리는 단지 "kCIInputIntensity" 를 설정하면 됩니다.
(+ 그 이유는) 스위프트는 자동적으로 초기의 "CFFloat" 값을 "NSNumber" 객체로 변환해줍니다.
"setValue(value:forKey:)" 와 함께 사용하기에 적합하도록요.
나머지는 동일하고, 이제부터는 "amountSliderValueChanged(sender:)" 를, CIFilter 의 아웃풋(filter.outputImage)을 UIImageView 에 렌더링 하기 위해 사용할 것입니다.
오늘은 여기까지, 다음 시간에~~~~
'Programming > iOS' 카테고리의 다른 글
iOS - Core Image tutorial(코어 이미지 튜토리얼) (4) (1) | 2019.05.13 |
---|---|
iOS - Core Image tutorial(코어 이미지 튜토리얼) (3) (0) | 2019.05.11 |
iOS - Core Image tutorial(코어 이미지 튜토리얼) (1) (3) | 2019.05.06 |
iOS - 초보자를 위한 Grand Central Dispatch (GCD) Part. 2 (0) | 2019.04.24 |
iOS - 초보자를 위한 Grand Central Dispatch (GCD) Part. 1 (0) | 2019.04.21 |
- Total
- Today
- Yesterday
- tca
- fallthrough
- ARC
- function
- 딕셔너리
- 패스트캠퍼스
- 리터럴
- lifecycle
- OOP
- 패캠
- iOS개발스쿨
- Operator
- 프로그래밍
- var
- inswag
- 깃허브
- swiftUI
- array
- commit
- 튜플
- ios
- 컨버전
- GCD
- 스위프트
- Dictionary
- Swift
- 타입
- fastcampus
- 열거형
- 개발스쿨
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |