iOS8 Core Image In Swift:人脸检测以及马赛克
iOS8 Core Image In Swift:自动改善图像以及内置滤镜的使用
iOS8 Core Image In Swift:更复杂的滤镜
iOS8 Core Image In Swift:人脸检测以及马赛克
iOS8 Core Image In Swift:视频实时滤镜
Core Image不仅内置了诸多滤镜,还能检测图像中的人脸,不过Core Image只是检测,并非识别,检测人脸是指在图像中寻找符合人脸特征(只要是个人脸)的区域,识别是指在图像中寻找指定的人脸(比如某某某的脸)。Core Image在找到符合人脸特征的区域后,会返回该特征的信息,比如人脸的范围、眼睛和嘴巴的位置等。
人脸检测并标记检测到的区域
- 新建一个Single View Application工程
- 然后在Storyboard里放入UIImageView,ContentMode设置为Aspect Fit
- 将UIImageView连接到VC里
- 放入一个名为“人脸检测”的UIButton,然后连接到VC的faceDetecting方法上
- 关闭Auto Layout以及Size Classes
class ViewController: UIViewController {
@IBOutlet var imageView: UIImageView!
lazy var originalImage: UIImage = {
return UIImage(named: "Image")
}()
lazy var context: CIContext = {
return CIContext(options: nil)
}()
......
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.imageView.image = originalImage
}
然后就可以准备实现faceDetecting方法了。
@IBAction func faceDetecing() {
let inputImage = CIImage(image: originalImage)
let detector = CIDetector(ofType: CIDetectorTypeFace,
context: context,
options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
var faceFeatures: [CIFaceFeature]!
if let orientation: AnyObject = inputImage.properties()?[kCGImagePropertyOrientation] {
faceFeatures = detector.featuresInImage(inputImage,
options: [CIDetectorImageOrientation: orientation]
) as [CIFaceFeature]
} else {
faceFeatures = detector.featuresInImage(inputImage) as [CIFaceFeature]
}
println(faceFeatures)
......
使用kCGImagePropertyOrientation的时候,可能需要导入ImageIO框架 |
- 获取所有的面部特征
- 用bounds实例化一个UIView
- 把View显示出来
@IBAction func faceDetecing() {
let inputImage = CIImage(image: originalImage)
let detector = CIDetector(ofType: CIDetectorTypeFace,
context: context,
options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
var faceFeatures: [CIFaceFeature]!
if let orientation: AnyObject = inputImage.properties()?[kCGImagePropertyOrientation] {
faceFeatures = detector.featuresInImage(inputImage, options: [CIDetectorImageOrientation: orientation]) as [CIFaceFeature]
} else {
faceFeatures = detector.featuresInImage(inputImage) as [CIFaceFeature]
}
println(faceFeatures)
for faceFeature in faceFeatures {
let faceView = UIView(frame: faceFeature.bounds)
faceView.layer.borderColor = UIColor.orangeColor().CGColor
faceView.layer.borderWidth = 2
imageView.addSubview(faceView)
}
}
这样写是否可以呢?如果你运行起来会得到这样的效果:
- 调整transform,让它正过来
- 缩放bounds,让它适配imageView
@IBAction func faceDetecing() {
let inputImage = CIImage(image: originalImage)
let detector = CIDetector(ofType: CIDetectorTypeFace,
context: context,
options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
var faceFeatures: [CIFaceFeature]!
if let orientation: AnyObject = inputImage.properties()?[kCGImagePropertyOrientation] {
faceFeatures = detector.featuresInImage(inputImage, options: [CIDetectorImageOrientation: orientation]) as [CIFaceFeature]
} else {
faceFeatures = detector.featuresInImage(inputImage) as [CIFaceFeature]
}
println(faceFeatures)
// 1.
let inputImageSize = inputImage.extent().size
var transform = CGAffineTransformIdentity
transform = CGAffineTransformScale(transform, 1, -1)
transform = CGAffineTransformTranslate(transform, 0, -inputImageSize.height)
for faceFeature in faceFeatures {
var faceViewBounds = CGRectApplyAffineTransform(faceFeature.bounds, transform)
// 2.
let scaleTransform = CGAffineTransformMakeScale(0.5, 0.5)
faceViewBounds = CGRectApplyAffineTransform(faceViewBounds, scaleTransform)
let faceView = UIView(frame: faceViewBounds)
faceView.layer.borderColor = UIColor.orangeColor().CGColor
faceView.layer.borderWidth = 2
imageView.addSubview(faceView)
}
}
现 在看起来就没有问题了,在第一步里我们放置了一个调整坐标系统的tranform,在第二步对bounds进行了缩放(等同于把x、y、width、 height全部乘以0.5),由于我们知道实际scale是0.5(原图600像素,imageView宽为300像素),就直接写死了0.5,但运行 后出现了一点点偏移:
@IBAction func faceDetecing() {
let inputImage = CIImage(image: originalImage)
let detector = CIDetector(ofType: CIDetectorTypeFace,
context: context,
options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
var faceFeatures: [CIFaceFeature]!
if let orientation: AnyObject = inputImage.properties()?[kCGImagePropertyOrientation] {
faceFeatures = detector.featuresInImage(inputImage, options: [CIDetectorImageOrientation: orientation]) as [CIFaceFeature]
} else {
faceFeatures = detector.featuresInImage(inputImage) as [CIFaceFeature]
}
println(faceFeatures)
// 1.
let inputImageSize = inputImage.extent().size
var transform = CGAffineTransformIdentity
transform = CGAffineTransformScale(transform, 1, -1)
transform = CGAffineTransformTranslate(transform, 0, -inputImageSize.height)
for faceFeature in faceFeatures {
var faceViewBounds = CGRectApplyAffineTransform(faceFeature.bounds, transform)
// 2.
var scale = min(imageView.bounds.size.width / inputImageSize.width,
imageView.bounds.size.height / inputImageSize.height)
var offsetX = (imageView.bounds.size.width - inputImageSize.width * scale) / 2
var offsetY = (imageView.bounds.size.height - inputImageSize.height * scale) / 2
faceViewBounds = CGRectApplyAffineTransform(faceViewBounds, CGAffineTransformMakeScale(scale, scale))
faceViewBounds.origin.x += offsetX
faceViewBounds.origin.y += offsetY
let faceView = UIView(frame: faceViewBounds)
faceView.layer.borderColor = UIColor.orangeColor().CGColor
faceView.layer.borderWidth = 2
imageView.addSubview(faceView)
}
}
在第二步里,除了通过宽、高比计算scale外,还计算了x、y轴的偏移,以确保在宽或高缩放的情况下都能正常工作(最后除以2是因为缩放时是居中显示,上下或左右都各有一半)。
面部马赛克
- 基于原图,创建一个将所有部分都马赛克的图片
- 为检测到的人脸创建一张蒙版图
- 用蒙版图,将完全马赛克的图和原图混合起来
创建完全马赛克的图
- 设置inputImage为原图
- 可以根据自己的需要,选择设置inputScale参数,inputScale取值为1到100,取值越大,马赛克就越大
为检测到的人脸创建蒙版图
- 使用CIRadialGradient滤镜创建一个把脸包围起来的圆
- 使用CISourceOverCompositing滤镜把各个蒙版(有几张脸其实就有几个蒙版)组合起来
混合马赛克图、蒙版图以及原图
- 设置inputImage为马赛克图
- 设置inputBackground为原图
- 设置inputMaskImage为蒙版图
@IBAction func pixellated() {
// 1.
var filter = CIFilter(name: "CIPixellate")
println(filter.attributes())
let inputImage = CIImage(image: originalImage)
filter.setValue(inputImage, forKey: kCIInputImageKey)
// filter.setValue(max(inputImage.extent().size.width, inputImage.extent().size.height) / 60, forKey: kCIInputScaleKey)
let fullPixellatedImage = filter.outputImage
// let cgImage = context.createCGImage(fullPixellatedImage, fromRect: fullPixellatedImage.extent())
// imageView.image = UIImage(CGImage: cgImage)
// 2.
let detector = CIDetector(ofType: CIDetectorTypeFace,
context: context,
options: nil)
let faceFeatures = detector.featuresInImage(inputImage)
// 3.
var maskImage: CIImage!
for faceFeature in faceFeatures {
println(faceFeature.bounds)
// 4.
let centerX = faceFeature.bounds.origin.x + faceFeature.bounds.size.width / 2
let centerY = faceFeature.bounds.origin.y + faceFeature.bounds.size.height / 2
let radius = min(faceFeature.bounds.size.width, faceFeature.bounds.size.height)
let radialGradient = CIFilter(name: "CIRadialGradient",
withInputParameters: [
"inputRadius0" : radius,
"inputRadius1" : radius + 1,
"inputColor0" : CIColor(red: 0, green: 1, blue: 0, alpha: 1),
"inputColor1" : CIColor(red: 0, green: 0, blue: 0, alpha: 0),
kCIInputCenterKey : CIVector(x: centerX, y: centerY)
])
println(radialGradient.attributes())
// 5.
let radialGradientOutputImage = radialGradient.outputImage.imageByCroppingToRect(inputImage.extent())
if maskImage == nil {
maskImage = radialGradientOutputImage
} else {
println(radialGradientOutputImage)
maskImage = CIFilter(name: "CISourceOverCompositing",
withInputParameters: [
kCIInputImageKey : radialGradientOutputImage,
kCIInputBackgroundImageKey : maskImage
]).outputImage
}
}
// 6.
let blendFilter = CIFilter(name: "CIBlendWithMask")
blendFilter.setValue(fullPixellatedImage, forKey: kCIInputImageKey)
blendFilter.setValue(inputImage, forKey: kCIInputBackgroundImageKey)
blendFilter.setValue(maskImage, forKey: kCIInputMaskImageKey)
// 7.
let blendOutputImage = blendFilter.outputImage
let blendCGImage = context.createCGImage(blendOutputImage, fromRect: blendOutputImage.extent())
imageView.image = UIImage(CGImage: blendCGImage)
}
我详细的分为了7个部分:
- 用CIPixellate滤镜对原图先做个完全马赛克
- 检测人脸,并保存在faceFeatures中
- 初始化蒙版图,并开始遍历检测到的所有人脸
- 由于我们要基于人脸的位置,为每一张脸都单独创建一个蒙版,所以要先计算出脸的中心点,对应为x、y轴坐标,再基于脸的宽度或高度给一个半径,最后用这些计算结果初始化一个CIRadialGradient滤镜(我将inputColor1的alpha赋值为0,表示将这些颜色值设为透明,因为我不关心除了蒙版以外的颜色,这点和苹果官网中的例子有太一样,苹果将其赋值为了1)
- 由于CIRadialGradient滤镜创建的是一张无限大小的图,所以在使用之前先对它进行裁剪(苹果官网例子中没有对其裁剪。。),然后把每一张脸的蒙版图合在一起
- 用CIBlendWithMask滤镜把马赛克图、原图、蒙版图混合起来
- 输出,在界面上显示
GitHub下载地址
UPDATED:
var scale = min(imageView.bounds.size.width / inputImage.extent().size.width,
imageView.bounds.size.height / inputImage.extent().size.height)
修正radius:
let radius = min(faceFeature.bounds.size.width, faceFeature.bounds.size.height) * scale
修正后的马赛克效果与人脸检测效果:
参考资料:
iOS8 Core Image In Swift:人脸检测以及马赛克的更多相关文章
- iOS8 Core Image In Swift:更复杂的滤镜
iOS8 Core Image In Swift:自动改善图像以及内置滤镜的使用 iOS8 Core Image In Swift:更复杂的滤镜 iOS8 Core Image In Swift:人脸 ...
- iOS8 Core Image In Swift:自动改善图像以及内置滤镜的使用
iOS8 Core Image In Swift:自动改善图像以及内置滤镜的使用 iOS8 Core Image In Swift:更复杂的滤镜 iOS8 Core Image In Swift:人脸 ...
- iOS8 Core Image In Swift:视频实时滤镜
iOS8 Core Image In Swift:自己主动改善图像以及内置滤镜的使用 iOS8 Core Image In Swift:更复杂的滤镜 iOS8 Core Image In Swift: ...
- 用caffe一步一步实现人脸检测
学习深度学习已有一段时间了,总想着拿它做点什么,今天终于完成了一个基于caffe的人脸检测,这篇博文将告诉你怎样通过caffe一步步实现人脸检测.本文主要参考唐宇迪老师的教程,在这里感谢老师的辛勤付出 ...
- Android+openCV人脸检测2(静态图片)
前几篇文章中有提到对openCV环境配置,这里再重新梳理导入和使用openCV进行简单的人脸检测(包括使用级联分类器) 一 首先导入openCVLibrary320 二 设置gradle的sdk版本号 ...
- 基于opencv3.0下的人脸检测和检测部分的高斯模糊处理
如题 这里将任务分解为三大部分: 1.录播放视频 2.人脸检测 3.部分高斯模糊 其中重点放在人脸检测和部分高斯模糊上 1.录播放视频(以opencv中的VideoCapture类进行实现) 首先罗列 ...
- Atitti opencv2.4 实现的人脸检测 attilax总结
Atitti opencv2.4 实现的人脸检测 attilax总结 1.1. 1.OpenCV人脸检测的方法1 1.2. /atiplat_img/src/com/attilax/facedetec ...
- 【计算机视觉】如何使用opencv自带工具训练人脸检测分类器
前言 使用opencv自带的分类器效果并不是很好,由此想要训练自己的分类器,正好opencv有自带的工具进行训练.本文就对此进行展开. 步骤 1.查找工具文件: 2.准备样本数据: 3.训练分类器: ...
- javacv 340使用 人脸检测例子【转载】
Java下使用opencv进行人脸检测 工作需要,研究下人脸识别,发现opencv比较常用,尽管能检测人脸,但识别率不高,多数是用来获取摄像头的视频流的,提取里面的视频帧,实现人脸识别时通常会和其他框 ...
随机推荐
- 【转】深入理解篇UIScrollerView
转自:http://www.mamicode.com/info-detail-1144770.html 接下来,我整理一下自己的思路,深入理解 UIScrollView 基本点 : 1 . UIScr ...
- iOS应用性能调优的25个建议和技巧【转】
转载自:http://blog.jobbole.com/37984/ 首页 最新文章 资讯 程序员 设计 IT技术 创业 在国外 营销 趣文 特别分享 更多 > - Navigation - ...
- js获取上传文件扩展名
File_box.value.substring(File_box.value.lastIndexOf(".") + 1);
- linux服务器伪分布模式安装hadoop-1.1.2
1:环境准备 1台linux服务器, hadoop安装包(apache官方网下载) jdk1.6+ 2:安装jdk ,配置好环境变量(etc/profile),java -version 测 ...
- C# Linq Group By 多个字段并返回给实体类List
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace stud ...
- C++中cin输入类型不匹配解决方法
#include <iostream> #include <set> using namespace std; int main() { int a; cin>>a ...
- hdu5347 MZL's chemistry(打表)
转载请注明出处: http://www.cnblogs.com/fraud/ ——by fraud MZL's chemistry Time Limit: 2000/1000 MS ...
- Apache配置多个监听端口和不同的网站目录的简单方法(转)
转自http://www.waaqi.com/archives/707.html 由于开发的多项目,每个项目又要独立,要用根目录地址. 所以这时候我们需要配置多个不同目录的Apache,如果是外部网可 ...
- encodeURI后台乱码(解决)
window.location.href = xxxx?a=encodeURI(encodeURI(name)) ; name是中文,页面部分需要编码两次 name = java.net.URLDec ...
- 使用 PHP 读取文本(TXT)文件 并分页显示
<?php //----------------you should save this file as m.php---------------- session_start(); if (e ...