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
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
> 開啓 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 檔案,
從右方的物件函式庫將 ImageView 與 Toolbar 拖拉到 view 物件上.
> a. 點選 UI 上的 ImageView, 按住 control 鍵, 拖拉到右方的 ViewController.h
類別內, 放開後, 在跳出的視窗內設定以下的值:
Connection: Outlet
Name: imageView
> Connect
類別內, 放開後, 在跳出的視窗內設定以下的值:
Connection: Outlet
Name: loadButton
> Connect
類別內, 放開後, 在跳出的視窗內設定以下的值:
Connection: Action
Name: loadButtonPressed
> Connect
#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. 編譯並執行:
原始圖檔: