iOS Vision的使用
学习需要持之以恒,不做记录的学习就和不以结婚为目的去谈恋爱一样。
感觉自己学习的路途就是一路在耍流氓,没有做笔记的好习惯是不行的!不光是要留有代码,还要抒发己见多多与人讨论,才能真正的掌握和理解。
Vision与Core ML简介
17年WWDC大会又给我们带来了许多新玩意儿,今天要说的是其中一个时下比较热门的库---Vision。从名字就可以看出是视觉方面的功能。具体来说就是基于Core ML封装的图像识别库,整体层次是这样的。
image苹果从 iOS 5 开始引入 NSLinguisticTagger 用于分析神经语言。在 iOS 8 中又出现了 Metal,Metal 提供了对 GPU 底层进行访问的能力。
去年,苹果在 Accelerate 框架中加入了基本神经网络子例程(BNNS),使得开发者能够创建用于推断(不是训练)的神经网络。
今年,苹果推出了 Core ML 和 Vision!
Core ML 使得在你的 app 中使用训练模型更加容易。
Vision 允许你轻松访问到苹果的面部识别、面部特征、文本、矩形、条形码和对象模型。
这两个框架是内置在 Metal 中的,它们高效运行在设备上,因此你并不需要将用户数据发送给服务器。
性能对比
image从苹果给的对比图可知相对于已有框架Core Image和AVFoundation,Vision的准确度是最好的,同时和Core Image支持平台数量是一样的,但是需要较多的处理时间及电源消耗。Vision又是苹果封装过的库,相较于Core Image这种底层库,API会友好太多,减少了我们的开发量,只要专心处理图像就可以了。
开始使用Vision
环境
Xcode 9 + ios 11
Vision的结构体系:
imageVision使用中的角色有:
Request,RequestHandler,results和results中的Observation数组。
我们在使用过程中是给各种功能的 Request 提供给一个 RequestHandler,Handler 持有需要识别的图片信息,并将处理结果分发给每个 Request 的 completion Block 中。可以从 results 属性中得到 Observation 数组。
observations数组中的内容根据不同的request请求返回了不同的observation,如:VNFaceObservation、VNTextObservation、VNBarcodeObservation、VNHorizonObservation,不同的Observation都继承于VNDetectedObjectObservation,而VNDetectedObjectObservation则是继承于VNObservation。每种Observation有boundingBox,landmarks等属性,存储的是识别后物体的坐标,点位等,我们拿到坐标后,就可以进行一些UI绘制。
Vision 支持的图片数据类型:
* CVPixelBufferRef
- CGImageRef
- CIImage
- NSURL
NSData
可以说几乎涵盖了iOS中图片相关的API,很实用很强大。
Vision 人脸检测实现
__weak typeof(self) weakSelf = self;
// 转换为CIImage
CIImage *convertImage = [[CIImage alloc] initWithImage:tempImage];
// 创建图像处理request (携带要处理的对象)
VNImageRequestHandler *detectRequestHandler = [[VNImageRequestHandler alloc] initWithCIImage:convertImage options:@{}];
// 创建图像处理完成的回调 (处理完成的回调block)
VNRequestCompletionHandler completionHandler = ^(VNRequest *request, NSError *error) {
NSArray *observations = request.results;
// 监测到所有的对象的点位,对每一个检测到的对象创建一个boxView
for (VNFaceObservation *observation in observations) {
CGFloat w = observation.boundingBox.size.width * tempImage.size.width;
CGFloat h = observation.boundingBox.size.height * tempImage.size.height;
CGFloat x = observation.boundingBox.origin.x * tempImage.size.width;
CGFloat y = tempImage.size.height - (observation.boundingBox.origin.y * tempImage.size.height) - h;
CGRect facePointRect = CGRectMake(x, y, w, h);①
UIView *boxView = [[UIView alloc]initWithFrame:facePointRect];
boxView.backgroundColor = [UIColor clearColor];
boxView.layer.borderColor = [UIColor redColor].CGColor;
boxView.layer.borderWidth = 2;
[weakSelf.detectCompleteView addSubview:boxView];
}
};
// 创建侦测人脸识别请求 (要怎样处理request handler携带的对象)
// 这里使用的是识别人脸
VNDetectFaceRectanglesRequest *detectRequest = [[VNDetectFaceRectanglesRequest alloc] initWithCompletionHandler:completionHandler];
// 执行
[detectRequestHandler performRequests:@[detectRequest] error:nil];
代码就是整个创建及回调处理过程,是不是很简单~。
可以看到我们首先要对图片做处理,要把输入的图片转成Vision需要类型。我这里是通过UIImagePickerController的代理imagePickerController:didFinishPickingImage:editingInfo:拿到的相册图片,并把处理完的图片放到已有的detectCompleteView中。需要注意的是图片转换及处理点位生成boxView的时候,一定要使用你在View中做好的detectCompleteView的frame,使从代理方法中接受到的图片和detectCompleteView的frame一样。
** 看到有人说performRequests 是在子线程做的处理,而且VNRequestCompletionHandler是和perform在一个线程中,但我在使用过程中发现回调的block其实是在主线程中的。所以对UI操作不会有阻塞的影响,我会继续跟进,如果有清楚具体原因的也可以留言,谢谢~ **
VNRequestCompletionHandler回调的处理
(lldb) po request.results
<__NSSingleObjectArrayI 0x1c40185f0> (
<VNFaceObservation: 0x1c419f210>E4A411F7-1E97-4262-9090-88831DB02219 1 [0.238984 0.354707 0.428141 0.321106] ID=0
)
①:可以看到处理完返回的VNFaceObservation给的值都是0-1,而且Vision使用的坐标原点是左下角,所以拿到值以后我们不能直接使用,而是要对它做处理匹配UIKit坐标系。PS:算出来的脸部是MAX Y值而不是min Y值。(2,4象限y值为相反数)
感想
本来打算一次把所有识别方式都放上来,但发现其实实现都是一样的,都是通过创建requestHandler,imageRequest 然后performe,最后等待VNRequestCompletionHandler的回调带回results和results中的Observation数组,只是相应的点位不同。比如VNDetectFaceLandmarksRequest带回的是
VNFaceLandmarks2D *landmarks = observation.landmarks;
如果需要显示,那么就根据给定的特征点进行绘制线条,同样的也是要对特征点做转换,区别于面部矩形识别在于得到的结果以point为准。也可以直接根据返回的点位做响应的识别,比如面部识别。
Vision 更像是一个工具库,对一些高频场景进行了封装,比如人脸、条形码、矩形和文字等。可以说Vision是基于底层 API 封装的高级功能可以帮我们减少很大的开发量。
Vision还可以很好的支持CoreML的创建,虽然苹果爸爸把预测模型整理的很好,只需要通过coremltools来转换模型为Xcode可识别的CoreML model,然后通过Xcode生成对接Model类我们就可以直接使用。但是因为每个模型的Input对图像的要求不一样,导致使用的时候我们还要针对模型来匹配尺寸。Vison在这个时候就可以发挥大作用!欲知详情,带下回分解~
↓↓↓当然你也可以参考这里↓↓↓
WWDC 2017 会议视频 506 :Vision Framework
WWDC 2017 会议视频 703:介绍 Core ML
Vision Framework: Building on Core ML
Advances in Core Image: Filters, Metal, Vision, and More