圖片截自 OpenCV 官網
最近在做一個手機上影像辨識的案子。要進行影像處理方面的工作,當然要先找到工具,而這方面的軟體工具最知名的應該就是 OpenCV 了。OpenCV 所指的是 Open Source Computer Vision Library,這是一套由 Intel 發展的跨平台電腦視覺程式庫,適合用來進行影像處理、電腦視覺等方面的應用,當然用這個程式庫是可以通過上架審核的。下文是在 iOS 平台上進行 OpenCV 應用開發的起手式,即如何透過 Xcode 來叫用 OpenCV 的操作方法。這是小弟的一個工作紀錄,希望整理一下網路上的資料,讓有這方面需求的朋友節省一下時間。
步驟如下:
1。下載 OpenCV for iOS 套件:可以在 OpenCV 官網上下載此套件。下載後,您應該可以看到如下的圖示。
2。開啟 Xcode 並新增一個 iOS Single View Application 專案:小弟所使用的是 Xcode 6.1.1。
3。在專案 Target 下 Build Phases 的 Link Binary with Libraries 中將下載來的程式庫加進來。
點按 Add Other...
選取檔案
加入完成
4。在 Storyboard 上加入一個 ImageView 與一個 Button View,並進行 outlet 的連結,小弟將 outlet 名稱分別設定成 imageRect 與 loadButton。
5。在 ViewController.h 檔中加入下列編譯器指令:
#ifndef __IPHONE_8_1
#warning "This project uses features only availablein iOS SDK 5.0 and later."
#endif
#ifdef __cplusplus
#import<opencv2/opencv.hpp>
#endif
#ifdef __OBJC__
#import<UIKit/UIKit.h>
#import<Foundation/Foundation.h>
#endif
6。在 ViewController.h 檔中加入 UIImagePickerControllerDelegate 與 UINavigationControllerDelegate 委派:
7。接著要開始寫碼了。OpenCV 使用 cv::Mat 來存放影像資料,所以先要編寫 UIImage 與 cv::Mat 的資料轉存方法。在 ViewController.h 中宣告二個方法:
static UIImage* MattoUIImage(const cv::Mat& m);
static void UIImagetoMat(const UIImage* image, cv::Mat& m);
然後再宣告一個要用來叫用 OpenCV 進行影像處理的方法:
-(void)operateWithOpenCV:(UIImage*)image;
8。因為在 Xcode 中 OpenCV 各個常式是以 C++ 的型式存在的,要使用這些常式的話,要將 ViewController.m 的名稱改成 ViewController.mm 才行。請直接在 ViewController.m 上更改其名稱(點按-間隔長一點-再點按)即可。
9。先做好 UIImagePickerControllerDelegate 所需的委派方法:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
[picker dismissViewControllerAnimated:YES completion:nil];
UIImage* image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
if (image != nil)
[self operateWithOpenCV:image];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[picker dismissViewControllerAnimated:YES completion:nil];
}
10。實作二個 UIImage <-> cv::Mat 的方法:
static UIImage* MattoUIImage(const cv::Mat& m)
{
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);
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;
}
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);
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);
}
11。還記得我們有一個載入影像的按鈕嗎?為它寫個方法,loadButtonPressed,來接收 Action message,讓按鈕被按下後,請 ImagePicker 去載入影像。別忘了,要將 Load 按鈕的 Target 連接到 ViewController 這裡來。
- (IBAction)loadButtonPressed:(id)sender {
UIImagePickerController* picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary])
return;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:picker animated:YES completion:nil];
}
12。至此就完成準備工作,可以將影像傳進 operateWithOpenCV 方法中去處理了。小弟從網路上找了一段應用 Canny 測邊法的程式為例,載入影像讓 OpenCV 幫我們處理。
-(void)operateWithOpenCV:(UIImage*)image{
if (image == nil)
return;
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);
_imageRect.contentMode = UIViewContentModeScaleAspectFit;
_imageRect.image = MattoUIImage(m);
}
以 OpenCV Canny 測邊法處理後的影像:
留言
張貼留言