iOS之使用CoreImage进行人脸识别

作者: Mr_Victory | 来源:发表于2016-12-15 00:59 被阅读15075次

    更新 :应各位朋友的需求,补上了OC版本的demo, OC版下载地址
    另外附上 : swift版下载地址

    CoreImage是Cocoa Touch中一个强大的API,也是iOS SDK中的关键部分,不过它经常被忽视。在本篇教程中,我会带大家一起验证CoreImage的人脸识别特性。在开始之前,我们先要简单了解下CoreImage framework 组成

    CoreImage framework组成

    Apple 已经帮我们把image的处理分类好,来看看它的结构:


    core Image.png

    主要分为三个部分:

    • 定义部分:CoreImage 和CoreImageDefines。见名思义,代表了CoreImage 这个框架和它的定义。
    • 操作部分:
      • 滤镜(CIFliter):CIFilter 产生一个CIImage。典型的,接受一到多的图片作为输入,经过一些过滤操作,产生指定输出的图片。
      • 检测(CIDetector):CIDetector 检测处理图片的特性,如使用来检测图片中人脸的眼睛、嘴巴、等等。
      • 特征(CIFeature):CIFeature 代表由 detector处理后产生的特征。
    • 图像部分:
      • 画布(CIContext):画布类可被用与处理Quartz 2D 或者 OpenGL。可以用它来关联CoreImage类。如滤镜、颜色等渲染处理。
      • 颜色(CIColor): 图片的关联与画布、图片像素颜色的处理。
      • 向量(CIVector): 图片的坐标向量等几何方法处理。
      • 图片(CIImage): 代表一个图像,可代表关联后输出的图像。

    在了解上述基本知识后,我们开始通过创建一个工程来带大家一步步验证Core Image的人脸识别特性。

    将要构建的应用

    iOS的人脸识别从iOS 5(2011)就有了,不过一直没怎么被关注过。人脸识别API允许开发者不仅可以检测人脸,也可以检测到面部的一些特殊属性,比如说微笑或眨眼。

    首先,为了了解Core Image的人脸识别技术我们会创建一个app来识别照片中的人脸并用一个方框来标记它。在第二个demo中,让用户拍摄一张照片,检测其中的人脸并检索人脸位置。这样一来,就充分掌握了iOS中的人脸识别,并且学会如何利用这个强大却总被忽略的API。

    话不多说,开搞!

    建立工程(我用的是Xcode8.0)


    这里提供了初始工程,当然你也可以自己创建(主要是为了方便大家)点我下载 用Xcode打开下载后的工程,可以看到里面只有一个关联了IBOutlet和imageView的StoryBoard。

    1.png

    使用CoreImage识别人脸


    在开始工程中,故事板中的imageView组件与代码中的IBOutlet已关联,接下来要编写实现人脸识别的代码部分。在ViewController.swift文件中写下如下代码:

    import UIKit
    import CoreImage // 引入CoreImage
    class ViewController: UIViewController {
        @IBOutlet weak var personPic: UIImageView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            personPic.image = UIImage(named: "face-1")
            // 调用detect
            detect()
    
        }
        //MARK: - 识别面部
        func detect() {
            // 创建personciImage变量保存从故事板中的UIImageView提取图像并将其转换为CIImage,使用Core Image时需要用CIImage
            guard let personciImage = CIImage(image: personPic.image!) else {
                return
            }
            // 创建accuracy变量并设为CIDetectorAccuracyHigh,可以在CIDetectorAccuracyHigh(较强的处理能力)与CIDetectorAccuracyLow(较弱的处理能力)中选择,因为想让准确度高一些在这里选择CIDetectorAccuracyHigh
            let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
            // 这里定义了一个属于CIDetector类的faceDetector变量,并输入之前创建的accuracy变量
            let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
            // 调用faceDetector的featuresInImage方法,识别器会找到所给图像中的人脸,最后返回一个人脸数组
            let faces = faceDetector?.features(in: personciImage)
            // 循环faces数组里的所有face,并将识别到的人脸强转为CIFaceFeature类型
            for face in faces as! [CIFaceFeature] {
                
                print("Found bounds are \(face.bounds)")
                // 创建名为faceBox的UIView,frame设为返回的faces.first的frame,绘制一个矩形框来标识识别到的人脸
                let faceBox = UIView(frame: face.bounds)
                // 设置faceBox的边框宽度为3
                faceBox.layer.borderWidth = 3
                // 设置边框颜色为红色
                faceBox.layer.borderColor = UIColor.red.cgColor
                // 将背景色设为clear,意味着这个视图没有可见的背景
                faceBox.backgroundColor = UIColor.clear
                // 最后,把这个视图添加到personPic imageView上
                personPic.addSubview(faceBox)
                // API不仅可以帮助你识别人脸,也可识别脸上的左右眼,我们不在图像中标识出眼睛,只是给你展示一下CIFaceFeature的相关属性
                if face.hasLeftEyePosition {
                    print("Left eye bounds are \(face.leftEyePosition)")
                }
                
                if face.hasRightEyePosition {
                    print("Right eye bounds are \(face.rightEyePosition)")
                }
            }
        }
    }
    

    编译并运行app,结果应如下图所示:

    2.png 根据控制台的输出来看,貌似识别器识别到了人脸:
    Found bounds are (314.0, 243.0, 196.0, 196.0)

    当前的实现中没有解决的问题:

    • 人脸识别是在原始图像上进行的,由于原始图像的分辨率比image view要高,因此需要设置image view的content mode为aspect fit(保持纵横比的情况下缩放图片)。为了合适的绘制矩形框,需要计算image view中人脸的实际位置与尺寸
    • 还要注意的是,CoreImage与UIView使用两种不同的坐标系统(看下图),因此要实现一个CoreImage坐标到UIView坐标的转换。

    UIView坐标系:

    UIView坐标系 CoreImage坐标系:
    CoreImage坐标系
    现在使用下面的代码替换detect()方法:
    func detect1() {
    
        guard let personciImage = CIImage(image: personPic.image!) else { return }
        let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
        let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
        let faces = faceDetector?.features(in: personciImage)
            
        // 转换坐标系
        let ciImageSize = personciImage.extent.size
        var transform = CGAffineTransform(scaleX: 1, y: -1)
        transform = transform.translatedBy(x: 0, y: -ciImageSize.height)
            
        for face in faces as! [CIFaceFeature] {
            print("Found bounds are \(face.bounds)")     
            // 应用变换转换坐标
            var faceViewBounds = face.bounds.applying(transform)
            // 在图像视图中计算矩形的实际位置和大小
            let viewSize = personPic.bounds.size
            let scale = min(viewSize.width / ciImageSize.width, viewSize.height / ciImageSize.height)
            let offsetX = (viewSize.width - ciImageSize.width * scale) / 2
            let offsetY = (viewSize.height - ciImageSize.height * scale) / 2
                
            faceViewBounds = faceViewBounds.applying(CGAffineTransform(scaleX: scale, y: scale))
            faceViewBounds.origin.x += offsetX
            faceViewBounds.origin.y += offsetY
                
            let faceBox = UIView(frame: faceViewBounds)
            faceBox.layer.borderWidth = 3
            faceBox.layer.borderColor = UIColor.red.cgColor
            faceBox.backgroundColor = UIColor.clear
            personPic.addSubview(faceBox)
                
            if face.hasLeftEyePosition {
                print("Left eye bounds are \(face.leftEyePosition)")
            }
                
            if face.hasRightEyePosition {
                print("Right eye bounds are \(face.rightEyePosition)")
            }
        }
    }
    

    上述代码中,首先使用仿射变换(AffineTransform)将Core Image坐标转换为UIKit坐标,然后编写了计算实际位置与矩形视图尺寸的代码。

    再次运行app,应该会看到人的面部周围会有一个框。OK,你已经成功使用Core Image识别出了人脸。


    3.png

    但是有的童鞋在使用了上面的代码运行后可能会出现方框不存在(即没有识别人脸)这种情况,这是由于忘记关闭Auto Layout以及Size Classes了。 选中storyBoard中的ViewController,选中view下的imageView。然后在右边的面板中的第一个选项卡中找到use Auto Layout ,将前面的✔️去掉就可以了

    4.png

    经过上面的设置后我们再次运行App,就会看到图三出现的效果了。

    构建一个人脸识别的相机应用


    想象一下你有一个用来照相的相机app,照完相后你想运行一下人脸识别来检测一下是否存在人脸。若存在一些人脸,你也许想用一些标签来对这些照片进行分类。我们不会构建一个保存照片后再处理的app,而是一个实时的相机app,因此需要整合一下UIImagePicker类,在照完相时立刻进行人脸识别。

    在开始工程中已经创建好了CameraViewController类,使用如下代码实现相机的功能:

    class CameraViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        @IBOutlet var imageView: UIImageView!
        let imagePicker = UIImagePickerController()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            imagePicker.delegate = self
        }
        
        @IBAction func takePhoto(_ sender: AnyObject) {
            
            if !UIImagePickerController.isSourceTypeAvailable(.camera) {
                return
            }
            
            imagePicker.allowsEditing = false
            imagePicker.sourceType = .camera
            
            present(imagePicker, animated: true, completion: nil)
        }
    
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    
            if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
                imageView.contentMode = .scaleAspectFit
                imageView.image = pickedImage
            }
            
            dismiss(animated: true, completion: nil)
            self.detect()
        }
        
        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            dismiss(animated: true, completion: nil)
        }
    }
    

    前面几行设置UIImagePicker委托为当前视图类,在didFinishPickingMediaWithInfo方法(UIImagePicker的委托方法)中设置imageView为在方法中所选择的图像,接着返回上一视图调用detect函数。

    还没有实现detect函数,插入下面代码并分析一下:

    func detect() {
        let imageOptions =  NSDictionary(object: NSNumber(value: 5) as NSNumber, forKey: CIDetectorImageOrientation as NSString)
        let personciImage = CIImage(cgImage: imageView.image!.cgImage!)
        let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
        let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
        let faces = faceDetector?.features(in: personciImage, options: imageOptions as? [String : AnyObject])
            
        if let face = faces?.first as? CIFaceFeature {
            print("found bounds are \(face.bounds)")
                
            let alert = UIAlertController(title: "提示", message: "检测到了人脸", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "确定", style: UIAlertActionStyle.default, handler: nil))
            self.present(alert, animated: true, completion: nil)
                
            if face.hasSmile {
                print("face is smiling");
            }
                
            if face.hasLeftEyePosition {
                print("左眼的位置: \(face.leftEyePosition)")
            }
                
            if face.hasRightEyePosition {
                print("右眼的位置: \(face.rightEyePosition)")
            }
        } else {
            let alert = UIAlertController(title: "提示", message: "未检测到人脸", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "确定", style: UIAlertActionStyle.default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
    }
    

    这个detect()函数与之前实现的detect函数非常像,不过这次只用它来获取图像不做变换。当识别到人脸后显示一个警告信息“检测到了人脸!”,否则显示“未检测到人脸”。运行app测试一下:

    检测到人脸.png 未检测到人脸.png

    我们已经使用到了一些CIFaceFeature的属性与方法,比如,若想检测人物是否微笑,可以调用.hasSmile,它会返回一个布尔值。可以分别使用.hasLeftEyePosition与.hasRightEyePosition检测是否存在左右眼。

    同样,可以调用hasMouthPosition来检测是否存在嘴,若存在则可以使用mouthPosition属性,如下所示:

    if (face.hasMouthPosition) {
        print("mouth detected")
    }
    

    如你所见,使用Core Image来检测面部特征是非常简单的。除了检测嘴、笑容、眼睛外,也可以调用leftEyeClosed与rightEyeClosed检测左右眼是否睁开,这里就不在贴出代码了。

    总结


    在这篇教程中尝试了CoreImage的人脸识别API与如何在一个相机app中应用它,构建了一个简单的UIImagePicker来选取照片并检测图像中是否存在人物。

    如你所见,Core Image的人脸识别是个强大的API!希望这篇教程能给你提供一些关于这个鲜为人知的iOS API有用的信息。

    点击swift版地址OC版地址下载最终工程, 如果觉得对您有帮助的话,请帮我点个星星哦,您的星星是对我最大的支持。(__) 嘻嘻……**

    相关文章

      网友评论

      • 首席科学家:推荐一份 python深度学习Tensorflow人工智能AI机器学习 视频教程:https://blog.csdn.net/u012201811/article/details/81184113
      • _zdd:有帮助
      • a59d5f2d275f:很详细 谢谢楼主 请问 能单独识别出嘴巴的位置么 谢谢🙏
      • Dealloc:有帮助
      • FallLeaf:您好,想问您一个问题,怎么获取到人脸部分的image呢?
      • 冰三尺:请问楼主有什么参数可以设置精准度吗, 有时会识别不出来?
      • Ego_1973:有没有什么识别人脸并且区分人脸的方法呢
      • 960d2a7a31bd:我在一个英文网站上看了一个跟你这个好像的教程.
        Mr_Victory:@2017I 谢谢你的肯定
        960d2a7a31bd:@Mr_Victory 不过没有你这个写的好!
        Mr_Victory:@2017I 对的
      • 吃蘑菇De大灰狼:CoreImage里面 还有CIDetectorTypeFace 学习了~
      • c566371e26f5:赞赞赞赞赞赞👍👍👍👍👍👍👍👍
      • 80aa2e208880:识别不准啊,好多有图片识别不到人脸,但相片里的人脸很清晰
      • 梵高的老巫婆:楼主 实时识别获取每一帧图片人脸识别的有吗?
      • 球球ol:我赞赏了,我感觉写的很好:smiley: 学习了
        Mr_Victory:@李世达 好的,多谢了,朋友。希望能帮助到你。
      • Love_Ju:横屏的时候
        Mr_Victory:这个主要是为了讲coreImage 进行人脸识别,横屏你可以自己做适配啊。
      • MagicCare:我想问一下那个缩放比例取最小值是什么原理,没搞懂啊
        Mr_Victory:@MagicCare 你自己试一下就知道了
        MagicCare:@Mr_Victory 但是为什么不是max
        Mr_Victory:这个很好理解啊。 就是为了让识别出的人脸区域(即红色方框区域)更加精确啊。
      • shineDeveloper:我想请教,就是怎样在打开imagepicker的时候就可以实时检测到人脸,demo里是拍了照之后检测的
        Mr_Victory:@细雨rain久 可以的
        shineDeveloper:@Mr_Victory 实时检测到人脸,我现在研究用讯飞的sdkhttp://www.xfyun.cn/?ch=bdtg
        Mr_Victory:@细雨rain久 这个我不知道。因为coreImage提供的API就是根据一张照片来识别人脸的,你可以自己查找相关资料,试一试。
      • 7d25a114db11:你好,受教了。想问一下这个能识别人脸数量吗,比如给出一张照片,识别上面有多少人。
        Mr_Victory:@FlyOstrich 这个可以的,我那个demo不就可以吗
      • Captain_XR:人脸识别只是识别人脸吗?不能区分男女的脸,或者像指纹那样区分出每个人吗?:blush:
        Mr_Victory:@游侠零零柒 这个并不能区分男女,也不能区分具体是哪个人。苹果原生没有这样的api。
      • 343cea0f7107:请问是否能检测出人的手?比如拍一张手心的照片,能否识别出这就是人的手心?
        Mr_Victory:@JcccZ 这个识别不了。网上说可以用OpenCV提取手掌特征点,貌似不能识别手心。 什么应用有这样的需求? 识别手心有什么用处吗?
        343cea0f7107:@Mr_Victory 这方面一片懵逼,谢谢啦:blush:
        Mr_Victory:@JcccZ coreImage没有检测人手的API,所以这个暂时实现不了。我在查查资料
      • 6ea54dc08ca1:有没有类似用人脸进行 先注册 在识别的那样的demo呢 谢谢
        Mr_Victory:@天佑love清清 暂时还没有哦
      • 来宝:楼主你这个能分辨出是真人还是照片不?
        Mr_Victory:@来宝 这种区分太高深了,你可以看看知乎上关于你这个问题的说法 https://www.zhihu.com/question/20984707
        来宝:@Mr_Victory 就是活体人和人的照片能不能区分
        Mr_Victory:@来宝 你这个问题不好回答。真人和照片你是怎么区分的,要说具体。比如说我用手机照了张相片,那么这张相片是真人呢,还是照片呢?
      • Mariosss:这个文章是你翻译的吗?
        Mr_Victory:@Mariosss 你说的这个暂未做到。估计实现起来比较麻烦。 因为这个识别是将一张照片转换成CIImage , 然后再进行识别。 而调用摄像头,在没有拍照的时候是取不出图片的, 那么就转换不了CIImage,从而识别不出人脸。
        Mariosss:@Mr_Victory 这个能直接调用摄像头的时候识别吗 就像贴图相机的那种
        Mr_Victory:@Mariosss 对,看国外一个学习网站上的内容,翻译了,并加上了自己的东西在里面。
      • GOOGxu:希望有OC :clap:
        GOOGxu:@Mr_Victory :clap: :clap:
        Mr_Victory:@霍GOOG旭 OC版本的已经上传至gitHub上面了,在文章的开头已经附上了下载地址。
      • 1012b281fac1:厉害,学一学这方面的,MARK
        Mr_Victory:@名字的问题 可以的,希望对你有所帮助
      • 其实你懂De:没有OC的吗 楼主
        Mr_Victory:@其实你懂的 不辛苦,哈哈。 OC版本的已经上传至gitHub上面了,在文章的开头已经附上了下载地址。
        其实你懂De:@Mr_Victory 辛苦 楼主
        Mr_Victory:@其实你懂的 我会尽快把OC的demo更新至github上面,请耐心等待
      • iOS_愛OS:建议你再git上贴上简书文章的地址
        Mr_Victory:@iOS_愛OS 好的,多谢提醒
      • iOS_愛OS:星星送上
        Mr_Victory:@iOS_愛OS 好的,非常感谢
      • iOS_愛OS:很详细……
        Mr_Victory:@iOS_愛OS :smile: ,共同学习

      本文标题:iOS之使用CoreImage进行人脸识别

      本文链接:https://www.haomeiwen.com/subject/ctmimttx.html