仔细想想距离写上篇文章已经很久了,最近都忙于工作和睡觉打游戏(朋友的名字叫游溪),以至于没有写接下来的东西。。记得上篇文章写的是关于自定义camera的(http://blog.csdn.net/lzan13/article/details/54017224)。。那么该篇将在原有的基础上加上人脸识别的功能。
已经亲测,可以实现!!!
三个类,很简单,其他难得我也不会搞:
主要通过:FaceDetectionListener 实现
废话不多说直接上代码:
AutioTakeActiity 该页面只有一个跳转按钮,和一个用来显示拍摄照片的imageview
package com.example.auto_take_picture; import android.Manifest; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.view.View; import android.widget.ImageView; import android.widget.Toast; /** * Created by Administrator on 2017/5/5 0005. */ public class AutioTakeActiity extends Activity { private static final int REQUEST_CAMERA_CODE_TEACHER = 1;// 拍照 private ImageView ima; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_takephoto_result); ima=(ImageView)findViewById(R.id.ima); } /** * 点击拍照 * @param view */ public void takePhoto(View view){ getPerssiom(); } /** * android6.0 申请拍照权限 */ private void getPerssiom(){ if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { gotoTake(); } else { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE }, REQUEST_CAMERA_CODE_TEACHER); } } /** * 跳转至自定义的拍照页面 */ private void gotoTake(){ Intent intent =new Intent(this,AutioTakePhotoActivity.class); startActivityForResult(intent ,1); } /** * 拍照完成之后拿到路径设置imageview * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode==RESULT_OK){ if(requestCode==1){ boolean isBack= data.getBooleanExtra("isBack",true); String path = data.getStringExtra("picPath"); Bitmap bitmap = BitmapFactory.decodeFile(path); Matrix matrix=new Matrix(); if(isBack){//由于拍照时旋转了90度。这里通矩阵旋转回来。(后置摄像头) matrix.setRotate(90); }else{ matrix.setRotate(-90); // ( 前置摄像头) } Bitmap bit= bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true); ima.setImageBitmap(bit); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case REQUEST_CAMERA_CODE_TEACHER: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults.length > 1 && grantResults[1] == PackageManager.PERMISSION_GRANTED && grantResults.length > 2 && grantResults[2] == PackageManager.PERMISSION_GRANTED) { gotoTake(); } else { Toast.makeText(this, "获取权限失败", Toast.LENGTH_SHORT).show(); } break; default: break; } } }
//上面类对应的布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/take" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="去拍照" android:padding="15dp" android:onClick="takePhoto" android:layout_gravity="center" /> <ImageView android:id="@+id/ima" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/show" /> </LinearLayout>
// 该类完成拍摄和展示,以及人脸识别的功能
package com.example.auto_take_picture; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.hardware.Camera; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; /** * Created by Administrator on 2017/5/5 0005. */ public class AutioTakePhotoActivity extends Activity implements SurfaceHolder.Callback{ private SurfaceHolder holder; private SurfaceView surfaceView; Camera mCamera; byte[] photoBytes; private ImageView image; private RelativeLayout rela_result; public static int cameraId; private boolean isBack=true; private FaceView faceView; private TextView txt_turn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_take_photo); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//保持屏幕常亮 rela_result=(RelativeLayout)findViewById(R.id.result) ; //在预览界面之上覆盖一层页面,显示拍摄的图片 image=(ImageView)findViewById(R.id.image); //显示拍摄的图片 surfaceView=(SurfaceView)findViewById(R.id.surfaceview); holder= surfaceView.getHolder(); faceView=(FaceView)findViewById(R.id.face_view); //自定义的view 。用来绘制检测人脸的方框; cameraId=findBackCamera(); //摄像头的id,区分前置还是后置摄像头 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); txt_turn=(TextView)findViewById(R.id.turn); txt_turn.setText("前置/后置------当前:后置"); holder.addCallback(this); } public void take(View view){ //点击拍照的方法。 Camera.Parameters parameters =mCamera.getParameters(); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); mCamera.autoFocus(new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean b, Camera camera) { if(b){ //如果焦点获取成功,拍照 mCamera.takePicture(null,null,pictureCallBack); //pictureCallBack 为拍照的回掉。 } } }); } /** * 摄像头的切换 * @param view */ public void turn(View view){ if(isBack){ cameraId=findFrontCamera(); //前置摄像头id txt_turn.setText("前置/后置------当前:前置"); }else{ cameraId=findBackCamera(); //后置 txt_turn.setText("前置/后置------当前:后置"); } isBack=!isBack; if (mCamera != null) { mCamera.stopPreview(); mCamera.release(); // 释放照相机 } mCamera = Camera.open(cameraId); setCameraParams(1080,1920);//随便设的值 try { mCamera.setPreviewDisplay(holder); mCamera.setDisplayOrientation(90); mCamera.startPreview(); mainHandler.sendEmptyMessage(1); } catch (IOException e) { e.printStackTrace(); } } /** * 点击完成保存图片,并将路径回传给上个页面 * @param view */ public void finish(View view){ //完成 File file=new File(Environment.getExternalStorageDirectory().getPath() + "/test2.jpg"); if (file.exists()) { file.delete(); } try { FileOutputStream fo=new FileOutputStream(file); fo.write(photoBytes); fo.close(); Toast.makeText(AutioTakePhotoActivity.this,"已保存图片", Toast.LENGTH_SHORT).show(); Intent intent =new Intent(AutioTakePhotoActivity.this,AutioTakeActiity.class); //完成拍照,回到mainActivity,并将图片的路径回传 intent.putExtra("picPath", Environment.getExternalStorageDirectory().getPath() + "/test2.jpg"); intent.putExtra("isBack",isBack); setResult(RESULT_OK,intent); finish(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 取消,放弃该图片 * @param view */ public void cancle(View view){ //取消 photoBytes=null; rela_result.setVisibility(View.GONE); mCamera.startPreview(); } /** * 显示拍出的图片 */ private void show(byte[] photoBytes){ Bitmap bitmap = BitmapFactory.decodeByteArray(photoBytes, 0, photoBytes.length); Matrix matrix=new Matrix(); if(isBack){ //后置摄像头 matrix.setRotate(90); }else{ matrix.setRotate(-90); //前置摄像头 } Bitmap bit= bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true); if(bitmap!=null&&!bitmap.isRecycled()){ bitmap.recycle(); } if(bit!=null){ image.setImageBitmap(bit); } rela_result.setVisibility(View.VISIBLE); } private Camera.PictureCallback pictureCallBack =new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] bytes, Camera camera) { //bytes 即是拍照回来的内容,将内容写入本地即可 photoBytes=bytes; show(photoBytes); } }; /** * 拿到前置摄像头id */ public static int findFrontCamera() { int cameraId = -1; int numberCameras = Camera.getNumberOfCameras(); for (int i = 0; i < numberCameras; i++) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(i, info); if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { cameraId = i; break; } } return cameraId; } /** * 拿到后置摄像头id */ public static int findBackCamera() { int cameraId = -1; int numberCameras = Camera.getNumberOfCameras(); for (int i = 0; i < numberCameras; i++) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(i, info); if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { cameraId = i; break; } } return cameraId; } @Override public void surfaceCreated(SurfaceHolder holder) { Log.e("zjun","surfaceCreated"); try { if(mCamera==null){ mCamera= Camera.open(cameraId); mCamera.setPreviewDisplay(holder); setCameraParams(1080,1920); } } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.e("zjun","surfaceChanged"); mCamera.startPreview(); mainHandler.sendEmptyMessage(1); } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (mCamera != null) { mCamera.release(); // 释放照相机 mCamera = null; } } /** * 设置参数 * @param width * @param height */ private void setCameraParams(int width, int height) { Camera.Parameters parameters = mCamera.getParameters(); // 获取摄像头支持的PictureSize列表 List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes(); /**从列表中选取合适的分辨率*/ Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width)); if (null == picSize) { picSize = parameters.getPictureSize(); } // 根据选出的PictureSize重新设置SurfaceView大小 parameters.setPictureSize(picSize.width,picSize.height); // 获取摄像头支持的PreviewSize列表 List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes(); Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width); if (null != preSize) { Log.e("zjun", "preSize.width=" + preSize.width + " preSize.height=" + preSize.height); parameters.setPreviewSize(preSize.width, preSize.height); } parameters.setJpegQuality(100); // 设置照片质量 if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 连续对焦模式 } mCamera.cancelAutoFocus();//自动对焦。 mCamera.setDisplayOrientation(90);// 设置PreviewDisplay的方向,效果就是将捕获的画面旋转多少度显示 mCamera.setParameters(parameters); } private MainHandler mainHandler = new MainHandler(); private void startGoogleDetect() { Camera.Parameters parameters =mCamera.getParameters(); if (parameters.getMaxNumDetectedFaces() > 0) { if (faceView != null) { faceView.clearFaces(); faceView.setVisibility(View.VISIBLE); } mCamera.setFaceDetectionListener(new GoogleDetectListenerImpl(AutioTakePhotoActivity.this, mainHandler)); mCamera.startFaceDetection(); } } private class MainHandler extends Handler { @Override public void handleMessage(final Message msg) { int what = msg.what; switch (what) { case 1: startGoogleDetect(); Log.e("renlei110", "开启人脸识别"); break; case 2: runOnUiThread(new Runnable() { @Override public void run() { Camera.Face[] faces = (Camera.Face[]) msg.obj; faceView.setFaces(faces); Log.e("renlei111", "收到人脸识别的信息"); } }); break; } super.handleMessage(msg); } } /** * 从列表中选取合适的分辨率 * 默认w:h = 4:3 * <p>注意:这里的w对应屏幕的height * h对应屏幕的width<p/> */ private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) { Log.i("zjun", "screenRatio=" + screenRatio); Camera.Size result = null; for (Camera.Size size : pictureSizeList) { float currentRatio = ((float) size.width) / size.height; if (currentRatio - screenRatio == 0) { result = size; break; } } if (null == result) { for (Camera.Size size : pictureSizeList) { float curRatio = ((float) size.width) / size.height; if (curRatio == 4f / 3) {// 默认w:h = 4:3 result = size; break; } } } return result; } }//上面类对应布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <SurfaceView android:id="@+id/surfaceview" android:layout_width="match_parent" android:visibility="visible" android:layout_height="match_parent" /> <TextView android:id="@+id/take" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="15dp" android:onClick="take" android:text="拍照" android:background="@android:color/holo_green_light" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true"/> <TextView android:id="@+id/turn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="15dp" android:onClick="turn" android:text="前置/后置" android:background="@android:color/holo_green_light" android:layout_centerHorizontal="true"/> <com.example.auto_take_picture.FaceView android:id="@+id/face_view" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" ></com.example.auto_take_picture.FaceView> <RelativeLayout android:id="@+id/result" android:layout_width="match_parent" android:background="#222" android:visibility="gone" android:layout_height="match_parent"> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_alignParentTop="true" android:layout_height="match_parent" /> <TextView android:id="@+id/finish" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="15dp" android:onClick="finish" android:text="完成" android:background="@android:color/holo_green_light" android:layout_alignParentBottom="true"/> <TextView android:id="@+id/cancle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="15dp" android:onClick="cancle" android:text="取消" android:layout_alignParentRight="true" android:background="@android:color/holo_green_light" android:layout_alignParentBottom="true"/> </RelativeLayout> </RelativeLayout>
//FaceView 类用来绘制人脸识别的方框
package com.example.auto_take_picture; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.hardware.Camera; import android.util.AttributeSet; import android.util.Log; import android.widget.ImageView; /** * Created by Administrator on 2017/5/9 0009. */ public class FaceView extends ImageView { private Context mContext; private Camera.Face[] mFaces; private Matrix mMatrix = new Matrix(); private boolean mirror; private Paint mLinePaint; public FaceView(Context context, AttributeSet attrs) { super(context, attrs); initPaint(); this.mContext = context; } public void setFaces(Camera.Face[] faces) { this.mFaces = faces; invalidate(); } public void clearFaces(){ mFaces = null; invalidate(); } @Override protected void onDraw(Canvas canvas) { if(mFaces == null || mFaces.length < 1){ return; } if (mFaces != null&&mFaces.length>=1) { canvas.translate(getWidth()/2,getHeight()/2); canvas.rotate(-0); mirror=(AutioTakePhotoActivity.cameraId== Camera.CameraInfo.CAMERA_FACING_FRONT) ; Log.e("mFaces","mFaces"+mFaces.length); for (int i = 0; i < mFaces.length; i++) { Camera.Face face= mFaces[i]; int width=face.rect.right-face.rect.left; int needWidth=getWidth()*width/2000; if(!mirror){ int cx = -face.rect.centerY(); //因为之前对camera做了旋转,所以这里需要转换一下坐 //后置摄像头 int cy = face.rect.centerX(); RectF rectF = new RectF(getWidth()*cx/2000f-needWidth/2,getHeight()*cy/2000f-needWidth/2,getWidth()*cx/2000f+needWidth/2,getHeight()*cy/2000f+needWidth/2); canvas.drawRect(rectF,mLinePaint); }else{ int cx = -face.rect.centerY(); //因为之前对camera做了旋转,所以这里需要转换一下坐 //前置摄像头 int cy = -face.rect.centerX(); RectF rectF = new RectF(getWidth()*cx/2000f-needWidth/2,getHeight()*cy/2000f-needWidth/2,getWidth()*cx/2000f+needWidth/2,getHeight()*cy/2000f+needWidth/2); canvas.drawRect(rectF,mLinePaint); } } } super.onDraw(canvas); } /** * <p>Here is the matrix to convert driver coordinates to View coordinates * in pixels.</p> * <pre> * Matrix matrix = new Matrix(); * CameraInfo info = CameraHolder.instance().getCameraInfo()[cameraId]; * // Need mirror for front camera. * boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT); * matrix.setScale(mirror ? -1 : 1, 1); * // This is the value for android.hardware.Camera.setDisplayOrientation. * matrix.postRotate(displayOrientation); * // Camera driver coordinates range from (-1000, -1000) to (1000, 1000). * // UI coordinates range from (0, 0) to (width, height). * matrix.postScale(view.getWidth() / 2000f, view.getHeight() / 2000f); * matrix.postTranslate(view.getWidth() / 2f, view.getHeight() / 2f); * </pre> */ private void prepareMatrix() { mMatrix.setScale(mirror ? -1 : 1, 1); mMatrix.postRotate(9); mMatrix.postScale(getWidth() / 2000f, getHeight() / 2000f); mMatrix.postTranslate(getWidth() / 2f, getHeight() / 2f); } private void initPaint() { mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); // int color = Color.rgb(0, 150, 255); int color = Color.rgb(98, 212, 68); // mLinePaint.setColor(Color.RED); mLinePaint.setColor(color); mLinePaint.setStyle(Paint.Style.STROKE); mLinePaint.setStrokeWidth(5f); mLinePaint.setAlpha(180); } }
《完。。》很简单的三个类,不怎么写文章,也不知道怎么说,如果发现有坑,恭候怼我。