本文概述
Swift已经存在了一段时间, 并且通过迭代, 它为我们带来了现代的面向对象编程语言的所有功能。这些包括可选, 泛型, 元组, 支持方法, 扩展和协议的结构等。但是, 如果你的应用程序依赖于使用C ++编写的库, 那么你将无法再依赖Swift。幸运的是, Objective-C ++在这里可以为我们提供帮助。
自从引入以来, Swift与Objective-C的互操作性非常好, 我们将使用Objective-C将Swift与C ++桥接。 Objective-C ++只不过是具有与C ++代码链接的功能的Objective-C, 在这篇博客文章中, 我们将使用它来创建一个简单的应用程序, 该应用程序将使用OpenCV识别图像中的srcmini徽标。当我们检测到该徽标时, 我们将打开srcmini主页。
如OpenCV网页所述:
” OpenCV是为提高计算效率而设计的, 并且着重于实时应用程序。该库以优化的C / C ++编写, 可以利用多核处理的优势。”
如果你想要快速开发和可靠的计算机视觉, 这是一个很好的解决方案。
创建OpenCV Objective-C ++包装器
在本教程中, 我们将设计与图像中的srcmini徽标匹配的应用程序, 并打开srcmini网页。首先, 创建一个新的Xcode项目并使用pod init设置CocoaPods。将OpenCV添加到Podfile pod’OpenCV, 并在终端中运行pod install。确保取消注释use_frameworks! Podfile中的语句。
现在, 当我们在Xcode项目中拥有OpenCV时, 我们必须将其与Swift连接。这是有关步骤的快速概述:
步骤1:创建一个新的Objective-C类OpenCVWrapper。当Xcode询问你”你是否要配置Objective-C桥接标头吗?”选择”创建桥接标题”。桥接标头是导入Objective-C类的位置, 然后它们在Swift中可见。
步骤2:为了在Objective-C中使用C ++, 我们必须将文件扩展名从OpenCVWrapper.m更改为OpenCVWrapper.mm。你只需在Xcode的项目导航器中重命名文件即可。添加.mm作为扩展名会将文件类型从Objective-C更改为Objective-C ++。
步骤3:使用以下导入将OpenCV导入OpenCVWrapper.mm。请在#import” OpenCVWrapper.h”上方写入给定的导入, 这一点很重要, 因为这样可以避免众所周知的BOOL冲突。 OpenCV包含具有值NO的枚举, 该值引起与Objective-C BOOL NO值的冲突。如果你不需要使用此类枚举的类, 那么这是最简单的方法。否则, 你必须在导入OpenCV之前取消定义BOOL。
#ifdef __cplusplus
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import <opencv2/videoio/cap_ios.h>
#endif
步骤4:将#import” OpenCVWrapper.h”添加到桥接头中。
打开OpenCVWrapper.mm并创建一个私有接口, 在此我们将声明私有属性:
@interface OpenCVWrapper() <CvVideoCameraDelegate>
@property (strong, nonatomic) CvVideoCamera *videoCamera;
@property (assign, nonatomic) cv::Mat logoSample;
@end
为了创建CvVideoCamera, 我们必须将UIImageView传递给它, 我们将通过指定的初始化程序来实现。
- (instancetype)initWithParentView:(UIImageView *)parentView delegate:(id<OpenCVWrapperDelegate>)delegate {
if (self = [super init]) {
self.delegate = delegate;
parentView.contentMode = UIViewContentModeScaleAspectFill;
self.videoCamera = [[CvVideoCamera alloc] initWithParentView:parentView];
self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
self.videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPresetHigh;
self.videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait;
self.videoCamera.defaultFPS = 30;
self.videoCamera.grayscaleMode = [NSNumber numberWithInt:0].boolValue;
self.videoCamera.delegate = self;
// Convert UIImage to Mat and store greyscale version
UIImage *templateImage = [UIImage imageNamed:@"srcmini"];
cv::Mat templateMat;
UIImageToMat(templateImage, templateMat);
cv::Mat grayscaleMat;
cv::cvtColor(templateMat, grayscaleMat, CV_RGB2GRAY);
self.logoSample = grayscaleMat;
[self.videoCamera start];
}
return self;
}
在其中, 我们配置CvVideoCamera在给定的parentView内渲染视频, 并通过委托向我们发送cv :: Mat图像进行分析。
processImage:方法来自CvVideoCameraDelegate协议, 在其中, 我们将进行模板匹配。
- (void)processImage:(cv::Mat&)image {
cv::Mat gimg;
// Convert incoming img to greyscale to match template
cv::cvtColor(image, gimg, CV_BGR2GRAY);
// Get matching
cv::Mat res(image.rows-self.logoSample.rows+1, self.logoSample.cols-self.logoSample.cols+1, CV_32FC1);
cv::matchTemplate(gimg, self.logoSample, res, CV_TM_CCOEFF_NORMED);
cv::threshold(res, res, 0.5, 1., CV_THRESH_TOZERO);
double minval, maxval, threshold = 0.9;
cv::Point minloc, maxloc;
cv::minMaxLoc(res, &minval, &maxval, &minloc, &maxloc);
// Call delegate if match is good enough
if (maxval >= threshold)
{
// Draw a rectangle for confirmation
cv::rectangle(image, maxloc, cv::Point(maxloc.x + self.logoSample.cols, maxloc.y + self.logoSample.rows), CV_RGB(0, 255, 0), 2);
cv::floodFill(res, maxloc, cv::Scalar(0), 0, cv::Scalar(.1), cv::Scalar(1.));
[self.delegate openCVWrapperDidMatchImage:self];
}
}
首先, 我们将给定的图像转换为灰度图像, 因为在init方法中, 我们将与srcmini徽标模板匹配的图像转换为灰度图像。下一步是检查是否有一定阈值的匹配项, 这将有助于我们缩放比例和角度。我们必须检查角度, 因为你可以以各种角度拍摄照片, 但仍然需要检测徽标。达到指定的阈值后, 我们将调用委托并打开srcmini的网页。
不要忘记将NSCameraUsageDescription添加到你的Info.plist中, 否则, 你的应用程序将在调用[self.videoCamera start];之后立即崩溃。
现在终于Swift
到目前为止, 我们专注于Objective-C ++, 因为所有逻辑都在其内部完成。我们的Swift代码将非常简单:
- 在ViewController.swift中, 创建UIImageView, CvVideoCamera将在其中渲染内容。
- 创建一个OpenCVWrapper实例, 并将UIImageView实例传递给它。
- 实施OpenCVWrapperDelegate协议以在检测到徽标时打开srcmini的网页。
class ViewController: UIViewController {
var wrapper: OpenCVWrapper!
@IBOutlet var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
wrapper = OpenCVWrapper.init(parentView: imageView, delegate: self)
}
}
extension ViewController: OpenCVWrapperDelegate {
//MARK: - OpenCVWrapperDelegate
func openCVWrapperDidMatchImage(_ wrapper: OpenCVWrapper) {
UIApplication.shared.open(URL.init(string: "https://srcmini02.com")!, options: [:], completionHandler: nil)
}
}
OpenCV Swift实战
在本文中, 我们展示了如何将C ++代码与Swift集成以及如何创建包装器类, 以便在本文中使用Swift与C ++代码进行桥接。现在, 当我们通过摄像头检测到srcmini徽标时, 我们将打开srcmini的主页。
为了将来进行更新, 你可能需要在后台线程中运行CV模板匹配。这样, 你就不会阻塞主线程, 并且UI将保持响应状态。因为这是OpenCV的简单示例, 所以模板匹配可能不会格外成功, 但是本文的目的是向你展示如何开始使用它。
如果你仍然对学习OpenCV及其在iOS中的更复杂的用法感兴趣, 建议你在iOS中使用MSER进行实时对象检测, 该教程将引导你使用iPhone的后置摄像头进行图像检测。