跳到主要內容

在 iOS 上使用 OpenCV 程式庫

螢幕快照 2014-12-12 下午4.54.30

圖片截自 OpenCV 官網

最近在做一個手機上影像辨識的案子。要進行影像處理方面的工作,當然要先找到工具,而這方面的軟體工具最知名的應該就是 OpenCV 了。OpenCV 所指的是 Open Source Computer Vision Library,這是一套由 Intel 發展的跨平台電腦視覺程式庫,適合用來進行影像處理、電腦視覺等方面的應用,當然用這個程式庫是可以通過上架審核的。下文是在 iOS 平台上進行 OpenCV 應用開發的起手式,即如何透過 Xcode 來叫用 OpenCV 的操作方法。這是小弟的一個工作紀錄,希望整理一下網路上的資料,讓有這方面需求的朋友節省一下時間。

步驟如下:

1。下載 OpenCV for iOS 套件:可以在 OpenCV 官網上下載此套件。下載後,您應該可以看到如下的圖示。

螢幕快照 2014-12-12 下午4.56.35

2。開啟 Xcode 並新增一個 iOS Single View Application 專案:小弟所使用的是 Xcode  6.1.1。

螢幕快照 2014-12-12 下午4.59.42

螢幕快照 2014-12-12 下午5.00.09

3。在專案 Target 下 Build Phases 的 Link Binary with Libraries 中將下載來的程式庫加進來。

螢幕快照 2014-12-12 下午5.06.58

螢幕快照 2014-12-12 下午5.07.13

點按 Add Other...

螢幕快照 2014-12-12 下午5.07.30

選取檔案

螢幕快照 2014-12-12 下午5.08.07

加入完成

4。在 Storyboard 上加入一個 ImageView 與一個 Button View,並進行 outlet 的連結,小弟將 outlet 名稱分別設定成 imageRect 與 loadButton。

螢幕快照 2014-12-12 下午5.13.43

螢幕快照 2014-12-12 下午5.16.51

螢幕快照 2014-12-12 下午5.17.20

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

螢幕快照 2014-12-12 下午5.22.42

6。在 ViewController.h 檔中加入 UIImagePickerControllerDelegate 與 UINavigationControllerDelegate 委派:

螢幕快照 2014-12-12 下午5.26.27

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 上更改其名稱(點按-間隔長一點-再點按)即可。

螢幕快照 2014-12-12 下午5.41.27

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 測邊法處理後的影像:

FullSizeRender

留言

這個網誌中的熱門文章

以 Ad-Hoc 方式測試軟體 iOS 7.1 裝置需要使用具 SSL 憑證的伺服器

在進行 iOS 的 app 開發時,常需要讓一些團隊成員或測試者測試 beta 版的 app。如果您以 Ad-Hoc 方式發送測試用 app 給測試者下載安裝的話,使用者的裝置若已更新至 iOS 7.1,則您需要找個有 SSL 憑證,支援 HTTPS 的 host 來放置您的 app,否則測試者要下載測試軟體時,會遇上憑證無效的錯誤訊息而無法下載安裝。 提供給您參考,若您有相關的解決方法,也請不吝賜教分享給大家。 [更新] 可將 ipa 與 plist 檔上傳到 Dropbox 中的"公開檔夾(public)"中,然後連到 itms-services://?action=download-manifest&amp;url=<plist 檔的位置>,即可進行 Ad Hoc 的發佈。當然,測試者的裝置需經過開發裝置的 UDID 碼註冊才行。 另外,Google Site 因不支援以 itms-services:// 的協定連結,所以無法透過 Google Site 的連結來進行 Ad Hoc 的發佈。  [回覆 -> 天天網友] 請如圖所示般,複製 .ipa 檔的公開連結,將 .ipa 的公開連結加到 .plist 檔中。以同樣的方法複製 .plist 檔的公開連結,並將連到 .plist 檔的超連結寫成 itms-services://?action=download-manifest&amp;url=<plist 檔超連結> 的形式(沒有角括號),放在網頁中,或以電郵傳給測試者,點選之後,即可將 .ipa 檔下載安裝。不是以 HTML 的 <a href=""> 來連結。 您的問題應該是沒有在網頁的連結中使用 itms-services:// 的方式來進行連結,或不是連結到 .plist 的公開連結上。 .plist 的 URL 看起來會像是 https://dl.dropboxusercontent.com/u/xxxxxxx/xxxxxx/xxxxxxxx.plist。網頁中連結的寫法是 <a href=itms-services://?action=download-manifest&amp;url=https://dl.dropboxuserco...

Apple 更新 12" MacBook

上一篇才打完,就看到 9to5Mac 發佈阿婆更新 12" MacBook 產品線的消息了。不到 10 分鐘就被打臉了。 新的 MacBook 採用 Intel 第六代 Core M Skylake 處理器,時脈有 1.1 GHz 及 1.2 GHz 二種,8 GB RAM 與 256 GB Flash Storage,高階款則有 512 GB 的儲存空間。電力可維持 10 個小時的無線上網瀏覽,機殼顏色有新的玻瑰金色,新款的售價與第一代 MacBook 相同,從 $1299 起跳。

某些特定的 iOS 開發者遭受由 iMessage 而來的 DoS 攻擊

據 MacRumors 引述 The Next Web 的報導指出,某些特定的 iOS 開發者遭受由 iMessage 而來的 DoS(Denial of Service)。 據遭受鎖定的 iOS 開發者 Grant Paul 的說明,Apple 並沒有為 iMessage 的訊息傳輸量設限,所以攻擊者可以用短時間內發出大量訊息的方式來進行阻斷式攻擊。另外,攻擊者也可發出以 Unicode 編碼的"複雜"文字或數量龐大的文字訊息,讓 iMessage 受不了,無法計算(render)文字訊息,這就可以讓 iMessage 無法順利開啟。 文章中也提到 iH8sn0w 這位越獄工具與軟體的開發者指出,透過 AppleScript 就可以發出這種 DoS 的攻擊。 有興趣的朋友可以跳轉收看。