iOS下OpenCV开发用OC仍是Swift

本文为做者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃ios

 

  其实标题中这个问题并不许确,准确的说法应该是iOS下的OpenCV开发是使用OC仍是Swift+OC。这个问题纠结了好久,研究了不少例子。先说结论:若是用到的算法规模不大且不熟悉cap_ios.h尽可能用Swift+OC。(欢迎高手来打脸)git

  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。程序员

1、Objective-C下开发OpenCV的基本流程github

  熟悉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方法,实现了摄像头的灰度模式)。 session

2、Swift下开发OpenCV的基本流程多线程

  因为Swift不能直接调用C++,全部对C++的调用须要通过OC来包裹处理,因此Swift使用OpenCV须要和OC进行混编,关于Swift+OC+OpenCV的混编流程大概描述一下:并发

  一、新建Swift项目;

  二、在Swift项目中新建ObjectiveC文件,后缀为.m的请修改成.mm,此时会提示是否生成bridgeXXX.h的头文件,确认生成;

  三、为.mm文件新建一个头文件,并在生成的bridgeXXX.h中import该头文件;

  四、至此在.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, 0)
            let bytes = CVPixelBufferGetBytesPerRow(buffer)
            let width = CVPixelBufferGetWidth(buffer)
            let height = CVPixelBufferGetHeight(buffer)
            let color = CGColorSpaceCreateDeviceRGB()
            let bits = 8
            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,将其转换为灰度模式。

3、对比

  从代码来看,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++。。。内存怎么办。。。有几点建议:1、使用完的mat注意release掉;2、多用vector少用数组;3、非计算尽可能面向对象,专用计算尽可能静态方法;4、若是能够,除了须要调用的库外,不要用C++,尽可能用Objective-C。若是你有更好的办法,请指点。。。

  如今回到开头的结论,其实在OC中也可使用AVFoundation来初始化摄像头,可是为了偷懒,相信大多数人都会直接用OpenCV自带的方法完成初始化。而OpenCV封装类对Android或iOS的系统是以兼容为主的,效率是其次的,因此若是在实际开发中,不论Android仍是iOS,都建议使用系统推荐的方式来调用camera,只有在处理画面帧时才调用OpenCV中的方法。并且随着Swift愈来愈完善,固然是推荐使用Swift+OC来进行OpenCV下的开发了。 

相关文章
相关标签/搜索