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;//預覽Surfaceprivate ImageReader cImageReader;private Surface captureSurface;//拍照SurfaceHandlerThread 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的sizeprivate 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>() {@Overridepublic 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 sizeSize 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 requirementif (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);}/*** 初始化并獲取相機開啟回調對象。當準備就緒后,發起預覽請求*/@SuppressLint("NewApi")private CameraDevice.StateCallback getCDeviceOpenCallback() {if (cDeviceOpenCallback == null) {cDeviceOpenCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(CameraDevice camera) {//打開攝像頭cDevice = camera;try {//創建Session,需先完成畫面呈現目標(此處為預覽和拍照Surface)的初始化camera.createCaptureSession(Arrays.asList(getPreviewSurface(), getCaptureSurface()), newCameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(CameraCaptureSession session) {cSession = session;//構建預覽請求,并發起請求Log.i(TAG, "[發出預覽請求]");try {session.setRepeatingRequest(getPreviewRequest(), getPreviewCallback(),getCHandler());} catch (CameraAccessException e) {Log.i(TAG, "--" + e.getMessage());}}@Overridepublic void onConfigureFailed(CameraCaptureSession session) {session.close();}}, getCHandler());} catch (CameraAccessException e) {Log.i(TAG, "--" + e.getMessage());}}@Overridepublic void onDisconnected(CameraDevice camera) {//關閉攝像頭camera.close();}@Overridepublic void onError(CameraDevice camera, int error) {//發生錯誤camera.close();}};}return cDeviceOpenCallback;}/*** 初始化并獲取相機線程處理** @return*/private Handler getCHandler() {if (cHandler == null) {//單獨開一個線程給相機使用cHandlerThread = new HandlerThread("cHandlerThread");cHandlerThread.start();cHandler = new Handler(cHandlerThread.getLooper());}return cHandler;}/*** 獲取支持的最高人臉檢測級別** @return*/private int getFaceDetectMode() {if (faceDetectModes == null) {Log.i(TAG, "getFaceDetectMode: ----");return CaptureRequest.STATISTICS_FACE_DETECT_MODE_FULL;} else {Log.i(TAG, "getFaceDetectMode: --2--" + faceDetectModes[faceDetectModes.length - 1]);return faceDetectModes[faceDetectModes.length - 1];}}/*** 初始化并獲取預覽回調對象** @return*/@SuppressLint("NewApi")private CameraCaptureSession.CaptureCallback getPreviewCallback() {if (previewCallback == null) {previewCallback = new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureCompleted(CameraCaptureSession session, CaptureRequestrequest, TotalCaptureResult result) {onCameraImagePreviewed(result);}};}return previewCallback;}/*** 生成并獲取預覽請求** @return*/@SuppressLint("NewApi")private CaptureRequest getPreviewRequest() {previewRequest = getPreviewRequestBuilder().build();return previewRequest;}/*** 初始化并獲取預覽請求構建對象,進行通用配置,并每次獲取時進行人臉檢測級別配置** @return*/@SuppressLint("NewApi")private CaptureRequest.Builder getPreviewRequestBuilder() {if (previewRequestBuilder == null) {try {previewRequestBuilder = cSession.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);previewRequestBuilder.addTarget(getPreviewSurface());previewRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);//自動曝光、白平衡、對焦} catch (CameraAccessException e) {Log.i(TAG, "--" + e.getMessage());}} // previewRequestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, getFaceDetectMode());//設置人臉檢測級別previewRequestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, CameraCharacteristics.STATISTICS_FACE_DETECT_MODE_SIMPLE);//設置人臉檢測級別previewRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, 0);return previewRequestBuilder;}/*** 獲取預覽Surface** @return*/private Surface getPreviewSurface() {if (previewSurface == null) {previewSurface = new Surface(cView.getSurfaceTexture());}return previewSurface;}/*** 處理相機畫面處理完成事件,獲取檢測到的人臉坐標,換算并繪制方框** @param result*/@SuppressLint({"NewApi", "LocalSuppress"})private void onCameraImagePreviewed(CaptureResult result) {Face faces[] = result.get(CaptureResult.STATISTICS_FACES);Log.i(TAG, "檢測到有人臉,進行拍照操作:faceLength=" + faces.length);if (faces.length > 0) {Log.i(TAG, "檢測到有人臉,-----------------------------------");//檢測到有人臉,控制相機進行拍照操作executeCapture();}}/*** 初始化拍照相關*/@SuppressLint("NewApi")private Surface getCaptureSurface() {if (cImageReader == null) {cImageReader = ImageReader.newInstance(getCaptureSize().getWidth(), getCaptureSize().getHeight(),ImageFormat.JPEG, 2);cImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {//拍照最終回調onCaptureFinished(reader);}}, getCHandler());captureSurface = cImageReader.getSurface();}return captureSurface;}/*** 獲取拍照尺寸** @return*/@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)private Size getCaptureSize() {if (captureSize != null) {return captureSize;} else {return new Size(cView.getWidth(), cView.getHeight());}}/*** 執行拍照*/@SuppressLint("NewApi")private void executeCapture() {try {Log.i(TAG, "發出請求");cSession.capture(getCaptureRequest(), getCaptureCallback(), getCHandler());} catch (CameraAccessException e) {Log.i(TAG, "--" + e.getMessage());}}@SuppressLint("NewApi")private CaptureRequest getCaptureRequest() {captureRequest = getCaptureRequestBuilder().build();return captureRequest;}@SuppressLint("NewApi")private CaptureRequest.Builder getCaptureRequestBuilder() {if (captureRequestBuilder == null) {try {captureRequestBuilder = cSession.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);//設置拍照回調接口captureRequestBuilder.addTarget(getCaptureSurface());//TODO 1 照片旋轉 // int rotation =getWindowManager().getDefaultDisplay().getRotation();int rotation = 0;int rotationTo = getOrientation(rotation);captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, rotationTo);} catch (CameraAccessException e) {Log.i(TAG, "--" + e.getMessage());}}return captureRequestBuilder;}@SuppressLint("NewApi")private CameraCaptureSession.CaptureCallback getCaptureCallback() {if (captureCallback == null) {captureCallback = new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureCompleted(CameraCaptureSession session, CaptureRequestrequest, TotalCaptureResult result) {}};}return captureCallback;}/*** Retrieves the JPEG orientation from the specified screen rotation.** @param rotation The screen rotation.* @return The JPEG orientation (one of 0, 90, 270, and 360)*/private int getOrientation(int rotation) {return (ORIENTATIONS.get(rotation) + cOrientation + 270) % 360;}/*** 處理相機拍照完成的數據** @param reader*/Bitmap takeBitmap = null;Bitmap takeBitmap2 = null;@SuppressLint("NewApi")private void onCaptureFinished(ImageReader reader) {Image image = reader.acquireLatestImage();ByteBuffer buffer = image.getPlanes()[0].getBuffer();byte[] data = new byte[buffer.remaining()];buffer.get(data);image.close();buffer.clear();takeBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);/*** 為了解決預覽和拍照左右顛倒問題*/Matrix m = new Matrix();m.postScale(-1, 1); // 鏡像水平翻轉takeBitmap2 = Bitmap.createBitmap(takeBitmap,0,0,takeBitmap.getWidth(),takeBitmap.getHeight(),m,true);if (ioShowBitmapListener != null) {ioShowBitmapListener.showBitmap(takeBitmap2);} // Runtime.getRuntime() // .gc();}@SuppressLint("NewApi")public void closeCamera() {if (cSession != null) {try {cSession.stopRepeating();} catch (CameraAccessException e) {e.printStackTrace();}cSession.close();cSession = null;}if (cDevice != null) {cDevice.close();cDevice = null;}if (cImageReader != null) {cImageReader.close();cImageReader = null;captureRequestBuilder = null;}if (cHandlerThread != null) {cHandlerThread.quitSafely();try {cHandlerThread.join();cHandlerThread = null;cHandler = null;} catch (InterruptedException e) {Log.i(TAG, "--" + e.getMessage());}}if (captureRequestBuilder != null) {captureRequestBuilder.removeTarget(captureSurface);captureRequestBuilder = null;}if (captureSurface != null) {captureSurface.release();captureSurface = null;}if (previewRequestBuilder != null) {previewRequestBuilder.removeTarget(previewSurface);previewRequestBuilder = null;}if (previewSurface != null) {previewSurface.release();previewSurface = null;}if (takeBitmap!=null){takeBitmap.recycle();takeBitmap=null;}}private IOShowBitmapListener ioShowBitmapListener;public void setIoShowBitmapListener(IOShowBitmapListener ioShowBitmapListener) {this.ioShowBitmapListener = ioShowBitmapListener;}public interface IOShowBitmapListener {void showBitmap(Bitmap bitmap);}}5. 參考文獻
https://blog.csdn.net/wangzhicheng1983/article/details/122660654
6. 下載地址
Demo下載地址
總結
以上是生活随笔為你收集整理的Android原生人脸识别Camera2示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 春节小作业总结1
- 下一篇: HDU 4403 A very hard