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
修正后的马赛克效果与人脸检测效果:
参考资料:
https://developer.apple.com/library/mac/documentation/graphicsimaging/conceptual/CoreImaging/ci_intro/ci_intro.html
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:人脸 ...
- 人脸检测--Supervised Transformer Network for Efficient Face Detection
Supervised Transformer Network for Efficient Face Detection ECCV2016 人脸检测: the cascaded network:end- ...
- 人脸检测--FaceBoxes: A CPU Real-time Face Detector with High Accuracy
FaceBoxes: A CPU Real-time Face Detector with High Accuracy 人脸检测已经研究了很多年,有很多算法.但是目前基于深度学习CNN网络的人脸检测算 ...
- 使用OpenCV进行人脸检测(Viola-Jones人脸检测方法)
扩展阅读: OpenCV用于人脸检测 参考文献:Paul Viola, Michael J. Jones. Robust Real-Time Face Detection[J]. Internatio ...
- Day 12: OpenCV —— Java开发者的人脸检测
今天我准备学习如何用Java来进行人脸检测.人脸检测有助于在任何数字图像上识别人脸,在做了一些研究后,我发现OpenCV的库可以帮我检测图像中的人脸.不过,我没能找到一个完整的通过Java使用Open ...
- 【机器学习】最容易实现的基于OpenCV的人脸检测代码、检测器及检测效果
基于opencv自带的人脸检测模型,实现简单的人脸检测功能,可作为机器学习初学者练手使用.简单易学,具体的方法及代码如下. 1.运行结果 输入原图 输出结果 2.工程需要加载的opencv库如下: 3 ...
- Android NDK开发——人脸检测与静默活体检测
前言 1.开发环境是win10,IDE是Android studio 北极狐,用到的库有NCNN,OpenCV. 2.NCNN库可以用官方编译好的releases库,也可以按官方文档自己编译. 3.O ...
- OpenCV3实现人脸识别(一)——基于OpenCV3级联分类器实现人脸检测与眼睛检测
前言 1.OpenCV官方训练好的人脸和眼睛的级联分类器,3.30的版本都放在opencv\sources\data这个文件夹下,在OpenCV这个文件夹中,主要有 Haar特征 和 LBP特征进行人 ...
最新文章
- LeetCode Generate Parentheses
- Paxos与zookeeper
- python函数调用位置_Python: 浅谈函数局部变量快在哪
- SAP UI5 jQuery.sap.getModulePath 的工作原理
- 牛客题霸 [数组中出现次数超过一半的数字] C++题解/答案
- vue 删除两个集合中相同的数据_vue.js如何删除数组里面的数据
- ARM 编译 phddns
- Kernel Method核方法—应用与理解
- 一个时代的落幕!继苹果、火狐、Linux Lite之后,微软也放弃Flash
- 欠薪投诉竟然要3个月才有结果,这办事效率……
- 如何破解一个正版软件只有三十天的方法
- 华为移动wifi显示无服务器,华为移动wifi设置方法
- js设计模式-状态模式-示例(高压锅状态)
- Microsemi Libero系列教程(全网首发)
- MSDC 4.3 接口规范(13)
- 闲谈IPv6-源IP地址的选择(RFC3484读后感)
- 收件人、寄件人如何根据快递单号查询物流进度
- 六面阿里天猫,已拿offer,我的面经复盘总结,原来进大厂没那么难了
- 少年,这里有5本Python3爬虫书
- Olympiad(求区间内的美丽数)超详细 (C,C++)
热门文章
- java计算器or_java计算器
- java outputstream下载_java – Spring OutputStream – 用IE下载pptx
- 中专三年计算机应用专业规划,中专计算机应用职业生涯规划书500字
- 一套完整的大型三甲医院信息管理(HIS)系统源码【免费分享源代码 】
- ppt中的流程图怎么整体移动_ppt里流程图随意移动的动画如何制作?
- 盐城大数据产业园人才公寓_盐城大数据产业园_概况_政策_规划_盐城大数据产业园隶属于市县级类型园区,占地5平方公里_集商网86Links园区招商网。...
- ENVI教程:InSAR技术,基线估算
- c语言自定义函数名称怎么命名,C语言函数名称为什么可以任意更改?
- 第三课 电子计算机的发展与应用 说课稿,计算机的发展与应用说课稿.doc
- 趣说西门子的经济型PLC S7-200SMART和S7-1200