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. 編譯並執行:
          原始圖檔:

          處理之後:

2013年9月5日 星期四

Using OpenCV on Mac OS X

since: 2013/09/05
update: 2013/12/15

reference:
1. Using OpenCV 2 with OS X – Hello World | Bert Balcaen
2. Homebrew — MacPorts driving you to drink? Try Homebrew!
3. Troubleshooting · mxcl/homebrew Wiki · GitHub

A. 建置環境:
     OS: Mac OS X 10.9
     Xcode: 5.0.2
     OpenCV: 2.4.6

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

B. 更新 Java:
     (先安裝 JDK: Java SE Downloads)
     系統偏好設定
> Java > 更新

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

C. 更新 / 安裝 Command Line Tools for Xcode:
     Xcode > Preferences... > Downloads > Components >
     Command Line Tools > Install

   如果沒有找到, 就自行下載: (update: 2013/12/15)
    > Downloads for Apple Developers
    > Categories > Developer Tools >
       command_line_tools_os_x_mavericks_for_xcode__late_october_2013.dmg
---------------------------------------------------------------------------------

D. 安裝 Homebrew package manager:

    打開終端機, 執行以下指令:(一整行)
  //ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

    
     (update: 2013/12/15)
       ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go/install)"

     > 按 "ENTER" 繼續 ....
   

    > 好了之後, 安裝任何軟體前, 先執行:
       brew doctor
       (說明: brew help)

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

E. 安裝 python:
     1. 檢查是否有安裝 python, 如果沒有的話就安裝.
         python -V

     2. 安裝 python: (即使已經有 python 了, 還是建議安裝 homebrew 的 python) 
         brew install python

         (update: 2013/12/15)
         => Setuptools and Pip have been installed. To update them
         pip install --upgrade setuptools
         pip install --upgrade pip
         (update: 2013/12/15)
         => To symlink "Idle" and the "Python Launcher" to ~/Applications
         brew linkapps

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

F. 安裝 OpenCV:

     1. 終端機: brew tap homebrew/science

     2. 終端機: brew install opencv

(update: 2013/12/15)
錯誤訊息:
opencv: Unsatisfied dependency: numpy
Brewed Python cannot `import numpy`. Install with:
  pip-2.7 install numpy
Error: An unsatisfied requirement failed this build.


=>  pip-2.7 install numpy
=> brew install opencv
     3. 如果有錯誤訊息的話, 執行: brew doctor
         並參考: Troubleshooting · mxcl/homebrew Wiki · GitHub 的說明.
         (相關指令: brew update, brew doctor, brew --config)
---------------------------------------------------------------------------------

G. 建立 Xcode 專案:

     1. Xcode > File > New > Project...
         OS X > Application > Command Line Tool

     2. Choose options for your project:
         Product Name: HelloWorld
         Type: C++
          > Next


     3. 幫專案新增一個叫作 OpenCV 的群組: 
         > 在此群組內, 新增檔案:

         > 先打: / (反斜線), 然後就可以輸入: /usr/local/lib > Go
         > 接著分別選取以下的檔案:
            libopencv_core.dylib
            libopencv_highgui.dylib

         > 並記得勾選: Copy items .... (但原作者說: 不要勾選)
                    及勾選: Add to targets   
         > Add   

        結果如下所示:

         備註: 這是最基本的. 依照使用 OpenCV 不同的功能, 你可能需要其它的檔案.
                   舉例來說, 如果你要作
特徵檢測(
feature detection), 你會需要:
                   
libopencv_features2d.dylib 檔案.

     4. 告訴 Xcode 哪裡可以找到 OpenCV 的標頭檔:
         > 專案 > Build Settings > Search Paths > Header Search Paths :
         > 新增: /usr/local/include

     5. 更改編譯器使用的標準 C++ 函式庫: 
          > 專案 > Build Settings > Apple LLVM compiler 4.2 - Language
          > C++ Standard Library: libstdc++ (GNU C++ standard library)
          (update: 2013/12/15)
          > libc++(LLVM C++ standard library with C++11 support) 預設的


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

H. 撰寫 OpenCV 程式:
      1. 開啟 main.cpp 檔案, 修改如下:
#include <iostream>
//@add for OpenCV
#include <opencv2/opencv.hpp>

int main(int argc, const char * argv[])
{

    // insert code here...
    /*
    std::cout << "Hello, World!\n";
    return 0;
    */
   
    //@add for OpenCV
    // load an image
    // make sure you change the path!
    cv::Mat img = cv::imread("/Lanli/RD/Projects/OpenCV_Mac/HelloWorld/pipi.png");
   
    // check if image was loaded
    if (img.data) {
        std::cout << "Image loaded" << std::endl;
    } else {
        std::cout << "Image not loaded" << std::endl;
    }

   
    // create a window and show the image
    cv::imshow("PiPi: a cat", img);
   
    // wait for a key press
    cv::waitKey();
}


      2. 編譯並執行: (按任意鍵結束程式)