Android下的人脸识别——FaceDetector的使用

这一篇要记录的是Android平台中的人脸识别技术,这里所说的人脸识别是Android系统内置的人脸识别API——FaceDetector,该API可以通过少量代码完成人脸识别,但是这种识别是最基本的识别,即只能识别出图像中的人脸,仅此而已,不能做更精确的识别(比如判断人脸的身份),下面记录我使用FaceDetector的过程:

首先来一张图:


我们的代码即将完成上图中的功能,选择图片后,点击检测人脸,就会在图片上绘制出人脸的区域来,下面是主要代码:

1、布局文件activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp"
    android:orientation="vertical" >
    
	<Button 
	    android:id="@+id/btn_select"
	    android:layout_width="match_parent"
	    android:layout_height="wrap_content"
	    android:text="选择图片"
	    />    
	    
	<Button 
	    android:id="@+id/btn_detect"
	    android:layout_width="match_parent"
	    android:layout_height="wrap_content"
	    android:text="检测人脸"
	    android:layout_marginTop="5dp"
	    />
	
	<ImageView 
	    android:id="@+id/image"
	    android:layout_width="fill_parent"
	    android:layout_height="fill_parent"
	    android:layout_marginTop="5dp"
	    />

</LinearLayout>
2、MainActivity.java代码

package com.example.testfacedetector;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.media.FaceDetector;
import android.media.FaceDetector.Face;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {
	
	private static final int REQUEST_CODE_SELECT_PIC = 120;
	private static final int MAX_FACE_NUM = 5;//最大可以检测出的人脸数量
	private int realFaceNum = 0;//实际检测出的人脸数量
	
	private Button selectBtn;
	private Button detectBtn;
	private ImageView image;
	private ProgressDialog pd;
	
	private Bitmap bm;//选择的图片的Bitmap对象
	private Paint paint;//画人脸区域用到的Paint
	
	private boolean hasDetected = false;//标记是否检测到人脸

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		initView();
		
		paint = new Paint();
		paint.setColor(Color.BLUE);
		paint.setStrokeWidth(2);
		paint.setStyle(Paint.Style.STROKE);//设置话出的是空心方框而不是实心方块
		
		pd = new ProgressDialog(this);
		pd.setTitle("提示");
		pd.setMessage("正在检测,请稍等");
	}
	
	/**
	 * 控件初始化
	 */
	private void initView(){
		selectBtn = (Button) findViewById(R.id.btn_select);
		selectBtn.setOnClickListener(this);
		detectBtn = (Button) findViewById(R.id.btn_detect);
		detectBtn.setOnClickListener(this);
		image = (ImageView) findViewById(R.id.image);
	}
	
	/**
	 * 从图库选择图片
	 */
	private void selectPicture(){
		Intent intent = new Intent();
		intent.setAction(Intent.ACTION_GET_CONTENT);
		intent.setType("image/*");
		startActivityForResult(intent, REQUEST_CODE_SELECT_PIC);
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if(requestCode == REQUEST_CODE_SELECT_PIC && resultCode == Activity.RESULT_OK){
			//获取选择的图片
			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 selectedImagePath = cursor.getString(columnIndex);
	        bm = BitmapFactory.decodeFile(selectedImagePath);
	        //要使用Android内置的人脸识别,需要将Bitmap对象转为RGB_565格式,否则无法识别
	        bm = bm.copy(Bitmap.Config.RGB_565, true);
	        cursor.close();
	        image.setImageBitmap(bm);
	        hasDetected = false;
		}
	}
	
	/**
	 * 检测人脸
	 */
	private void detectFace(){
		if(bm == null){
			Toast.makeText(this, "请先选择图片", Toast.LENGTH_SHORT).show();
			return ;
		}
		if(hasDetected){
			Toast.makeText(this, "已检测出人脸", Toast.LENGTH_SHORT).show();
		}else{
			new FindFaceTask().execute();
		}
	}
	
	private void drawFacesArea(FaceDetector.Face[] faces){
		Toast.makeText(this, "图片中检测到" + realFaceNum + "张人脸", Toast.LENGTH_SHORT).show();
		float eyesDistance = 0f;//两眼间距
		Canvas canvas = new Canvas(bm);
		for(int i = 0; i < faces.length; i++){
			FaceDetector.Face face = faces[i];
			if(face != null){
				PointF pointF = new PointF();
				face.getMidPoint(pointF);//获取人脸中心点
				eyesDistance = face.eyesDistance();//获取人脸两眼的间距
				//画出人脸的区域
				canvas.drawRect(pointF.x - eyesDistance, pointF.y - eyesDistance, pointF.x + eyesDistance, pointF.y + eyesDistance, paint);
				hasDetected = true;
			}
		}
		//画出人脸区域后要刷新ImageView
		image.invalidate();
	}
	
	/**
	 * 检测图像中的人脸需要一些时间,所以放到AsyncTask中去执行
	 * @author yubo
	 *
	 */
	private class FindFaceTask extends AsyncTask<Void, Void, FaceDetector.Face[]>{
		
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			pd.show();
		}

		@Override
		protected Face[] doInBackground(Void... arg0) {
			//最关键的就是下面三句代码
			FaceDetector faceDetector = new FaceDetector(bm.getWidth(), bm.getHeight(), MAX_FACE_NUM);
			FaceDetector.Face[] faces = new FaceDetector.Face[MAX_FACE_NUM]; 
			realFaceNum = faceDetector.findFaces(bm, faces);
			if(realFaceNum > 0){
				return faces;
			}
			return null;
		}
		
		@Override
		protected void onPostExecute(Face[] result) {
			super.onPostExecute(result);
			pd.dismiss();
			if(result == null){
				Toast.makeText(MainActivity.this, "抱歉,图片中未检测到人脸", Toast.LENGTH_SHORT).show();
			}else{
				drawFacesArea(result);
			}
		}
		
	}

	@Override
	public void onClick(View arg0) {
		switch(arg0.getId()){
			case R.id.btn_select://选择图片
				selectPicture();
				break;
			case R.id.btn_detect://检测人脸
				detectFace();
				break;
		}
	}

}

注释已经写得很详细了,代码也不多,但是这里的人脸识别太过基础,识别率很低,正脸识别起来基本没问题,一旦不是正脸,或者有多张脸的图像,识别起来就不行了

3、添加权限

最后别忘了在AndroidManifest.xml文件中添加必要的权限,因为我们的代码中有访问文件系统的操作,需要加入如下权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


源码下载点这里



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yubo_725

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值