一、概述
這里的人臉檢測是通過AVFoundation實現(xiàn)的實時人臉檢測功能,會在檢測到人臉自動建立相應(yīng)的焦點。
AVFoundation中通過特定的AVCaptureOutput類型的AVCaptureMetadataOutput實現(xiàn)這個功能。它的輸出同之前類似,輸出的不是靜態(tài)圖片或影片,而是元數(shù)據(jù)。定義了用來處理多種元數(shù)據(jù)類型的接口,當使用人臉檢測時,會輸出一個具體子類類型AVMetadataFaceObject。
AVMetadataFaceObject幾個重要屬性:
-
rollAngle:傾斜角,表示人的頭部向肩膀方向的側(cè)傾角度。 -
yawAngle:偏轉(zhuǎn)角,表示人臉繞y軸旋轉(zhuǎn)的角度。 -
bounds:邊界,對應(yīng)的是設(shè)備坐標。
人臉識別的整個流程與之前用到的靜態(tài)圖片和視頻捕捉是一樣的,不同的是一些配置的不同,以及對獲取到的臉部數(shù)據(jù)對象的處理。
二、實現(xiàn)流程
基本功能
- 1、創(chuàng)建會話,并配置輸入輸出
self.captureSession = [[AVCaptureSession alloc] init];
self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
AVCaptureDevice *videoDevice =
[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *videoInput =
[AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:error];
if (videoInput) {
if ([self.captureSession canAddInput:videoInput]) {
[self.captureSession addInput:videoInput];
self.activeVideoInput = videoInput;
} else {
if (error) {
}
}
}
// Setup the still image output
self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
//self.imageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};
if ([self.captureSession canAddOutput:self.imageOutput]) {
[self.captureSession addOutput:self.imageOutput];
} else {
if (error) {
}
}
// 添加元數(shù)據(jù)輸出捕捉
self.metadataOutput = [[AVCaptureMetadataOutput alloc] init];
if ([self.captureSession canAddOutput:self.metadataOutput]) {
[self.captureSession addOutput:self.metadataOutput];
// 添加新的捕捉會話輸出
NSArray *metadataObjectTypes = @[AVMetadataObjectTypeFace];
self.metadataOutput.metadataObjectTypes = metadataObjectTypes;
//指定輸出的元數(shù)據(jù)類型。
dispatch_queue_t mainqueue = dispatch_get_main_queue();
[self.metadataOutput setMetadataObjectsDelegate:self queue:mainqueue];
//有新的元數(shù)據(jù)被檢測到時,會都回調(diào)代理AVCaptureMetadataOutputObjectsDelegate中的方法
//可以自定義系列的調(diào)度隊列,不過由于人臉檢測用到硬件加速,而且許多人物都要在主線程中執(zhí)行,所以需要為這個參數(shù)指定主隊列。
- 2、設(shè)置回調(diào)代理方法
#pragma -- mark AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputMetadataObjects:(NSArray *)metadataObjects
fromConnection:(AVCaptureConnection *)connection {
//metadataObjects 就是人臉檢測結(jié)果的元數(shù)據(jù),
//包含多個人臉數(shù)據(jù)信息,可以做相應(yīng)處理,
// 比如將要實現(xiàn)的,在人臉上畫框標記。
}
- 3、開始會話和結(jié)束會話
- (void)startSession {
if (![self.captureSession isRunning]) {
dispatch_async(self.videoQueue, ^{
[self.captureSession startRunning];
});
}
}
- (void)stopSession {
if ([self.captureSession isRunning]) {
dispatch_async(self.videoQueue, ^{
[self.captureSession stopRunning];
});
}
}
-
4、設(shè)置必要的預(yù)覽層
視頻預(yù)覽層,和將要標記人臉的數(shù)據(jù)集合以及標記人臉方框的父layer。
self.faceLayers = [NSMutableDictionary dictionary];
// 存放人臉數(shù)據(jù):@{faceId:layer}
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.overlayLayer = [CALayer layer];
self.overlayLayer.frame = self.bounds;
self.overlayLayer.sublayerTransform = THMakePerspectiveTransform(10000);
//設(shè)置sublayerTransform屬性為CATransform3D,可以對所有子層應(yīng)用視角轉(zhuǎn)換。
[self.previewLayer addSublayer:self.overlayLayer];
static CATransform3D THMakePerspectiveTransform(CGFloat eyePosition) {
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1.0/eyePosition;
return transform;
// CoreAnimation中所使用的transformation matrix類型,用于進行縮放和旋轉(zhuǎn)等轉(zhuǎn)換。
// 設(shè)置m34可以應(yīng)用視角轉(zhuǎn)換,即讓子層繞Y軸旋轉(zhuǎn)。
}
- 5、元數(shù)據(jù)處理
NSArray *transformedFaces = [self transformedFacesFromFaces:faces];
// Listing 7.11
NSMutableArray *lostFaces = [self.faceLayers.allKeys mutableCopy];
for (AVMetadataFaceObject *face in transformedFaces) {
NSNumber *faceId = @(face.faceID);
[lostFaces removeObject:faceId];
// 如果對應(yīng)faceId還在,將它從要刪除視圖的數(shù)組中移除。
CALayer *layer = self.faceLayers[faceId];
// 查找faceId對應(yīng)的Layer
if (!layer) {
//如果沒有對應(yīng)layer,說明是新加入的faceId,需要新建對應(yīng)來layer
layer = [self makeFaceLayer];
[self.overlayLayer addSublayer:layer];
self.faceLayers[faceId] = layer;
}
layer.transform = CATransform3DIdentity;
//對每個人臉圖層,先將他的tansform屬性設(shè)置為CATransform3DIdentity
//然后重新設(shè)置之前的用過的變換
layer.frame = face.bounds;
}
// 刪除已經(jīng)移除人臉對應(yīng)的圖層
for (NSNumber *faceId in lostFaces) {
CALayer *layer = self.faceLayers[faceId];
[layer removeFromSuperlayer];
[self.faceLayers removeObjectForKey:faceId];
}
將取得的人臉元數(shù)據(jù)的坐標做相應(yīng)轉(zhuǎn)換。
- (NSArray *)transformedFacesFromFaces:(NSArray *)faces {
// Listing 7.11
NSMutableArray *transformedFaces = [[NSMutableArray alloc] init];
for (AVMetadataObject *face in faces) {
AVMetadataObject *transformedFace = [self.previewLayer transformedMetadataObjectForMetadataObject:face];
//將設(shè)備坐標空間的人臉對象轉(zhuǎn)化為視圖空間對象集合
[transformedFaces addObject:transformedFace];
//得到一個由AVMetadataFaceObject實例組成的集合,其中有創(chuàng)建用戶界面所需要的坐標點
}
return transformedFaces;
}
// 創(chuàng)建標記人臉的方框
- (CALayer *)makeFaceLayer {
CALayer *layer= [CALayer layer];
layer.borderWidth = 5.0f;
layer.borderColor = [UIColor colorWithRed:0.188 green:0.517 blue:0.877 alpha:1.0].CGColor;
return layer;
}
這樣基本已經(jīng)實現(xiàn)了人臉識別,以及標記功能。本書作者還對這個功能做了拓展,實現(xiàn)角度檢測的變換。相對比較復(fù)雜。
拓展
這里的拓展,增加了對標記人臉方框的角度變換實現(xiàn),它會隨著人臉的轉(zhuǎn)動和傾斜,方框也發(fā)生相應(yīng)的變換。
- 1、在上述元數(shù)據(jù)處理方法中加入方框角度變換的是實現(xiàn)方法即可
添加在for循環(huán)之中,創(chuàng)建重置完layer.transform之后。
if (face.hasRollAngle) {
//檢測人臉是否具有有效的傾斜角,如果沒有獲取屬性會有異常。
//如果有rollAngle,則獲取相應(yīng)的CATransform3D
//將它與標識變換關(guān)聯(lián)在一起,并設(shè)置圖層的transform屬性
CATransform3D t = [self transformForRollAngle:face.rollAngle];
layer.transform = CATransform3DConcat(layer.transform, t);
}
if (face.hasYawAngle) {
//檢測人臉是否具有有效的偏轉(zhuǎn)角,如果沒有獲取屬性會有異常。
//如果有hasYawAngle,則獲取相應(yīng)的CATransform3D
//將它與標識變換關(guān)聯(lián)在一起,并設(shè)置圖層的transform屬性
CATransform3D t = [self transformForYawAngle:face.hasYawAngle];
layer.transform = CATransform3DConcat(layer.transform, t);
}
- 2、Z軸的角度
// Rotate around Z-axis
- (CATransform3D)transformForRollAngle:(CGFloat)rollAngleInDegrees {
CGFloat rollAngleInRadians = THDegreesToRadians(rollAngleInDegrees);
//從對象得到rollAngle的單位是度,需要轉(zhuǎn)換為弧度制。
//將轉(zhuǎn)換結(jié)果賦值給CATransform3DMakeRotation函數(shù)
//x,y,z軸對應(yīng)參數(shù)分別以0,0,1,得到的就是繞Z軸的傾斜角旋轉(zhuǎn)轉(zhuǎn)換、
return CATransform3DMakeRotation(rollAngleInRadians, 0.f, 0.f, 1.f);
}
- 3、Y軸的角度
// Rotate around Y-axis
- (CATransform3D)transformForYawAngle:(CGFloat)yawAngleInDegrees {
// Listing 7.13
//從對象得到hasYawAngle的單位是度,需要轉(zhuǎn)換為弧度制。
//將轉(zhuǎn)換結(jié)果賦值給CATransform3DMakeRotation函數(shù)
//x,y,z軸對應(yīng)參數(shù)分別以0,-1,0,得到的就是繞Y軸的傾斜角旋轉(zhuǎn)轉(zhuǎn)換、
CGFloat yawAngleInRadians = THDegreesToRadians(yawAngleInDegrees);
CATransform3D yawAngleTransform = CATransform3DMakeRotation(yawAngleInRadians, 0.f, -1.f, 0.f);
//由于overlayer需要應(yīng)用sublayerTransform,圖層hi投影到Z軸
//人臉從一次移動到另一側(cè)時就會出現(xiàn)3D效果
return CATransform3DConcat(yawAngleTransform, [self orientationTransform]);
//應(yīng)用程序用戶界面固定為垂直方向,不過需要為設(shè)備方向計算一個相應(yīng)的旋轉(zhuǎn)變換。
//如果不這樣做,會導(dǎo)致人臉圖層的便宜效果不正確,這一轉(zhuǎn)換會同其他變換關(guān)聯(lián)。
}
- 4、設(shè)備方向的調(diào)整
- (CATransform3D)orientationTransform {
// Listing 7.13
CGFloat angle = 0.0;
switch ([UIDevice currentDevice].orientation) {
case UIDeviceOrientationPortraitUpsideDown:
angle = M_PI;
break;
case UIDeviceOrientationLandscapeRight:
angle = -M_PI;
break;
case UIDeviceOrientationLandscapeLeft:
angle = M_PI;
break;
case UIDeviceOrientationPortrait:
angle = 0;
break;
default:
break;
}
return CATransform3DMakeRotation(angle, 0.f, 0.f, 1.f);
// return CATransform3DIdentity;
}
角度變換:
static CGFloat THDegreesToRadians(CGFloat degrees) {
// Listing 7.13
return degrees * M_PI / 180;
}
三、總結(jié)
只是實現(xiàn)簡單地人臉識別,如果要實現(xiàn)更多的功能,相關(guān)的還有CoreAnimation以及Quartz框架的知識需要了解學習,還在努力中~~~