人脸识别,看似一个很高深的东西,但是苹果官方接口提供了一些基本的API供大家使用,普通需求完全够用,如果你要从底层自己实现或者有其它一些要求更高的需求就另辟蹊径吧。
1.使用CoreImage的API拿到图片的人脸特征信息
// 图像识别能力:可以在CIDetectorAccuracyHigh(较强的处理能力)与CIDetectorAccuracyLow(较弱的处理能力)中选择,因为想让准确度高一些在这里选择CIDetectorAccuracyHigh
NSDictionary *opts = [NSDictionary dictionaryWithObject:
CIDetectorAccuracyHigh forKey:CIDetectorAccuracy];
// 将图像转换为CIImage
CIImage *faceImage = [CIImage imageWithCGImage:image.CGImage];
CIDetector *faceDetector = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:opts];
// 识别出人脸特征信息数组
NSArray *features = [faceDetector featuresInImage:faceImage];
for (CIFaceFeature *faceFeature in features){
//faceFeature就是识别出的人脸特征信息
}
2.计算修正坐标轴信息
CoreImage的坐标轴和UIKit的坐标轴是不一样的。前者的原点是在视图左下角,x、y轴向右和向上递增;后者的原点是在视图左上角,x、y轴向右和向下递增。所以在UIKit上想拿到人脸特征信息使用时,必须先修正坐标轴数据。而如何计算也需要看图片展示时的内容模式是啥,
UIViewContentModeScaleAspectFit
UIViewContentModeScaleAspectFill
UIViewContentModeScaleToFill
它们的计算方法都是不一样的,下面就距离第一个来讲讲,其他两个显示策略不太常见,且第一个使用对了后其它两个也没啥问题了。
a.翻转人脸识别结果区域数据的y轴
//注意这里是原始图片尺寸,跟屏幕显示大小不一样,需要处理
CGSize inputImageSize = [faceImage extent].size;
//将image沿y轴对称
// CGAffineTransformMakeScale(-1.0, 1.0);//水平翻转
// CGAffineTransformMakeScale(1.0,-1.0);//垂直翻转
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, -1);//CGAffineTransformIdentity是恒等变换
//将图片上移
transform = CGAffineTransformTranslate(transform, 0, -inputImageSize.height);
//获取人脸的frame,仿射变换后的值
CGRect faceViewBounds = CGRectApplyAffineTransform(faceFeature.bounds, transform);
首先使用CGAffineTransformScale(CGAffineTransformIdentity, 1, -1)
将原有坐标系的y轴被翻转,这样一来y轴信息就变成UIKit坐标轴体系下的y轴方向,现在需要将x轴从图片的下方移动到图片的顶部,使用CGAffineTransformTranslate(transform, 0, -inputImageSize.height)
将变换信息向上移动图片高度即可。这里比较难理解,y轴关于x轴翻转问题有不明白可以看看这篇博客,这样就得到了将CoreImage坐标系变成UIKit坐标系的变换信息transform
上图就是原始坐标,翻转y轴后的坐标,高度修正后的坐标,到这里,只是把CoreImage的坐标信息拿到UIKit坐标系下使用,但是这个坐标信息是原始图片大小的信息,而我们现在使用了UIViewContentModeScaleAspectFit
,大小是经过原始图片缩放后得到的,所以需要进一步修正坐标信息。
b.修正图片缩放带来的坐标差值
CGSize viewSize = _imageView.bounds.size;
//由于imageview使用的是UIViewContentModeScaleAspectFit长边铺满,比例自适应图片,所以取较小比例
CGFloat scale = MIN(viewSize.width / inputImageSize.width,
viewSize.height / inputImageSize.height);
CGFloat offsetX = (viewSize.width - inputImageSize.width * scale) * 0.5;//修正如果是图片高度撑满图片视图的x值(空白区域修正)
CGFloat offsetY = (viewSize.height - inputImageSize.height * scale) * 0.5;//修正如果是图片宽度撑满图片视图的y值(空白区域修正)
// 缩放参数信息
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scale, scale);
// 修正
faceViewBounds = CGRectApplyAffineTransform(faceViewBounds,scaleTransform);//修正原始坐标信息
faceViewBounds.origin.x += offsetX;//修正如果是图片高度撑满图片视图的x值(空白区域修正)
faceViewBounds.origin.y += offsetY;//修正如果是图片宽度撑满图片视图的y值(空白区域修正)
经过上面代码修正后的rect值就是最后得到的人脸区域坐标值了。
c.眼睛嘴巴的位置计算
有了上面的坐标转换和修正经验,眼睛和嘴巴的位置计算就容易多了,直接上代码:
// 判断是否有左眼位置
if(faceFeature.hasLeftEyePosition){
CGPoint leftEyePoint = CGPointApplyAffineTransform(faceFeature.leftEyePosition, transform);
leftEyePoint = CGPointApplyAffineTransform(leftEyePoint, scaleTransform);
leftEyePoint = CGPointMake(leftEyePoint.x+offsetX, leftEyePoint.y+offsetY);
}
// 判断是否有右眼位置
if(faceFeature.hasRightEyePosition){
CGPoint rightEyePoint = CGPointApplyAffineTransform(faceFeature.rightEyePosition, transform);
rightEyePoint = CGPointApplyAffineTransform(rightEyePoint, scaleTransform);
rightEyePoint = CGPointMake(rightEyePoint.x+offsetX, rightEyePoint.y+offsetY);
}
// 判断是否有嘴位置
if(faceFeature.hasMouthPosition){
CGPoint mouthPoint = CGPointApplyAffineTransform(faceFeature.mouthPosition, transform);
mouthPoint = CGPointApplyAffineTransform(mouthPoint, scaleTransform);
mouthPoint = CGPointMake(mouthPoint.x+offsetX, mouthPoint.y+offsetY);
}
上面就是人脸识别API使用的难点了,你也可以直接下载demo体验理解。 一起学习,共同进步!