IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> GPUImage实现人脸实时识别 -> 正文阅读

[移动开发]GPUImage实现人脸实时识别

最近在研究OC的生物活检方面的实现,发现SDK中自带有相应的功能类,则进行了调研与实现。

实现过程中发现一个比较坑人的一个地方,就是GPUIMAGE这个框架里面对于视频采集使用的YUV格式,而YUV格式无法与OC的类库进行配合实现实时识别。

现在我们来剖析一下GPUImageVideoCamera的实现:

?

@interface GPUImageVideoCamera : GPUImageOutput <AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate>


- (id)initWithSessionPreset:(NSString *)sessionPreset cameraPosition:(AVCaptureDevicePosition)cameraPosition;

可以看到提供了一个初始化方法,此初始化方法内部的代码如下:?

- (id)initWithSessionPreset:(NSString *)sessionPreset cameraPosition:(AVCaptureDevicePosition)cameraPosition; 
{
	if (!(self = [super init]))
    {
		return nil;
    }
    
    cameraProcessingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
	audioProcessingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0);

    frameRenderingSemaphore = dispatch_semaphore_create(1);

	_frameRate = 0; // This will not set frame rate unless this value gets set to 1 or above
    _runBenchmark = NO;
    capturePaused = NO;
    outputRotation = kGPUImageNoRotation;
    internalRotation = kGPUImageNoRotation;
    captureAsYUV = YES;
    _preferredConversion = kColorConversion709;
    
	// Grab the back-facing or front-facing camera
    _inputCamera = nil;
	NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
	for (AVCaptureDevice *device in devices) 
	{
		if ([device position] == cameraPosition)
		{
			_inputCamera = device;
		}
	}
    
    if (!_inputCamera) {
        return nil;
    }
    
	// Create the capture session
	_captureSession = [[AVCaptureSession alloc] init];
	
    [_captureSession beginConfiguration];
    
	// Add the video input	
	NSError *error = nil;
	videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_inputCamera error:&error];
	if ([_captureSession canAddInput:videoInput]) 
	{
		[_captureSession addInput:videoInput];
	}
	
	// Add the video frame output	
	videoOutput = [[AVCaptureVideoDataOutput alloc] init];
	[videoOutput setAlwaysDiscardsLateVideoFrames:NO];
    
//    if (captureAsYUV && [GPUImageContext deviceSupportsRedTextures])
    if (captureAsYUV && [GPUImageContext supportsFastTextureUpload])
    {
        BOOL supportsFullYUVRange = NO;
        NSArray *supportedPixelFormats = videoOutput.availableVideoCVPixelFormatTypes;
        for (NSNumber *currentPixelFormat in supportedPixelFormats)
        {
            if ([currentPixelFormat intValue] == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
            {
                supportsFullYUVRange = YES;
            }
        }
        
        if (supportsFullYUVRange)
        {
            [videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
            isFullYUVRange = YES;
        }
        else
        {
            [videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
            isFullYUVRange = NO;
        }
    }
    else
    {
        [videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
    }
    
    runSynchronouslyOnVideoProcessingQueue(^{
        
        if (captureAsYUV)
        {
            [GPUImageContext useImageProcessingContext];
            //            if ([GPUImageContext deviceSupportsRedTextures])
            //            {
            //                yuvConversionProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString:kGPUImageVertexShaderString fragmentShaderString:kGPUImageYUVVideoRangeConversionForRGFragmentShaderString];
            //            }
            //            else
            //            {
            if (isFullYUVRange)
            {
                yuvConversionProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString:kGPUImageVertexShaderString fragmentShaderString:kGPUImageYUVFullRangeConversionForLAFragmentShaderString];
            }
            else
            {
                yuvConversionProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString:kGPUImageVertexShaderString fragmentShaderString:kGPUImageYUVVideoRangeConversionForLAFragmentShaderString];
            }

            //            }
            
            if (!yuvConversionProgram.initialized)
            {
                [yuvConversionProgram addAttribute:@"position"];
                [yuvConversionProgram addAttribute:@"inputTextureCoordinate"];
                
                if (![yuvConversionProgram link])
                {
                    NSString *progLog = [yuvConversionProgram programLog];
                    NSLog(@"Program link log: %@", progLog);
                    NSString *fragLog = [yuvConversionProgram fragmentShaderLog];
                    NSLog(@"Fragment shader compile log: %@", fragLog);
                    NSString *vertLog = [yuvConversionProgram vertexShaderLog];
                    NSLog(@"Vertex shader compile log: %@", vertLog);
                    yuvConversionProgram = nil;
                    NSAssert(NO, @"Filter shader link failed");
                }
            }
            
            yuvConversionPositionAttribute = [yuvConversionProgram attributeIndex:@"position"];
            yuvConversionTextureCoordinateAttribute = [yuvConversionProgram attributeIndex:@"inputTextureCoordinate"];
            yuvConversionLuminanceTextureUniform = [yuvConversionProgram uniformIndex:@"luminanceTexture"];
            yuvConversionChrominanceTextureUniform = [yuvConversionProgram uniformIndex:@"chrominanceTexture"];
            yuvConversionMatrixUniform = [yuvConversionProgram uniformIndex:@"colorConversionMatrix"];
            
            [GPUImageContext setActiveShaderProgram:yuvConversionProgram];
            
            glEnableVertexAttribArray(yuvConversionPositionAttribute);
            glEnableVertexAttribArray(yuvConversionTextureCoordinateAttribute);
        }
    });
    
    [videoOutput setSampleBufferDelegate:self queue:cameraProcessingQueue];
	if ([_captureSession canAddOutput:videoOutput])
	{
		[_captureSession addOutput:videoOutput];
	}
	else
	{
		NSLog(@"Couldn't add video output");
        return nil;
	}
    
	_captureSessionPreset = sessionPreset;
    [_captureSession setSessionPreset:_captureSessionPreset];

// This will let you get 60 FPS video from the 720p preset on an iPhone 4S, but only that device and that preset
//    AVCaptureConnection *conn = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
//    
//    if (conn.supportsVideoMinFrameDuration)
//        conn.videoMinFrameDuration = CMTimeMake(1,60);
//    if (conn.supportsVideoMaxFrameDuration)
//        conn.videoMaxFrameDuration = CMTimeMake(1,60);
    
    [_captureSession commitConfiguration];
    
	return self;
}

注意看此初始化方法:恶心就恶心在这儿了,初始化时,默认给此全局变量设置了一个YES的值。导致后面所有的功能均基于YUV对视频进行采集。

captureAsYUV = YES;

迫于无奈,进行修改,新增如下初始化方法:

?//原初始化方法

- (id)initWithSessionPreset:(NSString *)sessionPreset cameraPosition:(AVCaptureDevicePosition)cameraPosition;

//新增初始化方法

- (id)initWithSessionPreset:(NSString *)sessionPreset cameraPosition:(AVCaptureDevicePosition)cameraPosition yuvColorSpace:(BOOL)yuvColorSpace;

- (id)initWithSessionPreset:(NSString *)sessionPreset cameraPosition:(AVCaptureDevicePosition)cameraPosition yuvColorSpace:(BOOL)yuvColorSpace
{
    if (!(self = [super init]))
    {
        return nil;
    }
    
    cameraProcessingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
    audioProcessingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0);

    frameRenderingSemaphore = dispatch_semaphore_create(1);

    _frameRate = 0; // This will not set frame rate unless this value gets set to 1 or above
    _runBenchmark = NO;
    capturePaused = NO;
    outputRotation = kGPUImageNoRotation;
    internalRotation = kGPUImageNoRotation;
    captureAsYUV = yuvColorSpace;
    _preferredConversion = kColorConversion709;
    
    // Grab the back-facing or front-facing camera
    _inputCamera = nil;
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *device in devices)
    {
        if ([device position] == cameraPosition)
        {
            _inputCamera = device;
        }
    }
    
    if (!_inputCamera) {
        return nil;
    }
    
    // Create the capture session
    _captureSession = [[AVCaptureSession alloc] init];
    
    [_captureSession beginConfiguration];
    
    // Add the video input
    NSError *error = nil;
    videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_inputCamera error:&error];
    if ([_captureSession canAddInput:videoInput])
    {
        [_captureSession addInput:videoInput];
    }
    
    // Add the video frame output
    videoOutput = [[AVCaptureVideoDataOutput alloc] init];
    [videoOutput setAlwaysDiscardsLateVideoFrames:NO];
    
//    if (captureAsYUV && [GPUImageContext deviceSupportsRedTextures])
    if (captureAsYUV && [GPUImageContext supportsFastTextureUpload])
    {
        BOOL supportsFullYUVRange = NO;
        NSArray *supportedPixelFormats = videoOutput.availableVideoCVPixelFormatTypes;
        for (NSNumber *currentPixelFormat in supportedPixelFormats)
        {
            if ([currentPixelFormat intValue] == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
            {
                supportsFullYUVRange = YES;
            }
        }
        
        if (supportsFullYUVRange)
        {
            [videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
            isFullYUVRange = YES;
        }
        else
        {
            [videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
            isFullYUVRange = NO;
        }
    }
    else
    {
        [videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
    }
    
    runSynchronouslyOnVideoProcessingQueue(^{
        
        if (captureAsYUV)
        {
            [GPUImageContext useImageProcessingContext];
            //            if ([GPUImageContext deviceSupportsRedTextures])
            //            {
            //                yuvConversionProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString:kGPUImageVertexShaderString fragmentShaderString:kGPUImageYUVVideoRangeConversionForRGFragmentShaderString];
            //            }
            //            else
            //            {
            if (isFullYUVRange)
            {
                yuvConversionProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString:kGPUImageVertexShaderString fragmentShaderString:kGPUImageYUVFullRangeConversionForLAFragmentShaderString];
            }
            else
            {
                yuvConversionProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString:kGPUImageVertexShaderString fragmentShaderString:kGPUImageYUVVideoRangeConversionForLAFragmentShaderString];
            }

            //            }
            
            if (!yuvConversionProgram.initialized)
            {
                [yuvConversionProgram addAttribute:@"position"];
                [yuvConversionProgram addAttribute:@"inputTextureCoordinate"];
                
                if (![yuvConversionProgram link])
                {
                    NSString *progLog = [yuvConversionProgram programLog];
                    NSLog(@"Program link log: %@", progLog);
                    NSString *fragLog = [yuvConversionProgram fragmentShaderLog];
                    NSLog(@"Fragment shader compile log: %@", fragLog);
                    NSString *vertLog = [yuvConversionProgram vertexShaderLog];
                    NSLog(@"Vertex shader compile log: %@", vertLog);
                    yuvConversionProgram = nil;
                    NSAssert(NO, @"Filter shader link failed");
                }
            }
            
            yuvConversionPositionAttribute = [yuvConversionProgram attributeIndex:@"position"];
            yuvConversionTextureCoordinateAttribute = [yuvConversionProgram attributeIndex:@"inputTextureCoordinate"];
            yuvConversionLuminanceTextureUniform = [yuvConversionProgram uniformIndex:@"luminanceTexture"];
            yuvConversionChrominanceTextureUniform = [yuvConversionProgram uniformIndex:@"chrominanceTexture"];
            yuvConversionMatrixUniform = [yuvConversionProgram uniformIndex:@"colorConversionMatrix"];
            
            [GPUImageContext setActiveShaderProgram:yuvConversionProgram];
            
            glEnableVertexAttribArray(yuvConversionPositionAttribute);
            glEnableVertexAttribArray(yuvConversionTextureCoordinateAttribute);
        }
    });
    
    [videoOutput setSampleBufferDelegate:self queue:cameraProcessingQueue];
    if ([_captureSession canAddOutput:videoOutput])
    {
        [_captureSession addOutput:videoOutput];
    }
    else
    {
        NSLog(@"Couldn't add video output");
        return nil;
    }
    
    _captureSessionPreset = sessionPreset;
    [_captureSession setSessionPreset:_captureSessionPreset];

// This will let you get 60 FPS video from the 720p preset on an iPhone 4S, but only that device and that preset
//    AVCaptureConnection *conn = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
//
//    if (conn.supportsVideoMinFrameDuration)
//        conn.videoMinFrameDuration = CMTimeMake(1,60);
//    if (conn.supportsVideoMaxFrameDuration)
//        conn.videoMaxFrameDuration = CMTimeMake(1,60);
    
    [_captureSession commitConfiguration];
    
    return self;
}

以上代码的上的就是对外暴露初始化时是使用RGB还是YUV格式进行采样。

captureAsYUV = yuvColorSpace;

项目中使用的定义类:

?

#import <Foundation/Foundation.h>
#import <CoreImage/CoreImage.h>
#import <GPUImage.h>

@interface CIFaceFeatureMeta : NSObject

@property (nonatomic,strong) CIFeature *features;

@property (nonatomic,strong) UIImage *featureImage;
@end


@interface NSFaceFeature : NSObject
+ (CGRect)faceRect:(CIFeature*)feature;

- (NSArray<CIFaceFeatureMeta *> *)processFaceFeaturesWithPicBuffer:(CMSampleBufferRef)sampleBuffer
                                            cameraPosition:(AVCaptureDevicePosition)currentCameraPosition;
@end
//
//  NSFaceFeature.m
//  VJFaceDetection
//
//  Created by Vincent·Ge on 2018/6/19.
//  Copyright ? 2018年 Filelife. All rights reserved.
//

#import "NSFaceFeature.h"
#import "GPUImageBeautifyFilter.h"

// Options that can be used with -[CIDetector featuresInImage:options:]

/* The value for this key is an integer NSNumber from 1..8 such as that found in kCGImagePropertyOrientation.  If present, the detection will be done based on that orientation but the coordinates in the returned features will still be based on those of the image. */

typedef NS_ENUM(NSInteger , PHOTOS_EXIF_ENUM) {
    PHOTOS_EXIF_0ROW_TOP_0COL_LEFT          = 1, //   1  =  0th row is at the top, and 0th column is on the left (THE DEFAULT).
    PHOTOS_EXIF_0ROW_TOP_0COL_RIGHT         = 2, //   2  =  0th row is at the top, and 0th column is on the right.
    PHOTOS_EXIF_0ROW_BOTTOM_0COL_RIGHT      = 3, //   3  =  0th row is at the bottom, and 0th column is on the right.
    PHOTOS_EXIF_0ROW_BOTTOM_0COL_LEFT       = 4, //   4  =  0th row is at the bottom, and 0th column is on the left.
    PHOTOS_EXIF_0ROW_LEFT_0COL_TOP          = 5, //   5  =  0th row is on the left, and 0th column is the top.
    PHOTOS_EXIF_0ROW_RIGHT_0COL_TOP         = 6, //   6  =  0th row is on the right, and 0th column is the top.
    PHOTOS_EXIF_0ROW_RIGHT_0COL_BOTTOM      = 7, //   7  =  0th row is on the right, and 0th column is the bottom.
    PHOTOS_EXIF_0ROW_LEFT_0COL_BOTTOM       = 8  //   8  =  0th row is on the left, and 0th column is the bottom.
};


@implementation CIFaceFeatureMeta



@end


@interface NSFaceFeature()
@property (nonatomic, strong) CIDetector *faceDetector;

@end

@implementation NSFaceFeature

- (instancetype)init {
    self = [super init];
    [self loadFaceDetector];
    return self;
}

- (void)loadFaceDetector {
    NSDictionary *detectorOptions = @{CIDetectorAccuracy:CIDetectorAccuracyLow,
                                      CIDetectorTracking:@(YES)};
    self.faceDetector = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:detectorOptions];
}

- (NSArray<CIFaceFeatureMeta *> *)processFaceFeaturesWithPicBuffer:(CMSampleBufferRef)sampleBuffer
                                            cameraPosition:(AVCaptureDevicePosition)currentCameraPosition {
    return [NSFaceFeature processFaceFeaturesWithPicBuffer:sampleBuffer
                                               faceDetector:self.faceDetector
                                             cameraPosition:currentCameraPosition];
}

#pragma mark - Category Function
+ (CGRect)faceRect:(CIFeature*)feature {
    CGRect faceRect = feature.bounds;
    CGFloat temp = faceRect.size.width;
    temp = faceRect.origin.x;
    faceRect.origin.x = faceRect.origin.y;
    faceRect.origin.y = temp;
    return faceRect;
}

+ (NSArray<CIFaceFeatureMeta *> *)processFaceFeaturesWithPicBuffer:(CMSampleBufferRef)sampleBuffer
                                              faceDetector:(CIDetector *)faceDetector
                                            cameraPosition:(AVCaptureDevicePosition)currentCameraPosition {
    
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate);
    
    //从帧中获取到的图片相对镜头下看到的会向左旋转90度,所以后续坐标的转换要注意。
    CIImage *convertedImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer options:(__bridge NSDictionary *)attachments];
    
    if (attachments) {
        CFRelease(attachments);
    }
    
    NSDictionary *imageOptions = nil;
    UIDeviceOrientation curDeviceOrientation = [[UIDevice currentDevice] orientation];
    int exifOrientation;

    BOOL isUsingFrontFacingCamera = currentCameraPosition != AVCaptureDevicePositionBack;
    switch (curDeviceOrientation) {
        case UIDeviceOrientationPortraitUpsideDown:
            exifOrientation = PHOTOS_EXIF_0ROW_LEFT_0COL_BOTTOM;
            break;
        case UIDeviceOrientationLandscapeLeft:
            if (isUsingFrontFacingCamera) {
                exifOrientation = PHOTOS_EXIF_0ROW_BOTTOM_0COL_RIGHT;
            }else {
                exifOrientation = PHOTOS_EXIF_0ROW_TOP_0COL_LEFT;
            }
            
            break;
        case UIDeviceOrientationLandscapeRight:
            if (isUsingFrontFacingCamera) {
                exifOrientation = PHOTOS_EXIF_0ROW_TOP_0COL_LEFT;
            }else {
                exifOrientation = PHOTOS_EXIF_0ROW_BOTTOM_0COL_RIGHT;
            }
            break;
        default:
            exifOrientation = PHOTOS_EXIF_0ROW_RIGHT_0COL_TOP; //值为6。确定初始化原点坐标的位置,坐标原点为右上。其中横的为y,竖的为x,表示真实想要显示图片需要顺时针旋转90度
            break;
    }
    
    //exifOrientation的值用于确定图片的方向
    imageOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:exifOrientation] forKey:CIDetectorImageOrientation];
    
    NSArray<CIFeature *> *lt = [faceDetector featuresInImage:convertedImage options:imageOptions];
    
    NSMutableArray *at = [NSMutableArray arrayWithCapacity:0];
    [lt enumerateObjectsUsingBlock:^(CIFeature * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        CIFaceFeatureMeta *b = [[CIFaceFeatureMeta alloc] init];
        [b setFeatures:obj];
        
        UIImage *portraitImage = [[UIImage alloc] initWithCIImage: convertedImage scale: 1.0 orientation: UIImageOrientationRight];
        
        [b setFeatureImage:portraitImage];
        
        [at addObject:b];
    }];
    return at;
}
@end

页面控制器:

@interface AttendanceBioViewController : UIViewController

- (void)showWithResultsDelegateBlock:(void (^)(NSMutableDictionary *ej))delegateBlock useOtherBlock:(void (^)(void))useOtherBlock;
@end

?

//
//  AttendanceBioViewController.m
//  SGBProject
//
//  Created by carbonzhao on 2022/4/14.
//  Copyright ? 2022 All rights reserved.
//

#import "AttendanceBioViewController.h"
#import "GPUImageBeautifyFilter.h"
#import "NSFaceFeature.h"

@interface AttendanceBioViewController ()<GPUImageVideoCameraDelegate>
{
}
@property (nonatomic, strong) GPUImageStillCamera *videoCamera;
@property (nonatomic, strong) GPUImageView *filterView;
@property (nonatomic, strong) GPUImageBeautifyFilter *beautifyFilter;
@property (strong, nonatomic) NSFaceFeature *faceFeature;
@property (nonatomic, strong) UIImageView *maskView;
@property (strong, nonatomic) UIImage *capturedImage;

@property (strong, nonatomic) UISpinnerAnimationView *animationView;

@property (nonatomic, strong) UILabel *tipsLabel;
@property (nonatomic, strong) UILabel *resultLabel;

@property (nonatomic, assign) BOOL hasSmile;
@property (nonatomic, assign) BOOL leftEyeClosed;
@property (nonatomic, assign) BOOL rightEyeClosed;

@end

@implementation AttendanceBioViewController

- (instancetype)init
{
    if (self = [super init])
    {
        
    }
    return self;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    //GPUImageVideoCamera.m中的修改,YUV格式下无法进行实时人脸识别,如果要进行实时人脸识别,设置为captureAsYUV = NO;否则无法进行人脸实时识别
    [self setupCameraUI];
}



#pragma mark - otherMethod
- (void)setupCameraUI
{
    self.faceFeature = [NSFaceFeature new];
    //YUV格式下无法进行实时人脸识别,如果要进行实时人脸识别,设置为false
    self.videoCamera = [[GPUImageStillCamera alloc] initWithSessionPreset:AVCaptureSessionPresetMedium cameraPosition:AVCaptureDevicePositionFront yuvColorSpace:NO];
    self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
    self.videoCamera.horizontallyMirrorFrontFacingCamera = YES;
    
    WeakSelf(self);
    [NSAutoTimer scheduledTimerWithTimeInterval:2 target:self scheduledBlock:^(NSInteger timerIndex) {
        weakSelf.videoCamera.delegate = self;
    }];
    
    UILabel *lb = [[UILabel alloc] initWithFrame:CGRectMake(0, CG_NAV_BAR_HEIGHT, self.view.size.width, CG_NAV_BAR_HEIGHT)];
    [lb setBackgroundColor:[UIColor clearColor]];
    [lb setText:@"拿起手机,眨眨眼"];
    [lb setFont:[UIFont boldSystemFontOfSize:20]];
    [lb setTextAlignment:NSTextAlignmentCenter];
    [lb setTextColor:[UIColor blackColor]];
    [self setTipsLabel:lb];
    [self.view addSubview:lb];
    
    CGFloat sz = MIN(self.view.bounds.size.width, self.view.bounds.size.height)/2;
    CGRect ft = CGRectMake((self.view.bounds.size.width-sz)/2, lb.bottom+CG_NAV_BAR_HEIGHT, sz, sz);
    UISpinnerAnimationView *c1View = [[UISpinnerAnimationView alloc] initWithFrame:ft];
    [c1View.layer setMasksToBounds:YES];
    [c1View.layer setCornerRadius:sz/2];
    [c1View setAnimationType:UISpinnerAnimationViewAnimationSpinner];
    CGPoint arcCenter = CGPointMake(c1View.frame.size.width / 2, c1View.frame.size.height / 2);
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path addArcWithCenter:arcCenter radius:sz/2 startAngle:0.55 * M_PI endAngle:0.45 * M_PI clockwise:YES];
    
    CAShapeLayer *shapeLayer=[CAShapeLayer layer];
    shapeLayer.path = path.CGPath;
    shapeLayer.fillColor = [UIColor clearColor].CGColor;//填充颜色
    shapeLayer.strokeColor = [UIColor lightGrayColor].CGColor;//边框颜色
    shapeLayer.lineCap = kCALineCapRound;
    shapeLayer.lineWidth = 4;
    [c1View.layer addSublayer:shapeLayer];
    
    [self setAnimationView:c1View];
    [self.view addSubview:c1View];
    
    CGRect fm = CGRectMake(4, 4, c1View.bounds.size.width-8, c1View.bounds.size.height-8);
    self.filterView = [[GPUImageView alloc] initWithFrame:fm];
    self.filterView.fillMode = kGPUImageFillModePreserveAspectRatioAndFill;
//    self.filterView.center = c1View.center;
    [self.filterView.layer setMasksToBounds:NO];
    [self.filterView.layer setCornerRadius:fm.size.height/2];
    [self.filterView setClipsToBounds:YES];
    self.filterView.layer.shouldRasterize = YES;
    self.filterView.layer.rasterizationScale = [UIScreen mainScreen].scale;
    [c1View addSubview:self.filterView];
    
    self.beautifyFilter = [[GPUImageBeautifyFilter alloc] init];
    [self.videoCamera addTarget:self.beautifyFilter];
    [self.beautifyFilter addTarget:self.filterView];
    
    UIView *mview = [[UIView alloc] initWithFrame:ft];
    [mview.layer setMasksToBounds:YES];
    [mview.layer setCornerRadius:ft.size.height/2];
    [mview setClipsToBounds:YES];
    mview.layer.shouldRasterize = YES;
    mview.layer.rasterizationScale = [UIScreen mainScreen].scale;
    [self.view addSubview:mview];

    CGRect myRect = CGRectMake(0,0,ft.size.width, ft.size.height);
    //背景
    path = [UIBezierPath bezierPathWithRoundedRect:mview.bounds cornerRadius:0];
    //镂空
    UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:myRect];
    [path appendPath:circlePath];
    [path setUsesEvenOddFillRule:YES];

    CAShapeLayer *fillLayer = [CAShapeLayer layer];
    fillLayer.path = path.CGPath;
    fillLayer.fillRule = kCAFillRuleEvenOdd;
    fillLayer.fillColor = [UIColor clearColor].CGColor;
    fillLayer.opacity = 0;
    [mview.layer addSublayer:fillLayer];

    UIImageView *icView = [[UIImageView alloc] initWithFrame:CGRectMake(0, -mview.bounds.size.height, mview.bounds.size.width, mview.bounds.size.height)];
    [icView setImage:[UIImage imageNamed:@"scannet"]];
    [icView setAlpha:0.7];
    [mview addSubview:icView];
    [self setMaskView:icView];
    
    lb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, ft.size.width, 40)];
    [lb setBackgroundColor:RGBA(40, 40, 40,0.7)];
    [lb setText:@"没有检测到人脸"];
    [lb setFont:[UIFont systemFontOfSize:12]];
    [lb setTextAlignment:NSTextAlignmentCenter];
    [lb setTextColor:[UIColor whiteColor]];
    lb.hidden = YES;
    self.resultLabel = lb;
    [mview addSubview:lb];
    
    UIButton *cbtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [cbtn setFrame:CGRectMake(20, CG_NAV_BAR_HEIGHT, 20, 20)];
    [cbtn setBackgroundImage:[UIImage imageNamed:@"meeting_reserve_refuse_blue"] forState:UIControlStateNormal];
    [cbtn addTargetActionBlock:^(UIButton * _Nonnull aButton) {
        [weakSelf closeView:nil];
    }];
    [self.view addSubview:cbtn];
    
    [self.videoCamera startCameraCapture];
    
    [self loopDrawLine];
    self.atimer = [NSAutoTimer scheduledTimerWithTimeInterval:2 target:self scheduledBlock:^(NSInteger timerIndex) {
        [weakSelf loopDrawLine];
    } userInfo:nil repeats:YES];
}


-(void)loopDrawLine
{
    UIImageView *readLineView = self.maskView;
    [readLineView setFrame:CGRectMake(0, -readLineView.bounds.size.height, readLineView.bounds.size.width, readLineView.bounds.size.height)];
    [UIView animateWithDuration:2 animations:^
    {
//修改fream的代码写在这里
        CGRect ft = readLineView.frame;
        ft.origin.y += ft.size.height;
        ft.origin.y += ft.size.height;
        readLineView.frame = ft;
    }
    completion:^(BOOL finished)
    {
        
    }];
}


- (void)closeView:(void (^)(void))hideFinishedBlock
{
    [self.atimer invalidate];
    [self.videoCamera stopCameraCapture];
    [self dismissViewControllerAnimated:YES completion:^{
        if (hideFinishedBlock)
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                hideFinishedBlock();
            });
        }
    }];
}

- (void)showWithResultsDelegateBlock:(void (^)(NSMutableDictionary *ej))block useOtherBlock:(void (^)(void))useOtherBlock
{
    [self setDelegateBlock:block];
    [self setUseOtherBlock:useOtherBlock];
}


//向服务器上传采集的照片
-(void)uploadFaceImgWithCompleteBlock:(void(^)(void))completeBlock
{
    WeakSelf(self);
    UIImage *image = self.capturedImage;
    //调用你们自己的服务器接口,成功后调用completeBlock
}

#pragma mark - Face Detection
- (UIImage *)sampleBufferToImage:(CMSampleBufferRef)sampleBuffer
{
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CIImage *ciImage = [CIImage imageWithCVPixelBuffer:imageBuffer];
    CIContext *temporaryContext = [CIContext contextWithOptions:nil];
    CGImageRef videoImage = [temporaryContext createCGImage:ciImage fromRect:CGRectMake(0, 0, CVPixelBufferGetWidth(imageBuffer), CVPixelBufferGetHeight(imageBuffer))];
    UIImage *result = [[UIImage alloc] initWithCGImage:videoImage scale:1.0 orientation:UIImageOrientationUp];
    CGImageRelease(videoImage);
    return result;
}



- (NSMutableArray *)faceFeatureResults:(CIImage *)ciImage
{
    NSNumber *minSize = [NSNumber numberWithFloat:.45];
    NSMutableDictionary *options = [NSMutableDictionary dictionaryWithCapacity:0];
    [options setObject:CIDetectorAccuracyHigh forKey:CIDetectorAccuracy];
    [options setObject:minSize forKey:CIDetectorMinFeatureSize];
    [options setObject:[NSNumber numberWithBool:YES] forKey:CIDetectorSmile];
    [options setObject:[NSNumber numberWithBool:YES] forKey:CIDetectorEyeBlink];
    [options setObject:[NSNumber numberWithBool:YES] forKey:CIDetectorTracking];

    CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:options];
    NSMutableArray<CIFaceFeature *> *faceFeatures = (NSMutableArray<CIFaceFeature *> *) [detector featuresInImage:ciImage options:options];
    
    return faceFeatures;
}

- (void)willOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
    UIImage *resultImage = [self sampleBufferToImage:sampleBuffer];
    CIImage *ciImage = [[CIImage alloc] initWithImage:resultImage];
    WeakSelf(self);
    if (/*self.hasSmile && */(self.leftEyeClosed || self.rightEyeClosed))
    {
        NSArray *faceFeatures = [self faceFeatureResults:ciImage];
        if(faceFeatures.count > 0)
        {
            [self.videoCamera stopCameraCapture];
            self.capturedImage = resultImage;
            self.videoCamera.delegate = nil;
            [self.atimer invalidate];

            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.resultLabel setHidden:YES];

                [weakSelf.animationView startAnimation];

                [weakSelf uploadFaceImgWithCompleteBlock:^{
                    [weakSelf.animationView stopAnimation];
                }];
            });
        }
        else
        {
            self.hasSmile = NO;
            self.leftEyeClosed = NO;
            self.rightEyeClosed = NO;
        }
    }
    else
    {
        NSMutableArray<CIFaceFeature *> *faceFeatures = [self faceFeatureResults:ciImage];
        if (faceFeatures && faceFeatures.count > 0)
        {
            WeakSelf(self);
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.resultLabel setHidden:YES];
            });
            __block BOOL hasSmile=NO;
            __block BOOL leftEyeClosed = NO;
            __block BOOL rightEyeClosed = NO;
            [faceFeatures enumerateObjectsUsingBlock:^(CIFaceFeature *ft, NSUInteger idx, BOOL * _Nonnull stop)
            {
                hasSmile |= ft.hasSmile;
                leftEyeClosed |= ft.leftEyeClosed;
                rightEyeClosed |= ft.rightEyeClosed;
            }];
            
            self.hasSmile = hasSmile;
            self.leftEyeClosed = leftEyeClosed;
            self.rightEyeClosed = rightEyeClosed;
            if (/*hasSmile &&*/ (leftEyeClosed || rightEyeClosed))
            {
                //准备抓拍下一帧做为人脸
            }
            else
            {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [weakSelf.tipsLabel setText:@"请来一个阳光的微笑或眨眨眼"];
                });
            }
        }
        else
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.resultLabel setHidden:NO];
                [self.resultLabel setText:@"没有检测到脸"];
            });
        }
    }
}

@end

使用的动画类:参考以下连接

OC 环形等待UI spiner动画_zhaocarbon的博客-CSDN博客

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:15:26  更:2022-05-08 08:17:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 23:47:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码