在 IOS Swift 项目中使用 OpenCV 进行视频处理

Video processing with OpenCV in IOS Swift project

我在 Swift IOS 项目中集成了 opencv 使用桥接头(将 Swift 连接到 Objective C)和一个 Objective C 包装器(到将 Objective C 连接到 C++)。使用这种方法,我可以从 Swift 代码传递单个图像,在 C++ 文件中分析它们并取回它们。

I've seen that opencv provides CvVideoCamera object that can be integrated with an Objective C UIViewController.

但是由于我的 UIViewController 是用 Swift 编写的,我想知道这是否也可行?

这是我有机会自己玩这个之后对我最初答案的更新。是的,可以将 CvVideoCamera 与用 Swift 编写的视图控制器一起使用。如果你只是想用它在你的应用程序中显示来自相机的视频,那真的很简单。

#import <opencv2/highgui/cap_ios.h> 通过桥接 header。然后,在你的视图控制器中:

class ViewController: UIViewController, CvVideoCameraDelegate {
...
    var myCamera : CvVideoCamera!
    override func viewDidLoad() {
        ...
        myCamera = CvVideoCamera(parentView: imageView)
        myCamera.delegate = self
        ...
    }
}

ViewController 实际上不能符合 CvVideoCameraDelegate 协议,但是 CvVideoCamera 没有委托就无法工作,所以我们通过声明 ViewController 来解决这个问题在不实施任何方法的情况下采用该协议。这将触发编译器警告,但来自摄像机的视频流将显示在图像视图中。

当然,您可能希望实现 CvVideoCameraDelegate 的(仅)processImage() 方法以在显示视频帧之前对其进行处理。你不能在 Swift 中实现它的原因是因为它使用了 C++ 类型,Mat.

因此,您需要编写一个Objective-C++ class,其实例可以设置为相机的委托。 Objective-C++ class 中的 processImage() 方法将由 CvVideoCamera 调用,并依次调用 Swift class 中的代码。以下是一些示例代码片段。 在 OpenCVWrapper.h:

// Need this ifdef, so the C++ header won't confuse Swift
#ifdef __cplusplus
#import <opencv2/opencv.hpp>
#endif

// This is a forward declaration; we cannot include *-Swift.h in a header.
@class ViewController;

@interface CvVideoCameraWrapper : NSObject
...
-(id)initWithController:(ViewController*)c andImageView:(UIImageView*)iv;
...
@end

在包装器实现中,OpenCVWrapper.mm(它是一个 Objective-C++ class,因此扩展名为 .mm):

#import <opencv2/highgui/cap_ios.h>
using namespace cv;
// Class extension to adopt the delegate protocol
@interface CvVideoCameraWrapper () <CvVideoCameraDelegate>
{
}
@end
@implementation CvVideoCameraWrapper
{
    ViewController * viewController;
    UIImageView * imageView;
    CvVideoCamera * videoCamera;
}

-(id)initWithController:(ViewController*)c andImageView:(UIImageView*)iv
{
    viewController = c;
    imageView = iv;

    videoCamera = [[CvVideoCamera alloc] initWithParentView:imageView];
    // ... set up the camera
    ...
    videoCamera.delegate = self;

    return self;
}
// This #ifdef ... #endif is not needed except in special situations
#ifdef __cplusplus
- (void)processImage:(Mat&)image
{
    // Do some OpenCV stuff with the image
    ...
}
#endif
...
@end

然后你把 #import "OpenCVWrapper.h" 放在桥接 header 中,Swift 视图控制器可能看起来像这样:

class ViewController: UIViewController {
...
    var videoCameraWrapper : CvVideoCameraWrapper!

    override func viewDidLoad() {
        ...
        self.videoCameraWrapper = CvVideoCameraWrapper(controller:self, andImageView:imageView)
        ...
    }

参见 https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html 关于前向声明和 Swift/C++/Objective-C 互操作。网上有很多关于 #ifdef __cplusplusextern "C" 的信息(如果你需要的话)。

processImage() 委托方法中,您可能需要与某些 OpenCV API 交互,您还必须为此编写包装器。您可以在其他地方找到一些相关信息,例如此处:Using OpenCV in Swift iOS

更新 09/03/2019

应社区要求,请参阅评论,示例代码已放在 GitHub at https://github.com/aperedera/opencv-swift-examples

此外,截至撰写本文时,OpenCV iOS 框架的当前版本不再允许 Swift 代码使用 header(现在它在 videoio/cap_ios.h) 声明 CvVideoCameraDelegate 协议,因此您不能仅将其包含在桥接 header 中并声明视图控制器符合协议以在您的应用程序中简单地显示相机视频。