|
|
|
|
@ -0,0 +1,798 @@
|
|
|
|
|
package com.rehome.dywoa.ui.activity;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import static com.google.mlkit.vision.face.FaceDetectorOptions.PERFORMANCE_MODE_FAST;
|
|
|
|
|
import android.Manifest;
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.content.Intent;
|
|
|
|
|
import android.content.pm.PackageManager;
|
|
|
|
|
import android.graphics.Bitmap;
|
|
|
|
|
import android.graphics.BitmapFactory;
|
|
|
|
|
import android.graphics.Canvas;
|
|
|
|
|
import android.graphics.Color;
|
|
|
|
|
import android.graphics.ImageFormat;
|
|
|
|
|
import android.graphics.Matrix;
|
|
|
|
|
import android.graphics.Paint;
|
|
|
|
|
import android.graphics.Rect;
|
|
|
|
|
//import android.hardware.Camera;
|
|
|
|
|
import android.graphics.RectF;
|
|
|
|
|
import android.graphics.SurfaceTexture;
|
|
|
|
|
import android.hardware.Camera;
|
|
|
|
|
import android.hardware.camera2.CameraAccessException;
|
|
|
|
|
import android.hardware.camera2.CameraCaptureSession;
|
|
|
|
|
import android.hardware.camera2.CameraCharacteristics;
|
|
|
|
|
import android.hardware.camera2.CameraDevice;
|
|
|
|
|
import android.hardware.camera2.CameraManager;
|
|
|
|
|
import android.hardware.camera2.CameraMetadata;
|
|
|
|
|
import android.hardware.camera2.CaptureRequest;
|
|
|
|
|
import android.media.Image;
|
|
|
|
|
import android.media.ImageReader;
|
|
|
|
|
import android.os.Bundle;
|
|
|
|
|
import android.os.Handler;
|
|
|
|
|
import android.os.HandlerThread;
|
|
|
|
|
import android.text.TextUtils;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
import android.util.Size;
|
|
|
|
|
import android.view.Surface;
|
|
|
|
|
import android.view.SurfaceHolder;
|
|
|
|
|
import android.view.SurfaceView;
|
|
|
|
|
import android.view.TextureView;
|
|
|
|
|
import android.view.View;
|
|
|
|
|
import android.widget.Button;
|
|
|
|
|
import android.widget.ImageView;
|
|
|
|
|
import android.widget.LinearLayout;
|
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
|
|
|
import androidx.core.app.ActivityCompat;
|
|
|
|
|
import androidx.core.content.ContextCompat;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import com.google.android.gms.tasks.OnFailureListener;
|
|
|
|
|
import com.google.android.gms.tasks.OnSuccessListener;
|
|
|
|
|
import com.google.mlkit.vision.common.InputImage;
|
|
|
|
|
import com.google.mlkit.vision.face.Face;
|
|
|
|
|
import com.google.mlkit.vision.face.FaceDetection;
|
|
|
|
|
import com.google.mlkit.vision.face.FaceDetector;
|
|
|
|
|
import com.google.mlkit.vision.face.FaceDetectorOptions;
|
|
|
|
|
import com.rehome.dywoa.R;
|
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.Comparator;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Timer;
|
|
|
|
|
import java.util.TimerTask;
|
|
|
|
|
import com.rehome.dywoa.base.BaseActivity;
|
|
|
|
|
import com.rehome.dywoa.utils.BitmapUtil;
|
|
|
|
|
import com.rehome.dywoa.utils.BitmapUtils;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class FaceRecognitionAppActivity extends BaseActivity {
|
|
|
|
|
|
|
|
|
|
private LinearLayout ll_textureView;
|
|
|
|
|
private TextureView mTextureView;
|
|
|
|
|
private Surface mPreviewSurface;
|
|
|
|
|
private String mCameraId; // 后置相机id
|
|
|
|
|
private HandlerThread mBackgroundThread; // 处理回调结果的后台线程
|
|
|
|
|
private Handler mBackgroundHandler; // 处理回调结果的handler
|
|
|
|
|
private Size[] supportedSizes; // 相机支持的拍照分辨率大小
|
|
|
|
|
private CameraDevice mCameraDevice;
|
|
|
|
|
private CameraCaptureSession mCameraCaptureSession; // 相机会话
|
|
|
|
|
private CaptureRequest.Builder mPreviewBuilder;
|
|
|
|
|
private ImageReader mImageReader;
|
|
|
|
|
private int cameraPosition;//0代表前置摄像头,1代表后置摄像头
|
|
|
|
|
//摄像头Id数组
|
|
|
|
|
private String[] cameraIdList;
|
|
|
|
|
private static final int CAMERA_REQUEST_CODE = 3;//请求码
|
|
|
|
|
|
|
|
|
|
private ImageView imgView;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Timer timer;
|
|
|
|
|
private int faceRecognTricker = 1;
|
|
|
|
|
static boolean startFaceRecognition = true;
|
|
|
|
|
|
|
|
|
|
// private static final String[] permission = new String[]{
|
|
|
|
|
// Manifest.permission.CAMERA
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getContentViewID() {
|
|
|
|
|
return R.layout.activity_face_recognition_app;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void initView() {
|
|
|
|
|
initToolbar("人脸识别", "切换摄像头", new View.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
finish();
|
|
|
|
|
}
|
|
|
|
|
}, new View.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
if (cameraIdList != null && cameraIdList.length > 1) {
|
|
|
|
|
changeCamera();
|
|
|
|
|
} else {
|
|
|
|
|
showToast("当前设备不支持摄像头切换");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void initData() {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
//setContentView(R.layout.activity_face_recognition);
|
|
|
|
|
startFaceRecognition = true;
|
|
|
|
|
faceRecognTricker = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ll_textureView = findViewById(R.id.ll_textureView);
|
|
|
|
|
// mTextureView = new TextureView(this);
|
|
|
|
|
// ll_textureView.addView(mTextureView);
|
|
|
|
|
mTextureView = findViewById(R.id.textureView);
|
|
|
|
|
|
|
|
|
|
imgView = findViewById(R.id.iv_photo);
|
|
|
|
|
|
|
|
|
|
mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int i, int i1) {
|
|
|
|
|
mPreviewSurface = new Surface(surfaceTexture); // 得到用于预览的Surface
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture, int i, int i1) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// button.setOnClickListener(new View.OnClickListener() {
|
|
|
|
|
// @Override
|
|
|
|
|
// public void onClick(View view) {
|
|
|
|
|
//
|
|
|
|
|
// // 1、配置人脸检测器
|
|
|
|
|
// FaceDetectorOptions faceDetectorOptions = new FaceDetectorOptions.Builder()
|
|
|
|
|
// .setPerformanceMode(PERFORMANCE_MODE_FAST)
|
|
|
|
|
// .build();
|
|
|
|
|
// //2、获取人脸检测器
|
|
|
|
|
// FaceDetector detector = FaceDetection.getClient(faceDetectorOptions);
|
|
|
|
|
//
|
|
|
|
|
// // 3、从资源中加载图片
|
|
|
|
|
// bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_photo);
|
|
|
|
|
// imgView.setImageBitmap(bitmap);
|
|
|
|
|
// InputImage image = InputImage.fromBitmap(bitmap, 0);
|
|
|
|
|
//
|
|
|
|
|
// // 4、处理图片
|
|
|
|
|
// detector.process(image)
|
|
|
|
|
// .addOnSuccessListener(new OnSuccessListener<List<Face>>() {
|
|
|
|
|
// @Override
|
|
|
|
|
// public void onSuccess(List<Face> faces) {
|
|
|
|
|
// //Log.e("TAG", "onSuccess: " + 1);
|
|
|
|
|
// showLog("onSuccess: " + 1);
|
|
|
|
|
// if (faces != null) {
|
|
|
|
|
// showLog(String.valueOf(faces.size()));
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// imgView.setImageBitmap(drawWithRectangle(faces));
|
|
|
|
|
// }
|
|
|
|
|
// })
|
|
|
|
|
// .addOnFailureListener(new OnFailureListener() {
|
|
|
|
|
// @Override
|
|
|
|
|
// public void onFailure(@NonNull Exception e) {
|
|
|
|
|
// Notice();
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
Handler handler = new Handler();
|
|
|
|
|
handler.postDelayed(new Runnable() {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
openFrontCamera();
|
|
|
|
|
}
|
|
|
|
|
}, 1000);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startFaceDetector(Bitmap rectBitmap, byte[] bytes) {
|
|
|
|
|
// 1、配置人脸检测器
|
|
|
|
|
FaceDetectorOptions faceDetectorOptions = new FaceDetectorOptions.Builder()
|
|
|
|
|
.setPerformanceMode(PERFORMANCE_MODE_FAST)
|
|
|
|
|
.build();
|
|
|
|
|
//2、获取人脸检测器
|
|
|
|
|
FaceDetector detector = FaceDetection.getClient(faceDetectorOptions);
|
|
|
|
|
|
|
|
|
|
// 3、从资源中加载图片
|
|
|
|
|
// bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_photo);
|
|
|
|
|
// imgView.setImageBitmap(bitmap);
|
|
|
|
|
InputImage image = InputImage.fromBitmap(rectBitmap, 0);
|
|
|
|
|
|
|
|
|
|
// 4、处理图片
|
|
|
|
|
detector.process(image)
|
|
|
|
|
.addOnSuccessListener(new OnSuccessListener<List<Face>>() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onSuccess(List<Face> faces) {
|
|
|
|
|
//Log.e("TAG", "onSuccess: " + 1);
|
|
|
|
|
showLog("onSuccess: " + 1);
|
|
|
|
|
// imgView.setImageBitmap(rectBitmap);
|
|
|
|
|
// mTextureView.setVisibility(View.GONE);
|
|
|
|
|
if (faces != null && faces.size() > 0) {
|
|
|
|
|
showLog(String.valueOf(faces.size()));
|
|
|
|
|
//imgView.setImageBitmap(drawWithRectangle(faces,rectBitmap));
|
|
|
|
|
//imgView.setImageBitmap(rectBitmap);
|
|
|
|
|
showLog(String.valueOf(rectBitmap.getWidth()));
|
|
|
|
|
showLog(String.valueOf(rectBitmap.getHeight()));
|
|
|
|
|
//Bitmap bitmapResize = BitmapUtils.resizeBitmap(rectBitmap, rectBitmap.getWidth()/4, rectBitmap.getHeight()/4);
|
|
|
|
|
Bitmap bitmapResize = BitmapUtils.resizeBitmap(rectBitmap, 480, 640);
|
|
|
|
|
byte[] bitmapResizeByte = BitmapUtils.bitmapToByte(bitmapResize);
|
|
|
|
|
showLog(String.valueOf(bitmapResize.getWidth()));
|
|
|
|
|
showLog(String.valueOf(bitmapResize.getHeight()));
|
|
|
|
|
showLog(String.valueOf(bitmapResizeByte.length));
|
|
|
|
|
//imgView.setImageBitmap(bitmapResize);
|
|
|
|
|
imgView.setImageBitmap(drawWithRectangle(faces, bitmapResize));
|
|
|
|
|
|
|
|
|
|
//BitmapUtils.resizeBitmap(rectBitmap,rectBitmap.getWidth()/3, rectBitmap.getHeight()/3);
|
|
|
|
|
mTextureView.setVisibility(View.GONE);
|
|
|
|
|
//stopCamera();
|
|
|
|
|
|
|
|
|
|
Intent resultIntent = new Intent();
|
|
|
|
|
resultIntent.putExtra("FaceRecognition", bitmapResizeByte);
|
|
|
|
|
setResult(RESULT_OK, resultIntent);
|
|
|
|
|
finish();
|
|
|
|
|
} else {
|
|
|
|
|
startFaceRecognition = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.addOnFailureListener(new OnFailureListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onFailure(@NonNull Exception e) {
|
|
|
|
|
Notice();
|
|
|
|
|
startFaceRecognition = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Bitmap保存成文件
|
|
|
|
|
private void saveBitmapToFile(Bitmap bitmap) {
|
|
|
|
|
|
|
|
|
|
// 创建一个输出流来写入图片数据
|
|
|
|
|
FileOutputStream fos = null;
|
|
|
|
|
try {
|
|
|
|
|
File file = new File("路径/文件名"); // 指定保存位置及文件名
|
|
|
|
|
fos = new FileOutputStream(file);
|
|
|
|
|
// 将Bitmap对象转换成字节数组并写入输出流
|
|
|
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
|
|
|
bitmap.compress(Bitmap.CompressFormat.PNG, 0 /* 无压缩 */, bos);
|
|
|
|
|
byte[] bytes = bos.toByteArray();
|
|
|
|
|
fos.write(bytes);
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
} finally {
|
|
|
|
|
try {
|
|
|
|
|
if (fos != null) {
|
|
|
|
|
fos.close();
|
|
|
|
|
}
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onResume() {
|
|
|
|
|
super.onResume();
|
|
|
|
|
faceRecognTricker = 1;
|
|
|
|
|
if (timer == null) {
|
|
|
|
|
try {
|
|
|
|
|
timer = new Timer();
|
|
|
|
|
timer.schedule(new TimerTask() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
faceRecognTricker++;
|
|
|
|
|
}
|
|
|
|
|
}, 1000, 1000);
|
|
|
|
|
// 设定指定的时间time,此处为10000毫秒
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onPause() {
|
|
|
|
|
super.onPause();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onDestroy() {
|
|
|
|
|
super.onDestroy();
|
|
|
|
|
startFaceRecognition = true;
|
|
|
|
|
if (timer != null) {
|
|
|
|
|
timer.cancel();
|
|
|
|
|
}
|
|
|
|
|
stopPreview();
|
|
|
|
|
closeCaptureSession();
|
|
|
|
|
stopBackgroundThread();
|
|
|
|
|
closeCamera();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void openFrontCamera() {
|
|
|
|
|
// 获取CameraManager
|
|
|
|
|
CameraManager cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
|
|
|
|
|
try {
|
|
|
|
|
// 获取相机列表
|
|
|
|
|
cameraIdList = cameraManager.getCameraIdList();
|
|
|
|
|
mCameraId = null;
|
|
|
|
|
for (String cameraId : cameraIdList) {
|
|
|
|
|
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId); // 获取相机特性
|
|
|
|
|
if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) { // 前置相机
|
|
|
|
|
cameraPosition = 0;
|
|
|
|
|
mCameraId = cameraId;
|
|
|
|
|
// 拍照支持的分辨率
|
|
|
|
|
supportedSizes = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageReader.class);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 打开相机结果的回调函数(监听器)
|
|
|
|
|
CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onOpened(@NonNull CameraDevice cameraDevice) {
|
|
|
|
|
mCameraDevice = cameraDevice;
|
|
|
|
|
// 回调函数的代码在子线程中执行,所以不能直接发出Toast消息,只能通过主线程发出
|
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
try {
|
|
|
|
|
createCaptureSession();
|
|
|
|
|
//Toast.makeText(getBaseContext(), "Camera opened", Toast.LENGTH_SHORT).show();
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
|
|
|
|
|
mCameraDevice = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onError(@NonNull CameraDevice cameraDevice, int i) {
|
|
|
|
|
cameraDevice.close();
|
|
|
|
|
mCameraDevice = null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 启动处理返回结果的后台线程
|
|
|
|
|
if (mBackgroundHandler == null) startBackgroundThread();
|
|
|
|
|
// 打开相机需要的权限检查
|
|
|
|
|
|
|
|
|
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
|
ActivityCompat.requestPermissions(FaceRecognitionAppActivity.this, new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE);
|
|
|
|
|
showToast("像机权限未授权,请先授权像机权限");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 打开相机
|
|
|
|
|
cameraManager.openCamera(mCameraId, stateCallback, mBackgroundHandler);
|
|
|
|
|
showLog(mCameraId);
|
|
|
|
|
//cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
|
|
|
|
|
} catch (CameraAccessException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void openBackCamera() {
|
|
|
|
|
// 获取CameraManager
|
|
|
|
|
CameraManager cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
|
|
|
|
|
try {
|
|
|
|
|
// 获取相机列表
|
|
|
|
|
cameraIdList = cameraManager.getCameraIdList();
|
|
|
|
|
mCameraId = null;
|
|
|
|
|
for (String cameraId : cameraIdList) {
|
|
|
|
|
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId); // 获取相机特性
|
|
|
|
|
if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) { // 后置相机
|
|
|
|
|
cameraPosition = 1;
|
|
|
|
|
mCameraId = cameraId;
|
|
|
|
|
// 拍照支持的分辨率
|
|
|
|
|
supportedSizes = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageReader.class);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 打开相机结果的回调函数(监听器)
|
|
|
|
|
CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onOpened(@NonNull CameraDevice cameraDevice) {
|
|
|
|
|
mCameraDevice = cameraDevice;
|
|
|
|
|
// 回调函数的代码在子线程中执行,所以不能直接发出Toast消息,只能通过主线程发出
|
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
try {
|
|
|
|
|
createCaptureSession();
|
|
|
|
|
//Toast.makeText(getBaseContext(), "Camera opened", Toast.LENGTH_SHORT).show();
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
|
|
|
|
|
mCameraDevice = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onError(@NonNull CameraDevice cameraDevice, int i) {
|
|
|
|
|
cameraDevice.close();
|
|
|
|
|
mCameraDevice = null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 启动处理返回结果的后台线程
|
|
|
|
|
if (mBackgroundHandler == null) startBackgroundThread();
|
|
|
|
|
// 打开相机需要的权限检查
|
|
|
|
|
|
|
|
|
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
|
ActivityCompat.requestPermissions(FaceRecognitionAppActivity.this, new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE);
|
|
|
|
|
showToast("像机权限未授权,请先授权像机权限");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 打开相机
|
|
|
|
|
cameraManager.openCamera(mCameraId, stateCallback, mBackgroundHandler);
|
|
|
|
|
showLog(mCameraId);
|
|
|
|
|
//cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
|
|
|
|
|
} catch (CameraAccessException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startBackgroundThread() {
|
|
|
|
|
mBackgroundThread = new HandlerThread("CameraBackground");
|
|
|
|
|
mBackgroundThread.start();
|
|
|
|
|
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void stopBackgroundThread() {
|
|
|
|
|
if (mBackgroundThread != null) {
|
|
|
|
|
mBackgroundThread.quitSafely();
|
|
|
|
|
try {
|
|
|
|
|
mBackgroundThread.join();
|
|
|
|
|
mBackgroundThread = null;
|
|
|
|
|
mBackgroundHandler = null;
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建CaptureSession
|
|
|
|
|
*/
|
|
|
|
|
private void createCaptureSession() throws CameraAccessException {
|
|
|
|
|
// 创建ImageReader
|
|
|
|
|
createImageReader();
|
|
|
|
|
// 接收图像数据的Surface
|
|
|
|
|
List<Surface> surfaceList = new ArrayList<Surface>();
|
|
|
|
|
surfaceList.add(mPreviewSurface);
|
|
|
|
|
surfaceList.add(mImageReader.getSurface());
|
|
|
|
|
// 处理创建结果的回调函数(监听器)
|
|
|
|
|
CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
|
|
|
|
|
mCameraCaptureSession = cameraCaptureSession;
|
|
|
|
|
// 回调函数的代码在子线程中执行,所以不能直接发出Toast消息,只能通过主线程发出
|
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
try {
|
|
|
|
|
startPreview();
|
|
|
|
|
//Toast.makeText(getBaseContext(), "CaptureSession created", Toast.LENGTH_SHORT).show();
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
// 创建CameSession
|
|
|
|
|
mCameraDevice.createCaptureSession(surfaceList, sessionStateCallback, mBackgroundHandler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建ImageReader
|
|
|
|
|
*/
|
|
|
|
|
private void createImageReader() {
|
|
|
|
|
if (mImageReader == null) {
|
|
|
|
|
// 创建ImageReader,最多保存5张图片
|
|
|
|
|
int width = supportedSizes[0].getWidth();
|
|
|
|
|
int height = supportedSizes[0].getHeight();
|
|
|
|
|
Log.i("app", "capture: size=" + width + ", " + height);
|
|
|
|
|
mImageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
|
|
|
|
|
ImageReader.OnImageAvailableListener listener = new ImageReader.OnImageAvailableListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onImageAvailable(ImageReader imageReader) {
|
|
|
|
|
//获取最新的一帧的Image
|
|
|
|
|
Image image = imageReader.acquireLatestImage();
|
|
|
|
|
//因为是ImageFormat.JPEG格式,所以 image.getPlanes()返回的数组只有一个,也就是第0个。
|
|
|
|
|
ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
|
|
|
|
|
byte[] bytes = new byte[byteBuffer.remaining()];
|
|
|
|
|
byteBuffer.get(bytes);
|
|
|
|
|
//ImageFormat.JPEG格式直接转化为Bitmap格式。
|
|
|
|
|
Bitmap temp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
|
|
|
|
//因为摄像机数据默认是横的,所以需要旋转90度。
|
|
|
|
|
//Bitmap newBitmap = BitmapUtil.rotateBitmap(temp, 90);
|
|
|
|
|
//抛出去展示或存储。
|
|
|
|
|
|
|
|
|
|
//一定需要close,否则不会收到新的Image回调。
|
|
|
|
|
image.close();
|
|
|
|
|
// showLog("Image available");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
try {
|
|
|
|
|
// ByteBuffer buffer = image.getPlanes()[0].getBuffer();
|
|
|
|
|
// byte[] bytes = new byte[buffer.remaining()];
|
|
|
|
|
// buffer.get(bytes);
|
|
|
|
|
// Bitmap rectBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);//将data byte型数组转换成bitmap文件
|
|
|
|
|
if(faceRecognTricker%4==0){
|
|
|
|
|
showLog("Image available");
|
|
|
|
|
if(startFaceRecognition==true){
|
|
|
|
|
startFaceRecognition=false;
|
|
|
|
|
Bitmap newBitmap;
|
|
|
|
|
if (cameraPosition == 1) {
|
|
|
|
|
newBitmap = BitmapUtil.rotateBitmap(temp, 90);
|
|
|
|
|
} else {
|
|
|
|
|
newBitmap = BitmapUtil.rotateBitmap(temp, -90);
|
|
|
|
|
}
|
|
|
|
|
startFaceDetector(newBitmap, bytes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//mBackgroundHandler.post(new ImageSaver(image, picFile)); // 在后台线程中将图像保存到文件
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
mImageReader.setOnImageAvailableListener(listener, mBackgroundHandler); // 设置监听器,拍照完成后会执行上面的方法
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 开始预览
|
|
|
|
|
*
|
|
|
|
|
* @throws CameraAccessException
|
|
|
|
|
*/
|
|
|
|
|
private void startPreview() throws CameraAccessException {
|
|
|
|
|
// 通过Builder模式创建CaptureRequest,创建时使用预览模板
|
|
|
|
|
CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
|
|
|
|
|
// 自动对焦
|
|
|
|
|
// builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
|
|
|
|
|
// // 人脸检测模式
|
|
|
|
|
// builder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_SIMPLE);
|
|
|
|
|
// 加入接收图像数据的Surface
|
|
|
|
|
builder.addTarget(mPreviewSurface);
|
|
|
|
|
builder.addTarget(mImageReader.getSurface());
|
|
|
|
|
// 创建处理结果的回调函数(监听器)
|
|
|
|
|
CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {};
|
|
|
|
|
// 发送重复请求,让相机不断向预览组件发送图像数据
|
|
|
|
|
mCameraCaptureSession.setRepeatingRequest(builder.build(), captureCallback, mBackgroundHandler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 停止预览
|
|
|
|
|
*
|
|
|
|
|
* @throws CameraAccessException
|
|
|
|
|
*/
|
|
|
|
|
private void stopPreview() {
|
|
|
|
|
try {
|
|
|
|
|
if (mCameraCaptureSession != null) {
|
|
|
|
|
mCameraCaptureSession.stopRepeating(); // 取消重复请求
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 关闭CaptureSession
|
|
|
|
|
*/
|
|
|
|
|
private void closeCaptureSession() {
|
|
|
|
|
try {
|
|
|
|
|
if (mCameraCaptureSession != null) {
|
|
|
|
|
mCameraCaptureSession.close();
|
|
|
|
|
mCameraCaptureSession = null;
|
|
|
|
|
//Toast.makeText(getBaseContext(), "CaptureSession closed", Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 关闭相机
|
|
|
|
|
*/
|
|
|
|
|
private void closeCamera() {
|
|
|
|
|
try {
|
|
|
|
|
if (mCameraDevice != null) {
|
|
|
|
|
mCameraDevice.close();
|
|
|
|
|
mCameraDevice = null;
|
|
|
|
|
//Toast.makeText(getBaseContext(), "Camera closed", Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 停止相机
|
|
|
|
|
*/
|
|
|
|
|
private void stopCamera() {
|
|
|
|
|
try {
|
|
|
|
|
stopPreview();
|
|
|
|
|
closeCaptureSession();
|
|
|
|
|
stopBackgroundThread();
|
|
|
|
|
closeCamera();
|
|
|
|
|
if(mImageReader!=null){
|
|
|
|
|
mImageReader=null;
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
切换摄像头
|
|
|
|
|
*/
|
|
|
|
|
public synchronized void changeCamera() {
|
|
|
|
|
//startFaceRecognition=true;
|
|
|
|
|
stopCamera();
|
|
|
|
|
if (cameraPosition == 0) {
|
|
|
|
|
openBackCamera();
|
|
|
|
|
} else {
|
|
|
|
|
openFrontCamera();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void showLog(String logText) {
|
|
|
|
|
if (isApkInDebug(context)) {
|
|
|
|
|
if (TextUtils.isEmpty(logText)) {
|
|
|
|
|
Log.i("app", "logText is null");
|
|
|
|
|
} else {
|
|
|
|
|
Log.i("app", logText);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Notice() {
|
|
|
|
|
showLog("识别失败");
|
|
|
|
|
Toast.makeText(this, "识别失败", Toast.LENGTH_SHORT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 为人脸绘制边框
|
|
|
|
|
*
|
|
|
|
|
* @param faces 采集的人脸
|
|
|
|
|
* @return {@link Bitmap}
|
|
|
|
|
*/
|
|
|
|
|
private Bitmap drawWithRectangle(List<Face> faces, Bitmap rectBitmap) {
|
|
|
|
|
|
|
|
|
|
//复制一个新的Bitmap
|
|
|
|
|
Bitmap copiedBitmap = rectBitmap.copy(rectBitmap.getConfig(), true);
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
for (Face face : faces) {
|
|
|
|
|
//获取边界状态
|
|
|
|
|
Rect bounds = face.getBoundingBox();
|
|
|
|
|
// 初始化Paint
|
|
|
|
|
Paint paint = new Paint();
|
|
|
|
|
// 设置矩形颜色
|
|
|
|
|
paint.setColor(Color.BLUE);
|
|
|
|
|
// 设置绘制样式为轮廓绘制
|
|
|
|
|
paint.setStyle(Paint.Style.STROKE);
|
|
|
|
|
// 设置为你需要的宽度
|
|
|
|
|
paint.setStrokeWidth(10);
|
|
|
|
|
|
|
|
|
|
Canvas canvas = new Canvas(copiedBitmap);
|
|
|
|
|
canvas.drawRect(bounds, paint);
|
|
|
|
|
}
|
|
|
|
|
return copiedBitmap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
|
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
|
|
|
if (CAMERA_REQUEST_CODE == requestCode) {
|
|
|
|
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
|
|
|
openFrontCamera();
|
|
|
|
|
} else {
|
|
|
|
|
//权限拒绝
|
|
|
|
|
Toast.makeText(this, "权限拒绝,无法开启摄像头,请手动设置开启APP访问摄像头权限", Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param sizeMap 相机尺寸集合
|
|
|
|
|
* @param viewWidth 相机宽度
|
|
|
|
|
* @param viewHeight 相机高度
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static Size getMinPreSizeNew(Size[] sizeMap, int viewWidth, final int viewHeight) {
|
|
|
|
|
List<Size> sizeWidthEqual = new ArrayList<>();
|
|
|
|
|
//先查找preview中是否存在与surfaceview相同宽高的尺寸
|
|
|
|
|
for (Size size : sizeMap) {
|
|
|
|
|
//选择更高倍率相机尺寸
|
|
|
|
|
if (size.getWidth() > viewHeight && size.getHeight() > viewWidth) {
|
|
|
|
|
float beishuW = size.getWidth() * 1.0f / viewHeight * 1.0f;
|
|
|
|
|
float beishuH = size.getHeight() * 1.0f / viewWidth * 1.0f;
|
|
|
|
|
if (beishuH == beishuW) {
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//优先选择大小相等的尺寸
|
|
|
|
|
if ((size.getWidth() == viewHeight) && (size.getHeight() == viewWidth)) {
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
//再次选择能够满足我们遮罩层的的容差的
|
|
|
|
|
if (size.getHeight() == viewWidth) {
|
|
|
|
|
sizeWidthEqual.add(size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (sizeWidthEqual.size() > 0) {
|
|
|
|
|
//找一个分辨率最大的尺寸
|
|
|
|
|
Collections.sort(sizeWidthEqual, new Comparator<Size>() {
|
|
|
|
|
@Override
|
|
|
|
|
public int compare(Size o1, Size o2) {
|
|
|
|
|
return Math.abs(o1.getWidth()-viewHeight) -Math.abs(o2.getWidth()-viewHeight);//相机宽度由小到大排序
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
//图片宽度和屏幕高度越接近 像素越好
|
|
|
|
|
return sizeWidthEqual.get(0);
|
|
|
|
|
} else {
|
|
|
|
|
//以上都不满足自己设置一个条件选择一个相机尺寸展示就可以了。理论上都是被淘汰四五年前的低端机
|
|
|
|
|
return sizeMap[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|