CoreImage系列二:人脸检测

这是CoreImage系列的第二章,主要有三点

1.对静态图片进行人脸检测与打马赛克
2.对摄像头录像进行人脸检测与打马赛克
3.将处理后的视频数据存入本地

技术难点其实并没有多少,主要是记录自己在这个过程中踩过的坑和API熟悉。


人脸检测

CoreImage其实在iOS5的时候就推出了人脸检测功能,但是并不强大;仅仅做到能识别出人脸、五官等等,很多第三方SDK中的人脸检测用到更底层的OpenCV,当然这也更复杂,涉及到很多算法等等。但就我们日常的检测来说,CoreImage提供的就已经足够了。

CIDetector

这个类就是CoreImage中进行检测的类,它可以进行很多检测,人脸只是其中的一种。
它的用法其实很简单,就是输入一张CoreImage,它会自动检测,输入一个检测到的数组。

CIDetector *dectecor = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:nil];
NSArray<CIFeature *> *array =  [detector featuresInImage:image];

这个CIFeature里面就有检测到的信息。

@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;

有检测到的人脸位置,左眼、右眼、嘴的位置。但是,除了脸的位置,其他的都不是特别准确。

检测到人脸之后就简单了,我们先用一个框标记出人脸位置。


检测红框.png

这是什么👻,说好的检测到了喃。
其实这就是第一个坑,检测到的这个人脸位置,并不是真正在imageview的位置,而是在这个图片真实大小的位置。

/** A face feature found by a CIDetector.All positions are relative to the original image. */

图片真实大小.png

因为这张图片宽度为1366,在image中显示是被按比例缩放了的,所以我们相应的要对坐标进行缩放,使之对应到imageview的frame上。
缩小比列

CGFloat scale = imageView.bounds.size.height/imageView.image.size.height;
CGRect frame = CGRectMake(obj.bounds.origin.x * scale,obj.bounds.origin.y * scale, obj.bounds.size.width * scale, obj.bounds.size.height * scale)

你现在会想,这下好了吧。


Snip20170803_3.png

Too young!苹果爸爸会让你这么容易就处理完成么。这个框为什么还是匹配不上啊!!!!
因为在这个bounds的originPoint在左下角,而不是UIKit中的左上角,所以我们还需要对y进行转换。

y = self.imageView.bounds.size.height - obj.bounds.origin.y * scale - obj.bounds.size.height * scale
检测成功.png

OK,现在终于检测成功了。接下来我们上马变骑兵。


打马赛克

CoreImage并没有直接对某一块进行打码处理的filter,我们需要换其他方式。

1.先将整张图进行打码
2.将人脸的地方扣出来
3.将整张打码的图进行人脸的地方mask,类似于view的mask一样
4.然后将mask出来的图片覆盖到原图上去

看起来挺复杂的,但是按着顺序来一步一步走,还是挺简单的。
1、首先,我们对整张图片进行模糊,

CIFilter *pixellateFilter = [CIFilter filterWithName:@"CIPixellate"];
[self.pixellateFilter setValue:image forKey:kCIInputImageKey];
[self.pixellateFilter setValue:@30 forKey:kCIInputScaleKey];
CIImage *pixelImage = pixellateFilter.outputImage;

这个参数30是模糊的程度,可以自由设置。设置的越大,每块马赛克的大小越大。
2、接下来我们将人脸的地方位置标记出来,形成一个“模板”。

CIFilter *radialGradientFileter = [CIFilter filterWithName:@"CIRadialGradient"];
[radialGradientFileter setValue:[CIColor colorWithRed:0 green:1 blue:0 alpha:1] forKey:@"inputColor0"];
[radialGradientFileter setValue:[CIColor colorWithRed:0 green:0 blue:0 alpha:0] forKey:@"inputColor1"];
[radialGradientFileter setValue:@(MIN(obj.bounds.size.width/2, obj.bounds.size.height/2)) forKey:@"inputRadius0"];
[radialGradientFileter setValue:@(MIN(obj.bounds.size.width/2, obj.bounds.size.height/2)+1) forKey:@"inputRadius1"];
CIVector *centerVector = [CIVector vectorWithX:obj.bounds.origin.x + obj.bounds.size.width/2 Y:obj.bounds.origin.y + obj.bounds.size.height/2];
[radialGradientFileter setValue:centerVector forKey:kCIInputCenterKey];

我们形成了一张无色背景,在特定的地方生成了一个绿色的圈,这个圈有2层,一层是obj.bounds.size.width/2,一层是obj.bounds.size.width/2+1。通过设置kCIInputCenterKey来设置绿圈的位置。
因为我们这儿只有1个数据,只需要生成一个抠图的地方就行,如果我们检测多张脸的话,还需要用CISourceOverCompositing来将这些抠图合并,形成一张总的抠图模板。
3、mask

CIFilter *blendWithMaskFileter = [CIFilter filterWithName:@"CIBlendWithMask"];
[blendWithMaskFileter setValue:image forKey:kCIInputBackgroundImageKey];
[blendWithMaskFileter setValue:pixelImage forKey:kCIInputImageKey];
[blendWithMaskFileter setValue:radialGradientImage forKey:kCIInputMaskImageKey];
CIImage *endImage = [blendWithMaskFileter outputImage];

将这三张图输入到mask滤镜中,原图是背景图kCIInputBackgroundImageKey,打码的图是输入图kCIInputImageKey,抠图是kCIInputMaskImageKey。
这样,最后输出的就是将人脸打码的图片。


打码图.png

这样我们就能将我们处理过的视频直接写入到本地了。Demo在这里。

本来准备将摄像头的也写入这一篇,发现有点长了,重新看一篇吧。

参考文章

About Core Image

CIRadialGradient

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容