CoreImage系列二:人脸检测

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

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

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


人脸检测

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

CIDetector

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

CIDetector *dectecor = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:nil];
NSArray *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;

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

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


CoreImage系列二:人脸检测_第1张图片
检测红框.png

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

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

CoreImage系列二:人脸检测_第2张图片
图片真实大小.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)

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


CoreImage系列二:人脸检测_第3张图片
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
CoreImage系列二:人脸检测_第4张图片
检测成功.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。
这样,最后输出的就是将人脸打码的图片。


CoreImage系列二:人脸检测_第5张图片
打码图.png

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

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

参考文章

About Core Image

CIRadialGradient

你可能感兴趣的:(CoreImage系列二:人脸检测)