Android原生人脸识别Camera2示例
Posted zhang106209
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android原生人脸识别Camera2示例相关的知识,希望对你有一定的参考价值。
1. 简介
Camera2 是最新的低级 android 相机包,取代了已弃用的Camera类。Camera2 为复杂的用例提供了深入的控制,但需要您管理特定于设备的配置。源码地址
2. Camera2相机开发流程总结
1.检测并访问相机资源:检查手机是否存在相机资源,如果存在则请求访问相机资源。
2.创建预览界面:创建继承自SurfaceView并实现SurfaceHolder接口的拍摄预览类。有了拍摄预览类,即可创建一个布局文件,将预览画面与设计好的用户界面控件融合在一起,实时显示相机的预览图像。
3.设置拍照监听器:给用户界面控件绑定监听器,使其能响应用户操作, 开始拍照过程。
4.拍照并保存文件:将拍摄获得的图像转换成位图文件,最终输出保存成各种常用格式的图片。
5.释放相机资源:相机是一个共享资源,当相机使用完毕后,必须正确地将其释放,以免其它程序访问使用时发生冲突。
3. Camera2消息流转
1.由SystemService创建CameraManager;
2.整个camera2由CameraManager来进行统一管理;
3.CameraManager通过CameraDevice、CameraCharacteristic和CameraCaptureSession对Camera进行操作;
4.CameraDevice和CameraCaptureSession都有回调函数完成相关操作,包括相机打开,capture过程处理,capture完成处理等。
5.Android设备发送CameraCaptureRequest给Camera,而Camera处理完后发送元数据CameraMetaData给Android设备。
4. 示例类
4.1 Camera2Utils
package com.zw.camera2test.camera2;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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.CameraMetadata;
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.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.WindowManager;
import androidx.annotation.RequiresApi;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Camera2Utils
private static final String TAG = "Camera2Utils";
private static Camera2Utils mCameraUtils;
private CameraManager cManager;
private Size cPixelSize;//相机成像尺寸
private int cOrientation;
private Size captureSize;
private int[] faceDetectModes;
private TextureView cView;//用于相机预览
private Surface previewSurface;//预览Surface
private ImageReader cImageReader;
private Surface captureSurface;//拍照Surface
HandlerThread cHandlerThread;//相机处理线程
Handler cHandler;//相机处理
CameraDevice cDevice;
CameraCaptureSession cSession;
CameraDevice.StateCallback cDeviceOpenCallback = null;//相机开启回调
CaptureRequest.Builder previewRequestBuilder;//预览请求构建
CaptureRequest previewRequest;//预览请求
CameraCaptureSession.CaptureCallback previewCallback;//预览回调
CaptureRequest.Builder captureRequestBuilder;
CaptureRequest captureRequest;
CameraCaptureSession.CaptureCallback captureCallback;
private Context mContext;
WindowManager mWindowManager;
//为了使照片竖直显示
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
ORIENTATIONS.append(Surface.ROTATION_180, 270);
ORIENTATIONS.append(Surface.ROTATION_270, 180);
public static Camera2Utils getInstance()
if (mCameraUtils == null)
synchronized (Camera2Utils.class)
if (mCameraUtils == null)
mCameraUtils = new Camera2Utils();
return mCameraUtils;
public void init(WindowManager windowManager, Context context, TextureView textureView)
this.mWindowManager = windowManager;
this.mContext = context;
this.cView = textureView;
@SuppressLint("MissingPermission")
public void startPreview()
boolean isFront = true;
//前置摄像头
String cId = "";
if (isFront)
cId = CameraCharacteristics.LENS_FACING_BACK + "";
else
cId = CameraCharacteristics.LENS_FACING_FRONT + "";
cManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
//根据摄像头ID,开启摄像头
try
//获取开启相机的相关参数
CameraCharacteristics characteristics = cManager.getCameraCharacteristics(cId);
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size[] previewSizes = map.getOutputSizes(SurfaceTexture.class);//获取预览尺寸
Size[] captureSizes = map.getOutputSizes(ImageFormat.JPEG);//获取拍照尺寸
cOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);//获取相机角度
Rect cRect = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);//获取成像区域
cPixelSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);//获取成像尺寸,同上
Log.i(TAG, "获取相机角度 : " + cOrientation);
//可用于判断是否支持人脸检测,以及支持到哪种程度
faceDetectModes = characteristics.get(CameraCharacteristics
.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);//支持的人脸检测模式
int maxFaceCount = characteristics.get(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT);
int mFaceDetectMode = CaptureRequest.STATISTICS_FACE_DETECT_MODE_OFF;
for (int i = 0; i < faceDetectModes.length; i++)
int face = faceDetectModes[i];
if (face == CaptureRequest.STATISTICS_FACE_DETECT_MODE_FULL || face == CaptureRequest.STATISTICS_FACE_DETECT_MODE_SIMPLE)
Log.i(TAG, "相机硬件不支持人脸检测---" + face);
mFaceDetectMode = CaptureRequest.STATISTICS_FACE_DETECT_MODE_FULL;
break;
if (mFaceDetectMode == CaptureRequest.STATISTICS_FACE_DETECT_MODE_OFF)
Log.i(TAG, "相机硬件不支持人脸检测");
return;
//支持的最大检测人脸数量
//此处写死640*480,实际从预览尺寸列表选择
// previewSizes[2];
Size sSize = new Size(640, 480);
//设置预览尺寸(避免控件尺寸与预览画面尺寸不一致时画面变形)
transformImage(previewSizes, cView.getWidth(), cView.getHeight());
cView.getSurfaceTexture().setDefaultBufferSize(sSize.getWidth(), sSize.getHeight());
cManager.openCamera(cId, getCDeviceOpenCallback(), getCHandler());
catch (CameraAccessException e)
private void transformImage(Size[] previewSizes, int width, int height)
Size mPreviewSize = getOptimalSize(previewSizes, width, height);
if (mPreviewSize == null || cView == null)
return;
Matrix matrix = new Matrix();
int rotation = mWindowManager.getDefaultDisplay().getRotation();
RectF textureRectF = new RectF(0, 0, width, height);
RectF previewRectF = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
float centerX = textureRectF.centerX();
float centery = textureRectF.centerY();
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_270)
else if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270)
previewRectF.offset(centerX - previewRectF.centerX(), centery - previewRectF.centerY());
matrix.setRectToRect(textureRectF, previewRectF, Matrix.ScaleToFit.FILL);
float scale = Math.max((float) width / mPreviewSize.getWidth(), (float) height / mPreviewSize.getHeight());
matrix.postScale(scale, scale, centerX, centery);
matrix.postRotate(90 * (rotation - 2), centerX, centery);
cView.setTransform(matrix);
/**
* 解决预览变形问题
*
* @param sizeMap
* @param width
* @param height
* @return
*/
//选择sizeMap中大于并且最接近width和height的size
private Size getOptimalSize(Size[] sizeMap, int width, int height)
List<Size> sizeList = new ArrayList<>();
for (Size option : sizeMap)
if (width > height)
if (option.getWidth() > width && option.getHeight() > height)
sizeList.add(option);
else
if (option.getWidth() > height && option.getHeight() > width)
sizeList.add(option);
if (sizeList.size() > 0)
return Collections.min(sizeList, new Comparator<Size>()
@Override
public int compare(Size lhs, Size rhs)
return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
);
return sizeMap[0];
private Size getOptimalPreviewSize(Size[] sizes, int w, int h)
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
Size size = null;
for (int i = 0; i < sizes.length; i++)
size = sizes[i];
double ratio = (double) size.getWidth() / size.getHeight();
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.getHeight() - targetHeight) < minDiff)
optimalSize = size;
minDiff = Math.abs(size.getHeight() - targetHeight);
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null)
minDiff = Double.MAX_VALUE;
for (int i = 0; i < sizes.length; i++)
size = sizes[i];
if (Math.abs(size.getHeight() - targetHeight) < minDiff)
optimalSize = size;
minDiff = Math.abs(size.getHeight() - targetHeight);
return optimalSize;
private void configureTransform(int viewWidth, int viewHeight)
int rotation = 1;
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation)
// matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
else if (Surface.ROTATION_180 == rotation)
matrix.postRotate(180, centerX, centerY);
cView.setTransform(matrix);
/**以上是关于Android原生人脸识别Camera2示例的主要内容,如果未能解决你的问题,请参考以下文章
使用 Android Camera2 API 进行人脸检测和画圆
基于OpenCV和C++原生(JNI)的Android数字图像处理+人脸识别demo