Tuesday, October 25, 2011

Porting OpenCV 2.3.1 to iPhone 3GS/4/4S - 如何將OpenCV中影像處理輸出資料結構Mat的data轉出成為UIimage

透過OpenCV處理完的影像資料可以透過如下的程序轉回UIImage,就可以在iOS中任意顯示或儲存:
1. 先將OpenCV處理後在Mat資料結構中的RGB資料(result.data)轉成RGBA資料(img_data

    unsigned char * img_data = (unsigned char *)malloc(img_width * img_height * 4);

// Convert RGB data in Mat (result) to RGBA raw data buffer (img_data)
    for(i=0;i<img_height;i++){
        for(j=0;j<img_width;j++){
            img_data[i*img_width*4+j*4]=result.data[i*result.step[0]+j*3*2];
            img_data[i*img_width*4+j*4+1]=result.data[i*result.step[0]+j*3*2+2];
            img_data[i*img_width*4+j*4+2]=result.data[i*result.step[0]+j*3*2+4];
            img_data[i*img_width*4+j*4+3]=0xFF;
        }
    }


2. 透過如下的程序將RGBA資料(img_data)在轉成UIImage

    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4 * img_width;
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, img_data, img_width*img_height*4, NULL);
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst|kCGBitmapByteOrder32Little;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    CGImageRef imageRef = CGImageCreate(img_width, img_height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];

Porting OpenCV 2.3.1 to iPhone 3GS/4/4S - 如何將UIImage轉成OpenCV中資料結構Mat的data 當成影像處理之輸入

在Xcode iOS下要調用OpenCV的相關函數還有個難題就是如何將iOS中慣用來處來影像的Buffer - UIImage轉化成OpenCV內部使用Mat型態的資料結構內,下面這段程式紀錄如何達成此一目的的程序:
1. 先將UIImage中的影像資料轉出到資料型態為unsigned char *img中(img中為RGB raw data)

                UIImage *myImage1 = [UIImage imageNamed:@"test.jpg"];
                CGImageRef imageRef = [myImage1 CGImage];
                NSUInteger width = CGImageGetWidth(imageRef);
                NSUInteger height = CGImageGetHeight(imageRef);
                CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
                unsigned char *rawData = (unsigned char *)malloc(height * width * 4);
                NSUInteger bytesPerPixel = 4;
                NSUInteger bytesPerRow = bytesPerPixel * width;
                NSUInteger bitsPerComponent = 8;
                CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                             bitsPerComponent, bytesPerRow, colorSpace,
                                                             kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
                CGColorSpaceRelease(colorSpace);
                
                CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
                CGContextRelease(context);
                unsigned char * img = (unsigned char *)malloc(height * width * 3);
                for(int i=0;i
                    for(int j=0;j
                        img[i*width*3+j*3]=rawData[i*width*4+j*4];
                        img[i*width*3+j*3+1]=rawData[i*width*4+j*4+1];
                        img[i*width*3+j*3+2]=rawData[i*width*4+j*4+2];
                    }
                }
                free(rawData);


2. 呼叫OpenCV函式庫前先製作符合需求的Mat型態的資料結構full_img如下
        int refcount=1;
        Mat full_img;
        full_img.dims=2;
        full_img.rows=img_height;
        full_img.cols=img_width;
        full_img.flags|=0x00000010;
        full_img.refcount=&refcount;
        full_img.data=img;
        full_img.datastart=full_img.data;
        full_img.dataend=full_img.data+full_img.rows*full_img.cols*3;
        full_img.datalimit=full_img.data+full_img.rows*full_img.cols*3;
        full_img.size[0] = full_img.rows;
        full_img.size[1] = full_img.cols;
        full_img.step[0] = full_img.cols*3;
        full_img.step[1] = 3;

3. 需要使用到Mat型態的資料結構的函數呼叫,就可以傳入full_img當引數了


Porting OpenCV 2.3.1 to iPhone 3GS/4/4S - 如何在Xcode中使用OpenCV 2.3.1 iOS函式庫

要在Xcode中使用已經編譯好的OpenCV函式庫基本上非常簡易,照如下步驟即可輕易使用:
1. 將要使用已經編譯好的函式庫只接拖拉進Xcode的專案下


2. 將頭檔(header files)所在資料夾加入Build Settings裡的User Header Search Paths中


3. 要使用函式庫的xxx.m或是xxx.mm檔中引入相關的頭檔,即可使用相關函數

p.s. 在Xcode專案交雜使用xxx.m/xxx.mm/xxx.c/xxx.cpp時,記得xxx.c中的函數,可以在引入頭檔後直接在xxx.m中調用;而在xxx.cpp中的函數,只能在xxx.mm中才能調用,xxx.m是不能調用xxx.cpp中的函數的!

Friday, October 21, 2011

幫凶律師 The Associate - John Grisham

前陣子因為看了John Grishan的The Confession覺得寫得真好,所以又看了他這本The Associate,說實話,看完後是有點失望,因為劇情虎頭蛇尾,感覺好像是掰不下去,所以草草結束了(不知道有沒可能是因為我因為看了The Confession後對本書期望太高以至於失望很大),在網路上已經看到一些人寫得閱讀紀錄,有興趣的話可以去看看打發時間,但不推薦囉。

Porting OpenCV 2.3.1 to iPhone 3GS/4/4S - 如何使用cmake+Xcode編譯OpenCV 2.3.1函式庫給iOS使用

要能在Xcode中使用OpenCV 2.3.1函式庫來實作自己的程式首要之務當然是要能建立出符合iOS使用的OpenCV 2.3.1的函式庫,如下的步驟紀錄了這個過程:
1. 下載並安裝CMakeMac OSX 64/32-bit Universal
2. 下載並解壓縮OpenCV-2.3.1a.tar.bz2
3. 執行CMake並指定Where is the source code(你的OpenCV資料夾位置)、Where to build the binaries(編譯後OpenCV函式庫要放的資料夾)以及CMAKE_INSTALL_PREFIX(安裝編譯的檔案夾),如下圖:
4. 在CMake裡面取消下列的選項:

  • BUILD_DOCS
  • BUILD_EXAMPLES
  • BUILD_NEW_PYTHON_SUPPORT,
  • BUILD_SHARED_LIBS
  • BUILD_TESTS
  • BUILD_WITH_DEBUG_INFO
  • ENABLE_SSE, WITH_EIGEN
  • WITH_OPENEXR
  • WITH_PVAPI
  • WITH_QT
  • WITH_QT_OPENGL
  • WITH_QUICKTIME

5. CMake裡面Configure選擇Xcode然後按下Generate,執行完後在你設定輸出函式庫的資料夾裡就會產生OpenCV.xcodeproj 。

6. 用Xcode打開OpenCV.xcodeproj,將Base SDK選成iOS(我用的是iOS5.0),如圖:

7. Xcode中OpenCV Build Settings的Architectures選Standard (armv7),如圖:

8. Xcode中OpenCV Build Settings的Compiler for C/C++/Objective-C選LLVM GCC4.2,如圖:

8. Xcode中OpenCV TARGETS將opencv_highgui、 opencv_traincascade、 opencv_createsamples、 opencv_haartraining、 opencv_performance、 opencv_stitching,如圖:

9. 接著就是選擇TARGETS中的ALL_BUILD來編譯;如果沒有意外,成功編譯後在你設定編譯後OpenCV函式庫要放的資料夾中的lib資料夾下就會有所有opencv的函式庫了;最後,你還可以選擇TARGETS中的install來編譯,成功的話,在你設定安裝編譯的檔案夾中就會產生集成後的標頭檔方便你使用。

Thursday, October 20, 2011

Porting OpenCV 2.3.1 to iPhone 3GS/4/4S - 在iOS中使用thread來實作耗時的程序

在使用OpenCV函式庫中必然有大量運算的需求,因此就需要用thread來分離大量運算的部份才不至於使UI卡住,下面就來實際操作如何在iOS中使用thread來實作耗時的程序:
1. 在xxxViewController.h的@interface xxxViewController中宣告
            NSThread * stitchThread;


2. 在xxxViewController.m中實作stitchThreadProc如下(將有大量需求的運算放在其中):

       - (void)stitchThreadProc:(id)info {
    
            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
            //大量運算放於此
            //...

            [pool drain];
    
       }

3. 使用如下的方式呼叫並啟動thread:

        stitchThread = [[NSThread alloc] initWithTarget:self selector:@selector(stitchThreadProc:) object:nil];
        
        [stitchThread start];


完成了,是不是很簡單呢!

Wednesday, October 19, 2011

Porting OpenCV 2.3.1 to iPhone 3GS/4/4S - 在耗時的程序中使用UIActivityIndicatorView來通知使用者程式還在執行中

在使用OpenCV函式庫時,你會發現很多運算都是相當耗時,這時候我們就需要利用iOS中的UIActivityIndicatorView來通知使用者程式還在執行運算,請稍待。如何使用呢?精簡的步驟如下:

1. 在xxxViewController.h的@interface xxxViewController...中宣告
           UIActivityIndicatorView * activityIndicator;
         
           並加上如下的程式碼:
       @property (nonatomic, retain) IBOutlet UIActivityIndicatorView * activityIndicator;
       -(void)spinActivityIndicatorBegin;
       -(void)spinActivityIndicatorEnd;

2. 使用Interface Builder 擺一個UIActivityIndicatorView在你想要放置的位置上,並連接到上面這個 activityIndicator;設置屬性為 hidden when stopped.
3. 在xxxViewController.m實作spinActivityIndicatorBeginspinActivityIndicatorEnd如下:
       - (void) spinActivityIndicatorBegin {
            [activityIndicator startAnimating];
       }

       - (void) spinActivityIndicatorEnd {
            [activityIndicator stopAnimating];
       }
4. 在xxxViewController.m耗時運算的前後作如下的呼叫:
       [NSThread detachNewThreadSelector: @selector(spinActivityIndicatorBegin) toTarget:self withObject:nil];
       //此處為耗時運算

       [NSThread detachNewThreadSelector: @selector(spinActivityIndicatorEnd) toTarget:self withObject:nil];

完成後執行程式應該就可以看到小菊花在你設定的位置轉阿轉的囉!


Porting OpenCV 2.3.1 to iPhone 3GS/4/4S - 序曲

這陣子剛好需要熟悉Xcode iOS的程式設計,所以挑了個題目來實作操練一下,剛好看到OpenCV釋出2.3.1,因為剛好裡面有個新的Photo Stitch範例,想說就挑他來練靶,經歷了2個星期的摸索,終於成功的將OpenCV 2.3.1編譯成iOS上的library,並套用他來生成iPhone 3GS/4/4S可以執行Photo Stitch的小程式。下面的video是實際運行程式的過程演示:



範例中用來接合的兩張照片如下:



及接合後的結果:


這個demo是在iPhone 3GS上演示,接合效果還不錯,但是效能不是頂好,日後有精力再來針對演算法參數與程式最佳化進行較調;這邊陸續把一些移植過程中的稜稜角角給記錄下來,應該會包括下面幾項:

  1. 如何使用cmake+Xcode編譯OpenCV 2.3.1函式庫給iOS使用
  2. 如何在Xcode中使用OpenCV 2.3.1 iOS函式庫
  3. 如何將UIImage轉成OpenCV中資料結構Mat的data 當成影像處理之輸入
  4. 如何將OpenCV中影像處理輸出資料結構Mat的data轉出成為UIimage
  5. 在iOS中使用thread來實作耗時的程序
  6. 在耗時的程序中使用UIActivityIndicatorView來通知使用者程式還在執行中
待續