Обнаружение лиц и рисование круга с помощью Android Camera2 API

В настоящее время я пытаюсь преобразовать Camera2.Face в фактический вид rect, чтобы нарисовать круг по лицу, обнаруженному API Camera2.

Я могу получить количество граней и его данных в 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<Rect> faceRects;
        faceRects = new ArrayList<>();
        for (int i = 0; i < faces.length; i++) {
            Log.i("Test", "Activity face" + i + " bounds: " + faces[i].getBounds());
            if (faces[i].getScore() > 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);
    }
}

Ответ 1

NEW: Я управляю всеми моими поворотами телефона. Значение offsetDxDy зависит от моего макета, но если я должен сказать вам правду, я не знаю, почему я ставлю значение 100. Это хорошо работает на моем Huawei P9, и я нашел его эмпирическим способом. Я все еще не пытался выяснить, зависит ли это от моего телефона или от моего XML-макета или того и другого.

В любом случае матрицы теперь найдены, поэтому вы можете адаптировать их так, чтобы они соответствовали вашим потребностям.

Примечание: my setRotation не является настолько общим, потому что я не параметризировал его на

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

Вы можете попробовать сделать это, чтобы иметь полный общий код, работающий с SENSOR_ORIENTATION, отличный от того, который указан в этом примере, который равен 270.

Таким образом, этот код работает с телефоном с аппаратным датчиком камеры с ориентацией 270.

У Huawei P9 есть.

Просто, чтобы дать вам представление о привязке вращения к ориентации датчика HW, которая также хорошо работает на моем 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)

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, где вы можно найти код: https://github.com/shadowsheep1/android-camera2-api-face-recon. Надеюсь, помочь тебе.

введите описание изображения здесь

В любом случае, чтобы дать вам также некоторую теорию, то, что вы делаете, - это двумерное преобразование плоскости. Я имею в виду, что у вас есть плоскость (HW Sensor), и вам нужно переназначить объект на этом самолете на плоскости предварительного просмотра.

Итак, вам нужно позаботиться о:

  • Вращение. Это зависит от вашего вращения датчика HW и вращения телефона.
  • Зеркалирование. Горизонтальное зеркалирование, которое зависит, если вы используете камеру на передней панели или нет, и вертикальное зеркалирование, зависящее от вращения телефона). Зеркалирование выполняется с помощью знака "-" в матрице масштабирования.
  • Перевод. Это зависит от того, где ваш объект был помещен вращением (которое также зависит от того, из какого центра вращения вы имеете дело) и перевода. Таким образом, вы должны заменить в своем предварительном просмотре. Просмотрите свои объекты.

Математическая теория

Я также написал технический пост в своем блоге некоторое время назад, но они на итальянском языке.