Fork me on GitHub

8/10/2010

[Mac OS X] 如何得到 UIImage/CGImage 的 piexl 資料

透過 UIImagePickerControllerDelegate 取得影像的 UIImage 資料之後,該如何取得 UIImage 當中的像素資料,以進行接下來的影像處理,在此記錄一下。

首先參考的是這篇 Getting the pixel data from a CGImage object,當中提到的第一個方法 CGDataProviderCopyData 在 Mac OS X 10.5 以後才出現,相當方便但是無法確定自己得到的 color model 是否和影像一致,如當中的 WARNING 所說明:

WARNING: The pixel data returned by CGDataProviderCopyData has not been color matched and is in the format that the image is in, as described by the various CGImageGet functions (see for Getting Information About an Image more information)

比較保險的作法,也就是在 Mac OS X 10.5 之前的作法,是自行 create 一個 bitmap context,然後利用 CGDrawImage 將影像畫到這個你指定格式的 context 上頭,如此一來你就可以利用 CGBitmapContextGetData 來取得影像的像素資料!

IMPORTANT: 以下範例 creates a bitmap context with a 8-bits per component ARGB color space, draws the source image to this context, then retrieves the image bits in this color space from the context. Regardless of what the source image format is (CMYK, 24-bit RGB, Grayscale, and so on) it will be converted over to this color space. For more information about creating bitmap contexts for other pixel formats, see the Quartz 2D Programming Guide.

其中在利用 CGBitmapContextCreate 建立 bitmap context 時候有一個 colormodel 參數,該資料當中的 CGColorSpaceGenericRGB 已經被 deprecated 了,解決的方案是利用 CGColorSpaceCreateDeviceRGB() 取代,請參考 Warning: kCGColorSpaceGenericRGB is deprecated。

針對像素處理完畢之後,需要由處理過後的 CGImage 創建一個新的 UIImage 然後指定到你要顯示的地方:
newImgRef = CGBitmapContextCreateImage(cgctx);
UIImage *newimg = [UIImage imageWithCGImage:newImgRef];
imageView.image = newimg;

參考上述兩點,整理的程式碼如下:

- (void)imagePickerController:(UIImagePickerController *)picker 
        didFinishPickingImage:(UIImage *)image
        editingInfo:(NSDictionary *)editingInfo 
{
 CGImageRef imageRef = image.CGImage;
 
 // Create the bitmap context
        cgctx = CreateARGBBitmapContext(imageRef);
        if (cgctx == NULL) 
        { 
        // error creating context
        return;
        }
 
 // Get image width, height.
        size_t w = CGImageGetWidth(imageRef);
        size_t h = CGImageGetHeight(imageRef);
        CGRect rect = {{0,0},{w,h}}; 
 
 // Draw the image to the bitmap context. Once we draw, the memory 
        // allocated for the context for rendering will then contain the 
        // raw image data in the specified color space.
        CGContextDrawImage(cgctx, rect, imageRef); 
 
 // Now we can get a pointer to the image data associated with the bitmap
        // context.
        void *data = CGBitmapContextGetData(cgctx);
 imagePtr = data;
 unsigned char A, R, G, B;
 
        if (imagePtr != NULL)
        { 
            // **** You have a pointer to the image data ****  
            // **** Do stuff with the data here ****
     for (int x = 0; x < w; x  ) {
         for (int y = 0; y < h; y  ) {
  
      A = imagePtr[(x (y*w))*4 + 0];
      R = imagePtr[(x (y*w))*4 + 1];
      G = imagePtr[(x (y*w))*4 + 2];
      B = imagePtr[(x (y*w))*4 + 3];  
  }
     }
        }
 
 newImgRef = CGBitmapContextCreateImage(cgctx);
 UIImage *newimg = [UIImage imageWithCGImage:newImgRef];
 
 
 // do something with selectedImage and originalImage
 imageView.image = newimg;
        [picker dismissModalViewControllerAnimated:YES];
}


其中的 CreateARGBBitmapContext 方法如下:

CGContextRef CreateARGBBitmapContext (CGImageRef inImage)
{
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;
 
 // Get image width, height. We'll use the entire image.
    size_t pixelsWide = CGImageGetWidth(inImage);
    size_t pixelsHigh = CGImageGetHeight(inImage);
 
    // Declare the number of bytes per row. Each pixel in the bitmap in this
    // example is represented by 4 bytes; 8 bits each of red, green, blue, and
    // alpha.
    bitmapBytesPerRow   = (pixelsWide * 4);
    bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);
 
    // Use the generic RGB color space.
    colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL)
    {
        fprintf(stderr, "Error allocating color space\n");
        return NULL;
    }
 
    // Allocate memory for image data. This is the destination in memory
    // where any drawing to the bitmap context will be rendered.
    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL) 
    {
        fprintf (stderr, "Memory not allocated!");
        CGColorSpaceRelease( colorSpace );
        return NULL;
    }
 
    // Create the bitmap context. We want pre-multiplied ARGB, 8-bits 
    // per component. Regardless of what the source image format is 
    // (CMYK, Grayscale, and so on) it will be converted over to the format
    // specified here by CGBitmapContextCreate.
    context = CGBitmapContextCreate (bitmapData,
          pixelsWide,
          pixelsHigh,
          8,      // bits per component
          bitmapBytesPerRow,
          colorSpace,
          kCGImageAlphaPremultipliedFirst);
    if (context == NULL)
    {
        free (bitmapData);
        fprintf (stderr, "Context not created!");
    }
 
    // Make sure and release colorspace before returning
    CGColorSpaceRelease( colorSpace );
 
    return context;
}


參考資料:
如何把照片加到 iPhone Simulator 的 Library 當中
http://www.youtube.com/watch?v=1NqHnYGNAp8

OpenGL ES from the Ground Up: Table of Contents
http://iphonedevelopment.blogspot.com/2009/05/opengl-es-from-ground-up-table-of.html

如何得到 CGImage 的 pixel data
http://developer.apple.com/mac/library/qa/qa2007/qa1509.html

Warning: kCGColorSpaceGenericRGB is deprecated
http://iphoneinaction.manning.com/iphone_in_action/2009/06/warning-kcgcolorspacegenericrgb-is-deprecated.html

Convert CGImage to UIImage
http://www.iphonedevsdk.com/forum/iphone-sdk-development/21806-convert-cgimage-uiimage.html

......

8/04/2010

[C] 測量程式執行時間

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{ 
    int count,a;
    clock_t c0, c1;
    double cputime;
    c0 = clock();

    /* foo()  */
    for (count = 1l; count < 12345678; count  ) {
        a = 64*64;
    }
    /* end foo( 0 */

    c1= clock();
    cputime = (c1 - c0)/(CLOCKS_PER_SEC );

    printf ("\tElapsed CPU time test:   %f  sec\n",cputime);

    return 0;
}


HMM 程式碼學習資源

Hidden Markov Models in C#
http://crsouza.blogspot.com/2010/03/hidden-markov-models-in-c.html#using
http://www.codeproject.com/Articles/69647/Hidden-Markov-Models-in-Csharp.aspx

用Excel徹底了解Baum-Welch Algorithm流程
An Interactive Spreadsheet for Teaching the Forward-Backward Algorithm (2002)
http://www.cs.jhu.edu/~jason/papers/#tnlp02

Linux平台的HMM Source Code
Hidden Markov Model (HMM) Software: Implementation of Forward-Backward, Viterbi, and Baum-Welch algorithms
http://www.kanungo.com/software/software.html#umdhmm

A C++ Implementation of Hidden Markov Model
http://webdocs.cs.ualberta.ca/~lindek/hmm.htm

在 Mac 上跑 CUDA

1. 先按照 Getting Start MacOS 文件上的步驟依序將 Driver, Toolkit 和 SDK 安裝完成。

其中有一個步驟必須設定環境變數可以參考如下:http://macuknow.com/node/4674

於終端機上輸入 "vi ~/.bash_profile " (於家目錄下開啟名為.bash_profile之文字檔)
先按一下 "i" 鍵 插入文字, 並將下列兩行貼上

export PATH=/usr/local/cuda/bin:$PATH
export DYLD_LIBRARY_PATH=/usr/local/cuda/lib:

再按一下 "esc" 鍵 結束編輯
並鍵入 ":wq" 儲存離開

2. 安裝完成後,驗證安裝

在 terminal window 當中執行 "ncvv -V"

3. 在 CUDA SDK 當中有許多範例程式可以參考,必須先他們 compile 一遍產出執行檔。

到 "/Developer/GPU Computing/C" 路徑下,執行 "make" 指令,於是這些範例程式的執行檔都會被輸出至
"/Developer/GPU Computing/C/bin/darwin/release" 當中。於是可以執行 deviceQuery 這個範例看看自己 GPU
的硬體規格。

4. 目前寫程式仍然是在 windosw 當中利用 VS2008,測試效能則在有 CUDA enabled 顯示卡的小白上執行,
所以該如何將寫好的 cuda 程式碼,放進 mac 編譯執行呢!

4-1 CUDA SDK 的路徑 "/Developer/GPU Computing/C/src/" 下有很多範例專案,可以在此隨便複製一個然後
在偷偷更名,並且將資料夾當中的檔案置換成自己寫的 CUDA 程式碼。

4-2 這邊要注意,如果你的專案必須要 include 什麼 .h 檔的話,需要自行修改當中的 Makefile。

4-3 如果專案當中有使用到 CImag 的話,在 Makefile 當中的最底下那一行 "include ../../common/common.mk",
找到 common.mk 這個檔案,並且加入以下:

# for CImg
LINK := g++ -fPIC -O2 -lm -lpthread -L/usr/X11R6/lib -lm -lpthread -lX11

因為 CImg 有使用到 X11 的一些 function 所以必須要把這個連結加進去 (http://cimg.sourceforge.net/reference/group__cimg__overview.html)

4-4 compile 的話就將 terminal window 指到專案路徑下,執行 "make" 指令,就會輸出執行檔至
"/Developer/GPU Computing/C/bin/darwin/release" 當中

4-5 需要注意的是,如果有用到 CImg 當中顯示影像或者相關影像編輯的功能,則必須透過 X11 來執行這個執行檔
方法是:打開 X11,將路徑指到該執行檔之路徑,執行 "./cudaApp"

...

4/17/2010

[objC] 記憶體管理

今天看了一些記憶體管理的章節,目前仍然有些許疑惑,不過將目前為止的理解記錄一下。

ObjC 當中的物件均以指標形式創建傳遞,每一個物件都會有一個 reference count,當物件被配置記憶體、被創建、或被複製時,其 reference count 都會初始為 1,任何對於這個物件施行操作的方法,都必須控制好這個 reference count,操作 reference count 可以透過每個繼承 NSObject 物件的 instant method: retain(+1) 和 release(-1)。

當 reference count 變成 0 的時候代表此物件已經不再被任何程式需要,因此就會被消滅,因此當該物件被多個擁有者使用的時候,必須小心的操作其 reference count,避免還有人要使用該物件時,其 reference count 已經變設成 0!

對類別當中的成員物件進行 存取子 (accessor) 操作時,需遵循: 1. 先將傳入物件 retain(+1) 2. 再 release 成員物件 3. 將傳入物件指向成員物件。

一般的情形下:你 New 了一個 A 類別的物件 a,B 類別的物件 b 為其成員變數 (b 的 ref count =1),現在你要透過 setter 方法從外部傳入一個物件 b' 去設定 b 的值,首先你 new 了一個 b' 物件 (b' 的 ref count =1),傳入 setter 後 b' retain (b' 的 ref count =2),接著 release b (b 的 ref count =0, 被摧毀),然後將 b' 指向 b,此時 b 和 b' 指向相同物件。

將 b' 物件傳入後,需要將 b' release 掉,因此 (b' 和 b 的 ref count = 1),當物件 a 使用完畢後則要再 release b,因此 (b' 和 b 的 ref count = 0, 被摧毀)。

如果先 release 再 retain 的話遇到傳入 b' 和 b 原本就指向相同物件的情形,將導致成員物件先被解構,而無法賦值...

.......

4/16/2010

[Programming] 小心在 for 迴圈當中的 if

如果要有條件的去loop一個迴圈,那麼不要自以為聰明的先算好會有多少個index會進去那個迴圈,如果進去該迴圈的總數是重要的,那麼就放一個counter去數!

...