import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
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.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import com.gvm.three.View.AutoFitTextureView;
import com.gvm.three.View.MyToast;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static com.sun.activation.registries.LogSupport.log;
public class CameraHelperFace {
Activity mActivity;
private AutoFitTextureView mTextureView;
int PREVIEW_WIDTH = 1080; //预览的宽度
int PREVIEW_HEIGHT = 1440; //预览的高度
int SAVE_WIDTH = 720; //保存图片的宽度
int SAVE_HEIGHT = 1280; //保存图片的高度
private CameraManager mCameraManager;
private ImageReader mImageReader = null;
private CameraDevice mCameraDevice = null;
private CameraCaptureSession mCameraCaptureSession = null;
private String mCameraId = "0";
private CameraCharacteristics mCameraCharacteristics;
private int mCameraSensorOrientation = 0; //摄像头方向
private int mCameraFacing = CameraCharacteristics.LENS_FACING_BACK; //默认使用后置摄像头
private int mDisplayRotation =0;
private int mFaceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF; //人脸检测模式
private boolean canTakePic = true; //是否可以拍照
private boolean canExchangeCamera = false; //是否可以切换摄像头
private boolean openFaceDetect = true; //是否开启人脸检测
private Matrix mFaceDetectMatrix = new Matrix(); //人脸检测坐标转换矩阵
private ArrayList mFacesRect = new ArrayList<RectF>(); //保存人脸坐标信息
private FaceDetectListener mFaceDetectListener = null; //人脸检测回调
private Handler mCameraHandler;
private HandlerThread handlerThread = new HandlerThread("CameraThread");
private Size mPreviewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT); //预览大小
private Size mSavePicSize = new Size(SAVE_WIDTH, SAVE_HEIGHT); //保存图片大小
interface FaceDetectListener {
public void onFaceDetect(Face[] faces, ArrayList<RectF> facesRect);
}
public void setFaceDetectListener(FaceDetectListener listener) {
this.mFaceDetectListener = listener;
}
public CameraHelperFace(Activity mActivity, AutoFitTextureView textureView) {
this.mActivity = mActivity;
this.mTextureView = textureView;
init();
}
public void init() {
try {
mDisplayRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); //手机方向
handlerThread.start();
mCameraHandler = new Handler(handlerThread.getLooper());
mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
configureTransform(width, height);
try {
initCameraInfo();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
releaseCamera();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 初始化
*/
private void initCameraInfo() throws CameraAccessException {
mCameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
String[] cameraIdList = mCameraManager.getCameraIdList();
if (cameraIdList.length == 0) {
MyToast.show(mActivity, "没有可用相机");
return;
}
for (String id :cameraIdList) {
CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(id);
int facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
if (facing == mCameraFacing) {
mCameraId = id;
mCameraCharacteristics = cameraCharacteristics;
}
log("设备中的摄像头 $id");
}
int supportLevel = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (supportLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
MyToast.show(mActivity,"相机硬件不支持新特性");
}
//获取摄像头方向
mCameraSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
//获取StreamConfigurationMap,它是管理摄像头支持的所有输出格式和尺寸
StreamConfigurationMap configurationMap = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size[] savePicSize = configurationMap.getOutputSizes(ImageFormat.JPEG); //保存照片尺寸
Size[] previewSize = configurationMap.getOutputSizes(SurfaceTexture.class); //预览尺寸
List<Size> savePicSizeList=new ArrayList<>();
for(int i=0;i<savePicSize.length;i++){
savePicSizeList.add(savePicSize[i]);
}
List<Size> previewSizeList=new ArrayList<>();
for(int i=0;i<previewSize.length;i++){
previewSizeList.add(previewSize[i]);
}
boolean exchange = exchangeWidthAndHeight(mDisplayRotation, mCameraSensorOrientation);
int tWidth = 0, tHeight = 0;
if (exchange) {
tWidth = mSavePicSize.getHeight();
tHeight = mSavePicSize.getWidth();
} else {
tWidth = mSavePicSize.getWidth();
tHeight = mSavePicSize.getHeight();
}
if (savePicSizeList != null) {
mSavePicSize = getBestSize(
tWidth,
tHeight,
tWidth,
tHeight,
savePicSizeList);
}
int mWidth = 0, mHeight = 0;
int nWidth=0,nHeight=0;
if (exchange) {
mWidth = mPreviewSize.getHeight();
mHeight = mPreviewSize.getWidth();
nWidth=mTextureView.getHeight();
nHeight=mTextureView.getWidth();
} else {
mWidth = mPreviewSize.getWidth();
mHeight = mPreviewSize.getHeight();
}
if (previewSizeList != null) {
mPreviewSize = getBestSize(
mWidth,
mHeight,
nWidth,
nHeight,
previewSizeList);
}
mTextureView.getSurfaceTexture().setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
log("预览最优尺寸 :${mPreviewSize.width} * ${mPreviewSize.height}, 比例 ${mPreviewSize.width.toFloat() / mPreviewSize.height}");
log("保存图片最优尺寸 :${mSavePicSize.width} * ${mSavePicSize.height}, 比例 ${mSavePicSize.width.toFloat() / mSavePicSize.height}");
//根据预览的尺寸大小调整TextureView的大小,保证画面不被拉伸
int orientation = mActivity.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE){
mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
} else{
mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
}
mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.JPEG, 1);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader it) {
Image image = it.acquireNextImage();
ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
int byteArray = byteBuffer.remaining();
byteBuffer.get(byteArray);
it.close();
}
}, mCameraHandler);
if (openFaceDetect){
initFaceDetect();
}
openCamera();
}
/**
* 初始化人脸检测相关信息
*/
private void initFaceDetect() {
int faceDetectCount = mCameraCharacteristics.get(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT); //同时检测到人脸的数量
int[] faceDetectModes = mCameraCharacteristics.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES); //人脸检测的模式
if (faceDetectModes != null) {
mFaceDetectMode = CaptureRequest.STATISTICS_FACE_DETECT_MODE_FULL;
}else{
mFaceDetectMode = CaptureRequest.STATISTICS_FACE_DETECT_MODE_OFF;
}
if (mFaceDetectMode == CaptureRequest.STATISTICS_FACE_DETECT_MODE_OFF) {
MyToast.show(mActivity, "相机硬件不支持人脸检测");
return;
}
Rect activeArraySizeRect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); //获取成像区域
float scaledWidth = (float)mPreviewSize.getWidth() / (float)activeArraySizeRect.width();
float scaledHeight = (float)mPreviewSize.getHeight() / (float)activeArraySizeRect.height();
boolean mirror = mCameraFacing == CameraCharacteristics.LENS_FACING_FRONT;
mFaceDetectMatrix.setRotate(mCameraSensorOrientation);
if (mirror) {
mFaceDetectMatrix.postScale(-scaledWidth, scaledHeight);
} else {
mFaceDetectMatrix.postScale(scaledWidth, scaledHeight);
}
if (exchangeWidthAndHeight(mDisplayRotation, mCameraSensorOrientation))
mFaceDetectMatrix.postTranslate(mPreviewSize.getHeight(), mPreviewSize.getWidth());
Log.v("camera","成像区域 "+activeArraySizeRect.width()+","+activeArraySizeRect.height());
Log.v("camera","预览区域 "+mPreviewSize.getWidth()+","+ mPreviewSize.getHeight());
Log.v("camera","同时检测到人脸的数量 "+faceDetectCount);
}
/**
* 打开相机
*/
private void openCamera() throws CameraAccessException {
if (ContextCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
MyToast.show(mActivity, "没有相机权限!");
return;
}
mCameraManager.openCamera(mCameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
log("onOpened");
mCameraDevice = camera;
try {
createCaptureSession(camera);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
MyToast.show(mActivity, "打开相机失败!$error");
}
}, mCameraHandler);
}
/**
* 创建预览会话
*/
private void createCaptureSession(CameraDevice cameraDevice) throws CameraAccessException {
final CaptureRequest.Builder captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
Surface surface = new Surface(mTextureView.getSurfaceTexture());
captureRequestBuilder.addTarget(surface); // 将CaptureRequest的构建器与Surface对象绑定在一起
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 闪光灯
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);// 自动对焦
if (openFaceDetect && mFaceDetectMode != CaptureRequest.STATISTICS_FACE_DETECT_MODE_OFF)
captureRequestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_SIMPLE);//人脸检测
List<Surface> sList = new ArrayList<>();
sList.add(surface);
sList.add(mImageReader.getSurface());
// 为相机预览,创建一个CameraCaptureSession对象
cameraDevice.createCaptureSession(sList, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
MyToast.show(mActivity, "开启预览会话失败!");
}
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
mCameraCaptureSession = session;
try {
session.setRepeatingRequest(captureRequestBuilder.build(), new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
if (openFaceDetect && mFaceDetectMode != CaptureRequest.STATISTICS_FACE_DETECT_MODE_OFF)
handleFaces(result);
canExchangeCamera = true;
canTakePic = true;
}
@Override
public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
super.onCaptureStarted(session, request, timestamp, frameNumber);
}
@Override
public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
super.onCaptureFailed(session, request, failure);
log("onCaptureFailed");
MyToast.show(mActivity, "开启预览失败!");
}
}, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}, mCameraHandler);
}
/**
* 处理人脸信息
*/
private void handleFaces(final TotalCaptureResult result) {
Face[] faces = result.get(CaptureResult.STATISTICS_FACES);
mFacesRect.clear();
for (Face face : faces) {
// if (faces.length > 0) {
// Face face = faces[0];
Rect bounds = face.getBounds();
int left = bounds.left;
int top = bounds.top;
int right = bounds.right;
int bottom = bounds.bottom;
RectF rawFaceRect = new RectF(left, top, right, bottom);
mFaceDetectMatrix.mapRect(rawFaceRect);
RectF resultFaceRect = null;
if (mCameraFacing == CaptureRequest.LENS_FACING_FRONT) {
resultFaceRect = rawFaceRect;
} else {
resultFaceRect = new RectF(rawFaceRect.left, rawFaceRect.top - mPreviewSize.getWidth(), rawFaceRect.right, rawFaceRect.bottom - mPreviewSize.getWidth());
}
mFacesRect.add(resultFaceRect);
log("原始人脸位置: ${bounds.width()} * ${bounds.height()} ${bounds.left} ${bounds.top} ${bounds.right} ${bounds.bottom} 分数: ${face.score}");
log("转换后人脸位置: ${resultFaceRect.width()} * ${resultFaceRect.height()} ${resultFaceRect.left} ${resultFaceRect.top} ${resultFaceRect.right} ${resultFaceRect.bottom} 分数: ${face.score}");
}
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mFaceDetectListener.onFaceDetect(result.get(CaptureResult.STATISTICS_FACES), mFacesRect);
}
});
Log.v("Camera","onCaptureCompleted 检测到 "+faces.length+"张人脸");
}
/**
* 拍照
*/
public void takePic() throws CameraAccessException {
if (mCameraDevice == null || !mTextureView.isAvailable() || !canTakePic) return;
// mCameraDevice.apply {
//
// CaptureRequest.Builder captureRequestBuilder = createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
// mImageReader.getSurface();
// { captureRequestBuilder.addTarget(it) }
//
// captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 自动对焦
// captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH) ; // 闪光灯
// captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, mCameraSensorOrientation); //根据摄像头方向对保存的照片进行旋转,使其为"自然方向"
// mCameraCaptureSession.capture(captureRequestBuilder.build(),null,mCameraHandler);
// }
}
/**
* 切换摄像头
*/
public void exchangeCamera() throws CameraAccessException {
if (mCameraDevice == null || !canExchangeCamera || !mTextureView.isAvailable()) {
return;
}
if (mCameraFacing == CameraCharacteristics.LENS_FACING_FRONT) {
mCameraFacing = CameraCharacteristics.LENS_FACING_BACK;
mCameraId="0";
} else {
mCameraFacing = CameraCharacteristics.LENS_FACING_FRONT;
mCameraId="1";
}
mPreviewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT); //重置预览大小
releaseCamera();
initCameraInfo();
}
/**
* 根据提供的参数值返回与指定宽高相等或最接近的尺寸
*
* @param targetWidth 目标宽度
* @param targetHeight 目标高度
* @param maxWidth 最大宽度(即TextureView的宽度)
* @param maxHeight 最大高度(即TextureView的高度)
* @param sizeList 支持的Size列表
* @return 返回与指定宽高相等或最接近的尺寸
*/
private Size getBestSize(int targetWidth, int targetHeight, int maxWidth, int maxHeight, List<Size> sizeList) {
List<Size> bigEnough = new ArrayList<Size>(); //比指定宽高大的Size列表
List<Size> notBigEnough = new ArrayList<Size>(); //比指定宽高小的Size列表
for (Size size : sizeList) {
//宽<=最大宽度 && 高<=最大高度 && 宽高比 == 目标值宽高比
if (size.getWidth() <= maxWidth && size.getHeight() <= maxHeight
&& size.getWidth() == size.getHeight() * targetWidth / targetHeight) {
if (size.getWidth() >= targetWidth && size.getHeight() >= targetHeight)
bigEnough.add(size);
else
notBigEnough.add(size);
}
log("系统支持的尺寸: ${size.width} * ${size.height} , 比例 :${size.width.toFloat() / size.height}");
}
log("最大尺寸 :$maxWidth * $maxHeight, 比例 :${targetWidth.toFloat() / targetHeight}");
log("目标尺寸 :$targetWidth * $targetHeight, 比例 :${targetWidth.toFloat() / targetHeight}");
//选择bigEnough中最小的值 或 notBigEnough中最大的值
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CameraHelperFace.CompareSizesByArea());
} else if (notBigEnough.size() > 0) {
return Collections.max(notBigEnough, new CameraHelperFace.CompareSizesByArea());
} else {
return sizeList.get(0);
}
}
/**
* 根据提供的屏幕方向 [displayRotation] 和相机方向 [sensorOrientation] 返回是否需要交换宽高
*/
/**
* 根据提供的屏幕方向 [displayRotation] 和相机方向 [sensorOrientation] 返回是否需要交换宽高
*/
private boolean exchangeWidthAndHeight(int displayRotation, int sensorOrientation) {
boolean exchange = false;
switch (displayRotation) {
case Surface.ROTATION_0:
case Surface.ROTATION_180:
if (sensorOrientation == 90 || sensorOrientation == 270) {
exchange = true;
}
break;
case Surface.ROTATION_90:
case Surface.ROTATION_270:
if (sensorOrientation == 0 || sensorOrientation == 180) {
exchange = true;
}
break;
default:
log("Display rotation is invalid: $displayRotation");
break;
}
log("屏幕方向 $displayRotation");
log("相机方向 $sensorOrientation");
return exchange;
}
public void releaseCamera() {
if(mCameraCaptureSession!=null){
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
if(mCameraDevice!=null) {
mCameraDevice.close();
mCameraDevice = null;
}
if(mImageReader!=null) {
mImageReader.close();
mImageReader = null;
}
canExchangeCamera = false;
}
public void releaseThread() throws InterruptedException {
handlerThread.quitSafely();
handlerThread.join();
}
/**
* Configures the necessary [android.graphics.Matrix] transformation to `mTextureView`.
* This method should be called after the camera preview size is determined in
* setUpCameraOutputs and also the size of `mTextureView` is fixed.
*
* @param viewWidth The width of `mTextureView`
* @param viewHeight The height of `mTextureView`
*/
private void configureTransform(int viewWidth, int viewHeight) {
int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0f, 0f, viewWidth, viewHeight);
RectF bufferRect = new RectF(0f, 0f, mPreviewSize.getHeight(), mPreviewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
int scale = Math.max(
viewHeight / mPreviewSize.getHeight(),
viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate((90 * (rotation - 2)), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180f, centerX, centerY);
}
mTextureView.setTransform(matrix);
}
public class CompareSizesByArea implements Comparator<Size> {
@Override
public int compare(Size size1, Size size2) {
return java.lang.Long.signum((long)(size1.getWidth() * size1.getHeight() - size2.getWidth() * size2.getHeight()));
}
}
}
android开发,CameraHelperFace人脸识别功能,java语言版本(网上下载的都是kotlin版本)
最新推荐文章于 2024-05-06 09:34:02 发布