iOS下OpenCV开发用OC还是Swift
本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃
其实标题中这个问题并不准确,准确的说法应该是iOS下的OpenCV开发是使用OC还是Swift+OC。这个问题纠结了很久,研究了很多例子。先说结论:如果用到的算法规模不大且不熟悉cap_ios.h尽量用Swift+OC。(欢迎高手来打脸)
iOS下OpenCV开发的例子很多,大家可以直接去GitHub上扒拉,但是Swift还是比较少,先贡献几个能运行的例子:Objective-C(《Instant OpenCV for iOS》的例程,作者是 Alexander Shishkov和 Kirill Kornyakov,OpenCV的活跃分子),Swift(作者是Hiroki Ishiura,一个日本的程序员老哥,饱受秃顶的困扰。。。)16年Joseph Howse出版了一本书《iOS Application Development with OpenCV 3》,算是小白的入门教程吧(目前无中文,正版很贵,而且比较难下到电子书,Google图书里有部分英文电子版),第一章就提高了关于language的选择,由于opencv的核心是用C++写的,Swift不能直接调用C++,需要通过Objective-C作为中间层,所以作者在书中的例程都是基于Objective-C。
一、Objective-C下开发OpenCV的基本流程
熟悉Android下OpenCV开发的都知道,OpenCV提供了封装好的JavaCameraView和NativeCameraView两个类,连接摄像头时需要通过类实例进行初始化设置(如设置画面帧大小、帧率等信息),在iOS的开发中,OpenCV也提供了cap_ios.h,对摄像头的初始化通过CvVideoCamera类来实现。和Android开发类似,CvVideoCamera也对摄像头的初始化进行了封装。
@interface ViewController : UIViewController<CvVideoCameraDelegate>{
CvVideoCamera *videoCamera;
}
@property (nonatomic, retain) CvVideoCamera* videoCamera;
@property (weak, nonatomic) IBOutlet UIImageView *imgView;
如代码所示:OC中需要在.h头文件中定义CvVideoCamera,ViewController继承UIViewController,可以看出CvVideoCameraDelegate是一个协议。
@class CvVideoCamera;
@protocol CvVideoCameraDelegate <NSObject>
#ifdef __cplusplus
// delegate method for processing image frames
- (void)processImage:(cv::Mat&)image;
#endif
@end
通过cap_ios.h可以看出,处理画面帧时只需要重写processImage方法。
- (void)viewDidLoad {
[super viewDidLoad];
self.videoCamera = [[CvVideoCamera alloc] initWithParentView:imgView];self.videoCamera.delegate = self;
self.videoCamera.defaultAVCaptureDevicePosition =
AVCaptureDevicePositionFront;
self.videoCamera.defaultAVCaptureSessionPreset =
AVCaptureSessionPreset640x480;
self.videoCamera.defaultAVCaptureVideoOrientation =
AVCaptureVideoOrientationPortrait;
}
- (void)processImage:(cv::Mat&)image{
cv::cvtColor(image, image, CV_BGR2GRAY);
}
在ViewController.m的viewDidLoad方法中对摄像头进行初始化。如需要对画面帧进行处理重写processImage方法即可,有点类似Android中onCameraViewStarted的回调方法(上面代码通过OpenCV中的cv::cvtColor方法,实现了摄像头的灰度模式)。
二、Swift下开发OpenCV的基本流程
由于Swift不能直接调用C++,所有对C++的调用需要经过OC来包裹处理,所以Swift使用OpenCV需要和OC进行混编,关于Swift+OC+OpenCV的混编流程大概描述一下:
1、新建Swift项目;
2、在Swift项目中新建ObjectiveC文件,后缀为.m的请修改为.mm,此时会提示是否生成bridgeXXX.h的头文件,确认生成;
3、为.mm文件新建一个头文件,并在生成的bridgeXXX.h中import该头文件;
4、至此在.mm文件中已经可以调用OpenCV中的方法,如果你需要cpp或者hpp文件,请记住,它们不能和Swift直接交互,必须通过OC作为桥梁。
由于在Swift代码中无法直接使用CvVideoCamera,初始化摄像头需要通过AVFoundation来实现。
import AVFoundation
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {//粗体为AVFoundation中的delegate
@IBOutlet weak var imageView: UIImageView!
var session: AVCaptureSession!
var device: AVCaptureDevice!
var output: AVCaptureVideoDataOutput!
override func viewDidLoad() {
super.viewDidLoad()
// Prepare a video capturing session.
self.session = AVCaptureSession()
self.session.sessionPreset = AVCaptureSessionPreset640x480
……
}
……
如上代码所示,在Swift初始化摄像头的过程中没有用到OpenCV的任何东西。如果需要对画面帧进行处理,需要实现AVCaptureVideoDataOutputSampleBufferDelegate的captureOutput方法,从命名上可以看出这也是一个Delegate(协议),其中有一个captureOutput方法(代码如下),可以在方法内处理捕捉到的实时画面帧。
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
// 将捕捉到的image buffer 转换成 UIImage.
guard let buffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
print("could not get a pixel buffer")
return
}
let capturedImage: UIImage
do {
CVPixelBufferLockBaseAddress(buffer, CVPixelBufferLockFlags.readOnly)
defer {
CVPixelBufferUnlockBaseAddress(buffer, CVPixelBufferLockFlags.readOnly)
}
let address = CVPixelBufferGetBaseAddressOfPlane(buffer, )
let bytes = CVPixelBufferGetBytesPerRow(buffer)
let width = CVPixelBufferGetWidth(buffer)
let height = CVPixelBufferGetHeight(buffer)
let color = CGColorSpaceCreateDeviceRGB()
let bits =
let info = CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue
guard let context = CGContext(data: address, width: width, height: height, bitsPerComponent: bits, bytesPerRow: bytes, space: color, bitmapInfo: info) else {
print("could not create an CGContext")
return
}
guard let image = context.makeImage() else {
print("could not create an CGImage")
return
}
capturedImage = UIImage(cgImage: image, scale: 1.0, orientation: UIImageOrientation.up)
//调用你写在.mm文件中的OpenCV方法
}
}
代码中的captureOutput方法捕捉到了实时的画面帧,通过提取buffer中的画面帧并将其转换为UIImage,为了实现和OC中同样的灰度画面,需要另外定义一个ViewProcress.mm和ViewProcress.h,通过ViewProcress.mm中的OpenCV方法来处理你捕捉到的UIImage,将其转换为灰度模式。
三、对比
从代码来看,Swift是在曲线救国,为了验证,我写了一个简单的高斯背景建模方法处理实时画面帧cv::BackgroundSubtractorMOG2,这个方法的实时开销在OpenCV的常用方法中应该仅次于光流法,在两段代码中我都将帧率设置为30FPS。通过实际运行来看,Swift平均帧率达到30FPS,而OC只有15FPS。主要原因有两点:
1. 并发编程,OC中没用并发,而在Swift中使用了DispatchQueue,这得益于Swift3带来的改变,处理画面帧的时候需要并发,但是不能随便并发,而是要在一个队列中,所以就用到了DispatchQueue,这是Swift下帧率高的主要原因,你可能会说,那我在OC下也用多线程就好了,OK,请看下一个点分析。
2. OC中的摄像头是通过OpenCV封装的协议来初始化的,高斯背景建模方法直接作用于画面帧(processImage方法已经将实时画面帧转换为Mat,可以直接处理)。而Swift的是直接用AVFoundation初始化摄像头,captureOutput方法获取的是UIImage,需要在.mm文件中先将UIImage转换为Mat再调用高斯背景建模方法,处理完成再返回给UIView。关于帧率的初始化,OpenCV只提供了一个defaultFPS的设置方法,如果帧率超出范围,反而会衰减的更厉害,而在Swift中因为无法直接使用OpenCV提供的方法,所以初始化帧率必须通过AVFoundation的CMTimeMake函数来处理,设置一个最小帧率,当帧率无法满足时系统会自动平衡。这是帧率稳定的主要原因。
特别提示:注意内存管理!注意内存管理!注意内存管理!重要的事情说三遍。如果你用Swift来写,那么就会涉及Swift、Objective-C、C++。。。内存怎么办。。。有几点建议:一、使用完的mat注意release掉;二、多用vector少用数组;三、非计算尽量面向对象,专用计算尽量静态方法;四、如果可以,除了需要调用的库外,不要用C++,尽量用Objective-C。如果你有更好的办法,请指点。。。
现在回到开头的结论,其实在OC中也可以使用AVFoundation来初始化摄像头,但是为了偷懒,相信大多数人都会直接用OpenCV自带的方法完成初始化。而OpenCV封装类对Android或iOS的系统是以兼容为主的,效率是其次的,所以如果在实际开发中,不论Android还是iOS,都建议使用系统推荐的方式来调用camera,只有在处理画面帧时才调用OpenCV中的方法。而且随着Swift越来越完善,当然是推荐使用Swift+OC来进行OpenCV下的开发了。
//2017-04-08更新,补充Swift下调用摄像头并捕捉画面帧的方法。
iOS下OpenCV开发用OC还是Swift的更多相关文章
- iOS下OpenCV开发配置的两个常见问题(sign和link)
本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 先上可以运行官方推荐的<OpenCV for iOS samples>的demo链 ...
- iOS下JS与原生OC互相调用(总结)
这是去年总结的一篇文章,也一并先放到这个目录下好了. iOS开发免不了要与UIWebView打交道,然后就要涉及到JS与原生OC交互,今天总结一下JS与原生OC交互的两种方式. JS调用原生OC篇 方 ...
- iOS开发之 -- oc和swift下输出乘法口诀表
闲来无事,写着玩: oc: //乘法口诀表输出 ; i<=; i++) { ; j<=i; j++) { NSLog(@"%dx%d=%d\n",i,j,i*j); } ...
- ios开发——实用技术OC-Swift篇&本地通知与远程通知详解
本地通知与远程通知详解 一:本地通知 Local Notification的作用 Local Notification(本地通知) :是根据本机状态做出的通知行为,因此,凡是仅需依赖本机状态即可判 ...
- ios开发——实用技术OC-Swift篇&触摸与手势识别
iOS开发学习之触摸事件和手势识别 iOS的输入事件 触摸事件 手势识别 手机摇晃 一.iOS的输入事件 触摸事件(滑动.点击) 运动事件(摇一摇.手机倾斜.行走),不需要人为参与的 远程控制 ...
- iOS开发之-- oc 和 swift混编之自建桥接文件
进行swift开发的时候,oc 的项目已经进行了很长一段时间,所以默认使用Xcode自建的桥接文件的时候,这个桥接文件名称是固定的,放置的目录也是无法更改的,所以我就想自己创建一个桥接文件,然后在ta ...
- OpenCV2学习笔记01:Linux下OpenCV开发环境的搭建
个人已经厌倦了Windows下的开发方式,于是决定转到Linux平台上来,当然我也知道这个转变会很艰辛,但是我还是要坚持.所以,后面的所有开发我都会基于Linux和Qt,先从开发环境的搭建开始做起,当 ...
- Mac下OpenCV开发环境配置(Terminal和Xcode)
亲证可用:http://www.jianshu.com/p/11959977589a Mac OS X 10.1 Xcode 7.2(7C68) OpenCV 2.4.13 Mac OS10.11 ...
- Mac下OpenCV开发
1. 环境搭建 a) 安装Homebrew i. 下载地址:http://github.com/mxcl/homebrew/tarball/maste ...
随机推荐
- java 如何判断操作系统是Linux还是Windows
String os = System.getProperty("os.name"); if(os.toLowerCase().startsWith("win") ...
- 过度拟合(overfilting)
过拟合概念:是指分类器能够百分之百的正确分类样本数据(训练集中的样本数据),对训练集以外的数据却不能够正确分类. 原因:1:模型(算法)太过复杂,比如神经网络,算法太过精细复杂,规则太过严格,以至于任 ...
- JVM菜鸟进阶高手之路八(一些细节)
转载请注明原创出处,谢谢! gc日志问题 查看docker环境的gc日志,发现是下面这种情况,很奇怪,一直怀疑是docker环境那里是否有点问题,并没有怀疑配置,之前物理机上面的gc日志都是正常那种. ...
- JS表单对象和图片对象--JavaScript基础
1.表单对象 1)访问者输入正确的密码才可进入网站 HTML DOM prompt() 方法,prompt() 方法用于显示可提示用户进行输入的对话框.prompt(text,defaultText) ...
- Java为什么把String设计成不可变的(immutable)
在java中,String是字符串常量,可以从内存,同步机制,数据结构等方面分析 1:字符串中常量池的需要 String不同于普通基础变量类型的地方在于对象.java中的字符串对象都保存在字符串常量池 ...
- 初学者一些常用的SQL语句(一)
一.数据库的创建create database 数据库名create database bbb二.表的创建 ***[]:可选项*** null:空值 not null 不为空***只有字符型能指定长度 ...
- Python基础知识总结
看了一个礼拜Python的书,断断续续的看了一大半.今天刚好没有课,想着也没什么事情干,就把这几天Python总结一下,都是一些基础知识 变量和对象的引用 在python中一切都是对象,不像C,jav ...
- mac 安装Beautiful Soup
Beautiful Soup是一个Python的一个库,主要为一些短周期项目比如屏幕抓取而设计.有三个特性使得它非常强大: 1.Beautiful Soup提供了一些简单的方法和Python术语,用于 ...
- 第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ----初始化一个线程
使用线程的一个常见问题就是如何能够在一个线程开始运行之前,适当地将它初始化.初始化最常见的理由就是为了调整优先权.另一个理由是为了在SMP 系统中设定线程比较喜欢的 CPU.第10 章谈到 MFC 时 ...
- hdu1356&hdu1944 博弈论的SG值(王道)
S-NimProblem DescriptionArthur and his sister Caroll have been playing a game called Nim for some ti ...