iOS技术栈IOSIOS面试裤

选取图像 + 人脸识别

2016-05-13  本文已影响1847人  earthX

今天无意翻到了人脸识别,突然想在ios设备上实现,这里我们主要用到了几个类:CIContext,CIDetector

CIContext

CIContext 是Core Image的一个对象,Core Image是一个OS X和iOS的图像处理框架。通过它Core Image可以绘制一个CIFilter产生的结果。一个Core Image Context可以基于CPU或GPU。

CIDetector

Core Image 已经提供了 CIDetector 类。用它来做人脸检测已经相当好了,并且它已经被优化过,使用起来也很容易:

CIDetector *faceDetector = [CIDetector detectorOfType:CIDetectorTypeFace context:context options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];NSArray *faces = [faceDetector featuresInImage:image];

从该图片中检测到的每一张面孔都在数组 faces 中保存着一个 CIFaceFeature 实例。这个实例中保存着这张面孔的所处的位置和宽高,除此之外,眼睛和嘴的位置也是可选的。

@interface CIFaceFeature : CIFeature
{
    CGRect bounds;
    BOOL hasLeftEyePosition;
    CGPoint leftEyePosition;
    BOOL hasRightEyePosition;
    CGPoint rightEyePosition;
    BOOL hasMouthPosition;
    CGPoint mouthPosition;
    
    
    BOOL hasTrackingID;
    int trackingID;
    BOOL hasTrackingFrameCount;
    int trackingFrameCount;
    
    BOOL hasFaceAngle;
    float faceAngle;
    
    BOOL hasSmile;
    BOOL leftEyeClosed;
    BOOL rightEyeClosed;
}

人脸监测的步骤是:先获取图像,然后转成CIImage格式,利用CIFeature特征,使用探测器CIDetector拿到所有的人脸,然后在图中圈出,即可达到人脸识别的目的,关键代码如下:

//获取图片
    UIImage *image = self.imageview.image;
    //转成CIImage
    CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage];
    //拿到所有的脸
    NSArray <CIFeature *> *featureArray = [self.detector featuresInImage:ciImage];

    if (featureArray.count == 0) {
        NSLog(@"未检测到人脸");
        //初始化提示框;
        UIAlertController *alert1 = [UIAlertController alertControllerWithTitle:@"提示" message:@"未检测到人脸" preferredStyle: UIAlertControllerStyleAlert];
        [alert1 addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action){
            //点击按钮的响应事件;
        }]];
        
        //弹出提示框;
        [self presentViewController:alert1 animated:true completion:nil];
        
    }else{
        //遍历
        for (CIFeature *feature in featureArray){
            
            //将image沿y轴对称
            CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, -1);
            //将image往上移动
            CGFloat imageH = ciImage.extent.size.height;
            transform = CGAffineTransformTranslate(transform, 0, -imageH);
            //在image上画出方框
            CGRect feaRect = feature.bounds;
            //调整后的坐标
            CGRect newFeaRect = CGRectApplyAffineTransform(feaRect, transform);
            //调整imageView的frame
            CGFloat imageViewW = self.imageview.bounds.size.width;
            CGFloat imageViewH = self.imageview.bounds.size.height;
            CGFloat imageW = ciImage.extent.size.width;
            //显示
            CGFloat scale = MIN(imageViewH / imageH, imageViewW / imageW);
            //缩放
            CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scale, scale);
            
            //修正
            newFeaRect = CGRectApplyAffineTransform(newFeaRect, scaleTransform);
            newFeaRect.origin.x += (imageViewW - imageW * scale ) / 2;
            newFeaRect.origin.y += (imageViewH - imageH * scale ) / 2;
            NSLog(@"xxx:%f",newFeaRect.origin.x);
            
            //绘画
            UIView *breageView = [[UIView alloc] initWithFrame:newFeaRect];
            breageView.layer.borderColor = [UIColor redColor].CGColor;
            breageView.layer.borderWidth = 2;
            [self.imageview addSubview:breageView];
        }

    }
监测成功.png 监测失败.jpg

上述图像载入的时候,采用了一个用户头像上传的demo,顺道一起研究了,选取图像可以选择两种途径,第一种是拍照识别,第二种从个人相册中载入,并且将载入后的图像写入沙盒中。

选取图像

@property(nonatomic,strong) UIPopoverController *imagePickerPopover; //

需要注意的是 UIPopoverController ,是iPad开发中常见的一种控制器(在iPhone上不允许使用)
跟其他控制器不一样的是,它直接继承自NSObject,并非继承自UIViewController
它只占用部分屏幕空间来呈现信息,而且显示在屏幕的最前面,但是在IOS9的时候已经被废除。

可以选用IOS8提供的新特性 UIPresentationController,UIPresentationController是提供高级视图切换的类。它让管理present ViewController的过程变得简单。

在iPad的设置页面,可以通过popOver弹出一个UIViewController,这个弹出的,可以和用户交互的Controller叫做PresentedViewController,而后面那个被部分遮挡的UIViewController叫做PresentingViewController,而在UIPresentationController中,PresentedViewController是presentation的content,而PresentingViewController叫做Chrome。

UIViewController *contentController = [[UIViewController alloc] init];
UIPopoverController *poc = [UIPopoverController alloc] initWithContentViewController:contentController];
[poc presentPopoverFromBarButtonItem:item permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

但是这个是ipad的类,如果要创建一个在iPad与iPhone上通用的方法,那么需要以下的代码:

    UIViewController *contentController = [[UIViewController alloc] init];

    if([[UIDevice currentDevice] userInterfaceIdim] == UIUserInterfaceIdiomPad){
        UIPopoverController *poc = [UIPopoverController alloc] initWithContentViewController:contentController];
        [poc presentPopoverFromBarButtonItem:item permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    }else{
       [self presentViewController:contentController animated:YES completion:nil];
    }

然而我们如果使用UIPopoverPresentationController,那么就不再需要判断设备,例如:

    UIViewController *contentController = [[UIViewController alloc] init];

    UIPopoverPresentationController *popVC = contentController.popoverPresentationController;
    popVC.barButtonItem = item;
    popVC.permittedArrowDirections = UIPopoverArrowDirectionAny;
    popVC.delegate = self;

    [self presentViewController:contentController animated:YES completion:nil];

- (UIModalPresentationStyle)adaptivePresentationStyleForPresemtatopmController:(UIPresentationController * controller)
{
    return UIModalPresentationFullScreen;
}

- (UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style
{ 
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller.presentedViewController];
    return navController;

}

  1. 初始化UIImagePickerController 类;
  2. 设置UIImagePickerController 实例的数据来源类型;
  3. 设置设置代理;
  4. 如果需要做图片修改的话设置allowsEditing =yes。

数据来源类型一共有三种:

enum {
   UIImagePickerControllerSourceTypePhotoLibrary ,//来自图库
   UIImagePickerControllerSourceTypeCamera ,//来自相机
   UIImagePickerControllerSourceTypeSavedPhotosAlbum //来自相册
};

在用这些来源的时候最好检测以下设备是否支持;

 if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
    {
        NSLog(@"支持相机");
    }
    if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary])
    {
        NSLog(@"支持图库");
    }
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum])
    {
        NSLog(@"支持相片库");
    }

调用摄像头来获取资源

- (void)viewDidLoad {
    [super viewDidLoad];
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker = [[UIImagePickerController alloc]init];
    picker.view.backgroundColor = [UIColor orangeColor];
    UIImagePickerControllerSourceType sourcheType = UIImagePickerControllerSourceTypeCamera;
    picker.sourceType = sourcheType;
    picker.delegate = self;
    picker.allowsEditing = YES;
}

上面只是实例了UIImagePickerController及其属性 在需要获取图片的时候需要弹出窗口调用

[self presentViewController:picker animated:YES completion:nil];

完整的选取相册代码如下:

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"请选择打开方式" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    [alert addAction:[UIAlertAction actionWithTitle:@"打开相机" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action){
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        //创建UIPopoverController对象前先检查当前设备是不是ipad
        if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
            self.imagePickerPopover = [[UIPopoverController alloc] initWithContentViewController:imagePicker];
            self.imagePickerPopover.delegate = self;
            [self.imagePickerPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
        }else{
            [self presentViewController:imagePicker animated:YES completion:nil];
        }
        
    }]];
    
    [alert addAction:[UIAlertAction actionWithTitle:@"我的相册" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action){
    
        imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        //创建UIPopoverController对象前先检查当前设备是不是ipad
        if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
            self.imagePickerPopover = [[UIPopoverController alloc] initWithContentViewController:imagePicker];
            self.imagePickerPopover.delegate = self;
            [self.imagePickerPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
        }else{
        
            [self presentViewController:imagePicker animated:YES completion:nil];
        }
    }]];
    
    [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *_Nonnull action){
        //取消
    }]];
    
    //弹出提示框
    [self presentViewController:alert animated:YES completion:nil];

本文从人脸识别出发,记录了ios设备进行人脸识别时,使用的CIDetector类,顺带温习了一下拉起相册的相关知识,由UIPopoverController到UIPresentationController,详细记录。

附上传送门:https://github.com/earthX/EXFaceRecognition

上一篇 下一篇

猜你喜欢

热点阅读