在MacOS和iOS系统中使用OpenCV

来源:http://blog.devtang.com/blog/2012/10/27/use-opencv-in-ios/

前言

OpenCV 是一个开源的跨平台计算机视觉库,实现了图像处理和计算机视觉方面的很多通用算法。

最近试着在MacOS和iOS上使用OpenCV,发现网上关于在MacOS和iOS上搭建OpenCV的资料很少。好不容易搜到些资料,却发现由于OpenCV和XCode的版本更新,变得不再有用了。有些问题费了我很多时间,在此总结分享给大家,希望后来人少走些弯路。

可以预见到,随着XCode和OpenCV的版本更新,本文可能不再有效了。

所以特此注明,文本介绍的搭建方法仅针对于 XCode4.5.1 和 OpenCV 2.4.2版本。

(2013-6-22)更新: 我在XCode4.6.2 和 OpenCV 2.4.5版本的时候重新进行了一次环境搭建,以下内容做了相应调整,应该也是有效的。

MacOS系统中使用OpenCV

在Mac OS Lion中安装OpenCV

相信大部分Mac用户都安装了brew或port,如果你没有装,那么首先安装一下brew吧。使用如下命令安装brew:

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

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

1
brew install opencv

通常情况下这样做就应该会安装成功,但我在公司和家里面的电脑尝试的时候,brew都会报一些错误,我遇到的都是一些小问题,按照brew的提示信息,解决掉相应的问题即可。

安装成功后,你应该可以在“/usr/local/include”目录下找到名为opencv和opencv2的目录,这里面是OpenCV相关的头文件。你也可以在“/usr/local/lib”目录下找到许多以libopencv_开头的.dylib文件,这些是OpenCV的链接库文件。

在Mac OS Mountain Lion中安装OpenCV

按照该教程,先用brew安装cmake.

OpenCV官网下载Linux/Mac版的源码,将源码解压后,在控制台中切换到源码目录,执行如下操作:

1
2
3
4
5
6
7
8
# 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

上面的命令在执行时要注意,整个源码目录的路径不能带空格。否则编译会报错找不到一些文件。

安装成功后,你应该可以在“/usr/local/include”目录下找到名为opencv和opencv2的目录,这里面是OpenCV相关的头文件。你也可以在“/usr/local/lib”目录下找到许多以libopencv_开头的.dylib文件,这些是OpenCV的链接库文件。

在MacOS系统中使用OpenCV

接着我们可以试着在Xcode工程中使用OpenCV。

新建一个Cocoa Application的工程。工程建好后,选中工程的Target,在Build Settings一样,找到“Header Search Paths”这一个选项,将它的值改为“/usr/local/include”。

同样还需要设置的还有”Lib Search Paths”这一项,将它的值改为”/usr/local/lib/**“, 如下所示:

接着切换到Build Phases这个tab,在“Link Binary With Libraries”中,选项+号,然后将弹出的文件选择对话框目录切换到“/usr/local/lib”目录下,选择你需要使用的OpenCV链接库(通常情况下,你至少会需要core、highgui和imgproc库),如下图所示(截图中的OpenCV版本号可能和你的有差别,但应该不影响):

这里有一个技巧,因为 /usr 目录在对话框中默认不是可见的,可以按快捷键 command + shift + G,在弹出的“前往文件夹”对话框中输入 /usr/local/lib ,即可跳转到目标文件夹。如下图所示:

下一步是我自己试出来的,对于Lion操作系统,你需要在Build Settings中,将“C++ Language Dialect”设置成C++11,将“C++ Standard Library”设置成libstdc++ ,如下图所示。个人感觉是由于XCode默认设置的GNU++11、libc++与OpenCV库有一些兼容性问题,我在更改该设置前老是出现编译错误。后续版本在Montain Lion系统中解决了这个问题,不用进行这一步了。

把上面的设置都做好后,就可以在需要的使用OpenCV库的地方,加上opencv的头文件引用即可:

1
#import "opencv2/opencv.hpp"

注意,如果你的源文件扩展名是.m的,你还需要改成.mm,这样编译器才知道你将会在该文件混合使用C++语言和Objective-C语言。

OpenCV处理图象需要的格式是cv::Mat类,而MacOS的图象格式默认是NSImage,所以你需要知道如何在cv::Mat与NSImage之前相互转换。如下是一个NSImage的Addition,你肯定会需要它的。该代码来自stackoverflow上的这个贴子

NSImage+OpenCV.h 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//
//  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文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//
//  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

完成以上步骤后,恭喜你,你可以在源代码中自由地调用OpenCV的函数了。

在iOS系统中使用OpenCV

下载或编译opencv2.framework

接下来介绍如何在iOS程序中使用OpenCV。在iOS上使用最新的OpenCV库比较简单,进入opencv的官网,下载build好的名为opencv2.framework即可(下载地址)。

如果你比较喜欢折腾,也可以自行下载opencv的源码,在本地编译opencv2.framework。这里有官方网站的教程,步骤非常简单,不过我照着它的教程尝试了一下失败了。感觉还是XCode编译器与OpenCV代码的兼容性问题,所以就没有继续研究了。

在iOS程序中使用OpenCV

新建一个iOS工程,将opencv2.framework直接拖动到工程中。然后,你需要在Build Settings中,将“C++ Standard Library”设置成libstdc++。

因为opencv中的MIN宏和UIKit的MIN宏有冲突。所以需要在.pch文件中,先定义opencv的头文件,否则会有编译错误。将工程的.pch文件内容修改成如下所示:

1
2
3
4
5
6
7
8
9
10
#import <Availability.h>

#ifdef __cplusplus
    #import <opencv2/opencv.hpp>
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
#endif

把上面的设置都做好后,就可以在需要的使用OpenCV库的地方,加上opencv的头文件引用即可:

1
#import "opencv2/opencv.hpp"

还是那句话,如果你的源文件扩展名是.m的,你还需要改成.mm,这样编译器才知道你将会在该文件中混合使用C++语言和Objective-C语言。

同样,iOS程序内部通常用UIImage表示图片,而OpenCV处理图象需要的格式是cv::Mat,你会需要下面这个Addition来在cv::Mat和UIImage格式之间相互转换。该代码来自aptogo的开源代码,他的版权信息在源码头文件中。

UIImage+OpenCV.h 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//
//  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 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//
//  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

总结

上面2个环境搭建好后,你就可以在MacOS上试验各种图象处理算法,然后很方便地移值到iOS上。

一直觉得,图象和声音是移动设备上的特点和优势。因为移动设备没有了可以快速输入的键盘,屏幕也不大,在移动设备上,声音,图象和视频应该是相比文字更方便让人输入的东西。移动端APP应该利用好这些特点,才能设计出更加体贴的功能。

而且,通常情况下做图象处理都比较好玩,记得以前在学校做了一个在QQ游戏大厅自动下中国象棋的程序,其后台使用了网上下载的一个带命令行接口的象棋AI,然后我的代码主要做的事情就是识别象棋棋盘,然后将棋盘数据传给那个象棋AI,接着获得它返回的策略后,模拟鼠标点击来移动棋子。当时不懂什么图象算法,直接把棋子先截取下来保存,然后识别的时候做完全匹配,非常弱的办法,但是效果非常好,做出来也很好玩。嗯,所以文章最后,我想说的是:have fun!

Posted by 唐巧 Oct 27th, 2012  OpenCViOS

时间: 2024-10-29 08:09:28

在MacOS和iOS系统中使用OpenCV的相关文章

[转]在MacOS和iOS系统中使用OpenCV

OpenCV 是一个开源的跨平台计算机视觉库,实现了图像处理和计算机视觉方面的很多通用算法. 最近试着在MacOS和iOS上使用OpenCV,发现网上关于在MacOS和iOS上搭建OpenCV的资料很少.好不容易搜到些资料,却发现由于OpenCV和XCode的版本更新,变得不再有用了.有些问题费了我很多时间,在此总结分享给大家,希望后来人少走些弯路. 可以预见到,随着XCode和OpenCV的版本更新,本文可能不再有效了. 所以特此注明,文本介绍的搭建方法仅针对于 XCode4.5.1 和 Op

MAC下Unity编译成Xcode后倒入IOS系统中的各种坑总结

unity3d版本: 5.3.0f4 xcode版本: 7.3.1 - xcode7.3.1已经支持免开发者证书真机调试, 这个版本下不再需要花费99刀购买个人开发者证书,仅需要个人appstoreID即可进行调试: - 该版本unity3d工程编译成xcode可执行文件后,导入IOS过程中会报2次错误,具体描述如下: 1. "unknown type name __declspec" 解决方法:xcode中找到il2cpp-config.h文件,在181行(上下)找到 #define

如何在ios 系统 中抓包??

为了实现在ios系统上抓包,如下步骤: 1,设备越狱 2,在cydia-软件源-设置中改为开发者,否则有些deb搜索不到 安装如下软件:OpenSSH,OpenSSL,wget (下载工具) Aptitude 及 APT 0.6 Transitional (deb包工具) unzip 及 zip (解压缩及压缩打包工具) 3,安装并启动sshd后,通过ssh -l root IPAD_IP_ADDRESS登录,默认口令是:alpine,这是ios系统默认的root密码,记得及时修改. 4,命令行

有关iOS系统中调用相机设备实现二维码扫描功能的注意点(3/3)

今天我们接着聊聊iOS系统实现二维码扫描的其他注意点. 大家还记得前面我们用到的输出数据的类对象吗?AVCaptureMetadataOutput,就是它!如果我们需要实现目前主流APP扫描二维码的功能,即只有当二维码进入视图中心的方框中时才进行扫描识别功能,这样做的目的主要是为了提高用户的使用体验,需要用到这个类里面的一个属性:rectOfInterest,这个属性是一个CGRect结构体类型.但和我们平时经常使用的CGRect有点不一样.以下截取了官网文档对这个属性的表述: /*! @pro

64位IOS系统中敲壳提取32位程序

测试机一直是5s和6,Clutch敲壳,实际是一个内存dump的过程,所以每次敲壳后的程序载入IDA都不能开心的F5来查看交叉引用以及简要逻辑. 终于决定要搞下这个坑,发现竟然并不是太简单的事情,或许还是IOS的相关知识掌握的不多. 首先,我们查找到lipo这个东西.lipo是command-tools里面的工具,之前或许是随着xcode一起下发的,貌似某个6.x的版本之后,就独立了出来,需要单独下载. xcode-select --install 此处也是一个坑,为了文件交换的方便,我使用的是

移动端开发在iOS系统中 new Date() 返回 NaN 的问题

问题: 通过 new Date() 函数将后台返回的时间('2021-11-25')获取时间戳.在 chrome 浏览器中没有出现问题,但在 iPhone 真机测试的时候,显示的结果不符合预期.通过调试发现 iOS 中 new Date('2021-11-25') 返回的结果是 NaN,问题出现的原因是 iOS new Date() 中不能包含 - 符号. 解决办法: 最简单的办法,后台直接返回处理好的时间; 通过字符串的replace()方法(String.prototype.replace(

IOS系统中网络等待的Loading的实现方法 等待加载

镔哥就直接写一个简单的方法吧: 第一种方法: self.title=@"直视行情"; //[self getLodingView]; self.webView =[[UIWebView alloc]initWithFrame:CGRectMake(0, -2, self.view.frame.size.width,self.view.frame.size.height+30)]; [_webView setUserInteractionEnabled:NO]; [_webView se

[iOS] iOS系统中各种设置项的url链接

在代码中调用如下代码:NSURL*url=[NSURL URLWithString:@"prefs:root=WIFI"];[[UIApplication sharedApplication] openURL:url];即可跳转到设置页面的对应项. [font=]About — prefs:root=General&path=AboutAccessibility — prefs:root=General&path=ACCESSIBILITYAirplane Mode O

在iOS系统中使用fopen函数

这些天在应用中用到openssl编解码的时候遇到用fopen打开文件拿不到FILE的情况,造成crash,最后得到解决,以下是记录: 因为应用底层有部分代码是C编写的,在这里要打开一个文件来读取数据,一开始在Objective-C中创建文件的时候是用的NSURL,传到C代码中的时候使用[[url absoluteString] UTF8String],在fopen的时候拿到的fd是-1,没有打开文件. 追踪代码的时候发现,NSURL指向的文件路径是 file:///xxxx 这样的格式,但如果是