技术流

iOS Vision的使用

2018-02-09  本文已影响38人  Synemin

学习需要持之以恒,不做记录的学习就和不以结婚为目的去谈恋爱一样。

感觉自己学习的路途就是一路在耍流氓,没有做笔记的好习惯是不行的!不光是要留有代码,还要抒发己见多多与人讨论,才能真正的掌握和理解。

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的结构体系:

image

Vision使用中的角色有:

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


参考链接
Core ML 和 Vision: iOS 机器学习教程
iOS Core ML与Vision初识

上一篇下一篇

猜你喜欢

热点阅读