用AndroidSDK中的Face Detector实现人脸识别
很多手机图片管理应用都开始集成人脸识别功能。一提到人脸识别,模式识别,滤波,BlahBlah 一堆复杂的技术名字戳入脑海中,立刻觉得这玩意儿没法碰,太玄乎了。其实Android SDK从1.0版本中(API level 1)就已经集成了简单的人脸识别功能,通过调用FaceDetector 我们可以在Android平台上实现Bitmap多人脸识别(一张图中有多个人脸出现的话)。周五啦,我就简简单单写写,希望感兴趣的同学对这个深藏在Android SDK中的功能有所了解。

流程是这样的:
1. 读取一张图片至Bitmap (从Resource中,或是从手机相册中选取)
2. 使用FaceDetector API分析Bitmap,将探测到的人脸数据以FaceDetector.Face存储在一个Face list中;
3.将人脸框显示在图片上。
Step 1: 读取图片
从Drawable中读取图片资源
Bitmap sampleBmp=BitmapFactory.decodeResource(getResources(), R.drawable.sample1);
或者直接从手机的图片库读取(Album/Gallery)
private void readPictureFromAlbum()
{
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
"Select Picture"), ALBUM_REQUEST_CODE);
} @Override
protected void onActivityResult(int requestCode,int resultCode,Intent data){
super.onActivityResult(requestCode, resultCode, data); if (requestCode == ALBUM_REQUEST_CODE && resultCode == RESULT_OK && null != data) {
Uri selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA }; Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
cursor.close();
Bitmap galleryBmp=BitmapFactory.decodeFile(picturePath);
//placeholderFragment.detectFaces(galleryBmp);
}
}
当然,也可以直接从摄像头读取(Camera Capture)。但我读摄像头返回图片的代码在模拟器上运行正常,而在三星的手机上Bug多多,后来看了下确实不少人遇到读取三星手机摄像头报错的问题。所以这段代码我就先不贴了。
好了,我们拿到了Bitmap,识别起来!
Step 2: 通过FaceDetector API进行人脸识别
FaceDetecor只能读取RGB 565格式的Bitmap,所以在开始识别前,我们需要将上面得到的Bitmap进行一次格式转换。
Bitmap tmpBmp = inputImage.copy(Bitmap.Config.RGB_565, true);
图片格式没问题了,我们来创建一个FaceDetector的实例。FaceDetector是能从一张图中找出多个人脸的,可以通过设置MAX_FACES来控制搜索人脸的个数(我的程序里把MAX_FACES设成了1,只找出一个可信度最高的人脸)。
FaceDetector faceDet = new FaceDetector(tmpBmp.getWidth(), tmpBmp.getHeight(), MAX_FACES);
FaceDetector.Face[] faceList = new FaceDetector.Face[MAX_FACES];
faceDet.findFaces(tmpBmp, faceList);
通过调用FaceDetector 的findFaces方法,我们可以找到tmpBmp中的人脸数据,并存储在FaceDetector.Face 数组里(facelist)。
其实通过查看FaceDetector API文档我们发现,它查找人脸的原理是:找眼睛。它返回的人脸数据face,通过调用public float eyesDistance (),public void getMidPoint (PointF point),我们可以得到探测到的两眼间距,以及两眼中心点位置(MidPoint)。public float confidence () 可以返回该人脸数据的可信度(0~1),这个值越大,该人脸数据的准确度也就越高。
通过读取保存在Face中的人脸数据,我们可以得到一个以两眼间距为边长,中心在两眼中点的一个正方形。
for (int i=0; i < faceList.length; i++) {
FaceDetector.Face face = faceList[i];
Log.d("FaceDet", "Face ["+face+"]");
if (face != null) {
Log.d("FaceDet", "Face ["+i+"] - Confidence ["+face.confidence()+"]");
PointF pf = new PointF();
//getMidPoint(PointF point);
//Sets the position of the mid-point between the eyes.
face.getMidPoint(pf);
Log.d("FaceDet", "\t Eyes distance ["+face.eyesDistance()+"] - Face midpoint ["+pf.x+"&"+pf.y+"]");
RectF r = new RectF();
r.left = pf.x - face.eyesDistance() / 2;
r.right = pf.x + face.eyesDistance() / 2;
r.top = pf.y - face.eyesDistance() / 2;
r.bottom = pf.y + face.eyesDistance() / 2;
faceRects[i] = r;
detectedFaces++;
}
}
有了这组RectF,把它显示在图片上,我们就大功告成了。
Step3:对原图进行缩放,并在图上显示人脸框。
自然,这里我们需要使用一个自定义的View。我把它命名为FaceView,每当FaceView人脸检测完成,如果检测到人脸,则invalidate一下(这样才能调用View 的 onDraw方法),然后在onDraw里,将人脸框显示出来。这里涉及到自定义View,以及图片,人脸框的按比例缩放。这里贴一下大概的代码,示例代码你可以在文末的链接里下载。
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint imgPaint = new Paint();
if(inputImage!=null)
{
int imgWidth=inputImage.getWidth();
int imgHeight=inputImage.getHeight();
Rect src = new Rect();// 图片
src.top=0;
src.left=0;
src.right=src.left+imgWidth;
src.bottom=src.top+imgHeight;
Rect dst = new Rect();// 屏幕
int viewWidth=this.getWidth();
int width=0;
int height=0;
if(inputImage.getWidth()>viewWidth)
{
width=viewWidth;
height=(viewWidth*imgHeight)/imgWidth;
}
else
{
width=imgWidth;
height=imgHeight;
}
dst.top=0;
dst.left=0;
dst.right=dst.left+width;
dst.bottom=dst.top+height;
canvas.drawBitmap(inputImage, src, dst, imgPaint);
Log.v("FaceView","view width:"+this.getWidth());
if(detected)
{
Paint rectPaint = new Paint();
rectPaint.setStrokeWidth(2);
rectPaint.setColor(Color.RED);
rectPaint.setStyle(Paint.Style.STROKE);
//float scaleRatio=((float)width)/(float)imgWidth;
for (int i=0; i < detectedFaces; i++) {
RectF r = faceRects[i];
Log.v("FaceView","r.top="+r.top);
r.top=(r.top*width)/imgWidth;
r.left=(r.left*width)/imgWidth;
r.right=(r.right*width)/imgWidth;
r.bottom=(r.bottom*width)/imgWidth;
if (r != null)
canvas.drawRect(r, rectPaint);
}
detected=false;
detectedFaces=0;
}
}
}
注意:FaceDetector搜索人脸的过程是比较耗时的,尤其当图片Size较大(例如640*480)时,耗时个一两秒是很常见的。为防止程序长时间没相应报错,人脸检测部分我使用了AsyncTask


p.s 感谢下 公下 エリカ 清纯的图片ㅋㅋㅋ
注意:FaceDetector做些简单的人脸识别还可以,要是需要专业,快速,甚至和数据库比对匹配的那种高级人脸识别算法,可以试试OpenCV的Android开发包 http://opencv.org/platforms/android.html
Sample代码下载:
https://www.dropbox.com/s/3vz252c9olipnjv/FaceDetectionTutorialProject.zip
http://www.mobiletuts.me 一个及时更新的Android开发教程网站
用AndroidSDK中的Face Detector实现人脸识别的更多相关文章
- AdaBoost中利用Haar特征进行人脸识别算法分析与总结1——Haar特征与积分图
原地址:http://blog.csdn.net/watkinsong/article/details/7631241 目前因为做人脸识别的一个小项目,用到了AdaBoost的人脸识别算法,因为在网上 ...
- 基于iOS用CoreImage实现人脸识别
2018-09-04更新: 很久没有更新文章了,工作之余花时间看了之前写的这篇文章并运行了之前写的配套Demo,通过打印人脸特征CIFaceFeature的属性,发现识别的效果并不是很好,具体说明见文 ...
- Python的开源人脸识别库:离线识别率高达99.38%
Python的开源人脸识别库:离线识别率高达99.38% github源码:https://github.com/ageitgey/face_recognition#face-recognitio ...
- Python的开源人脸识别库:离线识别率高达99.38%(附源码)
Python的开源人脸识别库:离线识别率高达99.38%(附源码) 转https://cloud.tencent.com/developer/article/1359073 11.11 智慧上云 ...
- 免费人脸识别APi
今天对应一些免费的人脸识别的api 做了一下简单的对比,觉得百度开发出来的人脸识别接口还是最符合的我的要求,简单易用,容易上手. 据说百度的一些门禁也使用上了人脸识别的功能了,功能很强大,而且能识别出 ...
- face recognition[翻译][深度人脸识别:综述]
这里翻译下<Deep face recognition: a survey v4>. 1 引言 由于它的非侵入性和自然特征,人脸识别已经成为身份识别中重要的生物认证技术,也已经应用到许多领 ...
- Yale数据库上的人脸识别
一.问题分析 1. 问题描述 在Yale数据集上完成以下工作:在给定的人脸库中,通过算法完成人脸识别,算法需要做到能判断出测试的人脸是否属于给定的数据集.如果属于,需要判断出测试的人脸属于数据集中的哪 ...
- OpenCV学习(37) 人脸识别(2)
在前面一篇教程中,我们学习了OpenCV中基于特征脸的人脸识别的代码实现,我们通过代码 Ptr<FaceRecognizer> model = createEigenFaceR ...
- Faces人脸识别项目简介
Faces人脸识别 分为两个模块,Faces文件夹下存放人脸识别算法的代码,Web文件夹下存放网站搭建的代码 详情请查看各个模块下的readme文档 项目简介 核心算法 一款基于Dlib.opencv ...
随机推荐
- mongo学习-group操作以及java代码
原数据: /* 1 */ { "_id" : ObjectId("552a330e05c27486b9b9b650"), "_class" ...
- 如何打开Tango的ADF文件?
3ds max? opengl? ... Excel? vs? UltraEdit OpenGL Android API ADF文件数据结构:链接
- Linux 基础教程 26-基础网络配置
基本配置 要想上网,计算机需要有专门的网络连接设备,即网络接口卡或者网卡.网卡按照与计算机主机的连接方式可以分为PCI网卡.ISA网卡及无线网卡(USB网卡)等.在Linux中可以使用命令ls ...
- 洛谷P4556 [Vani有约会]雨天的尾巴(线段树合并)
题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地 ...
- ORACLE ERP 的前世今生
一个伟大的公司必有一个伟大的产品.如果说数据库是ORACLE在上世纪最后二十年赖以起家并奠定江湖地位的旗舰产品,那么,企业应用产品(或曰ERP)则毫无疑问是ORACLE在本世纪初的这近十年,征战疆场. ...
- [leetcode] 6. Balanced Binary Tree
这个题目纠结了一会儿,终于从二叉树转化到AVL了.题目如下: Given a binary tree, determine if it is height-balanced. For this pro ...
- Java Web系列:Java Web 项目基础
1.Java Web 模块结构 JSP文件和AXPX文件类似,路径和URL一一对应,都会被动态编译为单独class.Java Web和ASP.NET的核心是分别是Servlet和IHttpHandle ...
- mysql 按照时间查询
这篇文章转载自http://blog.csdn.net/wangjuan_01/article/details/51726588 今天 select * from 表名 where to_days(时 ...
- leetcode 移除元素
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成 ...
- C# 使用dynamic类型来访问JObject对象
dynamic是C#里面的动态类型,可在未知类型的情况访问对应的属性,非常灵活和方便. 使用Json.Net可以把一个Json字符串转换成一个JObject对象,如果有已知强类型,如果有已知对应的强类 ...