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, 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,將其轉換為灰度模式。

三、對比

  從代碼來看,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下的開發了。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM