검색결과 리스트
high order function에 해당되는 글 1건
- 2015.11.10 [swift2] Core Image Filter 예제
글
[swift2] Core Image Filter 예제
Swift
2015. 11. 10. 23:42
절차지향 또는 객체지향 프로그래밍에 익숙한 상태에서, 함수형 프로그래밍에 적응하기란 쉽지 않습니다. 다른 프로그래밍 방법론을 공부했을 때처럼 많은 함수형 프로그램 코드를 읽고 이해하고, 또 많은 예제를 실제로 작성해봐야만 숙달될 수 있습니다.
swift 함수형 프로그램의 예로 Core Image 필터 예제를 소개합니다. 이 예제는 objc.io 에서 출판된 Funtional Programming in Swift 에서 발췌하였습니다.
- 이 예제에서는 MaxOSX Core Image 프레임워크의 함수형 wrapper API 작성을 목표로 합니다.
- 또한 고차원함수(high-order function)와 같은 함수형 프로그래밍의 실질적인 활용예를 보는 것입니다.
Core Image
- Core Image는 강력한 이미지 프로세싱 프레임워크지만 사용하기가 불편합니다.
- Core Image API는 키밸류 코딩을 사용해서 설정할 수 있는 느슨하게 타이핑된 API 입니다.
- 파라미터 이름을 입력할 때 실수할 수 있는 여지가 많아 런타임 에러를 발생시킬 수 있습니다.
- 이번에 개발할 API 는 안전하고, 모듈화되었으며, (오타로 인한)런타임 에러가 발생하지 않도록 타입을 장점을 활용합니다.
Filter Type
- Core Image의 핵심클래스는 이미지 필터를 생성하는데 사용하는 CIFilter 클래스입니다.
- CIFilter 객체 생성시 반드시 kCIInputImageKey 키에 해당하는 input image를 전달해야합니다.
- kCIOutputImageKey 키를 사용해서 필터가 적용된 결과 이미지를 얻어올 수 있습니다.
- 또한 이 결과 이미지는 다른 필터의 입력으로 사용할 수 있습니다.
CIImage 타입을 파라미터로 받아 CIImage 타입의 결과 이미지를 반환하는 함수타입 Filter를 정의합니다.
typealias Filter = CIImage -> CIImage
Blur 필터 함수
- 이미지에 블러를 적용하는 blur 함수를 정의해보겠습니다.
- 이 블러필터는 한개의 blur radius 파라미터를 받습니다.
func blur(radius:Double) -> Filter {
return { image in
let parameters = [
kCIInputRadiusKey : radius,
kCIInputImageKey : image
]
let filter = CIFilter(name: "CIGaussianBlur", withInputParameters: parameters)
return filter!.outputImage!
}
}
- blur 함수는 CIImage 타입의 이미지를 인자로 받고, 결과 이미지(filter!.outputImage)를 리턴하는 Filter 함수를 반환합니다.
Color Overlay 필터 함수
- 이미지 위에 특정 컬러 레이어로 덮는 오버레이 필터를 정의해보겠습니다.
- CoreImage 에는 오버레이 필터가 없지만 이미 있는 필터들을 조합하여 만들것입니다.
- 컬러를 생성하는 color generator 필터와 레이어를 얹는 source-over compositing 필터를 사용하겠습니다.
Color 레이어 생성필터
func colorGenerator(color:NSColor) -> Filter {
return { image in
let parameters = [kCIInputColorKey:CIColor(color: color)!]
let filter = CIFilter(name: "CIConstantColorGenerator", withInputParameters: parameters)
return filter!.outputImage!.imageByCroppingToRect(image.extent)
}
}
- blur 필터와 유사하지만 input 이미지가 필요하지 않다는 차이가 있습니다.
- 생성된 컬러 레이어 이미지를 특정크기(인자로 받은 이미지의 크기)로 잘라주어야 합니다.
- 다음으로 두 이미지를 복합하는 필터를 정의합니다.
레이어 오버필터
func compositeSourceOver(overlay:CIImage) -> Filter {
return { image in
let parameters = [
kCIInputBackgroundImageKey: image,
kCIInputImageKey: overlay
]
let filter = CIFilter(name: "CISourceOverCompositing", withInputParameters: parameters)
return filter!.outputImage!.imageByCroppingToRect(image.extent)
}
}
- 마지막에 input 이미지의 크기만큼 결과 이미지를 자릅니다.
- 두 필터를 결합하여 color overlay 필터를 생성합니다.
Color Overlay 필터
func colorOverlay(color:NSColor) -> Filter {
return { image in
let overlay = colorGenerator(color)(image)
return compositeSourceOver(overlay)(image)
}
}
- colorOverlay 함수는 컬러를 인자로 받아 필터함수를 반환합니다.
- 이 필터함수에 이미지를 전달하면, 앞서 전달한 컬러로 오버레이된 이미지를 반환합니다.
Filter 복합하기
- blur 필터와 color overlay 필터를 복합해서 사용해보겠습니다.
- 원본 이미지 파일을 읽습니다.
let path = NSBundle.mainBundle().pathForResource("16.jpg", ofType: nil)!
let image = CIImage(contentsOfURL: NSURL(fileURLWithPath: path))!- blur 필터를 적용합니다.
let blurRadius = 5.0
let blurredImage = blur(blurRadius)(image)- 빨간색 레이어를 오버레이합니다.
let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2)
let overlaidImage = colorOverlay(overlayColor)(blurredImage)Function Composition
- 위 예제처럼 두 필터함수를 한문장의 표현식으로 표현할 수 있습니다.
let result = colorOverlay(overlayColor)(blur(blurRadius)(image))
- 하지만 여러 괄호들로 인해 조합된 필터의 수가 많아질 수록 가독성이 나타집니다.
- 더 좋은 방법은 필터 조합을 위한 커스텀 연산자를 정의하는 것입니다.
- 먼저 필터를 조합하는 함수를 정의해보겠습니다.
func composeFilters(filter1 filter1:Filter, filter2:Filter) -> Filter {
return { image in filter2(filter1(image)) }
}
- 위 함수는 두개의 필터를 인자로 받아 조합하여 새로운 필터를 정의합니다.
- 반환된 필터 함수는 CIImage 타입의 image 인자를 filter1과 filter2에 순차적으로 전달합니다.
- 아래와 같이 사용할 수 있습니다.
let myFilter1 = composeFilters(filter1: blur(blurRadius), filter2: colorOverlay(overlayColor))
let result1 = myFilter1(image)
- 이제 필터 조합을 위한 연산자를 정의하여 좀더 가독성 있게 만들어보겠습니다.
- 모든 곳에 자체 연산자를 정의할 필요는 없지만, 필터 조합은 이미지 처리 라이브러리에서 반복해서 발생하는 작업이기 때문에 그 용도가 적합합니다.
infix operator >>> { associativity left }
func >>> (filter1:Filter, filter2:Filter) -> Filter {
return { img in filter2(filter1(img)) }
}
- 이제 >>> 연산자를 이전에 정의했던 composeFilters 함수처럼 사용할 수 있습니다.
let myFilter2 = blur(blurRadius) >>> colorOverlay(overlayColor)
let result2 = myFilter2(image)
- >>> 연산자를 left-associativity 로 정의했기 때문에, 유닉스 파이프처럼 왼쪽에서 오른쪽으로 필터가 적용됩니다.
- 우리가 정의한 필터 조합은 Function Composition 의 한 예입니다.
전체소스
import Cocoa
import XCPlayground
// define Filter Type
typealias Filter = CIImage -> CIImage
// blur function
func blur(radius:Double) -> Filter {
return { image in
let parameters = [
kCIInputRadiusKey : radius,
kCIInputImageKey : image
]
let filter = CIFilter(name: "CIGaussianBlur", withInputParameters: parameters)
return filter!.outputImage!
}
}
// color layer function
func colorGenerator(color:NSColor) -> Filter {
return { image in
let parameters = [kCIInputColorKey:CIColor(color: color)!]
let filter = CIFilter(name: "CIConstantColorGenerator", withInputParameters: parameters)
return filter!.outputImage!.imageByCroppingToRect(image.extent)
}
}
// overlay function
func compositeSourceOver(overlay:CIImage) -> Filter {
return { image in
let parameters = [
kCIInputBackgroundImageKey: image,
kCIInputImageKey: overlay
]
let filter = CIFilter(name: "CISourceOverCompositing", withInputParameters: parameters)
return filter!.outputImage!.imageByCroppingToRect(image.extent)
}
}
// color overlay function
func colorOverlay(color:NSColor) -> Filter {
return { image in
let overlay = colorGenerator(color)(image)
return compositeSourceOver(overlay)(image)
}
}
// 원본 이미지 읽기
let path = NSBundle.mainBundle().pathForResource("16.jpg", ofType: nil)!
let image = CIImage(contentsOfURL: NSURL(fileURLWithPath: path))!
// apply blur
let blurRadius = 5.0
let blurredImage = blur(blurRadius)(image)
// apply color overlay
let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2)
let overlaidImage = colorOverlay(overlayColor)(blurredImage)
// define composition function
func composeFilters(filter1 filter1:Filter, filter2:Filter) -> Filter {
return { image in filter2(filter1(image)) }
}
let myFilter1 = composeFilters(filter1: blur(blurRadius), filter2: colorOverlay(overlayColor))
let result1 = myFilter1(image)
// define composition operator
infix operator >>> { associativity left }
func >>> (filter1:Filter, filter2:Filter) -> Filter {
return { img in filter2(filter1(img)) }
}
let myFilter2 = blur(blurRadius) >>> colorOverlay(overlayColor)
let result2 = myFilter2(image)
'Swift' 카테고리의 다른 글
| [swift2] 키워드로 보는 Swift (0) | 2015.11.30 |
|---|---|
| [swift2] Currying 함수 (0) | 2015.11.25 |
| [swift2] Core Image Filter 예제 (0) | 2015.11.10 |
| [swift2.1] Xcode7.1 Playground 변경사항 (0) | 2015.10.27 |
| [swift2] Functor 와 Monad (0) | 2015.10.25 |
| [swift2] 타입변환 연산자 (is, as, as?, as!) (0) | 2015.10.18 |