Статьи

Обнаружение лица в Android с помощью Google Play Services

Это первая часть серии статей о SitePoint, посвященных библиотекам API, составляющим Сервисы Google Play . Каждая статья в серии будет самодостаточной, поэтому вам не нужно читать другие, чтобы следовать.

API сервисов Google Play, которые мы рассмотрим, включают:

  • Обнаружение Лица
  • Сканер штрих-кода
  • Расположение и контекст
  • аналитика
  • AdMob
  • Карты
  • тождественность
  • Сервисы Play Games

Введение в API Mobile Vision

В последнем выпуске Google Play Services 7.8 добавлены новые API-интерфейсы Mobile Vision, которые обеспечивают основу для поиска объектов на фотографиях и видео. Инфраструктура включает в себя детекторы, которые находят и описывают визуальные объекты в изображениях или видеокадрах, и API, управляемый событиями, который отслеживает положение этих объектов в видео.

В настоящее время Mobile Vision API включает в себя детекторы лиц и штрих-кодов, которые можно применять по отдельности или вместе. В этой статье мы рассмотрим Face API, который может найти человеческие лица в изображениях, живых трансляциях и видео. Он может найти и отследить положение лицевых ориентиров, таких как глаза, нос и рот, и предоставить информацию о состоянии черт лица, например, открыты ли глаза, есть ли улыбка и т. Д.

Заметка

Чтобы запустить приложение, у вас должно быть устройство с Android 2.3 (Gingerbread) или выше, а также с Google Play Store. Это означает, что если вы не установили Google Apps на свое виртуальное устройство, вы не сможете протестировать код на эмуляторе. Эмулятор Android по умолчанию не поддерживает установку Служб Google на устройстве, но если вы используете эмулятор, такой как Genymotion, вы сможете это сделать (несмотря на то, что команда прекращает поддержку Служб Google ).

Обнаружение Лица

Распознавание лиц не является новой функцией на Android. Ранее было возможно выполнить обнаружение лица с помощью API FaceDetector.Face, представленного на уровне API Android 1. Инфраструктура обнаружения лица, предоставляемая API Mobile Vision, является усовершенствованием этого API.

Обратите внимание, что на данный момент Google Face API предоставляет только функции для распознавания лиц, а не распознавания лиц. Он может обнаружить присутствие человеческих лиц на изображении или видео, но не может определить, соответствуют ли два лица одному и тому же человеку. Это может сделать вывод об изменениях положения кадра к кадру, если лица в последовательных кадрах видео — это одно и то же лицо. Если лицо покидает поле зрения и снова входит, оно не распознается как ранее обнаруженное лицо.

Чтобы начать работу с API, создайте новый проект под названием FaceDetectionDemo, выберите значения по умолчанию на следующих экранах мастера создания проектов Android Studio, выбрав уровень API 15. API Mobile Vision обладает обратной совместимостью для всех устройств, которые поддерживают сервисы Google Play (т.е. Android 2.3 Gingerbread и выше).

Найти завершенный проект здесь .

Добавьте следующую зависимость в файл build.gradle (Module: app) .

compile 'com.google.android.gms:play-services:7.8.0'

Синхронизируйте файлы Gradle, консоль Gradle может потребовать загрузки библиотеки.

Загрузите изображения, которые мы будем использовать в руководстве.

В папке res создайте новый каталог ресурсов Android. Установите для его имени каталога значение raw, а для типа ресурса также значение raw . Вставьте 3 загруженных изображения в этот каталог res / raw .

В MainActivity.java измените метод onCreate(Bundle)

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    InputStream stream = getResources().openRawResource(R.raw.image01);
    Bitmap bitmap = BitmapFactory.decodeStream(stream);

    FaceDetector detector = new FaceDetector.Builder(getApplicationContext())
            .setTrackingEnabled(false)
            .build();

    // Create a frame from the bitmap and run face detection on the frame.
    Frame frame = new Frame.Builder().setBitmap(bitmap).build();
    SparseArray<Face> faces = detector.detect(frame);

    TextView faceCountView = (TextView) findViewById(R.id.face_count);
    faceCountView.setText(faces.size() + " faces detected");

    detector.release();

}

Добавьте idTextViewactivity_main.xml

 android:id="@+id/face_count"

Запустите приложение, и вы увидите количество лиц, найденных на изображении.

Количество обнаруженных человеческих лиц

На самом деле, если вы запустили приложение, вы, вероятно, получили неверный результат из вышеприведенного. Скоро мы увидим, почему, сначала давайте посмотрим на код, который только что запустился.

Сначала мы создаем Bitmapimage01.jpg . Затем мы создаем детектор для поиска лиц в предоставленном изображении. FaceDetector В приведенном выше примере мы установили для отслеживания значение false Для обнаружения несвязанных отдельных изображений это даст более точный результат. Для обнаружения последовательных изображений (например, видео в реальном времени) отслеживание дает более точный (и более быстрый) результат.

Детектор лица может работать с изображениями низкого разрешения (например, 320 × 240). Распознавание лиц на изображениях с низким разрешением выполняется быстрее, чем на изображениях с высоким разрешением. Имейте в виду, что при более низких разрешениях детектор лица может пропустить некоторые меньшие лица из-за меньшего количества деталей.

Затем мы создаем кадр из растрового изображения и запускаем синхронное определение лица на кадре.

Затем мы изменим текст TextView

Когда закончите с детектором, отпустите его, чтобы освободить собственные ресурсы.

Когда приложение, использующее Face API в первый раз, запускается на устройстве, GMS загружает на устройство собственную библиотеку. Обычно это завершается до того, как приложение запускается в первый раз. Но если эта загрузка еще не завершена, то вызов для обнаружения лиц не обнаружит никаких лиц. Таким образом, вы могли запустить приложение и получить 0 обнаруженных лиц .

Вы можете использовать isOperational() Вы можете, например, сделать что-то вроде ниже.

 if (!detector.isOperational()) {
    Toast.makeText(this, "Face detector dependencies are not yet available.", Toast.LENGTH_SHORT).show();
}

Детектор автоматически начнет работать после завершения загрузки. Если в вашем приложении указано неправильное количество лиц, его повторный запуск должен дать правильное количество.

Лучшее решение — добавить зависимость функциональности видения в файл AndroidManifest.xml вашего проекта, чтобы указать установщику, что он должен загрузить зависимость при установке приложения.

Добавьте следующее в файл манифеста как дочерний элемент тега application

 <meta-data android:name="com.google.android.gms.vision.DEPENDENCIES" android:value="face"/>

Если вы ранее использовали Сервисы Google Play в своем приложении, вы можете добавить следующее в файл манифеста, в котором указывается номер версии Сервисов Google Play, который использует ваше приложение. Но начиная с версии 7.0 Сервисов Google Play, если вы используете Gradle, он включается автоматически .

 <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>

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

Создайте класс, который расширяет ViewCustomView Измените его, как показано (код основан на примере Google FaceView ).

 package com.echessa.facedetectiondemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;

import com.google.android.gms.vision.face.Face;

/**
 * Created by echessa on 8/31/15.
 */
public class CustomView extends View {

    private Bitmap mBitmap;
    private SparseArray<Face> mFaces;

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * Sets the bitmap background and the associated face detections.
     */
    void setContent(Bitmap bitmap, SparseArray<Face> faces) {
        mBitmap = bitmap;
        mFaces = faces;
        invalidate();
    }

    /**
     * Draws the bitmap background and the associated face landmarks.
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if ((mBitmap != null) && (mFaces != null)) {
            double scale = drawBitmap(canvas);
            drawFaceRectangle(canvas, scale);
        }
    }

    /**
     * Draws the bitmap background, scaled to the device size.  Returns the scale for future use in
     * positioning the facial landmark graphics.
     */
    private double drawBitmap(Canvas canvas) {
        double viewWidth = canvas.getWidth();
        double viewHeight = canvas.getHeight();
        double imageWidth = mBitmap.getWidth();
        double imageHeight = mBitmap.getHeight();
        double scale = Math.min(viewWidth / imageWidth, viewHeight / imageHeight);

        Rect destBounds = new Rect(0, 0, (int)(imageWidth * scale), (int)(imageHeight * scale));
        canvas.drawBitmap(mBitmap, null, destBounds, null);
        return scale;
    }

    /**
     * Draws a rectangle around each detected face
     */
    private void drawFaceRectangle(Canvas canvas, double scale) {
        Paint paint = new Paint();
        paint.setColor(Color.GREEN);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);

        for (int i = 0; i < mFaces.size(); ++i) {
            Face face = mFaces.valueAt(i);
            canvas.drawRect((float)(face.getPosition().x * scale),
                (float)(face.getPosition().y * scale),
                (float)((face.getPosition().x + face.getWidth()) * scale),
                (float)((face.getPosition().y + face.getHeight()) * scale),
                paint);
        }
    }

}

Вышеупомянутое переопределяет метод onDraw()drawFaceRectangle()

Измените activity_main.xml

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">

    <com.echessa.facedetectiondemo.CustomView
        android:id="@+id/customView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

В приведенном выше коде мы удалили предыдущий TextView

В MainActivityonCreate(Bundle)

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    InputStream stream = getResources().openRawResource(R.raw.image01);
    Bitmap bitmap = BitmapFactory.decodeStream(stream);

    FaceDetector detector = new FaceDetector.Builder(getApplicationContext())
            .setTrackingEnabled(false)
            .build();

    // Create a frame from the bitmap and run face detection on the frame.
    Frame frame = new Frame.Builder().setBitmap(bitmap).build();
    SparseArray<Face> faces = detector.detect(frame);

    CustomView overlay = (CustomView) findViewById(R.id.customView);
    overlay.setContent(bitmap, faces);

    detector.release();

}

Выше мы создаем экземпляр объекта CustomViewsetContent()

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

Прямоугольная разметка лица

Достопримечательности

Ориентир — это достопримечательность лица. Левый глаз, правый глаз и основание носа — все это примеры ориентиров. Face API предоставляет возможность находить ориентиры на обнаруженном лице.

В MainActivityFaceDetector

 FaceDetector detector = new FaceDetector.Builder(getApplicationContext())
            .setTrackingEnabled(false)
            .setLandmarkType(FaceDetector.ALL_LANDMARKS)
            .build();

Все еще в onCreate()

 InputStream stream = getResources().openRawResource(R.raw.image02);

Здесь мы используем image02.jpg вместо image01.jpg . Можно использовать одно и то же изображение, но, поскольку image01.jpg содержит больше лиц, будет много отметок, когда мы отметим все обнаруженные ориентиры. image02.jpg содержит одно лицо, и поэтому он будет лучше подходит для этого использования.

Добавьте следующую функцию в CustomView

 /**
 * Draws a small circle for each detected landmark, centered at the detected landmark position.
 *
 * Note that eye landmarks are defined to be the midpoint between the detected eye corner
 * positions, which tends to place the eye landmarks at the lower eyelid rather than at the
 * pupil position.
 */
private void drawFaceAnnotations(Canvas canvas, double scale) {
    Paint paint = new Paint();
    paint.setColor(Color.GREEN);
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(5);

    for (int i = 0; i < mFaces.size(); ++i) {
        Face face = mFaces.valueAt(i);
        for (Landmark landmark : face.getLandmarks()) {
            int cx = (int) (landmark.getPosition().x * scale);
            int cy = (int) (landmark.getPosition().y * scale);
            canvas.drawCircle(cx, cy, 10, paint);
        }
    }

}

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

Вызовите вышеуказанный метод в onDraw()drawFaceRectangle()

 drawFaceAnnotations(canvas, scale);

Запустите приложение, и вы должны увидеть различные ориентиры обнаруженного лица. Ориентиры отмечают глаза, нос, рот и щеки.

Изображение с Landmarkd

классификация

Классификация определяет наличие определенной черты лица. Например, лицо может быть классифицировано относительно того, являются ли его глаза открытыми или закрытыми. Другой пример — улыбающееся лицо или нет.

Чтобы определить характеристики лица, измените оператор, который создает FaceDetectorMainActivity

 FaceDetector detector = new FaceDetector.Builder(getApplicationContext())
            .setTrackingEnabled(false)
            .setLandmarkType(FaceDetector.ALL_LANDMARKS)
            .setClassificationType(FaceDetector.ALL_CLASSIFICATIONS)
            .build();

Вы можете удалить опцию setLandmarkType Это поможет в скорости обработки.

Все еще в onCreate()

 InputStream stream = getResources().openRawResource(R.raw.image03);

В CustomView

 /**
 * Detects characteristics of a face
 */
private void detectFaceCharacteristics(Canvas canvas, double scale) {
    Paint paint = new Paint();
    paint.setColor(Color.RED);
    paint.setStyle(Paint.Style.FILL);
    paint.setStrokeWidth(1);
    paint.setTextSize(25.0f);

    for (int i = 0; i < mFaces.size(); ++i) {
        Face face = mFaces.valueAt(i);
        float cx = (float)(face.getPosition().x * scale);
        float cy = (float) (face.getPosition().y * scale);
        canvas.drawText(String.valueOf(face.getIsSmilingProbability()), cx, cy + 10.0f, paint);
    }
}

Мы используем drawText() Мы используем face.getIsSmilingProbability()

Вызовите эту функцию в onDraw()drawFaceAnnotations()

 detectFaceCharacteristics(canvas, scale);

Запустите приложение, и вы должны увидеть вероятность улыбки разных лиц. Как видите, улыбающееся лицо в правом верхнем углу имеет наибольшую вероятность.

Улыбка Вероятность

Отслеживание лица

Отслеживание лица расширяет обнаружение лица на видео последовательности. Любое лицо, появляющееся в видео на любой промежуток времени, может быть отслежено. То есть лица, обнаруженные в последовательных видеокадрах, могут быть идентифицированы как одно и то же лицо. Обратите внимание, что это не форма распознавания лиц, этот механизм просто делает выводы на основе положения и движения лица (лиц) в видеопоследовательности. Мы не будем рассматривать это в этом руководстве, но вы можете прочитать официальные руководства для получения дополнительной информации .

Вывод

Мы рассмотрели некоторые функции Face API. Чтобы узнать больше об Mobile Vision API, вы можете найти код для примеров приложений, которые используют API здесь .