1. 前言

前面【iOS视频捕获实践篇】制作了一个简单拍照和视频录制的相机,事实上,大多数app都有扫码的功能,因此仅仅一个相机是不够的。本篇文章来对前面的相机做一个升级,对拍照界面增人脸检测与追踪的功能。

2. MetadataOutput

AVCaptureOutput有一个子类 AVCaptureMetadataOutput,它用来输出元数据。元数据简单来说就是现实一个具体的物体,比如人脸、条码、动物等,所有元数据对象都有时间、持续时间、界限和类型。下面是一个元数据的抽象类,我们需要使用其子类。

1
2
3
4
5
6
@interface AVMetadataObject : NSObject {
@property(readonly) CMTime time;
@property(readonly) CMTime duration;
@property(readonly) CGRect bounds;
@property(readonly) AVMetadataObjectType type;
@end

在使用上和AVCaptureVideoDataOutput是类似的。设置一个代理和一个派发队列。

1
- (void)setMetadataObjectsDelegate:(nullable id<AVCaptureMetadataOutputObjectsDelegate>)objectsDelegate queue:(nullable dispatch_queue_t)objectsCallbackQueue;

在设置完代理后,我们需要实现下面这个方法:

1
2
3
- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection;
// 每次回调,metadataObjects 总是返回同一类元数据,比如是AVMetadataFaceObject就不会是AVMetadataMachineReadableCodeObject
// 因此处理时需要注意

3. 人脸追踪

航向偏角(Pitch)是沿着X轴旋转;

偏航(yaw)是沿着Y轴旋转;

侧滚(roll)是沿着Z轴旋转。

对应于人头来说,pitch就是点头,yaw摇头,roll就是左右肩膀摆头。下面是笛卡尔积坐标系。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PHZWfKIO-1663297785545)(./tc_avf_pyr.png)]

1
2
3
4
5
6
7
8
9
@interface AVMetadataFaceObject : AVMetadataObject <NSCopying> {
@property(readonly) NSInteger faceID;
// 左右摆头
@property(readonly) BOOL hasRollAngle;
@property(readonly) CGFloat rollAngle;
// 左右摇头
@property(readonly) BOOL hasYawAngle;
@property(readonly) CGFloat yawAngle;
@end

我们实现如下代理方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
@autoreleasepool {
dispatch_sync(dispatch_get_main_queue(), ^{
NSMutableArray *aArray = [NSMutableArray arrayWithArray:self.faceLayerMaps.allValues];
[metadataObjects enumerateObjectsUsingBlock:^(__kindof AVMetadataObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
AVMetadataFaceObject *faceObject = (AVMetadataFaceObject *)[self.cameraCore.videoPreviewLayer transformedMetadataObjectForMetadataObject:obj];
NSString *identifier = [NSString stringWithFormat:@"%zd", faceObject.faceID];
CAShapeLayer *layer = [self.codeLayerMaps objectForKey:identifier];

if (identifier && !layer) {
layer = [CAShapeLayer layer];
[self.faceLayerMaps setObject:layer forKey:identifier];
}
if (layer && !layer.superlayer) {
layer.fillColor = [[UIColor redColor] colorWithAlphaComponent:0.3].CGColor;
[self.view.layer addSublayer:layer];
}
[aArray removeObject:layer];
layer.path = [UIBezierPath bezierPathWithRect:faceObject.bounds].CGPath;
}];
for (CAShapeLayer *layer in aArray) {
[layer removeFromSuperlayer];
}
[aArray removeAllObjects];
[self.view setNeedsDisplay];
});
};
}

4. 总结

【iOS视频捕获】到这里就结束了,由于时间关系,在细节上面没有打磨。

对于barcode识别,CIImage的detector、VisionFramework等的使用,大家可以自行搜索使用。

瑞思拜~