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