CIImage Programming Guide Note
==
CIFilter 一次有一個效果。如果要有復合效果,請繼承 CIFilter,再將 recipe 寫入。
---
CIFilter 有人臉辨識、美術風格、影像修正、等等。
---
效能最佳化
- 最好是在每次 render 時,能 reuse CIContext。
- 使用 GPU context 時,不要混用 Core Animation。
- 注意 image 尺寸不要超出 cpu/gpu context 的限制。
- UIImageView 請放靜止圖片。
- 減少不必要的上傳下載到GPU
- 盡量使用YUV,也要注意只支援RGB的 CIFilter 與它們之間會花時間的 RGB/YUV 轉換。
---
CIImage 通常不表示一個真正的 bitmap,而是代表原始圖與一系列的指令(filters or command)。
> Describe how to produce an image
通常只有在 CIConext 執行 render 時,才會真正執行疊加的指令。
---
CIImage 能吃的 source
- 有路徑的圖片檔案。
- 今有圖片檔案內容的NSData結構。
- Quartz2D, UIKit, AppKit 的代表圖片 (CGImageRef/UIImage/NSBitmapImageRep)。
- Metal, OpenGL, OpenGL ES textures。
- CoreVideo 的 image/pixel buffers (CVImageBufferRef/CVPixelBufferRef)。
- 用來跨 process 共享的 IOSurfaceRef。
- 在 system memory 的 bitmap data。
---
CIFilter 用到的參數型別
CIFilter 利用 key-value 來設定參數。
value 的型別如下
- String
- Number
- CIVector, 一組 floating point 值來描述 position, size, rectrangles 或是其他。
- CIColor, color space 相關
- CIImage, 用來設定 input/output
- NSAffineTransform, 用來座標轉移
---
其他特殊的 CIFilter 類型
## compositing/blending
輸入兩張 input 產生一張 output。
example: CISourceInCompositing, CIMultiplyBlendMode
## generator
不輸入 input,反而根據一些參數產生一張 output。
example: CIQRCodeGenerator, CICode128BarcodeGenerator, CICheckboardGenerator, ...
## reduction
輸入一張 input,不輸出傳統的圖像,而是將一些描述性資訊,以圖像的方式擺在 output image。
example: CIAreaMaximum, CIAreaHistogram
## transition
輸入兩張 input,通常代表轉場前的圖片與轉場後的圖片。給的參數通常會是時間。output 即是轉場中的效果。
example: CIDissolveTransition, CICopyMachineTransition
---
與 UI 的 ImageView 互動
```
func apply(img1: NSImage) {
let inputImage: CIImage = CIImage(initWithBitmapImageRep: img1);
let filter: CIFilter = createFilter(....);
filter.setValue(inputImage, forKey: kCIInputImageKey);
let outputImage = filter.outputImage;
let img2: NSCIImageRep = NSImage(CIImage: outputImage);
return img2;
}
```
---
與 AVFoundation 互動
```
let filter = CIFilter(name: "CIGaussianBlur")!;
let composition = AVVideoComposition(asset: asset, applyCIFilterWithHandler: {
request in
// request is a AVAsynchronousCIImageFilteringRequest instance
// input
let source = request.sourceImage.clampingToExtend();
filter.setValue(source, forKey: kCIInputImageKey);
// time as input attribute
let seconds = CMTimeGetSeconds(request.compositionTime);
filter.setValue(seconds * 10.0, forKey: kCIInputRadiusKey);
// output
let output = filter.outputImage!.cropping(to: request.sourceImage.extend);
request.finish(with: output, context: nil);
})
```
// 處理前要先做 clamp effect,讓邊緣擴出去再作
// 目的是為了 effect 在 apply 時,邊緣的點會作用到它旁邊空的點,而有不太好的效果
// 處理完之後記得要再裁切掉擴增的邊緣
---
與 Metal 互動
```
class ViewController: UIViewController, MTKViewDelegate {
// property: MTL
var device: MTLDevice!
var commandQ: MTLCommandQueue!
var sourceTexture: MTLTexture!
// property: CoreImage
var context: CIContext
let filter = CIFilter(name: "CIGaussianBlur")!
let colorspace = CGColorSpaceCreateDeviceRGB()
func viewDidLoad() {
...
device = MTLCreateSystemDefaultDevice()
commandQ = device.newCommandQueue();
// UI's Metal-drawing view (MTKViewDelegate)
let view = self.view as! MTKView
view.delegate = self
view.device = device
view.fraembufferOnly = false;
context = CIContext(mtlDevice: device);
...
}
func draw(in view: MTKView) {
if let currentDrawable = view.currentDrawable {
// input
let inputImage = CIImage(mtlTexture: sourceTexture)
filter.setValue(inputIamge, forKey: kCIInputIamgeKey)
filter.setValue(20.0, forKey, kCIInputRadiusKey)
// output
let commadnBuf = commandQ.commandBuffer()
let outputImage = filter.outputImage!
context.render(
outputImage,
to: currentDrawable.texture,
commandBuffer: commandBuf,
bounds: inputImage.extend,
colorSpace: colorSpace
)
commandBuf.present(currentDrawable)
commandBuf.commit()
}
}
}
```
sourceTexture (:MTLTexture) can be created by `MTKTextureLoader`
---
Reference: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/CoreImaging/ci_tasks/ci_tasks.html#//apple_ref/doc/uid/TP30001185-CH3-DontLinkElementID_12