原始图片是Gustav Heurlin(1862-1939)的作品,他是一位20世纪初记录乡村生活的瑞典摄影师。他是autochrome彩色照相法的早期采用者,1919-1931年期间,国家地理杂志出版了他的许多照片。
当我们的用户看到一头有着一系列流行艺术色彩的猪时,他们会质疑他们对猪的成见,然后意识到,原来猪也是一种非常酷炫的动物。
[要获得这本书的完整项目,请前往本书作者的GitHub,https://github.com/JoeHowse/iOSWithOpenCV, 或者在PacktPublishing中登陆你的账号]
打开Xcode,点击Create New Xcode project按钮或者选择File|New|Project…菜单按钮.一个对话框将显示出来,询问你选择那种工程模板.如下面截图所示,选择iOS|Application|Single View Application:
Single View Application是最简单的工程模板,因为他仅仅创建了一个空的图形界面而没有特别的导航结构.点击Next按钮来确认选择.现在,一个对话框询问你选择几项项目设置,如下图所示填写表格:
让我们回顾下表格中的项目:
点击Next按钮来提交工程选项信息.现在,一个选择对话框提示你选择一个文件夹来存放工程文件.选择一个合适的位置,我们用
使用Finder或者终端复制文件到下面的位置:
返回到Xcode,浏览整个工程.导航到File|Add Files to “CoolPig”… 菜单按钮.Xcode会打开一个文件选择框,选择opencv2.framework然后点击Add按钮.用同样的步骤添加CoolPig/Piggy.png.注意这些文件会在Xcode最左边的区域ProjectNavigator面板中显示出来.在该面板中,拖动Piggy.png到CoolPig|SupportingFiles组中.当你完成该操作,ProjectNavigator面板中是下图所示的样子:
首先,让我们的app运行在没有状态栏的全屏模式下.在ProjectNavigator中选择位于最顶端的CoolPig工程文件.在位于Xcode窗口中央的编辑区域选择General选项卡.找到Deployment Info组,勾选Hide status bar和Requires full screen复选框,按照下图所示:
状态栏和全屏的设置存储在app的Info.plist文件中.在ProjectNavigator窗口中选择CoolPig|CoolPig|Info.plist,在编辑区域,注意到UIRequiresFullScreen和Status bar is initially hidden属性都被设置为YES.然而,我们还需要添加另一个属性来确保状态栏不会显示.将光标在列表中最后一项中停留,然后点击+按钮来出插入一个新的属性.输入View controller-based status bar appearance 作为属性的键,然后设置值为NO,如下图所示:
接下来,我们把一些需要添加的frameworks连接到工程中区.OpenCV依赖了苹果的两个框架,CoreGraphics.framework和UIKit.framework.为了优化,还可以使用Accelerate.framework库.
[Accelerate框架包含了苹果公司在矢量数学行业标准API的硬件加速的实现。值得注意的是,它实现了被称为Basic Linear Algebra Subpograms(BLAS)和Linear Algebra Package(LAPACK)的标准。OpenCV在包括iOS在内的各种平台上都利用了这些标准。]
在ProjectNavigator(工程浏览器)中选择CoolPig工程文件,然后在编辑区域选择Build Phases.找到Link Binary With Libraries组,然后点击+按钮,从对话框中选择Accelerate.framework,然后点击Add按钮.用同样的方式添加CoreGraphics.framework和UIKit.framework.完成后的编辑区域如下图所示:
现在,连接器可以找到OpenCV所依赖的库了.然而我们需要更改一些设置来确保编译器会在OpenCV的头文件中正常编译C++代码.在编辑区域中打开Build Settings选项卡,然后找到Apple LLVM 7.0 - Language组.设置Compile Source As项的值为Objective-C++,如下图所示:
或者,我们可以保留Compile Sources As项的默认值"According to File Type",这样的话我们需要将我们的源代码文件的后缀从".m"改为".mm" 这样Xcode会用Objective C++的方式来处理该文件.
在BuildSetting选项卡中,我们还有一个需要配置的.记得我们考虑将opencv2_contrib模块作为我们工程中的一个可选依赖吗,我们之前在小节1.2.3 在我们的代码中设置附加模块为可选中描述过.如果我们用附加模块构建了opencv2.framework,并且想使用它们的功能,让我们定义一个宏,宏的名字叫做WITH_OPENCV_CONTRIB.找到Apple LLVM 7.0 - Preprocessing组,编辑Preprocessor Macros|Debug 和Preprocessor Macros|Release" 添加WITH_OPENCV_CONTRIB宏,如下图所示:
最后,进行一项可选的配置,如果你想要设置app的图标.在ProjectNavigator面板中选择CoolPig|CoolPig|Assets.xcassets.Assets.xcassets是一个包含了为不同设备和使用场景(首页,Spotlight搜索,设置菜单)准备的许多图标的包.
在编辑区域中点击AppIcon列表项,然后拖拽一个图片文件到右侧每一个AppIcon的格子中.如果图片的尺寸不正确,Xcode会告诉你需要调整图片尺寸然后重新设置.如果你完成了添加图片,编辑区域如下图所示:
现在,我们的工程已经配置好了,接下来准备设计图形用户界面(GUI).Xcode内置了InterfaceBuilder工具,通过他我们可以设置GUI元素,连接GUI元素到我们代码里的变量和事件,甚至可以定义场景的转场.
CoolPig的界面仅仅是一个图片,然而,我们简单的工程里也有一个从静态加载屏幕(其中图像不改变颜色)到动态主屏幕(其中图像每两秒钟改变颜色)的转换。让我们先配置加载屏幕,然后配置主屏幕。
在工程导航器(project navigator)面板中选择CoolPig|CoolPig|LaunchScreen.storyboard.该文件是一个故事板,它存储一组场景集(本例中为单个场景)。在编辑器区域中出现一个场景层次结构。导航到View Controller Scene|View Controller|View。在编辑器区域的右侧出现一个空白视图,如下面的屏幕截图所示:
让我们在空视图中添加一个imageView。注意Xcode窗口右下角的可用GUI小部件的列表。这个区域被称为“library pane(库面板)”。滚动库面板的内容区域,找到Image View项并将其拖动到空视图中。现在,编辑器区域应该是这样的:
拖动高亮矩形的四个角,使image view 填满它的的父视图.结果如下图所示:
我们仍然需要采取进一步的操作,以确保image view会自动放大或缩小,以匹配所有设备的屏幕大小。点击编辑器区域底部的工具栏中的Pin按钮。按钮的图标看起来像一个矩形被固定在了两条线之间。现在,出现得到弹出菜单标题为Add New Constraints。约束定义了一个部件相对于其他部件的位置和大小。
现在,我们想定义image view相对于父视图的边距.为了定义每条边的边距,点击围绕中间方框的四个I型线它们会变成红色.现在,设置top和bottom的值为0,left和right的值设置为-20.有一些iOS设备有内置的水平边距,我们设置为负值,可以确保即使在这样的设备下,image view也可以延伸到屏幕的边缘设置如下图设置:
点击Add 4 Constraints按钮来提交这些参数.
最后,我们想要展示一个图片.打开Xcode窗口右上角的检查器面板(InspectorPane).在这里,我们可以配置当前选中的部件.选中Attributes选项卡.它的图标看起来像一个滑块.从Image的下拉类表中,选择Piggy.png.在Mode的下拉列表中选择AspectFill.这个模式确保图像会在横向和竖向上都填满image view,而不会拉伸图片.图像显示出来时,可能会在一条边上显示不全.现在,编辑区和检查器面板应该如下图所示:
到目前为止,我们已经完成了加载界面的布局.现在,我们将注意力放到主界面上来.在工程浏览器中选择CoolPig|CoolPig|Main.storyboard .这个storyboard中也只含有一个场景.选中它的view,增加一个image view,并且用设置加载界面的image view的方式来配置该image view.之后,在小节[连接界面元素到代码]中,我们会将这个新的image view 和我们代码中的变量连接起来.
作为单视图应用程序的一部分,Xcode已经帮我们创建好了以下代码:
对于CoolPig工程,我们只需要简单地修改ViewController.m.在工程导航器中选择CoolPig|CoolPig|ViewController.m,代码将会出现在编辑区域.在代码的开头,我们增加更多的#import
声明来包含需要用到的一些OpenCV模块的头文件,如下面代码所示:
#import
#import
#import
#ifdef WITH_OPENCV_CONTRIB
#import
#endif
我们需要生成一些随机数来为创建图像的随机着色.方便起见,我们定义了如下的宏,用来生成一个64位的浮点数,值为0到1
#define RAND_0_1() ((double)arc4random() / 0x100000000)
[arc4random()函数返回一个随机的32位整型数据,值的范围是0到2^32-1(0x100000000).第一次该函数被调用时,自动生成一个随机数种子]
ViewController.m的剩余部分用来处理私有接口和ViewController的实现.
在ViewController.h中,类申明如下:
@interface ViewController : UIViewController
@end
注意ViewController是UIViewController的子类,UIViewController是iOS SDK中非常重要的一个类.UIViewController管理着视图树的生命周期,并提供了很多默认的行为,你也可以重写这些方法.如果我们按照MVC的设计模式来开发程序,那么UIViewController就是控制器,或者协调器,用来将于平台有关的视图或者GUI与平台无关的模型或者业务逻辑分离开来.
然我们把注意力放回到ViewController.m中的ViewController类的私有接口上来.原始图片和更新的图片,都是该类的成员变量.他们都是OpenCV中cv::Mat类的实例,cv::Mat可以代表任何图形或者多维数据.ViewController也有一个我们用来展示图片的image view的引用.另一个属性是NSTimer对象,用来每2秒触发一个回调.最后,该类有一个updateImage方法,用来展示一个新的图片的随机变化效果.下面是View Controller的私有声明:
@interface ViewController () {
cv::Mat originalMat;
cv::Mat updatedMat;
}
@property IBOutlet UIImageView *imageView;
@property NSTimer *timer;
- (void)updateImage;
@end
现在,让我们实现ViewController类的方法.它继承了父类UIViewController的许多方法,我们可以重写任何一个方法.首先,我们要重写viewDidLoad方法,该方法在场景从对应的storyboard加载的时候调用.通常,这是很适合初始化view controller的成员变量的时机.我们viewDidLoad的实现,首先将从文件中加载Piggy.png并将它转换为OpenCV的RGB格式.如果图片原本不是灰度图,并且OpenCV附加的photo模块是可用的,我们将调用该模块的一个功能来调整白平衡.最后,我们开启一个定时器,每2秒条用一次updateImage方法.下面是viewDidLoad的代码:
- (void)viewDidLoad { [super viewDidLoad];
// Load a UIImage from a resource file.
UIImage *originalImage = [UIImage imageNamed:@"Piggy.png"];
// Convert the UIImage to a cv::Mat.
UIImageToMat(originalImage, originalMat);
switch (originalMat.type()) {
case CV_8UC1:
// The cv::Mat is in grayscale format.
// Convert it to RGB format.
cv::cvtColor(originalMat, originalMat, cv::COLOR_GRAY2RGB);
break;
case CV_8UC4:
// The cv::Mat is in RGBA format.
// Convert it to RGB format.
cv::cvtColor(originalMat, originalMat, cv::COLOR_RGBA2RGB);
#ifdef WITH_OPENCV_CONTRIB
// Adjust the white balance.
cv::xphoto::autowbGrayworld(originalMat, originalMat);
#endif
break;
case CV_8UC3:
// The cv::Mat is in RGB format.
#ifdef WITH_OPENCV_CONTRIB
// Adjust the white balance.
cv::xphoto::autowbGrayworld(originalMat, originalMat);
#endif
break;
default:
break;
}
// Call an update method every 2 seconds.
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(updateImage) userInfo:nil repeats:YES];
}
[NSTimer只有app在前台的时候才会调用回调.该特性对于我们的需求来说很方便,因为我们就是想在图片可见的时候更新图片]
现在,让我们完善updateImage帮助方法.他将让每个颜色通道的值乘以一个随机浮点数.下表描述了将每个颜色的通道乘以系数k之后的效果:
k的值 | Red通道乘以k的效果 | 绿色通道乘以k的效果 | 蓝色通道乘以k的效果 |
---|---|---|---|
0<=k<1 | 图像变暗,带有青色 | 图像变暗,带有品红色 | 图像变暗,带有黄色 |
k==1 | 无变化 | 无变化 | 无变化 |
k>1 | 图像变亮,带有红色 | 图像变量带有绿色 | 图像变量,带有蓝色 |
下面的代码产生一个随机的颜色,将其与原始图像相乘,并且在imageView中展示结果.
- (void)updateImage {
// Generate a random color.
double r = 0.5 + RAND_0_1() * 1.0;
double g = 0.6 + RAND_0_1() * 0.8;
double b = 0.4 + RAND_0_1() * 1.2;
cv::Scalar randomColor(r, g, b);
// Create an updated, tinted cv::Mat by multiplying the
// original cv::Mat and the random color.
cv::multiply(originalMat, randomColor, updatedMat);
// Convert the updated cv::Mat to a UIImage and display
// it in the UIImageView.
self.imageView.image = MatToUIImage(updatedMat);
}
[根据你的口味随意调整每一个随机颜色系数的范围.OpenCV会对乘法的结果进行处理,使得颜色通道的值是一个8位的数据0到255,而不会溢出。]
我们在50行代码中实现了ColPig的的所有自定义逻辑!
工程模板、故事板、iOS SDK和OpenCV提供了许多有用的抽象,从而使我们能够集中精力编写简洁的、特定于应用程序的代码。
让我们把Main.Storyboard中的image view连接到ViewController的imageView属性.在工程导航器中打开Main.Storyboard,按住command键并点击场景中的View Controller.一个暗背景的对话框会出现,在场景中右击Piggy.png image view然后拖拽到弹出的对话框中Outlets|imageView的旁边的小圆圈上,如下图所示:
松开鼠标,完成连接.然后关闭这个暗色的对话框.
我们已经做好了构建app,并在iOS模拟器上或者真机上运行它的准备.首先,如果你想要使用一个iOS真机设备,用一个USB线将它好Mac连接起来.第一次连接设备,Xcode的顶部工具栏会展示一个进度条,并提示正在处理符号文件.耐心等待知道消息消失.然后,点击在Xcode的顶部工具栏上点击CoolPig的下拉菜单,选择一个你想要使用的真机或者模拟器,比如Devices|Joseph’s iPad或者iOS Simulators|iPad Pro.点击Run按钮.Run按钮是一个标准的三角形播放符号.Xcode将构建app,将它复制到真机或者模拟器上,然后启动.现在你就可以看到颜色不断变化的猪了,app在iPad Mini上看起来像这样:
[如果你使用的是模拟器,你可能会发现模拟器的屏幕在mac屏幕上看起来太大了.要缩小模拟器的屏幕,在模拟器的菜单中选择**Window|Scal|50%**或者其它的值]
可喜可贺啊!我们已经构建和运行了第一个iOS应用程序,它包含了用户图像处理的OpenCV和一只极具艺术感的猪.?
###返回上一层###