[轉(zhuǎn)]在MacOS和iOS系統(tǒng)中使用OpenCV

OpenCV 是一個(gè)開(kāi)源的跨平臺(tái)計(jì)算機(jī)視覺(jué)庫(kù),實(shí)現(xiàn)了圖像處理和計(jì)算機(jī)視覺(jué)方面的很多通用算法。

最近試著在 MacOS 和 iOS 上使用 OpenCV,發(fā)現(xiàn)網(wǎng)上關(guān)于在 MacOS 和 iOS 上搭建 OpenCV 的資料很少。好不容易搜到些資料,卻發(fā)現(xiàn)由于 OpenCV 和 Xcode 的版本更新,變得不再有用了。有些問(wèn)題費(fèi)了我很多時(shí)間,在此總結(jié)分享給大家,希望后來(lái)人少走些彎路。

可以預(yù)見(jiàn)到,隨著 Xcode 和 OpenCV 的版本更新,本文可能不再有效了。

所以特此注明,文本介紹的搭建方法僅針對(duì)于 Xcode4.5.1 和 OpenCV 2.4.2 版本。

(2013-6-22)更新: 我在 Xcode4.6.2 和 OpenCV 2.4.5 版本的時(shí)候重新進(jìn)行了一次環(huán)境搭建,以下內(nèi)容做了相應(yīng)調(diào)整,應(yīng)該也是有效的。

MacOS 系統(tǒng)中使用 OpenCV

在 Mac OS Lion 中安裝 OpenCV

相信大部分 Mac 用戶都安裝了 brew 或 port,如果你沒(méi)有裝,那么首先安裝一下 brew 吧。使用如下命令安裝 brew:


ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"

在安裝好 brew 后,只需要一條命令就可以安裝 OpenCV 了:


brew install opencv

通常情況下這樣做就應(yīng)該會(huì)安裝成功,但我在公司和家里面的電腦嘗試的時(shí)候,brew 都會(huì)報(bào)一些錯(cuò)誤,我遇到的都是一些小問(wèn)題,按照 brew 的提示信息,解決掉相應(yīng)的問(wèn)題即可。

安裝成功后,你應(yīng)該可以在 “/usr/local/include” 目錄下找到名為 opencv 和 opencv2 的目錄,這里面是 OpenCV 相關(guān)的頭文件。你也可以在 “/usr/local/lib” 目錄下找到許多以 libopencv_ 開(kāi)頭的 .dylib 文件,這些是 OpenCV 的鏈接庫(kù)文件。

在 Mac OS Mountain Lion 中安裝 OpenCV

按照 該教程,先用 brew 安裝 cmake.

OpenCV 官網(wǎng) 下載 Linux/Mac 版的源碼,將源碼解壓后,在控制臺(tái)中切換到源碼目錄,執(zhí)行如下操作:


# make a separate directory for building

mkdir build

cd build

cmake -G "Unix Makefiles" ..

# Now, we can make OpenCV. Type the following in:

make -j8

sudo make install

上面的命令在執(zhí)行時(shí)要注意,整個(gè)源碼目錄的路徑不能帶空格。否則編譯會(huì)報(bào)錯(cuò)找不到一些文件。

安裝成功后,你應(yīng)該可以在 “/usr/local/include” 目錄下找到名為 opencv 和 opencv2 的目錄,這里面是 OpenCV 相關(guān)的頭文件。你也可以在 “/usr/local/lib” 目錄下找到許多以 libopencv_ 開(kāi)頭的 .dylib 文件,這些是 OpenCV 的鏈接庫(kù)文件。

在 MacOS 系統(tǒng)中使用 OpenCV

接著我們可以試著在 Xcode 工程中使用 OpenCV。

新建一個(gè) Cocoa Application 的工程。工程建好后,選中工程的 Target,在 Build Settings 一樣,找到 “Header Search Paths” 這一個(gè)選項(xiàng),將它的值改為 “/usr/local/include”。

同樣還需要設(shè)置的還有 “Lib Search Paths” 這一項(xiàng),將它的值改為 “/usr/local/lib/**”, 如下所示:

image

接著切換到 Build Phases 這個(gè) tab,在 “Link Binary With Libraries” 中,選項(xiàng) + 號(hào),然后將彈出的文件選擇對(duì)話框目錄切換到 “/usr/local/lib” 目錄下,選擇你需要使用的 OpenCV 鏈接庫(kù)(通常情況下,你至少會(huì)需要 core、highgui 和 imgproc 庫(kù)),如下圖所示(截圖中的 OpenCV 版本號(hào)可能和你的有差別,但應(yīng)該不影響):

image

這里有一個(gè)技巧,因?yàn)?/usr 目錄在對(duì)話框中默認(rèn)不是可見(jiàn)的,可以按快捷鍵 command + shift + G,在彈出的 “前往文件夾 “ 對(duì)話框中輸入 /usr/local/lib ,即可跳轉(zhuǎn)到目標(biāo)文件夾。如下圖所示:

image

下一步是我自己試出來(lái)的,對(duì)于 Lion 操作系統(tǒng),你需要在 Build Settings 中,將 “C++ Language Dialect” 設(shè)置成 C++11,將 “C++ Standard Library” 設(shè)置成 libstdc++ ,如下圖所示。個(gè)人感覺(jué)是由于 Xcode 默認(rèn)設(shè)置的 GNU++11、libc++ 與 OpenCV 庫(kù)有一些兼容性問(wèn)題,我在更改該設(shè)置前老是出現(xiàn)編譯錯(cuò)誤。后續(xù)版本在 Montain Lion 系統(tǒng)中解決了這個(gè)問(wèn)題,不用進(jìn)行這一步了。

image

把上面的設(shè)置都做好后,就可以在需要的使用 OpenCV 庫(kù)的地方,加上 opencv 的頭文件引用即可:


#import "opencv2/opencv.hpp"

注意,如果你的源文件擴(kuò)展名是 .m 的,你還需要改成 .mm,這樣編譯器才知道你將會(huì)在該文件混合使用 C++ 語(yǔ)言和 Objective-C 語(yǔ)言。

OpenCV 處理圖象需要的格式是 cv::Mat 類,而 MacOS 的圖象格式默認(rèn)是 NSImage,所以你需要知道如何在 cv::Mat 與 NSImage 之前相互轉(zhuǎn)換。如下是一個(gè) NSImage 的 Addition,你肯定會(huì)需要它的。該代碼來(lái)自 stackoverflow 上的 這個(gè)貼子。

NSImage+OpenCV.h 文件:


//

//  NSImage+OpenCV.h

//

//  Created by TangQiao on 12-10-26.

//

#import <Foundation/Foundation.h>

#import "opencv2/opencv.hpp"

@interface NSImage (OpenCV)

+(NSImage*)imageWithCVMat:(const cv::Mat&)cvMat;

-(id)initWithCVMat:(const cv::Mat&)cvMat;

@property(nonatomic, readonly) cv::Mat CVMat;

@property(nonatomic, readonly) cv::Mat CVGrayscaleMat;

@end

NSImage+OpenCV.mm 文件:


//

//  NSImage+OpenCV.mm

//

//  Created by TangQiao on 12-10-26.

//

#import "NSImage+OpenCV.h"

static void ProviderReleaseDataNOP(void *info, const void *data, size_t size)

{

    return;

}

@implementation NSImage (OpenCV)

-(CGImageRef)CGImage

{

    CGContextRef bitmapCtx = CGBitmapContextCreate(NULL/*data - pass NULL to let CG allocate the memory*/,

                                                   [self size].width,

                                                   [self size].height,

                                                   8 /*bitsPerComponent*/,

                                                   0 /*bytesPerRow - CG will calculate it for you if it's allocating the data.  This might get padded out a bit for better alignment*/,

                                                   [[NSColorSpace genericRGBColorSpace] CGColorSpace],

                                                   kCGBitmapByteOrder32Host|kCGImageAlphaPremultipliedFirst);

    [NSGraphicsContext saveGraphicsState];

    [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:bitmapCtx flipped:NO]];

    [self drawInRect:NSMakeRect(0,0, [self size].width, [self size].height) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];

    [NSGraphicsContext restoreGraphicsState];

    CGImageRef cgImage = CGBitmapContextCreateImage(bitmapCtx);

    CGContextRelease(bitmapCtx);

    return cgImage;

}

-(cv::Mat)CVMat

{

    CGImageRef imageRef = [self CGImage];

    CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);

    CGFloat cols = self.size.width;

    CGFloat rows = self.size.height;

    cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels

    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to backing data

                                                    cols,                      // Width of bitmap

                                                    rows,                     // Height of bitmap

                                                    8,                          // Bits per component

                                                    cvMat.step[0],              // Bytes per row

                                                    colorSpace,                 // Colorspace

                                                    kCGImageAlphaNoneSkipLast |

                                                    kCGBitmapByteOrderDefault); // Bitmap info flags

    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), imageRef);

    CGContextRelease(contextRef);

    CGImageRelease(imageRef);

    return cvMat;

}

-(cv::Mat)CVGrayscaleMat

{

    CGImageRef imageRef = [self CGImage];

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();

    CGFloat cols = self.size.width;

    CGFloat rows = self.size.height;

    cv::Mat cvMat = cv::Mat(rows, cols, CV_8UC1); // 8 bits per component, 1 channel

    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to backing data

                                                    cols,                      // Width of bitmap

                                                    rows,                     // Height of bitmap

                                                    8,                          // Bits per component

                                                    cvMat.step[0],              // Bytes per row

                                                    colorSpace,                 // Colorspace

                                                    kCGImageAlphaNone |

                                                    kCGBitmapByteOrderDefault); // Bitmap info flags

    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), imageRef);

    CGContextRelease(contextRef);

    CGColorSpaceRelease(colorSpace);

    CGImageRelease(imageRef);

    return cvMat;

}

+ (NSImage *)imageWithCVMat:(const cv::Mat&)cvMat

{

    return [[[NSImage alloc] initWithCVMat:cvMat] autorelease];

}

- (id)initWithCVMat:(const cv::Mat&)cvMat

{

    NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize() * cvMat.total()];

    CGColorSpaceRef colorSpace;

    if (cvMat.elemSize() == 1)

    {

        colorSpace = CGColorSpaceCreateDeviceGray();

    }

    else

    {

        colorSpace = CGColorSpaceCreateDeviceRGB();

    }

    CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data);

    CGImageRef imageRef = CGImageCreate(cvMat.cols,                                     // Width

                                        cvMat.rows,                                     // Height

                                        8,                                              // Bits per component

                                        8 * cvMat.elemSize(),                           // Bits per pixel

                                        cvMat.step[0],                                  // Bytes per row

                                        colorSpace,                                     // Colorspace

                                        kCGImageAlphaNone | kCGBitmapByteOrderDefault,  // Bitmap info flags

                                        provider,                                       // CGDataProviderRef

                                        NULL,                                           // Decode

                                        false,                                          // Should interpolate

                                        kCGRenderingIntentDefault);                     // Intent

    NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage:imageRef];

    NSImage *image = [[NSImage alloc] init];

    [image addRepresentation:bitmapRep];

    CGImageRelease(imageRef);

    CGDataProviderRelease(provider);

    CGColorSpaceRelease(colorSpace);

    return image;

}

@end

完成以上步驟后,恭喜你,你可以在源代碼中自由地調(diào)用 OpenCV 的函數(shù)了。

在 iOS 系統(tǒng)中使用 OpenCV

下載或編譯 opencv2.framework

接下來(lái)介紹如何在 iOS 程序中使用 OpenCV。在 iOS 上使用最新的 OpenCV 庫(kù)比較簡(jiǎn)單,進(jìn)入 opencv 的官網(wǎng),下載 build 好的名為 opencv2.framework 即可(下載地址)。

如果你比較喜歡折騰,也可以自行下載 opencv 的源碼,在本地編譯 opencv2.framework。這里 有官方網(wǎng)站的教程,步驟非常簡(jiǎn)單,不過(guò)我照著它的教程嘗試了一下失敗了。感覺(jué)還是 Xcode 編譯器與 OpenCV 代碼的兼容性問(wèn)題,所以就沒(méi)有繼續(xù)研究了。

在 iOS 程序中使用 OpenCV

新建一個(gè) iOS 工程,將 opencv2.framework 直接拖動(dòng)到工程中。然后,你需要在 Build Settings 中,將 “C++ Standard Library” 設(shè)置成 libstdc++。

因?yàn)?opencv 中的 MIN 宏和 UIKit 的 MIN 宏有沖突。所以需要在 .pch 文件中,先定義 opencv 的頭文件,否則會(huì)有編譯錯(cuò)誤。將工程的 .pch 文件內(nèi)容修改成如下所示:

#import <Availability.h>

#ifdef __cplusplus

    #import <opencv2/opencv.hpp>

#endif

#ifdef __OBJC__

    #import <UIKit/UIKit.h>

    #import <Foundation/Foundation.h>

#endif

把上面的設(shè)置都做好后,就可以在需要的使用 OpenCV 庫(kù)的地方,加上 opencv 的頭文件引用即可:

#import "opencv2/opencv.hpp"

還是那句話,如果你的源文件擴(kuò)展名是 .m 的,你還需要改成 .mm,這樣編譯器才知道你將會(huì)在該文件中混合使用 C++ 語(yǔ)言和 Objective-C 語(yǔ)言。

同樣,iOS 程序內(nèi)部通常用 UIImage 表示圖片,而 OpenCV 處理圖象需要的格式是 cv::Mat,你會(huì)需要下面這個(gè) Addition 來(lái)在 cv::Mat 和 UIImage 格式之間相互轉(zhuǎn)換。該代碼來(lái)自 aptogo 的開(kāi)源代碼,他的版權(quán)信息在源碼頭文件中。

UIImage+OpenCV.h 文件:


//

//  UIImage+OpenCV.h

//  OpenCVClient

//

//  Created by Robin Summerhill on 02/09/2011.

//  Copyright 2011 Aptogo Limited. All rights reserved.

//

//  Permission is given to use this source code file without charge in any

//  project, commercial or otherwise, entirely at your risk, with the condition

//  that any redistribution (in part or whole) of source code must retain

//  this copyright and permission notice. Attribution in compiled projects is

//  appreciated but not required.

//

#import <UIKit/UIKit.h>

@interface UIImage (UIImage_OpenCV)

+(UIImage *)imageWithCVMat:(const cv::Mat&)cvMat;

-(id)initWithCVMat:(const cv::Mat&)cvMat;

@property(nonatomic, readonly) cv::Mat CVMat;

@property(nonatomic, readonly) cv::Mat CVGrayscaleMat;

@end

UIImage+OpenCV.mm 文件:


//

//  UIImage+OpenCV.mm

//  OpenCVClient

//

//  Created by Robin Summerhill on 02/09/2011.

//  Copyright 2011 Aptogo Limited. All rights reserved.

//

//  Permission is given to use this source code file without charge in any

//  project, commercial or otherwise, entirely at your risk, with the condition

//  that any redistribution (in part or whole) of source code must retain

//  this copyright and permission notice. Attribution in compiled projects is

//  appreciated but not required.

//

#import "UIImage+OpenCV.h"

static void ProviderReleaseDataNOP(void *info, const void *data, size_t size)

{

    // Do not release memory

    return;

}

@implementation UIImage (UIImage_OpenCV)

-(cv::Mat)CVMat

{

    CGColorSpaceRef colorSpace = CGImageGetColorSpace(self.CGImage);

    CGFloat cols = self.size.width;

    CGFloat rows = self.size.height;

    cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels

    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to backing data

                                                    cols,                      // Width of bitmap

                                                    rows,                     // Height of bitmap

                                                    8,                          // Bits per component

                                                    cvMat.step[0],              // Bytes per row

                                                    colorSpace,                 // Colorspace

                                                    kCGImageAlphaNoneSkipLast |

                                                    kCGBitmapByteOrderDefault); // Bitmap info flags

    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), self.CGImage);

    CGContextRelease(contextRef);

    return cvMat;

}

-(cv::Mat)CVGrayscaleMat

{

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();

    CGFloat cols = self.size.width;

    CGFloat rows = self.size.height;

    cv::Mat cvMat = cv::Mat(rows, cols, CV_8UC1); // 8 bits per component, 1 channel

    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to backing data

                                                    cols,                      // Width of bitmap

                                                    rows,                     // Height of bitmap

                                                    8,                          // Bits per component

                                                    cvMat.step[0],              // Bytes per row

                                                    colorSpace,                 // Colorspace

                                                    kCGImageAlphaNone |

                                                    kCGBitmapByteOrderDefault); // Bitmap info flags

    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), self.CGImage);

    CGContextRelease(contextRef);

    CGColorSpaceRelease(colorSpace);

    return cvMat;

}

+ (UIImage *)imageWithCVMat:(const cv::Mat&)cvMat

{

    return [[[UIImage alloc] initWithCVMat:cvMat] autorelease];

}

- (id)initWithCVMat:(const cv::Mat&)cvMat

{

    NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize() * cvMat.total()];

    CGColorSpaceRef colorSpace;

    if (cvMat.elemSize() == 1)

    {

        colorSpace = CGColorSpaceCreateDeviceGray();

    }

    else

    {

        colorSpace = CGColorSpaceCreateDeviceRGB();

    }

    CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data);

    CGImageRef imageRef = CGImageCreate(cvMat.cols,                                     // Width

                                        cvMat.rows,                                     // Height

                                        8,                                              // Bits per component

                                        8 * cvMat.elemSize(),                           // Bits per pixel

                                        cvMat.step[0],                                  // Bytes per row

                                        colorSpace,                                     // Colorspace

                                        kCGImageAlphaNone | kCGBitmapByteOrderDefault,  // Bitmap info flags

                                        provider,                                       // CGDataProviderRef

                                        NULL,                                           // Decode

                                        false,                                          // Should interpolate

                                        kCGRenderingIntentDefault);                     // Intent 

    self = [self initWithCGImage:imageRef];

    CGImageRelease(imageRef);

    CGDataProviderRelease(provider);

    CGColorSpaceRelease(colorSpace);

    return self;

}

@end

總結(jié)

上面 2 個(gè)環(huán)境搭建好后,你就可以在 MacOS 上試驗(yàn)各種圖象處理算法,然后很方便地移值到 iOS 上。

一直覺(jué)得,圖象和聲音是移動(dòng)設(shè)備上的特點(diǎn)和優(yōu)勢(shì)。因?yàn)橐苿?dòng)設(shè)備沒(méi)有了可以快速輸入的鍵盤,屏幕也不大,在移動(dòng)設(shè)備上,聲音,圖象和視頻應(yīng)該是相比文字更方便讓人輸入的東西。移動(dòng)端 APP 應(yīng)該利用好這些特點(diǎn),才能設(shè)計(jì)出更加體貼的功能。

而且,通常情況下做圖象處理都比較好玩,記得以前在學(xué)校做了一個(gè)在 QQ 游戲大廳自動(dòng)下中國(guó)象棋的程序,其后臺(tái)使用了網(wǎng)上下載的一個(gè)帶命令行接口的象棋 AI,然后我的代碼主要做的事情就是識(shí)別象棋棋盤,然后將棋盤數(shù)據(jù)傳給那個(gè)象棋 AI,接著獲得它返回的策略后,模擬鼠標(biāo)點(diǎn)擊來(lái)移動(dòng)棋子。當(dāng)時(shí)不懂什么圖象算法,直接把棋子先截取下來(lái)保存,然后識(shí)別的時(shí)候做完全匹配,非常弱的辦法,但是效果非常好,做出來(lái)也很好玩。

原文連接:https://blog.devtang.com/2012/10/27/use-opencv-in-ios/
踩過(guò)的坑:https://juejin.im/post/5bf2a8e8e51d455f0d301716
OpenCV copyTo、clone、“=”與拷貝構(gòu)造函數(shù)的區(qū)別:
https://blog.csdn.net/chaipp0607/article/details/58603167

OpenCV學(xué)習(xí)(5) Mat的基本操作(2)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容