---
# System prepended metadata

title: Custom CIFilter

---

Custom CIFilter
==


## kernel

kernel, that specifies the calculations to perform on each source iamge pixel.

filter: process on image
kernel: process on pixel

kernel  使用 glslang 語言寫成。通常 CIFilter 會有一個副檔名為 .cikernel 的檔案含有 kernel code。

CIKernel 是一個含有 kernel routine 的 Core Image Object。通常透過傳入 kernel code 的 string 生成。

---

自定義的 CIFilter，可以被包裝在 Bundle 底下。會以 Image Unit 為單位。包有一個或多個的 CIFilter。

![](https://i.imgur.com/pKmT2Am.png)


---


![](https://i.imgur.com/6d9mQq6.png)

CISimpler 定義三件事
    - 座標轉換矩陣
    - 如何內插
    - 如何處理邊界點 
    
---

![](https://i.imgur.com/kgPBzEq.png)

Core Image 計算的方向反過來，先求目的地，再回推原始圖片。目的是為了減少採樣點數。

先決定 destination rect (圖左)
再決定會用到的 source pixels，來減低採樣的點數 (圖右)

---

Core Image 很聽明。會幫你做智慧 cache 與編譯最佳化。cache 策略採用 least-recently-used。最常用的保存，最少用的踢除。
盡量 reuse (Image/context) 來達到最好的 cache 效果。
Core Image 很聽明。在 kernel level 與 pass level，會利用一些編譯器技巧，來讓在 kernel 層級使用到的 registers 數量最少化。
Core Image 很聰明。它又用了編譯器技巧，知道 pixel 間的相依性關係，來最大化利用你的GPU與多核CPU。

---


Executable Filter and Nonexecutable Filter

Executable Filter 含有 machine code ，專門在 CPU 上跑。可能會安全的問題。


Nonexecutable Filter 即是 kernel program。 run 在 GPU 的 kernel 因為是在高度限制的區域，不用擔心病毒的問題。

Nonexecutable Filter 必須被包裝在 Image Unit 才能被使用。

---

By default, Core Image assumes that processing nodes are 
 - 128 bits-per-pixel
 - linear light
 - premultiplied RGBA floating-point values that use the GenericRGB color space.

---

Note that the working color space must be RGB-based. If you have YUV data as input (or other data that is not RGB-based), you can use ColorSync functions to convert to the working color space
 
With 8-bit YUV 4:2:2 sources, Core Image can process 240 HD layers per gigabyte. Eight-bit YUV is the native color format for video source such as DV, MPEG, uncompressed D1, and JPEG. You need to convert YUV color spaces to an RGB color space for Core Image.


---

Coordinate Spaces

Core Image 擁有一個自有的 working space 座標系。當讀取 source image 時，它會將 pixel 先轉換到**與 device 獨立的** working space 的座標。之後在開始要處理的時候，才會再將 pixel 轉換到目標圖像的座標。

Custom Filter 作者需要知道兩個座標。一個是目的地座標，也是你要畫的圖的位置。另一個是採樣座標，也就是你要算的 source 的點。 *destCoord* and *samplerCoord*

---


kernel(a per-pixel processing routine) is written as a computation where an output pixel is expressed using an inverse mapping back to the correspoinding pixels of the kernel's input images.

---


Core Image 有提供三種型式以 kernel 為基底的 Filter

 - Color Filter
 - Warp Filter
 - General Filter

General Filter 一般會做 顏色與位置的變換。但如果你只做某一件事，像是只做顏色變化而不會變動位置的話，可以使用內建的 CIColorKernel。其效能會比較好。相反的如果你要做位置變動的話，可以使用CIWarpKernel。

---

如果在 filter 裡，你的 source 點位置與 dest 點位置不是完全一樣的話，你必須要提供 ROI (region of interest) 的轉換函式。

---
    
Supplying an ROI function


ROI 描述 source 的區域。 Sampler 用此資訊來提供 pixel 的資訊給 kernel 作計算。

---

ROI signature: (Int32 samplerIndex, CGRect sourceRect) -> CGRect

ROI 函式會有一到多個的 Sampler。它的第一個參數即是描述 Sampler 的 index。
若是只會用到一個 sampler，則可以省略不參考它。



ROI 回傳 infinite(CGRectInfinite) 表示要全部採樣。


---

kernel code used terms


sample(sampler, coor) -> vec4

coor:
    samplerTransform(sampler, coord)
    samplerCoord(sampler)
    destCoord()

coor is a vec2
vec2::x
vec2::y
    
compute: 
    inversesqrt()
    dot()

data:
    - vec2
    - vec4
    - vec4::rgb
    - `__color`



---

Write a custom filter


1. 寫 kernel code 並以 image unit 存在 bundle 下。存成 *.cikernel 檔。
2. 利用 Mac Quartz Compose 工具測試 kernel 檔。
3. 定義 CIFilter 介面來描述會用到的輸入參數。
    ```
    @interface YOUR_FILTER_NAME: CIFilter
    {
        parameter1
        parameter2
        ...
    }
    @end
    ```
4. 寫 CIFilter 的 init 函式。
    - 會從 bundle 載入 cikernel 檔案的內容字串。
    - 再初始成 CIKernel 實體(class 層級)。
5. 寫 `customAttributes() -> NSDitionary*` 輸出字典。描述參數的各個資料，包括
    - input 與 output
    - 參數的 class
    - 參數最小值、最大值、與預設值
    - 其他輔助的控制變數 (選用)
6. 寫主要的 `outputImage() -> CIImage*`
    - 用到多少個 source images，就要產生多個 CISampler
    - 呼叫 `apply:arguments:options` 來代入輸入參數與選項變數 kernel function 
    - 將 `apply:arguments:options` 產生的 `CIImage*` 直接回傳
7. 註冊 filter
    - 使用 `CIPlugInRegistration` 註冊成 Image Unit (較推薦)
    - 使用 `[+CIFilter registerFIlename]` 註冊
        - 需要再實作 `filterWithName` 來產成物件實體

---

