实现过程:
- 创建一个窗口用来显示照相预览
- 判断预览中是否有人脸出现
- 探测到人脸时抓取当前图像
- 压缩当前图像提高上传速度
- 将压缩后图像调用链接post上传
- 解析返回结果
首先,自定义一个FaceDialogFragment,如下:
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.DialogFragment;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
public class FaceDialogFragment extends DialogFragment {
private ComponentActivity mActivity;
private SurfaceView surfaceView;
private TextView errorMsg;
private TextView again;
private TextView prompt_msg;
private Camera mCamera;
private SurfaceHolder mHolder;
private String TAG = "TAG";
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == 1){
errorMsg.setText("是否重新验证");
prompt_msg.setText("验证失败");
prompt_msg.setTextColor(getResources().getColor(R.color.red));
again.setVisibility(View.VISIBLE);
}else if(msg.what == 2){
Toast.makeText(mActivity,"识别成功",Toast.LENGTH_SHORT).show();
}
}
};
@Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = (ComponentActivity) getActivity();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.FragmentDialog);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.facedetch, container, false);
surfaceView = v.findViewById(R.id.face_view);
errorMsg = v.findViewById(R.id.error_msg);
again = v.findViewById(R.id.again);
prompt_msg = v.findViewById(R.id.prompt_msg);
TextView cancel = v.findViewById(R.id.cancel);
openSurfaceView();
again.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
errorMsg.setText("");
prompt_msg.setText("正在验证..");
prompt_msg.setTextColor(getResources().getColor(R.color.black));
surfaceView.setVisibility(View.VISIBLE);
mCamera.startPreview();
mCamera.startFaceDetection();
again.setVisibility(View.GONE);
}
});
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
return v;
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
private void openSurfaceView() {
mHolder = surfaceView.getHolder();
mHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
//判断是否有相机
if (mCamera == null) {
//前置相机
int CammeraIndexf = FindFrontCamera();
//后置相机
int CammeraIndexb = FindBackCamera();
if(CammeraIndexf!=-1){
mCamera = Camera.open(CammeraIndexf);
}else{
if(CammeraIndexb!=-1){
mCamera = Camera.open(CammeraIndexb);
}else{
Toast.makeText(mActivity,"不支持",Toast.LENGTH_SHORT).show();
return;
}
}
try {
// 设置脸部检测监听
mCamera.setFaceDetectionListener(new FaceDetectorListener());
//设置相机预览
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mHolder.getSurface() == null) {
// preview surface does not exist
Log.e(TAG, "mHolder.getSurface() == null");
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
Log.e(TAG, "Error stopping camera preview: " + e.getMessage());
}
try {
mCamera.setPreviewDisplay(mHolder);
//获取当前预览框的宽高
int measuredWidth = surfaceView.getMeasuredWidth();
int measuredHeight = surfaceView.getMeasuredHeight();
//设置相机预览图宽高
setCameraParms(mCamera, measuredWidth, measuredHeight);
mCamera.startPreview();
//开启脸部探测
startFaceDetection(); // re-start face detection feature
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
holder = null;
}
});
}
private void setCameraParms(Camera camera, int width, int height) {
// 获取摄像头支持的pictureSize列表
Camera.Parameters parameters = camera.getParameters();
parameters.setJpegQuality(100);
List<Camera.Size> pictureSizeList = parameters.getSupportedPreviewSizes();
for (int i=0;i<pictureSizeList.size();i++){
int pwidth = pictureSizeList.get(i).width;
int pheight = pictureSizeList.get(i).height;
//获取720宽度的预览图像
if (pwidth==720){
if (pheight == 720){
surfaceView.setLayoutParams(new LinearLayout.LayoutParams(width,width));
}else{
surfaceView.setLayoutParams(new LinearLayout.LayoutParams(width,width*3/2));
}
parameters.setPreviewSize(pwidth,pheight);
break;
}
}
// 对焦
if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
// 连续对焦
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
//旋转90度,获取正向预览
camera.setDisplayOrientation(90);
camera.cancelAutoFocus();
camera.setParameters(parameters);
}
/**
* 启动脸部检测,如果getMaxNumDetectedFaces()!=0说明不支持脸部检测
*/
public void startFaceDetection() {
// Try starting Face Detection
Camera.Parameters params = mCamera.getParameters();
// start face detection only *after* preview has started
if (params.getMaxNumDetectedFaces() > 0) {
// mCamera supports face detection, so can start it:
mCamera.startFaceDetection();
} else {
Log.e("tag", "【FaceDetectorActivity】类的方法:【startFaceDetection】: " + "不支持");
}
}
private class FaceDetectorListener implements Camera.FaceDetectionListener {
@Override
public void onFaceDetection(Camera.Face[] faces, Camera camera) {
if (faces.length > 0) {
//探测到人脸后利用回调获取当前图像
camera.takePicture(null,null,fjpgCallback);
Camera.Face face = faces[0];
Rect rect = face.rect;
Log.d("FaceDetection", "可信度:" + face.score + "face detected: " + faces.length +
" Face 1 Location X: " + rect.centerX() +
"Y: " + rect.centerY() + " " + rect.left + " " + rect.top + " " + rect.right + " " + rect.bottom);
Log.e("tag", "【FaceDetectorListener】类的方法:【onFaceDetection】: "+face.score +"="+faces.length );
mCamera.stopFaceDetection();
} else {
Log.e(TAG, "【FaceDetectorListener】类的方法:【onFaceDetection】: " + "没有脸部");
}
}
}
private Camera.PictureCallback fjpgCallback = new Camera.PictureCallback()
{
@SuppressLint("SimpleDateFormat")
public void onPictureTaken( byte[] data , Camera camera )
{
Bitmap bitmap = rotateMyBitmap(BitmapFactory.decodeByteArray( data , 0 , data.length ));
//获得压缩后的图片
Bitmap bitmap2 = compressImage(transImage(bitmap,480,720,100));
//将Bitamap转换为Base64格式
final String base64data = ImageUtils.bitmapToBase64(bitmap2);
new Thread(new Runnable() {
@Override
public void run() {
String backJson = HttpAssist.sendFace("调用地址URL","调用此 API 的 API Key",
"调用此 API 的 API Key","第一张图片的 URL",base64data);
//此处没有进行结果解析,只不过将返回的json字符串进行了切割,获取了两张照片的相似度数值
if (backJson.indexOf("confidence")!=-1){
String confidence = backJson.substring(
backJson.indexOf("confidence\":")+12,
backJson.indexOf("confidence\":")+19).trim();
if (confidence.indexOf(",")!=-1){
confidence = confidence.substring(0,confidence.indexOf(",")).trim();
}
double confidence1 = Double.parseDouble(confidence);
//当相似度大于80的时候 认定为同一人
if(confidence1>80){
handler.sendEmptyMessage(2);
//调用activity中的自定义方法,在识别成功后进行页面跳转
mActivity.onAuthenticated();
dismiss();
}else{
handler.sendEmptyMessage(1);
}
}else{
handler.sendEmptyMessage(1);
}
}
}).start();
};
//缩放图片
public Bitmap transImage(Bitmap bitmap, int width, int height, int quality) {
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
// 缩放图片的尺寸
float scaleWidth = (float) width / bitmapWidth;
float scaleHeight = (float) height / bitmapHeight;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
// 产生缩放后的Bitmap对象
Bitmap resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, false);
if (!bitmap.isRecycled()) {
bitmap.recycle();//记得释放资源,否则会内存溢出
}
return resizeBitmap;
}
//压缩图片
private Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 100;
while ( baos.toByteArray().length / 1024>100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
baos.reset();//重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
options -= 10;//每次都减少10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
return bitmap;
}
//获取前置摄像头
private int FindFrontCamera(){
int cameraCount = 0;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
cameraCount = Camera.getNumberOfCameras(); // get cameras number
for ( int camIdx = 0; camIdx < cameraCount;camIdx++ ) {
Camera.getCameraInfo( camIdx, cameraInfo ); // get camerainfo
if ( cameraInfo.facing ==Camera.CameraInfo.CAMERA_FACING_FRONT ) {
// 代表摄像头的方位,目前有定义值两个分别为CAMERA_FACING_FRONT前置和CAMERA_FACING_BACK后置
return camIdx;
}
}
return -1;
}
//获取后置摄像头
private int FindBackCamera(){
int cameraCount = 0;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
cameraCount = Camera.getNumberOfCameras(); // get cameras number
for ( int camIdx = 0; camIdx < cameraCount;camIdx++ ) {
Camera.getCameraInfo( camIdx, cameraInfo ); // get camerainfo
if ( cameraInfo.facing ==Camera.CameraInfo.CAMERA_FACING_BACK ) {
// 代表摄像头的方位,目前有定义值两个分别为CAMERA_FACING_FRONT前置和CAMERA_FACING_BACK后置
return camIdx;
}
}
return -1;
}
//选择获取的图片保持为正向
public Bitmap rotateMyBitmap(Bitmap bmp){
Matrix matrix = new Matrix();
matrix.postRotate(-90);
Bitmap bitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);
Bitmap nbmp2 = Bitmap.createBitmap(bmp, 0,0, bmp.getWidth(), bmp.getHeight(), matrix, true);
return nbmp2;
}
}
图片转换为Base64格式调用代码如下:
import android.graphics.Bitmap;
import android.util.Base64;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* 图片工具类
* */
public class ImageUtils {
public static String bitmapToBase64(Bitmap bitmap) {
String result = null;
ByteArrayOutputStream baos = null;
try {
if (bitmap != null) {
baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
baos.flush();
baos.close();
byte[] bitmapBytes = baos.toByteArray();
result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (baos != null) {
baos.flush();
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
}
图片上传代码如下:
import android.util.Log;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* Http post上传
* */
public class HttpAssist {
public static String sendFace(String url,String param1,String param2,String param3,String param4){
String d1 = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
OkHttpClient okHttpClient = new OkHttpClient();
FormBody formBody = new FormBody
.Builder()
.add("api_key",param1)
.add("api_secret",param2)
.add("image_url1",param3)
.add("image_base64_2",param4)
.build();
Request request = new Request.Builder()
.post(formBody)
.url(url)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
String result = response.body().string();
Log.e("tagRes",result);
String d2 = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
double d = Double.valueOf(d2) - Double.valueOf(d1);
Log.e("tag2t",d + "");
return result;
} catch (IOException e) {
Log.e("tage","出错了。。。"+e.getMessage().toString()+"==="+e.getCause());
e.printStackTrace();
}
return "-1";
}
}
FaceDialogFragment的布局代码facedetch.xml如下:
<?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">
<LinearLayout
android:id="@+id/finger_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="visible">
<SurfaceView
android:id="@+id/face_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/prompt_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="正在验证.."
android:textColor="#000"
android:textSize="16sp"
/>
<TextView
android:id="@+id/error_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:maxLines="1"
android:textSize="12sp"
android:textColor="#f45"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/password_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="请输入密码"
android:textColor="#000"
android:textSize="16sp"
/>
<EditText
android:id="@+id/pwd_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:password="true"
android:paddingTop="12dp"/>
<TextView
android:id="@+id/password_error_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:maxLines="1"
android:textSize="12sp"
android:textColor="#f45"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginTop="10dp"
android:background="#ccc"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/cancel"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:text="取消"
android:textColor="#5d7883"
android:textSize="16sp" />
<TextView
android:id="@+id/again"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:text="重新验证"
android:textColor="#5d7883"
android:textSize="16sp"
android:visibility="gone"
/>
<TextView
android:id="@+id/password"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:text="密码验证"
android:textColor="#5d7883"
android:textSize="16sp"
android:visibility="gone"
/>
</LinearLayout>
</LinearLayout>
最后在Activity中调用如下方法即可使用该功能:
private void showFaceDialog(String meip) {
FaceDialogFragment fragment = new FaceDialogFragment();
fragment.show(getFragmentManager(), "face");
}