2013年9月7日 星期六

Using OpenCV on iOS

since: 2013/09/07
update: 2013/09/07

reference:
1. OpenCV iOS Hello — OpenCV 2.4.6.0 documentation
2. CVPR 2012: OpenCV for iOS
3. 在xcode中应用opencv开发iphone应用程序 - Belial计算机视觉的专栏
4. ios - dismissModalViewControllerAnimated deprecated - Stack Overflow

A. 建置環境:
     OS: Mac OS X 10.8.4
     Xcode: 4.6.3
     OpenCV: 2.4.6
     iOS: 6.1.4
---------------------------------------------------------------------------------

B. 建立 Xcode 專案:
     1. Xcode > File > New > Project...
         iOS > Application > Single View Application
         > Next

     2. Choose options for your new project:
         Product Name: HelloOpenCV
         Checked: Use Storyboards, Use Autumatic Reference Couting
         > Next > Create

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

C. Link OpenCV framework with Xcode:
     1. 到  OpenCV 官網, 下載: OpenCV for iOS
         目前版本為: 2.4.6, 檔名為: opencv2.framework.zip
         > 解壓縮後得到: opencv2.framework 資料夾.

     2. Project(HelloOpenCV) > Build Phases > Link Binary With Libraries
         > + > Add Other... > 選取剛剛的 opencv2.framework 資料夾 >
         > Open

     3. 結果如下:

     4. 修改預編譯標頭檔(precompiled header):
         > 開啓 HelloOpenCV-Prefix.pch 檔案, 修改如下:
#import <Availability.h>

#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif


//@add for OpenCV
#ifdef __cplusplus
#import <opencv2/opencv.hpp>
#endif


#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
#endif


備註: 在專案中有使用到 OpenCV 的地方, 需要將對應的 .m 檔案改成 .mm 檔案,
          以支援 C++.


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

D. 設計 UI 與程式連結:
      1. 點選左邊 MainStoryboard.storyboard 檔案,
          從右方的物件函式庫將 ImageViewToolbar 拖拉到 view 物件上.

      2. 在點選 UI 的情況下, 點選右上方的 Assistant Editor:
           > a. 點選 UI 上的 ImageView, 按住 control 鍵, 拖拉到右方的 ViewController.h
                  類別內, 放開後, 在跳出的視窗內設定以下的值:

                  Connection: Outlet
                  Name: imageView
                  > Connect

           > b. 點選 UI 上的 BarButtonItem, 按住 control 鍵, 拖拉到右方的 ViewController.h
                  類別內, 放開後, 在跳出的視窗內設定以下的值:

                  Connection: Outlet
                  Name: loadButton
                  > Connect

           > c. 點選 UI 上的 BarButtonItem, 按住 control 鍵, 拖拉到右方的 ViewController.h
                  類別內, 放開後, 在跳出的視窗內設定以下的值:

                  Connection: Action
                  Name: loadButtonPressed
                  > Connect

      3. 完成後, ViewController.h 的程式碼如下:
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


//@add
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *loadButton;


//@add
- (IBAction)loadButtonPressed:(id)sender;

@end

      4. 修改 ViewController.m 的程式碼如下:
....
@implementation ViewController


//@add
@synthesize imageView;
@synthesize loadButton;

....

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

E. 設定 UIImagePickerController 的委任:
     1. 開啓 ViewController.h 檔案, 修改如下:
#import <UIKit/UIKit.h>

//@interface ViewController : UIViewController
//@add for UIImagePickerController delegate
@interface ViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>

//@add
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *loadButton;

//@add
- (IBAction)loadButtonPressed:(id)sender;

@end


     2. 開啓 ViewController.m 檔案, 修改如下:
....
@implementation ViewController
....

//@add for UIImagePickerController delegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
}


//@add for UIImagePickerController delegate

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
}


@end


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

F. 建立讓 OpenCV 的 cv::Mat 與 iOS 的 UIImage 互相轉換的函式:
     1. 修改 ViewController.h 檔案如下:
....
//@add
- (IBAction)loadButtonPressed:(id)sender;

//@add
// convert cv::Mat to UIImage

static UIImage* MattoUIImage(const cv::Mat& m);
// convert UIImage to cv::Mat
static void UIImagetoMat(const UIImage* image, cv::Mat& m);

     2. 先將 ViewController.m 的檔名改成 ViewController.mm 以支援 C++.

     3. 修改 ViewController.mm 檔案如下: (在 #import 下方, 最前面)
#import "ViewController.h"

//@add for OpenCV
// convert cv::Mat to UIImage

static UIImage* MattoUIImage(const cv::Mat& m)
{
    //CV_ASSERT(m.depth() == CV_8U);
    if (m.depth() != CV_8U)
        return nil;
   
    NSData* data = [NSData dataWithBytes:m.data length:m.elemSize()*m.total()];
    CGColorSpaceRef colorSpace = m.channels() ==1 ? CGColorSpaceCreateDeviceGray():CGColorSpaceCreateDeviceRGB();
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
   
    // Creating CGImage from cv::Mat
    CGImageRef imageRef = CGImageCreate(m.cols, m.rows, m.elemSize1()*8, m.elemSize()*8, m.step[0], colorSpace, kCGImageAlphaNoneSkipLast|kCGBitmapByteOrderDefault, provider, NULL, false, kCGRenderingIntentDefault);
   
    UIImage* finalImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);
   
    return  finalImage;
}


//@add for OpenCV
// convert UIImage to cv::Mat

static void UIImagetoMat(const UIImage* image, cv::Mat& m)
{
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
    CGFloat cols = image.size.width;
    CGFloat rows = image.size.height;
    m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
   
    CGContextRef contextRef = CGBitmapContextCreate(m.data, cols, rows, 8, m.step[0], colorSpace, kCGImageAlphaNoneSkipLast|kCGBitmapByteOrderDefault);
   
    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
   
    CGContextRelease(contextRef);
    //CGColorSpaceRelease(colorSpace);
}

....

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

G. 建立用來使用 OpenCV 的函式:
      1. 開啓 ViewController.h 檔案, 修改如下:
....
//@add
// convert cv::Mat to UIImage
static UIImage* MattoUIImage(const cv::Mat& m);
// convert UIImage to cv::Mat
static void UIImagetoMat(const UIImage* image, cv::Mat& m);


//@add for process With OpenCV
- (void)processWithOpenCV:(UIImage*)image;


@end

      2. 開啓 ViewController.mm 檔案, 修改如下:
....
//@add for process With OpenCV
- (void)processWithOpenCV:(UIImage*)image
{
    if (image == nil)
        return;
   
    // imageView.image = image;
    cv::Mat m, gray;
    UIImagetoMat(image, m);
   
    cv::cvtColor(m, gray, CV_BGR2GRAY);
    cv::GaussianBlur(gray, gray, cv::Size(5,5), 1.2,1.2);
    cv::Canny(gray, gray, 0, 50);
    m = cv::Scalar::all(255);
    m.setTo(cv::Scalar(0, 128, 255, 255), gray);
    imageView.contentMode = UIViewContentModeScaleAspectFit;
    imageView.image = MattoUIImage(m);
}


@end

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

H. 挑選圖片, 利用 OpenCV 處理後, 顯示出來:
      1. 開啓 ViewController.mm 檔案, 修改如下:
....
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    //@add for OpenCV

    NSString* filename = [[NSBundle mainBundle] pathForResource:@"HelloOpenCV" ofType:@"png"];
    UIImage* image = [UIImage imageWithContentsOfFile:filename];
    if (image != nil)
    {
        // process With OpenCV
        [self processWithOpenCV:image];
    }

}
....


- (IBAction)loadButtonPressed:(id)sender {
    //@add
    UIImagePickerController* picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
   
    if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary])
        return;
   
    picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

   
    //@for iOS 6 after(include)
    if ([self respondsToSelector:@selector(presentViewController:animated:completion:)]) {
        [self presentViewController:picker animated:YES completion:nil];
    } else {

       
        //@for iOS 5 before
        // 'presentModalViewController:animated:' is deprecated: first deprecated in iOS 6.0
        #pragma clang diagnostic push
        #pragma clang diagnostic ignored "-Wdeprecated-declarations"
        [self presentModalViewController:picker animated:YES];
        #pragma clang diagnostic pop
    }

}

//@add for UIImagePickerController delegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    //@add
    [picker dismissViewControllerAnimated:YES completion:nil];
    UIImage* image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];

   
    if (image != nil)
    {

        // process With OpenCV
        [self processWithOpenCV:image];
    }

}

//@add for UIImagePickerController delegate
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    //@add
    [picker dismissViewControllerAnimated:YES completion:nil];
}
....

      2. 編譯並執行:
          原始圖檔:

          處理之後:

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。