2011年12月31日 星期六

Filter4Cam 學習之 Core Image Tutorial

since: 2011/12/29
update: 2011/12/31

reference: Beginning Core Image in iOS 5 Tutorial | Ray Wenderlich


A. 前言

    1. Core Image 是一個強大的框架(framework) , 可以讓你輕易的在影像上應用濾鏡功
       能(filters), 例如更改自然飽和度(vibrance), 色彩(hue)曝光時間(exposure). 它使用
       GPU(或 CPU, 使用者可自行定義)來處理影像資料, 並且速度非常快. 快到足以處理即
       時的影格(video frames).

    2. Core Image 濾鏡能夠一次堆疊(stacked) 多個效果應用到圖片或影格上. 當多個濾鏡
        堆疊在一起時, 它們是效率高的因為它們建立了更改後(modified)的單一濾鏡應用到
        影像上, 取代了同時對影像作一個接著一個的濾鏡處理.

    3. 每個濾鏡都有它自己的參數, 並且可以在程式碼中查詢到有關濾鏡本身, 它的用途以
         及輸入參數的相關資訊. 這個系統也可以查尋到有哪些濾鏡可供使用. 目前, 只有在
         Mac 上可用的 Core Image 濾鏡的部分集合在 iOS 上是可以使用的. 然而, 當越來越
         多的濾鏡變成可用之時, 就可以使用此 API 發現到新的濾鏡屬性.

    4. 在這個導覽裡, 你會獲得對 Core Image 實際動手做的體驗. 我們將應用一些不同的
        濾鏡, 而你將會發現在影像上即時應用極酷的效果是如此的容易.

   ---------------------------------------------------------------------------------------------

B. Core Image 概觀

     在我們開始之前, 先來討論一些  Core Image 框架中最重要的類別.

   1. CIContext
      所有 core image 中的處理, 都是在 CIContext 裡完成的. 這有點類似 Core Graphics
      或 OpenGL 的 context. 

   2. CIImage
        這個類別持有(hold)影像資料. 它能夠由以下所建立: UIImage, 影像檔, 或像素
        (pixel)資料.

   3. CIFilter
      這個濾鏡類別有一個字典(dictionary)用來定義所描繪特定濾鏡的特性. 濾鏡的例子:
      自然飽和度(vibrance)濾鏡, 色彩反轉(color inversion)濾鏡, 裁剪(cropping)濾鏡
       .... 等等.

     當建立好專案時, 我們將會開始使用這些類別.  

   ---------------------------------------------------------------------------------------------

C. 開始新專案
   1. 開啟 Xcode 建立新專案:
       Xcode > File > New > New Project... >
       iOS > Application > Single View Application > Next
       Product Name: CITest
       Device Family: iPhone
       Use Storyboard: unchecked
       Use Automatic Reference Counting: checked
       Include Unit Tests: unchecked

   2. 首先, 第一件事是添加 Core Image 框架. 在 Mac 上它是 QuartzCore 框架的一部分,
        但是在 iOS 上它是獨立的框架.
        project container > Targets(project name) > Build Phases >
        Link Binaries with Library > press the +

     Search CoreI > double-click on CoreImage framework > Add

   3. 從 resources for this tutorial , 將 image.png 加到專案裡. 我喜歡把所有 images
       和 sound 檔案放到 Resources 的群組裡. 你的專案沒有那個群組, 所以如果你想要
       的話(非必需), control-click 已加入的 image.png 檔案, 選擇
       New Group from Selection, 接著 click 新產生的 folder 並將名稱改成 Resources.

   4. 現在我們要將狀態列(status bar)隱藏, 必需在 .plist 檔案增加一個 key 和一行 code,
       並且在 .xib 檔案更改一個設定來完成.
       a. 首先是 .plist 檔案. 開啓 Supporting Files 群組下的 CITest-Info.plist 檔案, 在任何
           空白的地方按下 control-click 選擇 Add Row,
           把 Key 改成 Status bar is initally hidden, Value 設為 YES.

       a. 接著是 .xib 檔案. 開啓 ViewController.xib 檔案, 選取 View. 在 Attributes
           inspector 將 Status Bar 的值設為 None.

           隨著 statusbar 的移除, 我們可以增加 statusbar 所佔去的 20 pixels.
           在 Size inspector 將 View 的 Height 從 460 改成 480.

   5. 在 .xib 檔案中, 從 objects panel 拖拉一個 UIImageView 到 View 物件裡.
      位置與尺寸大約符合以下的圖示:

   6. 接著, 點選 Assistant editor 並確認是顯示 ViewController.h 檔案, 從 UIImageView
       control-drag 到 @interface 下方的位置, 將 Connection 設為 Outlet, Name 設為
       imgV, 按下 Connect.

   7. 最後如下所示, 增加一些 code 到 AppDelegate.m
      application:didFinishLaunchingWithOptions: 方法裡(在 return YES 之前)

      [
[UIApplication sharedApplication] setStatusBarHidden:YES];

   8. 執行 project, 你應該會看到一個完全灰色的顯示畫面並且沒有狀態列.
       初始的設定已完成, 開始要進入 Core Image 了.

   ---------------------------------------------------------------------------------------------

D. 影像濾鏡基礎
   1. 我們將簡單地經由 CIFilter 執行影像並顯示在螢幕上作為開始.
       每次我們要將 CIFilters 應用到影像上, 需要做四件事:
       a. 建立 CIImage 物件
           CIImage 有下列的初始化方法: imageWithURL, imageWithData,
           imageWithCVPixelBuffer 與
           imageWithBitmapData:bytesPerRow:size:format:colorSpace.
           你大部份的時間很可能都是使用 imageWithURL.

       b. 建立 CIContext
           CIContext 能夠是以 CPUGPU 作為根基的.

       c. 建立 CIFilter
           當你建立濾鏡(filter)時, 需要依照你使用濾鏡的不同來設定數個屬性.

       d. 取得 filter output
           filter 會提供你一個輸出的影像作為 CIImage - 你可以使用 CIContext 將它
           (CIImage)轉換為 UIImage , 你會在接下來的步驟看到.

   2. 來看看它是如何運作的, 將以下的程式碼加到 ViewController.m 的 viewDidLoad:.
      // 建立一個持有影像檔路徑的 NSURL 物件
      NSString *filePath = [[NSBundle mainBundle] pathForResource:@"image"
     ofType:@"png"];
      NSURL *fileNameAndPath = [NSURL fileURLWithPath:filePath];

      // 建立 CIImage 與 CIContext:
      // 1. 利用 imageWithContentsOfURL 建立 CIImage
      // 2. CIContext 建構子利用 NSDictionary 來詳細說明有哪些選項可用, 包含色彩
      //     格式, 以及 context 是要在 CPU 或 GPU 上執行. 目前這個 app, 使用預設值
      //     即可, 所以傳入 nil 作為參數.
      CIImage *beginImage = [CIImage imageWithContentsOfURL:fileNameAndPath];
      CIContext *context = [CIContext contextWithOptions:nil];

      // 下一步, 建立 CIFilter 物件. CIFilter 建構子夾帶著濾鏡的名稱, 以及一個用來詳細
      //  說明此濾鏡的 keys 跟 values 的字典. 每一個濾鏡都有自己獨特的 keys 和有效的
      //  values 集合.
      //
      //  CISepiaTone(深褐色調)濾鏡的字典帶著二個參數值, 其 Key 為:
      // KCIInputImageKey (CIImage) 與
      // @"inputIntensity" (a float value, wrapped in an NSNumber, between 0 and 1).
      //
      // 在這裡, 我們設定 @"inputIntensity" 的值為 0.8. 大部份的濾鏡參數都有預設值,
      //  會在無提供值的情況下使用. 有一個例外, 就是 CIImage, 因為沒有預設值,
      //  所以必須提供該值.
      CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone" keysAndValues:
      kCIInputImageKey, beginImage,
      @"inputIntensity", [NSNumber numberWithFloat:0.8], nil];

      // 從 CIFilter 的 outputImage 屬性, 可以輕易的取得 CIImage
      CIImage *outputImage = [filter outputImage];

      // 一旦我們已經有了輸出的 CIImage, 我們需要將它轉變成 CGImage
      // (這樣才可轉成 UIImage 或直接畫到銀幕上). 這是需要 context 進來處理的地方.
      // 在 context 上配合所提供的 CIImage 呼叫 createCGImage:fromRect: 方法,
      // 會產生 CGImageRef.
然後, 使用 CGImageRef 呼叫 imageWithCGImage 建構子,
      // 可以產生 UIImage.

     CGImageRef cgimg = [context createCGImage:outputImage
     fromRect:[outputImage extent]];
     UIImage *newImg = [UIImage imageWithCGImage:cgimg];

      // 已經轉成 UIImage 後, 我們就可以在先前新增好的 UIImageView 上顯示.
      [imgV setImage:newImg];

      // 釋放 CGImageRef 的記憶體
      CGImageRelease(cgimg);

  3. 編譯並執行專案, 你就可以看到經由深褐色調(sepia tone)濾鏡處理過的影像結果.
      恭喜, 你已經成功地 使用 CIImagCIFilters 了!

   ---------------------------------------------------------------------------------------------

E. 更改濾鏡的設定值
   1. 很棒, 但這只是你能夠用 Core Image 濾鏡做到的效果的起始而已.
       讓我們加入一個 slider 並設定好, 然後我們就可以即時地調整影像的設定.

   2. 開啓 ViewController.xib 檔案, 拖拉一個 Slider 到 UIImageView 的下方. 確認
       Assistant Editor 是開啓的並顯示 ViewController.h 檔案, 從 slider control-drag
       到 @interface 的下方. 將 Connection 設為: Action, Name 設為: changeValue,
       確認 Event 是設為: Value Changed, 按下 Connect.

   3. 同樣地, 將 slider 連結到 outlet.
      再一次從 slider control-drag 到 @interface 下方, 但這次將 Connection 設為: Outlet
      Name 設為: amountSlider , 按下 Connect
    
      ViewController.h 結果調整如下:

   4. 每次當我們移動 slider 時, 就需要重新建立部分的處理步驟. 然而, 我們不想要重做
       整個過程, 那是非常沒有效率的並且會花費很多時間. 我們需要在類別中更改一些東
       西, 才能夠保持住( hold 住) 在 viewDidLoad 方法裡, 某些我們建立的物件.

   5. 我們所要做的最重要事情, 是每當需要使用 CIContext 時, 可以重復使用它
       (CIContext). 假如每次都重新建立 CIContext , 程式會執行的非常慢.
       其它我們能夠保持住的東西是 CIFilter 和最初持有影像的 CIImage.
       每一次的影像輸出, 我們都需要新的 CIImage, 但是剛開始的影像是保持不變的.

   6. 我們需要新增一些實體變數來完成這個任務.
       在 ViewController.m 私有的 @implementation 加入以下三個實體變數.
        @implementation ViewController {
            //@add
            CIContext *context;
            CIFilter *filter;
            CIImage *beginImage;
        }

   7. 也在 viewDidLoad 方法裡, 更改這些變數, 讓它們使用實體變數取代
       宣告新的區域變數.
    //CIImage *beginImage = [CIImage imageWithContentsOfURL:fileNameAndPath];
    beginImage = [CIImage imageWithContentsOfURL:fileNameAndPath];

    //CIContext *context = [CIContext contextWithOptions:nil];
    context = [CIContext contextWithOptions:nil];

    //CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone" keysAndValues: kCIInputImageKey, beginImage, @"inputIntensity", [NSNumber numberWithFloat:0.8], nil];
    filter = [CIFilter filterWithName:@"CISepiaTone" keysAndValues: kCIInputImageKey, beginImage, @"inputIntensity", [NSNumber numberWithFloat:0.8], nil];

   8. 現在開始實作 changeValue: 方法. 在這個方法中, 我們所要做的是去改變 CIFilter
        字典中 @"inputIntensity" key 的值. 一旦改變了這個值, 就需要去重複一些步驟:
         a. 從 CIFilter 取得輸出的 CIImage.
         b. 將 CIImage 轉成 CGImageRef.
         c. 將 CGImageRef 轉成 UIImage, 並於 image view 上顯示出來.

   9. 所以在 ViewController.m 中, 修改 changeValue: 方法如下所示.
- (IBAction)changeValue:(UISlider *)sender {
    //@add
    float slideValue = [sender value];   
    [filter setValue:[NSNumber numberWithFloat:slideValue]
              forKey:@"inputIntensity"];

    CIImage *outputImage = [filter outputImage];
    
    CGImageRef cgimg = [context createCGImage:outputImage
                                     fromRect:[outputImage extent]];
    
    UIImage *newImg = [UIImage imageWithCGImage:cgimg];    
    [imgV setImage:newImg];
    
    CGImageRelease(cgimg);
}

    10. 你會注意到在此方法的定義中, 我們將變數形態由 (id)sender 改成
         (UISlider *)sender. 我們知道將只會使用此方法來取得 UISlider 的值, 所以我們
         能夠進一步的做這樣的更改. 如果保留成 (id) 的形態, 就必須轉型(cast)成
         UISlider *, 不然的話接下一行就會丟出一個 error. 請確認在 ViewController.h 中,
         此方法的宣告改成: (以符合在這裡作的更改)
          - (IBAction)changeValue:(UISlider *)sender;
       
         我們從 slider 可以得到 float value. Slider 設定的預設值: 最小 0, 最大 1,
         預設 0.5. 這也是適合 CIFilter 的設定值範圍, 多麼方便啊!

         CIFilter 所擁有的方法可允許我們在其字典中, 對不同的 key 設定 value 值.
         在這裡, 我們從 slider 取得 float value, 設定成 @"inputIntensity" key 的
         NSNumber 物件 value.

         程式碼的其餘部分應該看起來很熟悉, 它是依循與 viewDidLoad 方法相同的邏輯.
         我們將會一再地使用這段程式碼. 從現在開始, 我們將使用 changeSlider 方法來描繪
         從 CIFilter 輸出到 UIImageView 的效果.

         編譯並執行, 你應該會有一個功能性的活動式 slider, 可以即時的改變影像的
         深褐色(sepia)值.


   ---------------------------------------------------------------------------------------------

F. 從相簿取得相片
   1. 現在我們可以在 app 的執行中直接更改濾鏡的設定值, 事情開始變得真正有趣了!
       但是, 如果這張花朵的照片不是我們所關心的? 那就來設定一個 UIPickerController,
       這樣就可以從相簿取得相片到程式裡, 供我們把玩.

   2. 我們需要建立一個 button 用來將相簿的 view 帶上來, 所以開啓 ViewController.xib
       檔案並拖拉一個 button 到 slider 的右邊.

   3. 然後, 確認 Assistant Editor 是開啓的並顯示 ViewController.h 檔案, 從 button
        control-drag 到 @interface 下方的位置, 將 Connection 設為 Action, Name 設為
        loadPhoto, 確認 Event 設為 Touch Up Inside, 按下 Connect.

   4. 我們需要先將 ViewController 設為符合(conform)
        UIImagePickerControllerDelegateUINaviationControllerDelegate, 並且實作
        該 delegates protocol 的方法. 在 ViewController.h 檔案中, 更改如下:
        @interface ViewController : UIViewController
        @interface ViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>

    5. 下一步, 切換到 ViewController.m 檔案, 實作 loadPhoto: 方法, 如下所示:
- (IBAction)loadPhoto:(id)sender {
    //@add
    UIImagePickerController *pickerC =
    [[UIImagePickerController alloc] init];

    pickerC.delegate = self;
    [self presentModalViewController:pickerC animated:YES];
}

    第一行的程式碼, 實體化一個新的 UIImagePickerController. 然後將這個 image picker
    的 delegate 設為 self (ViewController).

    接著, 實作以下二個方法:
//@add
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    [self dismissModalViewControllerAnimated:YES];
    NSLog(@"%@", info);
}

//@add
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    [self dismissModalViewControllerAnimated:YES];
}

    在這二個方法, 我們讓 UIPickerController 離開了(dismiss). 這就是 delegate 的工作,
    假如沒有在這邊處理的話, 我們只能盯著螢幕上永不消失的 image picker!    

    第一個方法尚未完成, 它只是對所選取的影像註銷(log out)某些訊息的持有者
    (placeholder). 而第二個: cancel 方法, 僅僅清除(rid)了 picker controller, 完成了.

 6. 編譯並執行, 然後 tap 按鈕就會帶出從相簿擷取照片的 image picker.

   7. 當你選取某張照片時, 會在 console(Debug area) 看到類似以下的東西:
2011-12-31 10:40:32.715 CITest[2208:707] {
    UIImagePickerControllerMediaType = "public.image";
    UIImagePickerControllerOriginalImage = "<UIImage: 0x10f760>";
    UIImagePickerControllerReferenceURL = "assets-library://asset/asset.JPG?id=1C4BFBF9-1C90-472A-900B-3AB04DF1EE07&ext=JPG";
}

     注意到它有使用者所選的原始照片(original image)的字典(dictionary)入口(entry).
     這就是我們所要取出來做濾鏡的.

   8. 現在, 我們已經找到選取照片的辦法, 如何去設定 beginImage(type: CIImage)
      去使用它呢? 只要更改 ViewController.m 檔案裡的 delegate 方法如下即可:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    [self dismissModalViewControllerAnimated:YES];
    //@add
    UIImage *gotImage = [info objectForKey:UIImagePickerControllerOriginalImage];
    beginImage = [CIImage imageWithCGImage:gotImage.CGImage];   
    [filter setValue:beginImage forKey:kCIInputImageKey];
    [self changeValue:amountSlider];
}

   我們需要從選擇的照片來建立新的 CIImage. 可以從字典中的
   UIImagePickerControllerOriginalImage key 常數取得的值來得到照片的 UIImage.
   注意, 最好使用常數定義來取代直接在程式碼中輸入字串, 因為 Apple 在未來有可能
   更改 key 的名稱. 完整的 key 常數清單, 請查閱 UIImagePickerController Delegate
   Protocol Reference. 

   接著, 我們需要將 UIImage 轉成 CIImage, 但是沒有方法可以將 UIImage 轉成
   CIImage. 不過, 有 [CIImage imageWithCGImage:] 這個方法. 因此可以藉由 UIImage
   呼叫 UIImage.CGImage 間接來取得 CIImage, 正好如此!

   然後在濾鏡的字典中設定 kCIInputImageKey key 的 value 為選取的相片: beginImage.
   最後一行看起來有點古怪. 還記得在 changeValue: 方法裡, 使用 slider 最新的數值
   來執行濾鏡並依此更新 image view 的結果?

   是的, 我們必需再次這麼做, 只要呼叫 changeValue: 方法. 即使 slider 的值沒有變動,
   仍然可以使用這個方法來讓工作完成.

   編譯並執行, 你將可以從相簿來選取照片並作濾鏡處理.

   當用濾鏡產生了深褐色(sepia)的照片, 如何能夠將它保留住? 可以使用螢幕截圖的方式,
   那是少數人會去做的事. 因此接下來, 來學學如何將照片存回相簿.

   ---------------------------------------------------------------------------------------------

G. 存回相簿
   1. 有件事你必須知道, 當你將照片存回相簿時, 它是一個持續性的過程, 甚至在離開
       app 後都可以繼續進行.

      當  GPU 停止時, 這可能會是個問題, 不管怎樣, 當我們切換到別的 app 時, 它仍然
      在處理中. 假設照片沒有完成儲存, 當我們回到相簿時, 將不會看到它.

      解決辦法是使用 CPU 的 CIRendering context. 預設是使用 GPU, 所以我們需要做
      一點更改來讓 CIContext 使用 CPU. 使用 CPU 的另外一個優點是對於 texture size
      沒有限制 (GPU 是有限制的, 可使用 inputImageMaximumSize 與
      -outputImageMaximumSize 來查詢目前的設備). GPU 提供較佳的效能, 因此對於
      相片而言, 較常使用 CPU (單一的影像是較串流的影像來得容易); 對於影片而言,
      GPU 是較佳的選擇.

   2. 在 context 中使用 CPU, 必須在 CIContext 初始化時, 加入 options 參數. 記得嗎?
      之前是傳入 nil. 好的, 在 ViewController.m 檔案中的 viewDidLoad: 方法修改如下:
    //context = [CIContext contextWithOptions:nil];
    //@update
    context = [CIContext contextWithOptions:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:kCIContextUseSoftwareRenderer]];

     恭喜, 現在你是使用 CPU 去執行 CI 計算. 然而請注意, software rendering 在模擬器
     上是不會運作的.

   3. 幫 app 新增一個按鈕, 來讓我們將目前所修改的照片儲存起來. 開啓
      ViewController.xib 檔案, 拖拉一個 button, 並連結到一個新的 savePhoto: 方法,
      就如如之前所做的.

   4. 先幫專案加入 AssetsLibrary framework, 這是用來存取被影像應用程式所管理的
       照片或影片. 在 ViewController.m 檔案加入以下的 #import
//@add
#import <AssetsLibrary/AssetsLibrary.h>

   5. 接著, 在 ViewController.m 檔案中, 實作 savePhoto: 方法如下:
- (IBAction)savePhoto:(id)sender {
    CIImage *saveToSave = [filter outputImage];
    CGImageRef cgImg = [context createCGImage:saveToSave fromRect:[saveToSave extent]];

    ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
   
    [library writeImageToSavedPhotosAlbum:cgImg
                                 metadata:[saveToSave properties]
                          completionBlock:^(NSURL *assetURL, NSError *error) {

                              CGImageRelease(cgImg);
                          }];
}

    在這段程式碼區塊中, 作了以下的處理:
    a. 從 filter 取得 CIImage 的輸出.
    b. 產生 CGImageRef.
    c. 將 CGImageRef 儲存到相簿裡.
    d. 釋放 CGImage. 這最後一個步驟是一個 callback block, 當處理完畢時才會觸發.

   6. 編譯並執行 app, 現在你就可以將完美的照片儲存到相簿裡, 並永久保存 !


   ---------------------------------------------------------------------------------------------

H. Image Metadata 是什麼?

   1. 來談一下 image metadata. 從移動式電話取得的影像, 會包含多樣的資料, 例如:
      GPS 座標, 影像格式, 與定位. 當我們將影像檔儲存到相簿時, 會想要保存這些屬性.

   2. 這些 metadata 是隨著 CIImage 關聯在一起的, 且能夠藉由 properties 方法來存取到.
      在之前的程式碼裡, 藉由傳入 CIImage metadata 到 ALAssetsLibrary 的儲存函式中.
    [library writeImageToSavedPhotosAlbum:cgImg
                                 metadata:[saveToSave properties]
                          completionBlock:^(NSURL *assetURL, NSError *error) {

                              CGImageRelease(cgImg);
                          }];


   ---------------------------------------------------------------------------------------------

I. 還有哪些濾鏡可以使用?
   1. 在 CIFilter API 中, 有 130 個濾鏡可以在 Mac OS 上使用, 再加上能夠建立客制化的
      濾鏡. 而在 iOS 平台上, 目前只有 48 個或更多可用, 原因是濾鏡才剛開始被加入.
      目前沒有方式可以在 iOS 平台上建立客制化的濾鏡(應該是指 Core Image), 但是
      有可能未來會提供.
  
  2. 為了要查出有哪些濾鏡可用, 可以使用
       [CIFilter filterNamesInCategory:kCICategoryBuiltIn] 方法. 這個方法會回傳一個
      包含濾鏡名稱的陣列. 另外, 每個濾鏡都有一個 attributes 方法, 會回傳一個
      有關此濾鏡資訊的字典(dictionary). 資訊包含了: 濾鏡的名稱, 濾鏡接受輸入的種類,
      預設及可接受的輸入值, 以及濾鏡的類型.

  3. 讓我們將它們組成一個類別方法, 可用來列印出所有目前可用的濾鏡訊息到 log.
     將下面這個類別方法加到 ViewController.m 檔案中 viewDidLoad: 方法的上方.
-(void)logAllFilters {
    NSArray *properties = [CIFilter filterNamesInCategory: kCICategoryBuiltIn];
    NSLog(@"%@", properties);
    
    for (NSString *filterName in properties) {
        CIFilter *fltr = [CIFilter filterWithName:filterName];
        NSLog(@"%@", [fltr attributes]);
    }
}
    這個方法, 簡單地從 filterNamesInCategory 方法得到整個濾鏡陣列.
    首先列印出濾鏡名稱的清單. 然後針對每個濾鏡名稱, 建立該濾鏡並且從該濾鏡
    列印出屬性字典(attributes dictionary).

  4. 接著, 在 viewDidLoad: 方法的最後呼叫它.
      [self logAllFilters];

    
      輸出結果如下:

2011-12-31 20:58:55.435 CITest[2959:707] (
    CIAdditionCompositing,
    CIAffineTransform,
    CICheckerboardGenerator,
    CIColorBlendMode,
    ....
)

2011-12-31 20:58:55.440 CITest[2959:707] {
    CIAttributeFilterCategories =     (
        CICategoryCompositeOperation,
        CICategoryVideo,
        CICategoryStillImage,
        CICategoryInterlaced,
        CICategoryNonSquarePixels,
        CICategoryHighDynamicRange,
        CICategoryBuiltIn
    );
    CIAttributeFilterDisplayName = Addition;
    CIAttributeFilterName = CIAdditionCompositing;
    inputBackgroundImage =     {
        CIAttributeClass = CIImage;
        CIAttributeType = CIAttributeTypeImage;
    };
    inputImage =     {
        CIAttributeClass = CIImage;
        CIAttributeType = CIAttributeTypeImage;
    };
}
....

2011年12月17日 星期六

My Second iApp Developement

Since: 2011/12/17
Update:
2012/02/11

Project Start: 2012/01/01

Available at App Store: N/A

相關參考:
1. I touchs: iPhone 開發流程
2.
I touchs: My First iApp Developement

3.
I touchs: Join iOS Developer Program
4. I touchs: 產生可於實體 iDevice 上運行的 App 之 (一) 取得憑證
5. I touchs: 產生可於實體 iDevice 上運行的 App 之 (二) 註冊設備
6. I touchs: 產生可於實體 iDevice 上運行的 App 之 (三) 新增 App ID
7. I touchs: 產生可於實體 iDevice 上運行的 App 之 (四) 新增 Provisioning Profile

8. I touchs: 從 Xcode 產生給測試人員使用的 .ipa 檔案

9. I touchs: Lala's Program Note 實作記錄: 34. App 上架


A. 說明
   之前第一個 app: Lala's Notes 上架了, 雖然賣相不佳; 主要是用來練習 Core Data
   與整個開發上架流程. 這次的專案的核心為 Core Image 想先簡略前置的規劃,
   如有需要再特別註記, 並且開發時間為 4 個月.

---------------------------------------------------------------------------

B. 定義專案(Project Planner)
   1. 專案名稱: Filter for Camera
   2. 專案定義: 有濾鏡功能的相機
   3. 專案目標與功能: 有趣或特殊的拍照濾鏡功能
       a.前後鏡頭切換
       b. 音效
       c. 旋轉
       d. facebook, twilter, email
       e. 閃光燈
       f. 顯示日期, save, 復原
       g. 自動對焦
       h. 照相與攝影功能
       i. observer 觀察者模式.
       j. 從相簿取得相片

       k. Lite 版與 Full 版
       l. iPad 版面調整

   4. 定位 Q & A:
      Q1: 你的程式可以做什麼?
      A1: 以濾鏡來處理拍照或選取的圖片.

      Q2: 和其他任何 app 相比, 你的 app 好在哪裡?
      A2: 操作簡單, 介面有趣,
濾鏡效果獨特.

      Q3: 對你的 app 而言, 最顯著的方面為何?
      A3:
濾鏡互動的趣味性與濾鏡的效果.

      Q4: 你會將產品放到 App Store 的哪個類別?
      A4: Photo & Video 及 Entertainment

      Q5: 理想上, 當顧客被你的產品吸引而進行第一次的體驗時, 他們會有什麼樣的經驗.
      A5: 不可預知性, 驚奇, 如同開啟未知的寶箱.

      Q6: 誰是你的核心顧客? 換句話說, 哪一個主要團體將會尋找你的 app,
              且在最後下載它?
      A6: 喜歡 surprise, 不喜歡一成不變的人. 喜歡嘗試新事物, 作實驗的人.

      Q7: 他們的特徵為何 (包含收入, 年齡, 性別) ?
      A7: 擁有創造性的感知, 以及
赤子之心, 外表比實際年輕, 追尋自己的理想.

      Q8: 為什麼這些顧客需要你的產品?
      A8: 藉由濾鏡看到現實之外的另一個現實.
(感受性的世界甚至是平行世界)

      Q9: 如何接觸到你的首要顧客群?
      A9: Blog, app Web, facebook

      Q10: 你希望你的 app 與什麼樣的字彙, 辭彙聯想在一起?
      A10:
Camera, Photo, Entertainment, Filter,
               及 fantasy, Dark, Parallel world 等濾鏡風格相關名稱.


      定位宣言:
      我的產品是: 獨一無二的
      由於它: 超越
時空的限制
      所以能夠做到: 藉由濾鏡看到另一個世界(存在於內心的, 或未曾察覺的)
      這是其他程式無法做到的.
 


   5. 專案需求: coding & art
   6. 專案預算: 法評估
   7. 專案時程:
       4 個月 (至 2012/04/30 止)
       相關學習: ~ 2011/12/31
       正式啓動: 2012/01/01

---------------------------------------------------------------------------

C. 獲取第三方資源(請求或付費) 

---------------------------------------------------------------------------

D. 建立流程圖(可用 OmniGraffle 軟體)
   

---------------------------------------------------------------------------

E. 描繪視圖(使用 OmniGraffle 軟體)
   略

---------------------------------------------------------------------------

F. 美化設計(skinning)

   略

---------------------------------------------------------------------------

G. 開發與編程
    略

---------------------------------------------------------------------------

H. 測試與部署
   略

2011年12月6日 星期二

Play Diablo III beta with mooege on Mac OSX 10.7.2


since: 2011/12/06
update: 2011/12/06

說明: 由於目前 Mac 上 Daiblo III beta 版本為: Beta Patch 7 – v.0.4.1.7931
          因此此版尚未破解(目前已破解的是:
Beta Patch 6),
          在此先記錄步驟.


Important: With Diablo 3-Patch 6, Check your 7841 Base.mpq the MD5 Hash
should be "3faf4efa2a96d501c9c47746cba5a7ad"
update: "5eb4983d4530e3b8bab0d6415d8251fa" has been confirmed to work
by other community members

p.s. 在終端機下執行: md5 filename 即可得到 MD5 Hash


相關網址:

1. mooege/mooege - GitHub
2. Compiling - mooege wiki

A. 下載並安裝 Mono SDK
   下載: Download - Mono
   目前版本: Mono 2.10.6
   檔名: MonoFramework-MDK-2.10.6_1.macos10.xamarin.x86.dmg


B. 下載並安裝 MonoDevelop 
   下載: Download - MonoDevelop  
   目前版本: MonoDevelop 2.8.4.1
   檔名: MonoDevelop-2.8.4.1.dmg


C. 下載 mooege 原始碼

   下載: Zipball
   目前版本: v0.7841-126-g5f0c5e3
   說明: 此版本目前有點問題, 因此改下載 raistlinthewiz / mooege 的版本.
   檔名: raistlinthewiz-mooege-99e9158.zip


D. 編譯

   1. 解壓縮 raistlinthewiz-mooege-99e9158.zip 檔案,
      進入 build 目錄下, 用 MonoDevelop 開啟 Mooege-Mono.sln 檔案.
      結果發現 "使用者界面" 大多變成亂碼.

   2. 因此先將 "使用者界面" 語言設成英文.
      a. 先結束 MonoDevelop, 接著開啓終端機, 執行以下指令:
          代表用英文開啓 MonoDevelop
          LANG=en_US.UTF-8 open /Applications/MonoDevelop.app

      b. 接著執行: MonoDevelop > Preference...

      c. Visual Style > General > User Interface Language: > English > OK
  
   > OK

   3. 再次開啓 build 目錄下的 Mooege-Mono.sln 檔案.
      對 Solution 下的 Mooege-Mono 按右鍵 > 選擇 Set as startup project

   4. 開始編譯:
      memu > Build > Build All


E. 拷貝檔案
   將 /Applications/Diablo III Beta/Data_D3/PC/MPQs 下的所有檔案
   copy 到 src\Mooege\bin\Debug\Assets\MPQ 目錄下


F. 執行 project
   MonoDevelop > menu > Run > Run 
   會開啓一個終端機視窗:


G. 啟動 Diablo III
   開啓新的終端機視窗, 並執行以下的指令:
    $ cd /Applications/Diablo\ III\ Beta/Diablo\ III.app/Contents/MacOS/
   
$ ./Diablo\ III -launch -auroraaddress localhost:1345


H. 登入遊戲
   帳號: test@
   密碼: (Anything)

2011年12月5日 星期一

Lala's Program Note 實作記錄: 34. App 上架

since: 2011/12/05
update: 2011/12/10


App status
Waiting For Review: 2011/12/05
In Review: 2011/12/09
Processing for App Store: 2011/12/09
Ready for Sale: 2011/12/09


A. 首先登入 iTunes Connect 網站, 可以看到六大功能:
   1. Sales and Trends: 銷售趨勢報告.

   2. Contracts, Tax, and Banking: 管理銀行帳戶資訊.

   3. Payments and Financial Reports: 每個月的財政報告與支付的款項. 

   4. Manage Users: 為帳號設定存取權限.

   5. Manage Your Applications: 管理在 iTunes Store 上的 App.

   6. Contact Us: 問題查詢與提問.

--------------------------------------------------------------------------------

B. 新增 App:
   1. 點選: Manage Your Applications > Add New App

   2. 輸入在 App Store 顯示的主要語言公司名稱.
       說明: 確定後即會與此帳號綁定, 以後無法修改.
       Primary Language: English
       Company Name: Lanli's Studio

   3. 設定 App 的資訊:
      App Name: Lala's Notes // App Store 上的顯示名稱, 不可重覆
      SKU Number: 001         // 用來識別你自己的不同 App, 不可更改
      Bundle ID: com.blogspot.LalaProgramNote // 須先建立好, 不可更改

   4. 設定銷售資訊:

   5. 設定 App 的詳細介紹:
      Version Number: 1.0.0 // App 的版本

      Description: A notebook with cute cat background. // App 的描述

      Primary Category: Book // App 的主要分類

      Secondary Category (optional): Productivity // App 的次要分類

      Keywords: note, notebook // App 的關鍵字

      Copyright: 2012 Lanli's Studio Inc // 所有權宣告

      Contact Email Address: lanli0210@gmail.com // Apple 聯絡我們的信箱

      Support URL: http://lala-s-notes.blogspot.com/ // 客戶支援的網站

      App URL (optional): // 關於此 App 的網站

      Review Notes (optional): // 給 Apple 評審人員的備註說明.

      Rating: // 設定 App 的分級

      上傳圖片:
      (1). App Icon: 512 x 512 pixel

      (2). iPhone and iPod touch Screenshots

      (3). iPad Screenshots


   6. 資料送出後:

--------------------------------------------------------------------------------
C. 修改 App 狀態:

   1. 回到  iTunes Connect > Manage Your Applications
      > 點選剛剛產生的 App 

     2. 目前 App 的狀態為: Prepare for Upload
        > 再點選: View Details

   3. 點選右上方的: Ready to Upload Binary

   4. 是否於 App 裡使用加密相關的功能: NO

   5. 接著出現使用 Application Loader 的說明:
If you have downloaded Xcode 3.2.5 or later, you should already have Application Loader stored here: /Developer/Applications/Utilities/Application Loader.app (or in your equivalent custom install location). If you do not find it, download and install the latest version of Xcode, which includes iOS SDK and Application Loader.

   6. App 的狀態已更新為 Waiting For Upload

--------------------------------------------------------------------------------

D. 利用 Xcode 上傳 App
   1. 將 Xcode 的 Scheme 設為: iOS Devide

   2. 執行: Menu > Product > Archive
   > 允許

   3. 按下 Submit 開始上傳.

   4. 成功上傳後, iTunes Connect 上的 App 狀態即更新為: Waiting For Review.
      說明: 等了很久都還沒上傳完畢, 因此改用以下 5. 之後的方式.

   5. Archive > Share...

   > 確認是 for AppStore

   > 輸出成 .ipa 檔

   6. 接著開啟: Application Loader
      /Developer/Applications/Utilities/Application Loader.app

   7. > Deliver Your App

   8. > Choose an application

   9. > 選取剛剛產生的 .ipa

   > Send
 
   > 完成

  10. 最後, 便可在 iTunes Connect 上, 看到 App 狀態更新為: Waiting For Review.

--------------------------------------------------------------------------------

E. 其它
   1. 約 3 天後, 收到 Apple 的 email 通知 App 目前為 Review 狀態.

   2. 接著, 當天不久即更新成 Ready for Sale 的狀態.

   2. 將 Availability Date 設定成今天 2011/12/09, 看看多久會生效.

   3. 狀態更新成 Pending Contract (懸而未決的契約).

   4. 上架了, 最快的是在 latvian(拉脫維亞) 國家.

5. Promo(推銷) Code 的使用
   說明:
   (1). Promotional Codes
       > You are given 50 codes for each version of an application.
         They are for non-commercial use and will expire four weeks after they
         are requested.

   (2). 方法一. 直接開啓 iPhone 上的 App Store > Featured > New
        > 點選最下面的 Redeem.

   (3). 方法二. 開啓 iTunes, 滑鼠移到到右上角顯示登入帳號右方的向下三角形符號.
        > 點選  Redeem(買回) > 接著輸入 Promo Code 即可下載 App.