iOS之使用CoreImage进行人脸识别

2018-09-04更新: 好久没有更新文章了,工做之余花时间看了以前写的这篇文章并运行了以前写的配套Demo,经过打印人脸特征CIFaceFeature的属性,发现识别的效果并非很好,具体说明见文章最底部的更新标题,后续我将分别用OpenCV(跨平台计算机视觉库) 和 Vision (iOS 11新API)两种库实现人脸面部识别,敬请期待~~
OC版下载地址, swift版下载地址

CoreImage是Cocoa Touch中一个强大的API,也是iOS SDK中的关键部分,不过它常常被忽视。在本篇教程中,我会带你们一块儿验证CoreImage的人脸识别特性。在开始以前,咱们先要简单了解下CoreImage framework 组成 CoreImage framework组成 Apple 已经帮咱们把image的处理分类好,来看看它的结构: 在这里插入图片描述swift

主要分为三个部分:数组

1.定义部分:CoreImage 和CoreImageDefines。见名思义,表明了CoreImage 这个框架和它的定义。 2.操做部分:app

滤镜(CIFliter):CIFilter 产生一个CIImage。典型的,接受一到多的图片做为输入,通过一些过滤操做,产生指定输出的图片。 检测(CIDetector):CIDetector 检测处理图片的特性,如使用来检测图片中人脸的眼睛、嘴巴、等等。 特征(CIFeature):CIFeature 表明由 detector处理后产生的特征。框架

3.图像部分:ide

画布(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。code

在这里插入图片描述

使用CoreImage识别人脸orm

在开始工程中,故事板中的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坐标系:

在这里插入图片描述

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识别出了人脸。 在这里插入图片描述

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

在这里插入图片描述 通过上面的设置后咱们再次运行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测试一下:

在这里插入图片描述 在这里插入图片描述

咱们已经使用到了一些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版地址下载最终工程, 若是以为对您有帮助的话,请帮我点个星星哦,您的星星是对我最大的支持。(__) 嘻嘻……** 更新: 好久没有更新文章了,工做之余花时间回顾了以前写的这篇文章并运行了以前写的配套Demo,经过打印人脸特征CIFaceFeature的属性(以下),发现识别的效果并非很好,以下图:

在这里插入图片描述

人脸特征CIFaceFeature的属性

/** CIDetector发现的脸部特征。
  全部的位置都是相对于原始图像. */
NS_CLASS_AVAILABLE(10_7, 5_0)
@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;
}

/** coordinates of various cardinal points within a face.
 脸部各个基点的坐标。

 Note that the left eye is the eye on the left side of the face
 from the observer's perspective. It is not the left eye from
 the subject's perspective.
请注意,左眼是脸左侧的眼睛从观察者的角度来看。 这不是左眼主体的视角.
 */
@property (readonly, assign) CGRect bounds;              // 指示图像坐标中的人脸位置和尺寸的矩形。
@property (readonly, assign) BOOL hasLeftEyePosition;    // 指示检测器是否找到了人脸的左眼。
@property (readonly, assign) CGPoint leftEyePosition;    // 左眼的坐标
@property (readonly, assign) BOOL hasRightEyePosition;   // 指示检测器是否找到了人脸的右眼。
@property (readonly, assign) CGPoint rightEyePosition;   // 右眼的坐标
@property (readonly, assign) BOOL hasMouthPosition;      // 指示检测器是否找到了人脸的嘴部
@property (readonly, assign) CGPoint mouthPosition;      // 嘴部的坐标

@property (readonly, assign) BOOL hasTrackingID;         // 指示面部对象是否具备跟踪ID。
/**
 * 关于trackingID:
 * coreImage提供了在视频流中检测到的脸部的跟踪标识符,您可使用该标识符来识别在一个视频帧中检测到的CIFaceFeature对象是在先前视频帧中检测到的同一个脸部。
 * 只有在框架中存在人脸而且不与特定人脸相关联时,该标识符才会一直存在。若是脸部移出视频帧并在稍后返回到帧中,则分配另外一个ID。 (核心图像检测面部,但不识别特定的面部。)
 * 这个有点抽象
 */
@property (readonly, assign) int trackingID;
@property (readonly, assign) BOOL hasTrackingFrameCount; // 指示面部对象的布尔值具备跟踪帧计数。
@property (readonly, assign) int trackingFrameCount;     // 跟踪帧计数

@property (readonly, assign) BOOL hasFaceAngle;          // 指示是否有关于脸部旋转的信息可用。
@property (readonly, assign) float faceAngle;            // 旋转是以度数逆时针测量的,其中零指示在眼睛之间画出的线相对于图像方向是水平的。

@property (readonly, assign) BOOL hasSmile;              // 是否有笑脸
@property (readonly, assign) BOOL leftEyeClosed;         // 左眼是否闭上
@property (readonly, assign) BOOL rightEyeClosed;        // 右眼是否闭上

问题:那么如何让人脸识别的效果更好呢? 如何让面部识别点更加精确呢?有没有别的方法呢? 答案是确定的。 如今市面上有不少成熟的面部识别产品:

Face++, 收费

Video++,收费

ArcFace 虹软人脸认知引擎, 收费

百度云人脸识别, 收费

阿里云识别, 收费

等等, 咱们看到都是收费的。 固然这些sdk是能够试用的。若是你有折腾精神,想本身尝试人脸识别的实现,咱们能够一块儿交流。 毕竟市面上的这些sdk也不是一开始就有的, 也是经过人们不断研究开发出来的。 并且本身折腾过程当中,经过不断地遇坑爬坑,对知识的理解更加深透,本身的技术也会有增进,不是吗? 很差意思,有点扯远了。 Core Image只是简单的图像识别, 并不能对流中的人脸进行识别。 它只适合对图片的处理。比较有名的OpenCV(跨平台计算机视觉库)就能够用来进行面部识别,识别精度天然很高。还有就是iOS 11.0+ 推出的Vision框架(让咱们轻松访问苹果的模型,用于面部检测、面部特征点、文字、矩形、条形码和物体)也能够进行面部识别。后面我将会用这两个框架讲解如何进行面部识别。敬请期待!!!

相关文章
相关标签/搜索