当前位置: 首页 > 工具软件 > Face-Detector > 使用案例 >

Android 的Face Detector实现人脸识别

岳卓君
2023-12-01

 

很多手机图片管理应用都开始集成人脸识别功能。一提到人脸识别,模式识别,滤波,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, nullnullnull);

            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

 

 

 

 

 

 类似资料: