使用Android Camera2 API进行人脸检测和绘制圆圈

目前我正在尝试将Camera2.Face转换为实际视图的rect,以便在Camera2 API检测到的脸部上绘制圆圈。

我可以通过以下代码获取面数及其数据到Callback:

private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { private void process(CaptureResult result) { Integer mode = result.get(CaptureResult.STATISTICS_FACE_DETECT_MODE); Face [] faces = result.get(CaptureResult.STATISTICS_FACES); if(faces != null && mode != null) Log.e("tag", "faces : " + faces.length + " , mode : " + mode ); } @Override public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) { process(partialResult); } @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { process(result); } } 

到目前为止,我尝试使用下面的代码将Face rect转换为实际的视图坐标(似乎它不起作用):

 /** * Callback from the CameraCaptureSession.CaptureCallback */ @Override public void onFaceDetection(Face[] faces) { if (mCameraView != null) { setFaceDetectionMatrix(); setFaceDetectionLayout(faces); } } /** * This method gets the scaling values of the face in matrix */ private void setFaceDetectionMatrix() { // Face Detection Matrix mFaceDetectionMatrix = new Matrix(); // Need mirror for front camera. boolean mirror = mCameraView.getFacing() == CameraView.FACING_FRONT; mFaceDetectionMatrix.setScale(mirror ? -1 : 1, 1); mFaceDetectionMatrix.postRotate(mCameraDisplayOrientation); Rect activeArraySizeRect = mCameraView.getCameraCharacteristics().get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); Log.i("Test", "activeArraySizeRect1: (" + activeArraySizeRect + ") -> " + activeArraySizeRect.width() + ", " + activeArraySizeRect.height()); Log.i("Test", "activeArraySizeRect2: " + cameraOverlayDrawingView.getWidth() + ", " + cameraOverlayDrawingView.getHeight()); float s1 = cameraOverlayDrawingView.getWidth() / activeArraySizeRect.width(); float s2 = cameraOverlayDrawingView.getHeight() / activeArraySizeRect.height(); mFaceDetectionMatrix.postScale(s1, s2); mFaceDetectionMatrix.postTranslate(cameraOverlayDrawingView.getWidth() / 2, cameraOverlayDrawingView.getHeight() / 2); } /** * This method set the matrix for translating rect */ private void setFaceDetectionLayout(Face[] faces) { if (faces.length == 0) { cameraOverlayDrawingView.setHaveFaces(false, null); } else if (faces.length > 0) { List faceRects; faceRects = new ArrayList(); for (int i = 0; i  50) { int left = faces[i].getBounds().left; int top = faces[i].getBounds().top; int right = faces[i].getBounds().right; int bottom = faces[i].getBounds().bottom; Rect uRect = new Rect(left, top, right, bottom); RectF rectF = new RectF(uRect); mFaceDetectionMatrix.mapRect(rectF); uRect.set((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom); Log.i("Test", "Activity rect" + i + " bounds: " + uRect); faceRects.add(uRect); } } cameraOverlayDrawingView.setHaveFaces(true, faceRects); } } 

    新:我管理了所有手机轮换。 我估计offsetDxDy取决于我的布局,但是如果我要告诉你真相我不知道为什么我把它的值设为100.它在我的华为P9上运行良好,我以经验的方式find它。 我仍然没有试图找出是否取决于我的手机或我的XML布局或两者。

    无论如何, 现在find矩阵 ,所以你可以调整它们以便它们能够满足你的需求。

    注意:我的setRotation不是那么通用,因为我没有对它进行参数化

     int orientationOffset = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 

    您可以尝试这样做,以便使用与SENSOR_ORIENTATION不同的完整通用代码,该示例与270的示例不同。

    因此,此代码适用于带有方向为270的硬件摄像头传感器的手机。

    华为P9有它。

    只是为了让您了解旋转绑定到硬件传感器方向,这也适用于我的P9(但我没有任何其他硬件可以测试)

     if (mSwappedDimensions) { // Display Rotation 0 mFaceDetectionMatrix.setRotate(orientationOffset); mFaceDetectionMatrix.postScale(mirror ? -s1 : s1, s2); mFaceDetectionMatrix.postTranslate(mPreviewSize.getHeight() + offsetDxDy, mPreviewSize.getWidth() + offsetDxDy); } else { // Display Rotation 90 e 270 if (displayRotation == Surface.ROTATION_90) { mFaceDetectionMatrix.setRotate(orientationOffset + 90); mFaceDetectionMatrix.postScale(mirror ? -s1 : s1, s2); mFaceDetectionMatrix.postTranslate(mPreviewSize.getWidth() + offsetDxDy, -offsetDxDy); } else if (displayRotation == Surface.ROTATION_270) { mFaceDetectionMatrix.setRotate(orientationOffset + 270); mFaceDetectionMatrix.postScale(mirror ? -s1 : s1, s2); mFaceDetectionMatrix.postTranslate(-offsetDxDy, mPreviewSize.getHeight() + offsetDxDy); } } 

    这是我的最终代码(也可在GitHub上find)

     int orientationOffset = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); Rect activeArraySizeRect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); // Face Detection Matrix mFaceDetectionMatrix = new Matrix(); Log.i("Test", "activeArraySizeRect1: (" + activeArraySizeRect + ") -> " + activeArraySizeRect.width() + ", " + activeArraySizeRect.height()); Log.i("Test", "activeArraySizeRect2: " + mPreviewSize.getWidth() + ", " + mPreviewSize.getHeight()); float s1 = mPreviewSize.getWidth() / (float)activeArraySizeRect.width(); float s2 = mPreviewSize.getHeight() / (float)activeArraySizeRect.height(); //float s1 = mOverlayView.getWidth(); //float s2 = mOverlayView.getHeight(); boolean mirror = (facing == CameraCharacteristics.LENS_FACING_FRONT); // we always use front face camera boolean weAreinPortrait = true; int offsetDxDy = 100; if (mSwappedDimensions) { // Display Rotation 0 mFaceDetectionMatrix.setRotate(270); mFaceDetectionMatrix.postScale(mirror ? -s1 : s1, s2); mFaceDetectionMatrix.postTranslate(mPreviewSize.getHeight() + offsetDxDy, mPreviewSize.getWidth() + offsetDxDy); } else { // Display Rotation 90 e 270 if (displayRotation == Surface.ROTATION_90) { mFaceDetectionMatrix.setRotate(0); mFaceDetectionMatrix.postScale(mirror ? -s1 : s1, s2); mFaceDetectionMatrix.postTranslate(mPreviewSize.getWidth() + offsetDxDy, -offsetDxDy); } else if (displayRotation == Surface.ROTATION_270) { mFaceDetectionMatrix.setRotate(180); mFaceDetectionMatrix.postScale(mirror ? -s1 : s1, s2); mFaceDetectionMatrix.postTranslate(-offsetDxDy, mPreviewSize.getHeight() + offsetDxDy); } } 

    这是公共github仓库,您可以在其中find代码: https : //github.com/shadowsheep1/android-camera2-api-face-recon 。 希望它可以帮到你。

    在此处输入图像描述

    无论如何只是为了给你一些理论,你正在做的是2D平面变换。 我的意思是你有一个平面(硬件传感器),你必须在预览平面上重新映射该平面上的对象。

    所以你必须照顾:

    • 旋转 :这取决于您的硬件传感器旋转和手机旋转。
    • 镜像水平镜像 ,取决于您是否使用前置摄像头,以及取决于手机旋转的垂直镜像 )。 镜像在缩放矩阵中使用“ – ”符号完成。
    • 翻译 :这取决于您的对象通过旋转放置的位置(这也取决于您正在处理的旋转中心)和翻译。 因此,您必须在预览中替换查看对象。

    数学理论

    我不久前在我的博客上也写了一些技术post,但它们都是意大利语。