服务器之家

服务器之家 > 正文

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

时间:2021-03-15 16:11     来源/作者:Mr_Victory

前言

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

coreimage framework组成

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

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

主要分为三个部分:

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。

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

使用coreimage识别人脸

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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,结果应如下图所示:

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

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

?
1
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坐标系:

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

coreimage坐标系:

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

现在使用下面的代码替换detect()方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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识别出了人脸。

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

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

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

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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函数,插入下面代码并分析一下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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测试一下:

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

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

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

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

?
1
2
3
if (face.hasmouthposition) {
 print("mouth detected")
}

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

总结

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

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

github地址:点击swift版地址oc版地址下载

本地下载:点击swift版地址oc版地址下载

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

原文链接:http://www.jianshu.com/p/e371099f12bd

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
返回顶部