Проверка персоны на живость с помощью ML Kit

Dec 05 2022
Это копия проверки селфи пользователей Persona с использованием ML Kit на Android. Так недавно мне довелось использовать Persona для одного из проектов.

Это копия проверки селфи пользователей Persona с использованием ML Kit на Android.

Так недавно мне довелось использовать Persona для одного из проектов. Я был действительно заинтригован тем, как они справляются с разделом «Захватите свое селфи». По сути, они просят вас взять камеру телефона и выполнить определенный набор действий, чтобы убедиться, что человек на другом конце провода настоящий, а не изображение (защита от мошенничества).

И, конечно же, будучи технарем, я не смог устоять перед желанием воспроизвести то же самое. Я использовал Android с ML Kit, но уверен, что его можно воспроизвести и на других платформах. Вот как выглядит окончательный результат,

Шаг 1: Определение стека

Как только я испытал способ съемки селфи с помощью Persona, я начал исследовать, как этого можно добиться. Конечно, обнаружение человеческого лица и его жестов было ключом к тому, чтобы мой поиск в Google имел направление для начала! И Google, будучи королем поиска, выдал мне то, что мне было нужно, на странице 1. В основном мы будем использовать для этого ML Kit Face Detection , поскольку он обеспечивает угол поворота и ограничивающую рамку, которые в основном используются для достижения желаемого результата.

Шаг 2. Настройка кода Android

Код состоит из следующих основных компонентов:

  • FacialScanView, т. е. пользовательская ViewGroup для обработки взаимодействия с пользовательским интерфейсом и наложения камеры. Он расширяет класс ViewGroup и отвечает за рендеринг вывода камеры и предоставление визуальных очередей пользователю.
  • FaceOverlayView — настраиваемое представление для информирования пользователя о прогрессе, сделанном динамически на основе жестов лица пользователя.
  • FaceDetectioHandler для обнаружения лица и жестов и содержит вспомогательные методы для вызова, если определенное действие было выполнено успешно.

Примечание. Прежде чем люди начнут ругать меня за использование Java для Android, в моей поддержке мои знания Kotlin находятся в стадии разработки.

Наиболее важной частью кода обнаружения является проверка того, было ли выполнено определенное действие. Действия, для которых я написал поддержку, следующие:

  • Center Face — Выровняйте свое лицо в отмеченной области.
  • Поверните влево
  • Повернуть вправо
  • Смотри сверху
  • Смотри снизу
  • мигать
  • Улыбка
  • public void updateFacialFeatures(FacialFeatures facialFeatures, RectF faceRect) {
            if(!isDetectionComplete) {
                this.facialFeatures = facialFeatures;
                switch (detectionType) {
                    case TOP:
                        if (facialFeatures.getRotationZ() < TOP_ANGLE)
                            progress = 100;
                        else if(facialFeatures.getRotationZ() > 0)
                            progress = 0;
                        else if (facialFeatures.getRotationZ() > TOP_ANGLE && facialFeatures.getRotationZ() < 0)
                            progress = -(int) ((facialFeatures.getRotationZ() / (3)) * 100);
                        break;
                    case BOTTOM:
                        if (facialFeatures.getRotationZ() > BOTTOM_ANGLE)
                            progress = 100;
                        else if(facialFeatures.getRotationZ() < 0)
                            progress = 0;
                        else if (facialFeatures.getRotationZ() < BOTTOM_ANGLE && facialFeatures.getRotationZ() > 0)
                            progress = (int) ((facialFeatures.getRotationZ() / (3)) * 100);
                        break;
                    case LEFT:
                        if (facialFeatures.getRotationY() > LEFT_ANGLE)
                            progress = 100;
                        else if(facialFeatures.getRotationY() < 0)
                            progress = 0;
                        else if (facialFeatures.getRotationY() < LEFT_ANGLE && facialFeatures.getRotationY() > 0)
                            progress = (int) ((facialFeatures.getRotationY() / (30)) * 100);
                        break;
                    case RIGHT:
                        if (facialFeatures.getRotationY() < RIGHT_ANGLE)
                            progress = 100;
                        else if(facialFeatures.getRotationY() > 0)
                            progress = 0;
                        else if (facialFeatures.getRotationY() > RIGHT_ANGLE && facialFeatures.getRotationY() < 0)
                            progress = (int) ((facialFeatures.getRotationY() / (30)) * 100);
                        break;
                    case BLINK:
                        if (!facialFeatures.isLeftEyeOpen() && !facialFeatures.isRightEyeOpen())
                            progress = 100;
                        break;
                    case SMILE:
                        if (facialFeatures.isSmiling())
                            progress = 100;
                        break;
                    case READ_TEXT:
                        //TODO
                        break;
                    case CENTER_FACE:
                        if (facialFeatures.isAllLandmarksDetected() && faceRect.contains(new RectF(facialFeatures.getFaceBounds())))
                            progress = 100;
                        else
                            progress= 0;
                        break;
                }
                if (progress == 100) {
                    endTime = System.currentTimeMillis();
                    isDetectionComplete = true;
                    detectionCallback.onCompletion(id, detectionType, getTimeTakenToPerformGesture(endTime));
                } else
                    detectionCallback.onProgress(id, detectionType, progress, getTimeTakenToPerformGesture(System.currentTimeMillis()));
            } else
                detectionCallback.onCompletion(id, detectionType, getTimeTakenToPerformGesture(endTime));
        }
    

    faceDetectionHandler = new FaceDetectionHandler(this);
    
            cameraView.setFacing(Facing.FRONT);
            cameraView.addFrameProcessor(new FrameProcessor() {
                @Override
                public void process(Frame frame) {
                    faceDetectionHandler.detectFacesIn(frame);
                }
            });
    

    @Override
        public void onFaceDetected(FacialFeatures facialFeatures) {
            //Log.i(TAG, facialFeatures.toString());
            if (stepComplete) {
                if(currentCount == 0)
                    faceOverlayView.addGesture(1, facialFeatures, DetectionType.CENTER_FACE);
                if(currentCount == 1)
                    faceOverlayView.addGesture(2, facialFeatures, DetectionType.RIGHT);
                if(currentCount == 2)
                    faceOverlayView.addGesture(3, facialFeatures, DetectionType.LEFT);
                stepComplete = false;
            } else {
                faceOverlayView.updateFacialFeature(facialFeatures);
            }
        }
    

    @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if(canDraw && !detectionComplete) {
                paint.setStyle(Paint.Style.FILL);
                paint.setColor(Color.BLACK);
                paint.setAlpha(150);
    
                //canvas.drawRect(overlayRect, paint);
                //canvas.drawPath(mPath, paint);
    
                if(detectionType != null) {
                    switch (detectionType) {
                        case TOP:
                            progressPaint.setColor(Color.WHITE);
                            canvas.drawLine(getWidth() / 2, startY, getWidth() / 2, endY, progressPaint);
                            canvas.drawLine(getWidth() / 2, startY, (getWidth() / 2) + 70, startY + 50, progressPaint);
                            canvas.drawLine(getWidth() / 2, startY, (getWidth() / 2) - 70, startY + 50, progressPaint);
                            progressPaint.setColor(Color.parseColor("#FF5DCFC1"));
                            canvas.drawLine(getWidth() / 2, endY, getWidth() / 2, endY - (gestureProgress * 2), progressPaint);
                            break;
                        case LEFT:
                            progressPaint.setColor(Color.WHITE);
                            canvas.drawLine(startX, getHeight() / 2, endX, getHeight() / 2, progressPaint);
                            canvas.drawLine(startX, getHeight() / 2, startX + 50, (getHeight() / 2) - 70, progressPaint);
                            canvas.drawLine(startX, getHeight() / 2, startX + 50, (getHeight() / 2) + 70, progressPaint);
                            progressPaint.setColor(Color.parseColor("#FF5DCFC1"));
                            canvas.drawLine(endX, getHeight() / 2, endX - (gestureProgress * 2), getHeight() / 2, progressPaint);
                            break;
                        case RIGHT:
                            progressPaint.setColor(Color.WHITE);
                            canvas.drawLine(startX, getHeight() / 2, endX, getHeight() / 2, progressPaint);
                            canvas.drawLine(endX, getHeight() / 2, endX - 50, (getHeight() / 2) - 70, progressPaint);
                            canvas.drawLine(endX, getHeight() / 2, endX - 50, (getHeight() / 2) +70, progressPaint);
                            progressPaint.setColor(Color.parseColor("#FF5DCFC1"));
                            canvas.drawLine(startX, getHeight() / 2, startX - (gestureProgress * 2), getHeight() / 2, progressPaint);
                            break;
                        case BOTTOM:
                            progressPaint.setColor(Color.WHITE);
                            canvas.drawLine(getWidth() / 2, startY, getWidth() / 2, endY, progressPaint);
                            canvas.drawLine(getWidth() / 2, endY, (getWidth() / 2) + 70, endY - 50, progressPaint);
                            canvas.drawLine(getWidth() / 2, endY, (getWidth() / 2) - 70, endY - 50, progressPaint);
                            progressPaint.setColor(Color.parseColor("#FF5DCFC1"));
                            canvas.drawLine(getWidth() / 2, startY, getWidth() / 2, startY + (gestureProgress * 2), progressPaint);
                            break;
                        case CENTER_FACE:
                            canvas.drawOval(faceContainer, facePaint);
                            break;
                    }
                }
            }
        }