zoukankan      html  css  js  c++  java
  • Android静态图片人脸识别的完整demo(附完整源码)

     

    Demo功能:利用android自带的人脸识别进行识别,标记出眼睛和人脸位置。点击按键后进行人脸识别,完毕后显示到imageview上。

    第一部分:布局文件activity_main.xml

    1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:id="@+id/layout_main"  
    4.     android:layout_width="match_parent"  
    5.     android:layout_height="match_parent"  
    6.     android:paddingBottom="@dimen/activity_vertical_margin"  
    7.     android:paddingLeft="@dimen/activity_horizontal_margin"  
    8.     android:paddingRight="@dimen/activity_horizontal_margin"  
    9.     android:paddingTop="@dimen/activity_vertical_margin"  
    10.     tools:context=".MainActivity" >  
    11.   
    12.     <TextView  
    13.         android:id="@+id/textview_hello"  
    14.         android:layout_width="wrap_content"  
    15.         android:layout_height="wrap_content"  
    16.         android:text="@string/hello_world" />  
    17.   
    18.     <ImageView  
    19.         android:id="@+id/imgview"  
    20.         android:layout_width="wrap_content"  
    21.         android:layout_height="wrap_content"  
    22.         android:layout_below="@id/textview_hello" />  
    23.   
    24.     <Button  
    25.         android:id="@+id/btn_detect_face"  
    26.         android:layout_width="wrap_content"  
    27.         android:layout_height="wrap_content"  
    28.         android:layout_below="@id/imgview"  
    29.         android:layout_centerHorizontal="true"  
    30.         android:text="检测人脸" />  
    31.   
    32. </RelativeLayout>  


    注意:ImageView四周的padding由布局文件里的这四句话决定:

    1. android:paddingBottom="@dimen/activity_vertical_margin"  
    2. android:paddingLeft="@dimen/activity_horizontal_margin"  
    3. android:paddingRight="@dimen/activity_horizontal_margin"  
    4. android:paddingTop="@dimen/activity_vertical_margin"  


    而上面的两个margin定义在dimens.xml文件里:

    1. <resources>  
    2.   
    3.     <!-- Default screen margins, per the Android Design guidelines. -->  
    4.     <dimen name="activity_horizontal_margin">16dp</dimen>  
    5.     <dimen name="activity_vertical_margin">16dp</dimen>  
    6.   
    7. </resources>  


    这里采用的都是默认的,可以忽略!

    第二部分:MainActivity.java

    1. package org.yanzi.testfacedetect;  
    2.   
    3. import org.yanzi.util.ImageUtil;  
    4. import org.yanzi.util.MyToast;  
    5.   
    6. import android.app.Activity;  
    7. import android.graphics.Bitmap;  
    8. import android.graphics.Bitmap.Config;  
    9. import android.graphics.BitmapFactory;  
    10. import android.graphics.Canvas;  
    11. import android.graphics.Color;  
    12. import android.graphics.Paint;  
    13. import android.graphics.Point;  
    14. import android.graphics.PointF;  
    15. import android.graphics.Rect;  
    16. import android.media.FaceDetector;  
    17. import android.media.FaceDetector.Face;  
    18. import android.os.Bundle;  
    19. import android.os.Handler;  
    20. import android.os.Message;  
    21. import android.util.DisplayMetrics;  
    22. import android.util.Log;  
    23. import android.view.Menu;  
    24. import android.view.View;  
    25. import android.view.View.OnClickListener;  
    26. import android.view.ViewGroup;  
    27. import android.view.ViewGroup.LayoutParams;  
    28. import android.widget.Button;  
    29. import android.widget.ImageView;  
    30. import android.widget.ProgressBar;  
    31. import android.widget.RelativeLayout;  
    32.   
    33. public class MainActivity extends Activity {  
    34.     static final String tag = "yan";  
    35.     ImageView imgView = null;  
    36.     FaceDetector faceDetector = null;  
    37.     FaceDetector.Face[] face;  
    38.     Button detectFaceBtn = null;  
    39.     final int N_MAX = 2;  
    40.     ProgressBar progressBar = null;  
    41.   
    42.     Bitmap srcImg = null;  
    43.     Bitmap srcFace = null;  
    44.     Thread checkFaceThread = new Thread(){  
    45.   
    46.         @Override  
    47.         public void run() {  
    48.             // TODO Auto-generated method stub  
    49.             Bitmap faceBitmap = detectFace();  
    50.             mainHandler.sendEmptyMessage(2);  
    51.             Message m = new Message();  
    52.             m.what = 0;  
    53.             m.obj = faceBitmap;  
    54.             mainHandler.sendMessage(m);  
    55.               
    56.         }  
    57.   
    58.     };  
    59.      Handler mainHandler = new Handler(){  
    60.   
    61.         @Override  
    62.         public void handleMessage(Message msg) {  
    63.             // TODO Auto-generated method stub  
    64.             //super.handleMessage(msg);  
    65.             switch (msg.what){  
    66.             case 0:  
    67.                 Bitmap b = (Bitmap) msg.obj;  
    68.                 imgView.setImageBitmap(b);  
    69.                 MyToast.showToast(getApplicationContext(), "检测完毕");  
    70.                 break;  
    71.             case 1:  
    72.                 showProcessBar();  
    73.                 break;  
    74.             case 2:  
    75.                 progressBar.setVisibility(View.GONE);  
    76.                 detectFaceBtn.setClickable(false);  
    77.                 break;  
    78.             default:  
    79.                 break;  
    80.             }  
    81.         }  
    82.   
    83.     };  
    84.     @Override  
    85.     protected void onCreate(Bundle savedInstanceState) {  
    86.         super.onCreate(savedInstanceState);  
    87.         setContentView(R.layout.activity_main);  
    88.         initUI();   
    89.         initFaceDetect();  
    90.         detectFaceBtn.setOnClickListener(new OnClickListener() {  
    91.   
    92.             @Override  
    93.             public void onClick(View v) {  
    94.                 // TODO Auto-generated method stub  
    95.                 mainHandler.sendEmptyMessage(1);  
    96.                 checkFaceThread.start();  
    97.                   
    98.             }  
    99.         });  
    100.   
    101.   
    102.   
    103.     }  
    104.   
    105.     @Override  
    106.     public boolean onCreateOptionsMenu(Menu menu) {  
    107.         // Inflate the menu; this adds items to the action bar if it is present.  
    108.         getMenuInflater().inflate(R.menu.main, menu);  
    109.         return true;  
    110.     }  
    111.     public void initUI(){  
    112.   
    113.         detectFaceBtn = (Button)findViewById(R.id.btn_detect_face);  
    114.         imgView = (ImageView)findViewById(R.id.imgview);  
    115.         LayoutParams params = imgView.getLayoutParams();  
    116.         DisplayMetrics dm = getResources().getDisplayMetrics();  
    117.         int w_screen = dm.widthPixels;  
    118.         //      int h = dm.heightPixels;  
    119.   
    120.         srcImg = BitmapFactory.decodeResource(getResources(), R.drawable.kunlong);  
    121.         int h = srcImg.getHeight();  
    122.         int w = srcImg.getWidth();  
    123.         float r = (float)h/(float)w;  
    124.         params.width = w_screen;  
    125.         params.height = (int)(params.width * r);  
    126.         imgView.setLayoutParams(params);  
    127.         imgView.setImageBitmap(srcImg);  
    128.     }  
    129.   
    130.     public void initFaceDetect(){  
    131.         this.srcFace = srcImg.copy(Config.RGB_565, true);  
    132.         int w = srcFace.getWidth();  
    133.         int h = srcFace.getHeight();  
    134.         Log.i(tag, "待检测图像: w = " + w + "h = " + h);  
    135.         faceDetector = new FaceDetector(w, h, N_MAX);  
    136.         face = new FaceDetector.Face[N_MAX];  
    137.     }  
    138.     public boolean checkFace(Rect rect){  
    139.         int w = rect.width();  
    140.         int h = rect.height();  
    141.         int s = w*h;  
    142.         Log.i(tag, "人脸 宽w = " + w + "高h = " + h + "人脸面积 s = " + s);  
    143.         if(s < 10000){  
    144.             Log.i(tag, "无效人脸,舍弃.");  
    145.             return false;  
    146.         }  
    147.         else{  
    148.             Log.i(tag, "有效人脸,保存.");  
    149.             return true;      
    150.         }  
    151.     }  
    152.     public Bitmap detectFace(){  
    153.         //      Drawable d = getResources().getDrawable(R.drawable.face_2);  
    154.         //      Log.i(tag, "Drawable尺寸 w = " + d.getIntrinsicWidth() + "h = " + d.getIntrinsicHeight());  
    155.         //      BitmapDrawable bd = (BitmapDrawable)d;  
    156.         //      Bitmap srcFace = bd.getBitmap();  
    157.   
    158.         int nFace = faceDetector.findFaces(srcFace, face);  
    159.         Log.i(tag, "检测到人脸:n = " + nFace);  
    160.         for(int i=0; i<nFace; i++){  
    161.             Face f  = face[i];  
    162.             PointF midPoint = new PointF();  
    163.             float dis = f.eyesDistance();  
    164.             f.getMidPoint(midPoint);  
    165.             int dd = (int)(dis);  
    166.             Point eyeLeft = new Point((int)(midPoint.x - dis/2), (int)midPoint.y);  
    167.             Point eyeRight = new Point((int)(midPoint.x + dis/2), (int)midPoint.y);  
    168.             Rect faceRect = new Rect((int)(midPoint.x - dd), (int)(midPoint.y - dd), (int)(midPoint.x + dd), (int)(midPoint.y + dd));  
    169.             Log.i(tag, "左眼坐标 x = " + eyeLeft.x + "y = " + eyeLeft.y);  
    170.             if(checkFace(faceRect)){  
    171.                 Canvas canvas = new Canvas(srcFace);  
    172.                 Paint p = new Paint();  
    173.                 p.setAntiAlias(true);  
    174.                 p.setStrokeWidth(8);  
    175.                 p.setStyle(Paint.Style.STROKE);  
    176.                 p.setColor(Color.GREEN);  
    177.                 canvas.drawCircle(eyeLeft.x, eyeLeft.y, 20, p);  
    178.                 canvas.drawCircle(eyeRight.x, eyeRight.y, 20, p);  
    179.                 canvas.drawRect(faceRect, p);  
    180.             }  
    181.   
    182.         }  
    183.         ImageUtil.saveJpeg(srcFace);  
    184.         Log.i(tag, "保存完毕");  
    185.           
    186.         //将绘制完成后的faceBitmap返回  
    187.         return srcFace;  
    188.   
    189.     }  
    190.     public void showProcessBar(){  
    191.         RelativeLayout mainLayout = (RelativeLayout)findViewById(R.id.layout_main);  
    192.         progressBar = new ProgressBar(MainActivity.this, null, android.R.attr.progressBarStyleLargeInverse); //ViewGroup.LayoutParams.WRAP_CONTENT  
    193.         RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);  
    194.         params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);  
    195.         params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);  
    196.         progressBar.setVisibility(View.VISIBLE);  
    197.         //progressBar.setLayoutParams(params);  
    198.         mainLayout.addView(progressBar, params);  
    199.           
    200.     }  
    201.   
    202.   
    203. }  


    关于上述代码,注意以下几点:

    1、 在initUI()函数里初始化UI布局,主要是将ImageView的长宽比设置。根据srcImg的长宽比及屏幕的宽度,设置ImageView的宽 度为屏幕宽度,然后根据比率得到ImageView的高。然后将Bitmap设置到ImageView里。一旦设置了ImageView的长和 宽,Bitmap会自动缩放填充进去,所以对Bitmap就无需再缩放了。

    2、 initFaceDetect()函数里初始化人脸识别所需要的变量。首先将Bitmap的ARGB格式转换为RGB_565格式,这是android自 带人脸识别要求的图片格式,必须进行此转化:this.srcFace = srcImg.copy(Config.RGB_565, true);

    然后实例化这两个变量:

    FaceDetector faceDetector = null;
    FaceDetector.Face[] face;

    faceDetector = new FaceDetector(w, h, N_MAX);
    face = new FaceDetector.Face[N_MAX];

    FaceDetector就是用来进行人脸识别的类,face是用来存放识别得到的人脸信息。N_MAX是允许的人脸个数最大值。

    3、真正的人脸识别在自定义的方法detectFace()里,核心代码:faceDetector.findFaces(srcFace, face)。在识别后,通过Face f  = face[i];得到每个人脸f,通过 float dis = f.eyesDistance();得到两个人眼之间的距离,f.getMidPoint(midPoint);得到人脸中心的坐标。下面这两句话得到左右人眼的坐标:

    1. Point eyeLeft = new Point((int)(midPoint.x - dis/2), (int)midPoint.y);  
    2. Point eyeRight = new Point((int)(midPoint.x + dis/2), (int)midPoint.y);  


    下面是得到人脸的矩形:

    1. Rect faceRect = new Rect((int)(midPoint.x - dd), (int)(midPoint.y - dd), (int)(midPoint.x + dd), (int)(midPoint.y + dd));  


    注意这里Rect的四个参数其实就是矩形框左上顶点的x 、y坐标和右下顶点的x、y坐标。

    4、实际应用中发现,人脸识别会发生误判。所以增加函数checkFace(Rect rect)来判断,当人脸Rect的面积像素点太小时则视为无效人脸。这里阈值设为10000,实际上这个值可以通过整个图片的大小进行粗略估计到。

    5、为了让用户看到正在识别的提醒,这里动态添加一个ProgressBar。代码如下:

    1. public void showProcessBar(){  
    2.     RelativeLayout mainLayout = (RelativeLayout)findViewById(R.id.layout_main);  
    3.     progressBar = new ProgressBar(MainActivity.this, null, android.R.attr.progressBarStyleLargeInverse); //ViewGroup.LayoutParams.WRAP_CONTENT  
    4.     RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);  
    5.     params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);  
    6.     params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);  
    7.     progressBar.setVisibility(View.VISIBLE);  
    8.     //progressBar.setLayoutParams(params);  
    9.     mainLayout.addView(progressBar, params);  
    10.   
    11. }  


    事实上这个ProgressBar视觉效果不是太好,用ProgressDialog会更好。这里只不过是提供动态添加ProgressBar的方法。

    6、 程序中设置了checkFaceThread线程用来检测人脸,mainHandler用来控制UI的更新。这里重点说下Thread的构造方法,这里是 模仿源码中打开Camera的方法。如果一个线程只需执行一次,则通过这种方法是最好的,比较简洁。反之,如果这个Thread在执行后需要再次执行或重 新构造,不建议用这种方法,建议使用自定义Thread,程序逻辑会更容易 控制。在线程执行完毕后,设置button无法再点击,否则线程再次start便会挂掉。

    1. Thread checkFaceThread = new Thread(){  
    2.   
    3.     @Override  
    4.     public void run() {  
    5.         // TODO Auto-generated method stub  
    6.         Bitmap faceBitmap = detectFace();  
    7.         mainHandler.sendEmptyMessage(2);  
    8.         Message m = new Message();  
    9.         m.what = 0;  
    10.         m.obj = faceBitmap;  
    11.         mainHandler.sendMessage(m);  
    12.   
    13.     }  
    14.   
    15. };  

    7、看下识别效果:

    原图:


    识别后:

    最后特别交代下,当人眼距离少于100个像素时会识别不出来。如果静态图片尺寸较少,而手机的densityDpi又比较高的话,当图片放在drawable-hdpi文件夹下时会发生检测不到人脸的情况,同样的测试图片放在drawable-mdpi就可以正常检测。原因是不同的文件夹下,Bitmap加载进来后的尺寸大小不一样。

    后续会推出Camera里实时检测并绘制人脸框,进一步研究眨眼检测,眨眼控制拍照的demo,敬请期待。如果您觉得笔者在认真的写博客,请为我投上一票。

    CSDN2013博客之星评选:

    http://vote.blog.csdn.net/blogstaritem/blogstar2013/yanzi1225627

    本文demo下载链接:

    http://download.csdn.net/detail/yanzi1225627/6783575

     

    参考文献:

    链接1:

    链接2:

  • 相关阅读:
    2013年第四届蓝桥杯C/C++B组省赛题目解析
    C++编程基础练习
    网络中TCP、IP、MAC、UDP的头部格式信息
    对C语言中指针的入门理解
    Linux命令_搜索文件
    【总结】牛客职播第九期:您的美团点评offer已送到门口,快来与我们一起影响世界!
    蓝桥杯题库基础练习:进制转换
    SQL存在一个表而不在另一个表中的数据, 更新字段为随机时间
    使用资源文件(内存)中的字体 或 使用文件中的字体
    C# 给某个方法设定执行超时时间 C#如何控制方法的执行时间,超时则强制退出方法执行 C#函数运行超时则终止执行(任意参数类型及参数个数通用版)
  • 原文地址:https://www.cnblogs.com/xgjblog/p/3853647.html
Copyright © 2011-2022 走看看