转载自 http://www.cnblogs.com/mainroadlee/p/android_sdk_face_detection.html
用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中读取图片资源
1
|
Bitmap sampleBmp=BitmapFactory.decodeResource(getResources(), R.drawable.sample1);
|
或者直接从手机的图片库读取(Album/Gallery)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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进行一次格式转换。
1
|
Bitmap tmpBmp = inputImage.copy(Bitmap.Config.RGB_565,
true
);
|
图片格式没问题了,我们来创建一个FaceDetector的实例。FaceDetector是能从一张图中找出多个人脸的,可以通过设置MAX_FACES来控制搜索人脸的个数(我的程序里把MAX_FACES设成了1,只找出一个可信度最高的人脸)。
1
|
FaceDetector faceDet =
new
FaceDetector(tmpBmp.getWidth(), tmpBmp.getHeight(), MAX_FACES);
|
1
2
|
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中的人脸数据,我们可以得到一个以两眼间距为边长,中心在两眼中点的一个正方形。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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,以及图片,人脸框的按比例缩放。这里贴一下大概的代码,示例代码你可以在文末的链接里下载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
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开发教程网站
再次说下,由于原图挂了,最近又比较忙,还没能及时补图,还有现在也有很多成熟的脸部检测方案了,比如免费的脸型检测SDK网站Face++,需要进阶功能的童靴可以自行登录face++.com去建立更加专业的脸型检测。