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