选取图像 + 人脸识别

今天无意翻到了人脸识别,突然想在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。

  • UIPopoverPresentationController
    它在iOS8中替代了UIPopoverController,它在功能上与旧的controller完全等同,并且新增了一些内置的适配特性,可以自动适配iPad与iPhone。以下是新版与旧版接口的比较:

  • UIPopoverController使用方法:

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];

  • 将UIViewController的modalPresentationStyle设置成UIModalPresentationPopover,这个值用来实现popover效果,并且各个平台自动适应。第二行中,通过popoverPresentationController属性来获取它的popoverPresentationController,而不是创建一个新的。然后设置它的一些界面属性,最后调用presentViewController方法来显示这个controller。这样就可以在iPad与iPhone显示自动适应的popover效果了。
    其中,iPhone上的自适应是在delegate中实现的:
- (UIModalPresentationStyle)adaptivePresentationStyleForPresemtatopmController:(UIPresentationController * controller)
{
    return UIModalPresentationFullScreen;
}

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

}

  • UIImagePickerController

    UIImagePickerController 是系统提供的用来获取图片和视频的接口,用UIImagePickerController 类来获取图片视频,大体分为以下几个步骤:

  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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,945评论 4 60
  • 想必大家都看过电影《钢铁侠》,小编还是死忠粉呢!电影中除了非常有魅力的小罗伯特.唐尼外,印象最深的就是钢铁侠的人工...
    神秘嘉宾方阅读 622评论 2 2
  • 馬笛阅读 78评论 0 0
  • 第四十六节:好事不出门,坏事传千里! 杜海明的账户爆仓事件在黄河期货公司内部成为了热门话题。人们议论纷纷,有可惜杜...
    luozi阅读 206评论 0 0