iOS利用CoreImage实现人脸识别详解

前言

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

CoreImage framework组成

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

主要分为三个部分:

1、定义部分:CoreImage 和CoreImageDefines。见名思义,代表了CoreImage 这个框架和它的定义。

2、操作部分:

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

3、图像部分:

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

使用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,结果应如下图所示:

根据控制台的输出来看,貌似识别器识别到了人脸:

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有用的信息。

Github地址:点击swift版地址OC版地址下载

本地下载:点击swift版地址,OC版地址下载

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • iOS利用CoreImage实现人脸识别详解

    前言 CoreImage是Cocoa Touch中一个强大的API,也是iOS SDK中的关键部分,不过它经常被忽视.在本篇教程中,我会带大家一起验证CoreImage的人脸识别特性.在开始之前,我们先要简单了解下CoreImage framework 组成 CoreImage framework组成 Apple 已经帮我们把image的处理分类好,来看看它的结构: 主要分为三个部分: 1.定义部分:CoreImage 和CoreImageDefines.见名思义,代表了CoreImage 这个

  • Matlab控制电脑摄像实现实时人脸检测和识别详解

    目录 一.理论基础 二.核心程序 三.仿真测试结果 一.理论基础 人脸识别过程主要由四个阶段组成:人脸检测.图像预处理.面部特征提取和特征识别.首先系统从视频或者相机中捕获图像,检测并分割出其中的人脸区域:接下来通过归一化.对齐.滤波等方法改善图像的质量,这里的质量主要由最终的人脸识别率决定:特征提取(降维)环节尤为重要,其初衷是减少数据量从而减轻计算负担,但良好的特征选取可以降低噪音和不相关数据在识别中的贡献度,从而提高识别精度:特征识别阶段需要根据提取的特征训练一个分类器,对于给定的测试样本

  • iOS指纹登录(TouchID)集成方案详解

    TouchID指纹识别是iPhone 5S设备中增加的一项重大功能.苹果的后续移动设备也相继添加了指纹功能,在实际使用中还是相当方便的,比如快捷登录,快捷支付等等.系统提供了相应框架,使用起来还是比较方便的.使用LAContext对象即可完成指纹识别,提高用户体验. 提示:指纹识别必须用真机测试,并且在iOS8以上系统. TouchID API使用 1.添加头文件 #import 2.判断系统版本 //首先判断版本 if (NSFoundationVersionNumber < NSFounda

  • Python摸鱼神器之利用树莓派opencv人脸识别自动控制电脑显示桌面

    前言 老早就看到新闻员工通过人脸识别监控老板来摸鱼. 有时候摸鱼太入迷了,经常在上班时间玩其他的东西被老板看到.自从在咸鱼上淘了一个树莓派3b,尝试做了一下内网穿透,搭建网站就吃灰了,接下来突发奇想就买了一个摄像头和延长线 接下来就是敲代码了 环境 树莓派3+ python3.7 win7 python3.6 过程 首先树莓派和电脑要在一个内网下面,就是一个路由器下面吧.要在树莓派设置里面开启摄像头,然后安装cv2,cv2有很多依赖库需要手动安装,很是费脑筋.原理介绍一下,人脸识别主要是依赖op

  • 利用Python生成随机验证码详解

    目录 1.先搞环境 2.开始码代码 3. 加干扰 4. 加入更多的干扰 5. 验证码 + 随机字符 6. 验证码保存本地(选) 最近感觉被大数据定义成机器人了,随便看个网页都跳验证码. 怎么用python绕验证码是个令人头秃的事情, 我投降!那么今天手把手教大家如何写验证码,去为难别人,让他们头秃. 说错了,其实就是教大家如何通过python代码去生成验证码~~ 1.先搞环境 1.我们需要你电脑有python3.4以上的版本 2.pip安装PIL包 pip install pillow 3.默念

  • Python+MediaPipe实现检测人脸功能详解

    目录 MediaPipe概述 人脸检测 MediaPipe概述 谷歌开源MediaPipe于2019年6月首次推出.它的目标是通过提供一些集成的计算机视觉和机器学习功能,使我们的生活变得轻松. MediaPipe是用于构建多模态(例如视频.音频或任何时间序列数据).跨平台(即eAndroid.IOS.web.边缘设备)应用ML管道的框架. Mediapipe还促进了机器学习技术在各种不同硬件平台上的演示和应用程序中的部署. 应用 人脸检测 多手跟踪 头发分割 目标检测与跟踪 目标:三维目标检测与

  • MySQL数据库设计之利用Python操作Schema方法详解

    弓在箭要射出之前,低声对箭说道,"你的自由是我的".Schema如箭,弓似Python,选择Python,是Schema最大的自由.而自由应是一个能使自己变得更好的机会. Schema是什么? 不管我们做什么应用,只要和用户输入打交道,就有一个原则--永远不要相信用户的输入数据.意味着我们要对用户输入进行严格的验证,web开发时一般输入数据都以JSON形式发送到后端API,API要对输入数据做验证.一般我都是加很多判断,各种if,导致代码很丑陋,能不能有一种方式比较优雅的验证用户数据呢

  • IOS Swift基础之switch用法详解

    IOS  Swift基础之switch用法详解 概述 Swift中的switch语句与Java等语言中的switch有很大的相似点,但是也有不同的地方,并且更加灵活. Swift中switch的case语句中不需要添加break Swift中需要考虑所有情况,default是必要的. case分支可以添加多个条件,用,分割 case不局限与常量,可以使使用范围 switch里可以使用元组 switch默认不需要添加break,执行一个case之后就跳出语句,如果想要继续下面的语句可以使用fall

  • IOS点击按钮隐藏状态栏详解及实例代码

    IOS点击按钮隐藏状态栏详解 前言: 最近学习IOS的基础知识,实现隐藏状态栏的功能,这里就记录下来,希望对大家有所帮助 实例代码: @interface SecondViewController () @property (nonatomic, assign,getter=isHideStatus) BOOL hideStatus; @end @implementation SecondViewController - (void)viewDidLoad { [super viewDidLoa

  • IOS 获取APP 版本号的实例详解

    IOS 获取APP 版本号的实例详解 看代码的时候看到一句,用于获取.plist文件的版本号 labelVersion.text = [NSString stringWithFormat:@"v%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString*)kCFBundleVersionKey]]; 比较感兴趣的是后面的参数 kcFBundleVersionKey ,竟然是CFBundle.h已经定于好的属性,下面有

随机推荐