iOS

Vision学习笔记

Posted by 李清发 on September 11, 2017

目录[+]

Top

苹果在WWDC2017上发布了iOS11的beta版。伴随着iOS11的发布,ARKit、Core ML、FileProvider、IdentityLookup、Core NFC、Vision等一系列新的API相继与开发者见面。本篇文章主要介绍Vision的概念及简单的使用方法。

什么是Vision

Vision是一个「利用高性能图像分析和计算机视觉技术进行人脸识别、特征检测、场景分类等功能」的开发框架。根据官方文档,Vision包含以下功能:

  • Face Detection and Recognition (人脸检测识别)
  • Machine Learning Image Analysis (机器学习图片分析)
  • Object Detection and Tracking (对象检测和追踪)
  • Barcode Detection (条形码检测)
  • Text Detection (文本检测)
  • ……..

事实上,Vision就是建立在Core ML层之上的,正是Apple把已经训练好的Core ML模型集成进Vision框架才得以实现了以上的基础功能。如果你的需求更为复杂,那么你必须把二者结合起来使用才能达到目的(在使用Vision的基础功能时,其实就已经使用到了Core ML,只是没有显式的直接写Core ML代码)。如果你想继续了解Core ML以及二者如何协同工作,请移步这里

如何使用Vision

条形码识别

在Vision推出之前,我们常用AVCaptureMetadataOutput API来实现条形码/二维码识别。现在有了第二种方式Vision,并且在速度上有了较大的改善。

step 1

连接到硬件

- (void)setupCamera{
  	AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
	AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:self.device 	error:nil];
	AVCaptureOutput *output = [[AVCaptureVideoDataOutput alloc]init];
  	//要在非UI线程中输出
  	[output setSampleBufferDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
	AVCaptureSession *session =  [[AVCaptureSession alloc]init];
  	[session setSessionPreset:AVCaptureSessionPresetHigh];
    [session addInput:self.input];
    [session addOutput:self.output];
}
step2

创建VNSequenceRequestHandler对象(这里要从视屏流中不断的处理,所以用队列,这里很重要)

VNSequenceRequestHandler *requestHandler = [VNSequenceRequestHandler new];
step3

从AVCaptureOutput的代理方法中获取到像机数据,并交由Vision处理

- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    if (!pixelBuffer) {
      return;
    }
  	VNDetectBarcodesRequest *qrcodeRequset = [[VNDetectBarcodesRequest alloc]initWithCompletionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error){
      if (request.results.count > 0) {
        //获取到数据以后,如果需要处理UI,则必须会到主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            VNBarcodeObservation *barcode = request.results.firstObject;
          	//最终得到的数据
            NSString *ss = barcode.payloadStringValue;
         })  
      }
    }];
  //把每一个qrcodeRequset对象加入到处理队列
  [self.requestHandler performRequests:@[qrcodeRequset] onCVPixelBuffer:pixelBuffer error:NULL];
}

CVPixelBuffer 是apple的图像最基础的存储接口,在视频采集处理编码流程中起到重要中间数据媒介和纽带的作用。

人脸识别

step 1

获取一张照片(感谢小锅同学提供的照片)

UIImage *faceImage = [UIImage imageNamed:@"lqf"];
//转化为CIImage
CIImage *convertImage = [[CIImage alloc]initWithImage:faceImage];
step2

识别人脸

// 创建处理requestHandler
VNImageRequestHandler *detectRequestHandler = [[VNImageRequestHandler alloc]initWithCIImage:convertImage options:@{}];
// 创建BaseRequest
VNImageBasedRequest *detectRequest = [[VNImageBasedRequest alloc]init];
// 设置回调
CompletionHandler completionHandler = ^(VNRequest *request, NSError * _Nullable error) {
 NSArray *observations = request.results;
  //识别结果回调
 [self faceRectangles:observations image:image complete:complete];
};
//创建人脸矩形识别请求
VNDetectFaceRectanglesRequest *detectRequest =  [[VNDetectFaceRectanglesRequest alloc]initWithCompletionHandler:completionHandler];
// 发送识别请求
[detectRequestHandler performRequests:@[detectRequest] error:nil];
Step3

得到人脸位置并画矩形标识

-(void)faceRectangles:(NSArray *)observations image:(UIImage *_Nullable)image complete:(detectImageHandler _Nullable )complete{
    NSMutableArray *tempArray = @[].mutableCopy;
    DSDetectData *detectFaceData = [[DSDetectData alloc]init];
    for (VNFaceObservation *observation  in observations) {
        NSValue *ractValue = [NSValue valueWithCGRect:[self convertRect:observation.boundingBox imageSize:image.size]];
        [tempArray addObject:ractValue];
    }
    detectFaceData.faceAllRect = tempArray;
    if (complete) {
        complete(detectFaceData);
    }
}
//该方法用于获取到识别结果中的人脸位置相对于真实照片中的位置
-(CGRect)convertRect:(CGRect)oldRect imageSize:(CGSize)imageSize{
    CGFloat w = oldRect.size.width * imageSize.width;
    CGFloat h = oldRect.size.height * imageSize.height;
    CGFloat x = oldRect.origin.x * imageSize.width;
    CGFloat y = imageSize.height - (oldRect.origin.y * imageSize.height) - h;
    return CGRectMake(x, y, w, h);
}

来看下识别结果:

相比Core Image,AV Capture的人脸识别功能,Vision在精准度上面有了很大的提高,但是带来的另外一个不足就是在耗能方面也提高了不少。

以上

根据两个例子我们基本上可以总结出Vision使用的基本步骤,也就是将各种功能的 Request 提供给一个 RequestHandler,Handler 持有图片信息,并将处理结果分发给每个 Request 的 completion Block 中。可以从 results 属性中得到 Observation 数组,然后进行更新 UI 等操作。因为 completion Block 所执行的队列跟 perform request 的队列相同,所以更新 UI 时记得使用主队列。

最后

Vision使用起来简直不要太简单,在此还是要感谢苹果爸爸封装的这么友好。但是,怎样把如此优秀的框架运用到自己的项目中并形成可观的生产力,就是你我开发者需要努力的地方了。

知识共享许可协议
文章四川掌麦科技有限公司创作,采用知识共享署名 4.0 国际许可协议进行许可。