人臉識別技術 (三) —— 基于AVFoundation實現視頻中人臉的識別

版本記錄

版本號 時間
V1.0 2018.01.31

前言

人臉識別是圖像識別技術中的一種,廣泛的應用于很多領域,接下來這幾篇我們就一起來研究幾種關于人臉識別的技術。感興趣的可以參考上面幾篇文章。
1. 人臉識別技術 (一) —— 基于CoreImage實現對靜止圖片中人臉的識別
2. 人臉識別技術 (二) —— 基于CoreImage實現視頻中人臉的識別

基于AVFoundation的視頻中人臉識別技術

前兩篇文章我們利用CoreImage對靜止圖像和視頻中的人臉進行識別,這一篇我們就利用AVFoundation進行視頻中的人臉識別,主要是用類AVCaptureMetadataOutput


功能實現

下面我們就看一下實現代碼。

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController () <AVCaptureMetadataOutputObjectsDelegate>

@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureDevice *captureDevice;
@property (nonatomic, strong) AVCaptureDeviceInput *captureVideoDeviceInput;
@property (nonatomic, strong) AVCaptureMetadataOutput *metaDataOutput;
@property (nonatomic, strong) AVCaptureConnection *captureConnection;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
@property (nonatomic, strong) NSMutableArray <UIView *> *faceViewArrM;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.faceViewArrM = [NSMutableArray array];
    
    self.captureSession = [[AVCaptureSession alloc] init];
    if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
        self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
    }
    else {
        self.captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
    }
    
    for (AVCaptureDevice *device in [AVCaptureDevice devices]) {
        if ([device hasMediaType:AVMediaTypeVideo]) {
            if (device.position == AVCaptureDevicePositionFront) {
                self.captureDevice = device;
            }
        }
    }
    
    //添加輸入
    [self addVideoInput];
    
    //添加輸出
    [self addVideoOutput];
    
    //添加預覽圖層
    [self addPreviewLayer];
    
    [self.captureSession commitConfiguration];
    [self.captureSession startRunning];
    
}

#pragma mark -  Object Private Function

- (void)addVideoInput
{
    NSError *error;
    self.captureVideoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:self.captureDevice error:&error];
    if (error) {
        return;
    }
    if ([self.captureSession canAddInput:self.captureVideoDeviceInput]) {
        [self.captureSession addInput:self.captureVideoDeviceInput];
    }
}

- (void)addVideoOutput
{
    self.metaDataOutput = [[AVCaptureMetadataOutput alloc] init];
    [self.metaDataOutput setMetadataObjectsDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];

    if ([self.captureSession canAddOutput:self.metaDataOutput]) {
        [self.captureSession addOutput:self.metaDataOutput];
    }
    
    self.metaDataOutput.metadataObjectTypes =  @[AVMetadataObjectTypeFace];
    
    //設置鏈接管理對象
    self.captureConnection = [self.metaDataOutput connectionWithMediaType:AVMediaTypeVideo];
    //視頻旋轉方向設置
    self.captureConnection.videoScaleAndCropFactor = self.captureConnection.videoMaxScaleAndCropFactor;;
    //視頻穩定設置
    if ([self.captureConnection isVideoStabilizationSupported]) {
        self.captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
    }
}

- (void)addPreviewLayer
{
    self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
    [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
    self.previewLayer.frame = self.view.bounds;
    [self.view.layer addSublayer:self.previewLayer];
}

#pragma mark -  AVCaptureVideoDataOutputSampleBufferDelegate

- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    if (metadataObjects.count > 0) {
        NSLog(@"檢測到了人臉,數目為%ld", metadataObjects.count);
        NSLog(@"%@", metadataObjects);
    }
}


@end

下面看一下部分輸出結果

2018-01-31 15:59:56.128966+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.129194+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x10158e0d0, faceID=2, bounds={0.1,0.3 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255234842406416>"
)
2018-01-31 15:59:56.162409+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.162630+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x1014a4930, faceID=2, bounds={0.1,0.3 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255234875712416>"
)
2018-01-31 15:59:56.195745+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.195976+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x101491800, faceID=2, bounds={0.1,0.3 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255234909018416>"
)
2018-01-31 15:59:56.228822+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.229070+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x1014aa890, faceID=2, bounds={0.1,0.3 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255234942324458>"
)
2018-01-31 15:59:56.262194+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.262442+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x101589740, faceID=2, bounds={0.1,0.3 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255234975630458>"
)
2018-01-31 15:59:56.295511+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.295749+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x10158de70, faceID=2, bounds={0.1,0.3 0.4x0.6}, rollAngle=300.0, yawAngle=0.0, time=255235008936541>"
)
2018-01-31 15:59:56.328751+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.328978+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x10158e0d0, faceID=2, bounds={0.0,0.4 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255235042242708>"
)
2018-01-31 15:59:56.362188+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.362413+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x1015810f0, faceID=2, bounds={0.0,0.4 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255235075548583>"
)
2018-01-31 15:59:56.395353+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.395606+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x10158de70, faceID=2, bounds={0.0,0.4 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255235108854666>"
)
2018-01-31 15:59:56.428496+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.428747+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x101580fb0, faceID=2, bounds={0.0,0.4 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255235142160875>"
)
2018-01-31 15:59:56.462256+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.462490+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x1015810f0, faceID=2, bounds={0.0,0.4 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255235175466875>"
)
2018-01-31 15:59:56.495412+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.495651+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x10158de70, faceID=2, bounds={0.0,0.4 0.3x0.6}, rollAngle=300.0, yawAngle=0.0, time=255235208772750>"
)
2018-01-31 15:59:56.528425+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.528668+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x101535360, faceID=2, bounds={0.0,0.4 0.4x0.6}, rollAngle=300.0, yawAngle=0.0, time=255235242078875>"
)
2018-01-31 15:59:56.561876+0800 JJFaceDetector_demo3[4747:1474803] 檢測到了人臉,數目為1
2018-01-31 15:59:56.562123+0800 JJFaceDetector_demo3[4747:1474803] (
    "<AVMetadataFaceObject: 0x10158e0d0, faceID=2, bounds={0.0,0.3 0.4x0.6}, rollAngle=300.0, yawAngle=0.0, time=255235275384791>"
)

這里檢測到人臉就會有輸出,檢測不到就不會有輸出,比如我們不出現在視頻中就沒有輸出,或者將對象類型從人臉修改為二維碼,那么即使你出現在視頻中,也不會有任何輸出。

self.metaDataOutput.metadataObjectTypes =  @[AVMetadataObjectTypeQRCode];

這里就不給大家展示效果了,原因只有一個 —— 因為長的太丑


幾個問題

1. info.plist增加相機權限的key

這個是老問題,一筆帶過而已,不多說。

2. metaDataOutput.metadataObjectTypes的設置時機

假如我們向下面這么寫代碼

- (void)addVideoOutput
{
    self.metaDataOutput = [[AVCaptureMetadataOutput alloc] init];
    [self.metaDataOutput setMetadataObjectsDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
    self.metaDataOutput.metadataObjectTypes =  @[AVMetadataObjectTypeQRCode];
    if ([self.captureSession canAddOutput:self.metaDataOutput]) {
        [self.captureSession addOutput:self.metaDataOutput];
    }
  ... ...
}

運行一下就會崩潰,輸出消息如下

2018-01-31 17:09:55.220854+0800 JJFaceDetector_demo3[4760:1487855] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMetadataOutput setMetadataObjectTypes:] Unsupported type found - use -availableMetadataObjectTypes'
*** First throw call stack:
(0x185fb2364 0x1851f8528 0x18b988308 0x1004495d0 0x100449148 0x18f493ae8 0x18f4936c0 0x18f49a28c 0x18f4974ec 0x18f5063a0 0x18f6f3d10 0x18f6f8aa8 0x18f9942d4 0x18fc6c12c 0x18f993f38 0x18f994798 0x1901197d0 0x190119674 0x18fe872dc 0x1900203dc 0x18fe8718c 0x18fc6b78c 0x18f6f71ec 0x18fb0cde8 0x1886761f0 0x18867eaf8 0x10081128c 0x10081d9e4 0x1886aa7f8 0x1886aa49c 0x1886aaa38 0x185f5a97c 0x185f5a8fc 0x185f5a184 0x185f57d5c 0x185e77e58 0x187d24f84 0x18f4f767c 0x10044a0f8 0x18599456c)
libc++abi.dylib: terminating with uncaught exception of type NSException

在網上也碰到了別人類似的遭遇,只需要更改設置時機就可以了,崩潰的原因是:metadataObjectTypes要在AVCaptureSession類對象[self.captureSession addOutput:self.metaDataOutput];即添加了AVCaptureMetadataOutput的對象后,方可對其進行設置。如下所示:

- (void)addVideoOutput
{
    self.metaDataOutput = [[AVCaptureMetadataOutput alloc] init];
    [self.metaDataOutput setMetadataObjectsDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
    if ([self.captureSession canAddOutput:self.metaDataOutput]) {
        [self.captureSession addOutput:self.metaDataOutput];
    }
    
    self.metaDataOutput.metadataObjectTypes =  @[AVMetadataObjectTypeFace];
    ... ...
}

這樣設置就可以了。

后記

本篇已結束,后面更精彩~~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容